28 KiB
XBM-37 Quad SPI Capture Analysis
Manufacturer: T-Smart
Model: XBM-37 (toy quadcopter)
TX/RX RF Chip: SV7241A (QFN-20, NRF24L01+ clone / BK2425 derivative)
Protocol Basis: Closely related to FQ777 (same RF chip family, same bind address, same ssv ESB air encoding)
Capture Tool: Logic analyzer – digital (02a) and SPI-decoded (all "b" files)
Table of Contents
- Capture File Inventory
- Key Protocol Parameters
- SPI Initialization Sequence
- Bind Sequence – Deep Analysis (02a + 02b)
- Bind vs No-RX Comparison (01b vs 02b)
- Normal Data Packet Format
- Per-Channel Control Analysis (03b – 24b)
- RF Timing Summary
- MPM Implementation Notes
1. Capture File Inventory
All captures are located in Captures_XBM-37/. The "b" files are SPI-decoded exports
(columns: Time [s], Packet ID, MOSI, MISO). The "a" file is the raw digital export
(columns: Time[s], MOSI, MISO, SCK, CSN, CE, IRQ).
| File | Type | Description | SPI Transactions | TX Payloads | Duration |
|---|---|---|---|---|---|
01b-XBM-37_Quad_TX-PowerOn-NoRX.csv |
SPI | TX power-on, no RX present | 5,437 | 1,352 | 2.963 s |
02a-XBM-37_Quad_TX-PowerOn-withRX-Bind.csv |
Digital | TX power-on + bind with RX (all 6 lines) | 405,271 samples | 1,345 payloads | 4.380 s |
02b-XBM-37_Quad_TX-PowerOn-withRX-Bind.csv |
SPI | TX power-on + bind with RX | 5,409 | 1,345 | 2.949 s |
03b-XBM-37_Quad_Aileron-Center-Left-Center-Right-Center.csv |
SPI | Aileron stick: center → left → center → right → center | 6,039 | 1,509 | 3.127 s |
04b-XBM-37_Quad_Elevator-Center-Back-Center-Forward-Center.csv |
SPI | Elevator stick: center → back → center → fwd → center | 6,035 | 1,509 | 3.123 s |
05b-XBM-37_Quad_Throttle-Low-High-Low.csv |
SPI | Throttle: low → high → low | 6,034 | 1,509 | 3.122 s |
06b-XBM-37_Quad_Rudder-Center-Left-Center-Right-Center.csv |
SPI | Rudder (yaw): center → left → center → right → center | 6,034 | 1,509 | 3.122 s |
07b-XBM-37_Quad_RateModeSwitch-1-2-3.csv |
SPI | Rate/speed mode switch: 1 → 2 → 3 | 6,034 | 1,509 | 3.122 s |
08b-XBM-37_Quad_FlipSwitch-PushButton_Off-On.csv |
SPI | Flip trick button: off → on | 6,034 | 1,509 | 3.122 s |
09b-XBM-37_Quad_VideoSwitch-Off-On-Off-On.csv |
SPI | Video record toggle: off → on → off → on | 6,023 | 1,506 | 3.116 s |
10b-XBM-37_Quad_PictureSwitch-PushButton-3X.csv |
SPI | Photo/picture button: pressed 3 times | 6,019 | 1,505 | 3.113 s |
11b-XBM-37_Quad_HeadlessSwitch-PushButton-Off-On.csv |
SPI | Headless mode button: off → on | 6,022 | 1,506 | 3.116 s |
12b-XBM-37_Quad_ReturnToHomeSwitch-PushButton-Off-On.csv |
SPI | RTH button: off → on | 6,034 | 1,508 | 3.127 s |
13b-XBM-37_Quad_LED-LightsSwitch-PushButton-On-Off.csv |
SPI | LED lights toggle: on → off | 6,019 | 1,505 | 3.113 s |
14b-XBM-37_Quad_OK-Switch-PushButton-Off-On.csv |
SPI | OK button: off → on | 6,027 | 1,507 | 3.120 s |
20b-XBM-37_Quad_Elevator-Trim-Center-Forward-Max_32-Clicks.csv |
SPI | Elevator trim: center → forward max (32 clicks) | 38,683 | 9,671 | 20.004 s |
21b-XBM-37_Quad_Elevator-Trim-Center-Back-Max_31-Clicks.csv |
SPI | Elevator trim: center → back max (31 clicks) | 33,215 | 8,304 | 17.191 s |
22b-XBM-37_Quad_Aileron-Trim-Center-Left-Max_32-Clicks.csv |
SPI | Aileron trim: center → left max (32 clicks) | 32,315 | 8,079 | 16.730 s |
23b-XBM-37_Quad_Aileron-Trim-Center-Right-Max_31-Clicks.csv |
SPI | Aileron trim: center → right max (31 clicks) | 28,495 | 7,124 | 14.756 s |
24b-XBM-37_Quad_Ail-Trim-Forward-Max_Ele-Trim-Right-Max_Push-OK-Button-2X.csv |
SPI | Elevator trim at forward max, aileron trim at right max, OK button pressed 2× | 29,275 | 7,319 | 15.176 s |
Notes
- The "b" SPI files each contain one SPI byte per row. Multiple rows sharing the same
Packet IDbelong to one CSN-low SPI transaction. - Files 03b–24b were all captured in normal (post-bind) flight mode. The TX had previously completed a successful bind.
- File 01b and 02b are functionally identical captures (confirmed by direct payload comparison) except for a tiny time offset (~7 µs between corresponding packets). This confirms the TX transmits identically whether or not an RX is present; see Section 5.
2. Key Protocol Parameters
| Parameter | Value |
|---|---|
| RF transceiver chip | SV7241A (QFN-20, NRF24L01+ / BK2425 derivative) |
| RF frequency band | 2.4 GHz ISM |
| Air data rate (SV7241A) | 2 Mbps (RF_SETUP = 0x26, bit5 = RF_DR = 1) |
| Air data rate (nRF24L01+ equivalent) | 250 kbps via ssv_pack_dpl() method |
| Packet size (raw, pre-pack) | 8 bytes |
| Packet size (after ssv_pack_dpl) | 12 bytes |
| Enhanced ShockBurst | Yes (dynamic payload length, no auto-ACK) |
| SPI clock frequency | ~143 kHz (7 µs period) |
| Bind packet count | 400 |
| Bind address (broadcast) | E7 E7 E7 E7 67 |
| Data address format | [TX_ID₀ TX_ID₁ TX_ID₂ E7 67] |
| Bind channel (first packet) | 0x00 (universal) |
| Hop channels (bind + data) | 0x49, 0x34, 0x26, 0x07 (4 channels) |
| Packet period (steady state) | ~2,070 µs (~2.07 ms) |
| CE high pulse width | ~1,172 µs |
| CE low gap (inter-packet) | ~896 µs |
| TX-only mode | Yes — EN_RXADDR = 0x00; TX never listens |
3. SPI Initialization Sequence
The sequence below is from 02b (PIDs 0–24), occurring over the first ~5 ms after the SPI
bus becomes active (~150–335 ms after TX power-on due to startup delays).
3.1 SV7241A Private Bank Registers
The SV7241A (like BK2425) has bank-switched extended registers at addresses 0x18–0x1B. Register 0x1F selects the bank. These registers hold RF/analog calibration constants specific to the chip and do not exist on nRF24L01+ — they are ignored when implementing with MPM.
| PID | Register | Data Written | Purpose |
|---|---|---|---|
| 0 | 0x1F | 00 |
Select bank 0 |
| 1 | 0x1B | 10 E1 D3 3D |
Bank 0 – RF calibration reg 1 |
| 2 | 0x19 | 06 AA A2 DB |
Bank 0 – RF calibration reg 2 |
| 3 | 0x1F | 01 |
Select bank 1 |
| 4 | 0x19 | 77 48 9A E8 |
Bank 1 – internal reg |
| 5 | 0x1B | 76 87 CA 01 |
Bank 1 – internal reg |
| 6 | 0x1F | 02 |
Select bank 2 |
| 7 | 0x1B | A0 00 18 A0 |
Bank 2 – internal reg |
| 8 | 0x1F | 04 |
Select bank 4 |
| 9 | 0x18 | 01 00 F0 00 |
Bank 4 – internal reg |
| 10 | 0x1F | 05 |
Select bank 5 |
| 11 | 0x18 | 84 03 2A 03 |
Bank 5 – internal reg |
| 12 | 0x19 | 90 BF 00 00 |
Bank 5 – internal reg |
| 13 | 0x1A | A0 0F 00 00 |
Bank 5 – internal reg |
3.2 Standard NRF24L01-Compatible Registers
Written immediately after the private registers (PIDs 14–24):
| PID | Register | Written Value | Decoded Meaning |
|---|---|---|---|
| 14 | CONFIG (0x00) | 0x0C |
EN_CRC=1, CRCO=1 (2-byte CRC), PWR_UP=0, PRIM_RX=0 (TX) |
| 15 | CONFIG (0x00) | read back 0x0C |
Verify CONFIG |
| 16 | TX_ADDR (0x10) | E7 E7 E7 E7 67 |
Bind broadcast address |
| 17 | RX_ADDR_P0 (0x0A) | E7 E7 E7 E7 67 |
Matches TX_ADDR for bind |
| 18 | EN_AA (0x01) | 0x00 |
Auto-ACK disabled on all pipes |
| 19 | EN_RXADDR (0x02) | 0x00 |
All RX pipes disabled – TX only |
| 20 | RF_CH (0x05) | 0x49 |
Initial RF channel (73 MHz offset) |
| 21 | FEATURE (0x1D) | 0x04 |
EN_DPL = 1 (dynamic payload length) |
| 22 | DYNPD (0x1C) | 0x01 |
DPL enabled on pipe 0 |
| 23 | RF_SETUP (0x06) | 0x26 |
SV7241A: 2 Mbps, max power |
| 24 | CONFIG (0x00) | 0x0E |
PWR_UP=1 → TX powered up |
RF_SETUP = 0x26 mapping:
| Chip | Bit 5 meaning | Value 0x26 decodes as |
|---|---|---|
| SV7241A | RF_DR (0=1Mbps, 1=2Mbps) | 2 Mbps, max power |
| nRF24L01+ | RF_DR_LOW (0=normal, 1=250kbps) | 250 kbps, max power |
→ On nRF24L01+, writing 0x26 would set 250 kbps (not 2 Mbps). For MPM emulation the
correct approach (same as FQ777) is NRF24L01_SetBitrate(NRF24L01_BR_250K) combined with
ssv_pack_dpl() to produce a compatible air packet.
Startup timing:
The TX power-on to first SPI transaction takes ~150 ms (CE is held high from t = –72 ms until t = 0, then the ~150 ms wait elapses before SPI activity at t ≈ +181 ms in the 02b file). The entire init register sequence completes in ~6 ms.
4. Bind Sequence – Deep Analysis (02a + 02b)
4.1 Overview
After the ~150 ms startup wait, the TX sends exactly 400 bind packets before switching to normal data mode.
| Property | Value |
|---|---|
| Total bind packets | 400 |
| Bind address | E7 E7 E7 E7 67 (fixed broadcast) |
| First bind packet channel | 0x00 (universal channel) |
| Remaining 399 bind packet channels | Cycling: 0x49 → 0x34 → 0x26 → 0x07 → 0x49 → … |
| Bind packet payload | 20 14 07 03 TX_ID₀ TX_ID₁ TX_ID₂ CKSUM |
| Bind packet content | Identical across all 400 transmissions |
The first bind packet is sent on RF channel 0x00, ensuring a newly powered-on RX (sitting on its default channel) can hear the bind announcement regardless of any prior state. Subsequent bind packets cycle through the four data-hopping channels.
4.2 Bind Packet Structure
Byte 0 1 2 3 4 5 6 7
[20 14 07 03 TX_ID₀ TX_ID₁ TX_ID₂ CKSUM]
| Byte | Value (this TX) | Description |
|---|---|---|
| B0 | 0x20 |
Bind identifier byte (constant — marks this as a bind packet) |
| B1 | 0x14 |
Protocol variant constant (XBM-37 specific; FQ777 uses 0x15) |
| B2 | 0x07 |
Protocol variant constant (XBM-37 specific; FQ777 uses 0x05) |
| B3 | 0x03 |
Protocol variant constant (XBM-37 specific; FQ777 uses 0x06) |
| B4 | 0x91 |
TX_ID byte 0 — unique per TX unit |
| B5 | 0x05 |
TX_ID byte 1 — unique per TX unit |
| B6 | 0x05 |
TX_ID byte 2 — unique per TX unit |
| B7 | 0x9B |
Checksum = (TX_ID₀ + TX_ID₁ + TX_ID₂) & 0xFF = (0x91+0x05+0x05) & 0xFF |
Checksum formula for bind packet: B7 = (B4 + B5 + B6) & 0xFF
(This differs from the data packet checksum which sums B0–B6.)
4.3 TX ID
TX ID is 3 bytes embedded in B4–B6 of the bind packet. This TX unit has:
TX_ID = [0x91, 0x05, 0x05]
After bind, the TX_ADDR is set to: [TX_ID₀ TX_ID₁ TX_ID₂ 0xE7 0x67]
= [91 05 05 E7 67]
The RX extracts TX_ID from the bind packet and uses it to construct the matching address for normal data reception.
4.4 Bind Channel Sequence (from 02b SPI decoded)
Packet #1: ch=0x00 [20 14 07 03 91 05 05 9B] ← universal announce
Packet #2: ch=0x49 [20 14 07 03 91 05 05 9B]
Packet #3: ch=0x34 [20 14 07 03 91 05 05 9B]
Packet #4: ch=0x26 [20 14 07 03 91 05 05 9B]
Packet #5: ch=0x07 [20 14 07 03 91 05 05 9B]
Packet #6: ch=0x49 [20 14 07 03 91 05 05 9B]
...continues cycling 0x49→0x34→0x26→0x07...
Packet #400: ch=0x26 [20 14 07 03 91 05 05 9B] ← last bind
Channel usage across all 400 bind packets:
| Channel | Count | Notes |
|---|---|---|
| 0x00 | 1 | First packet only |
| 0x49 (73 MHz) | 100 | Regular cycle |
| 0x34 (52 MHz) | 100 | Regular cycle |
| 0x26 (38 MHz) | 100 | Regular cycle (includes last bind packet) |
| 0x07 (7 MHz) | 99 | Regular cycle |
4.5 Bind-to-Normal Transition
After the 400th bind packet the TX:
- Completes the 400th transmission (CE pulse ~1,172 µs, then CE low).
- Waits ~16.6 ms (no SPI activity; firmware processing time).
- Writes the new TX_ADDR:
W_TX_ADDR [91 05 05 E7 67](SPI pid=1625 at t=1.175882 s). - Immediately clears STATUS and starts normal data mode.
- First normal packet: ch=0x07 (continuation of the hop sequence from where bind ended).
Last bind (400th): t=1.159258 s, ch=0x26
TX_ADDR change: t=1.175882 s (+16.6 ms gap)
First data packet: t=1.176619 s, ch=0x07 [E1 70 70 70 20 20 00 71]
4.6 IRQ / STATUS Analysis (from 02a Digital Capture)
The IRQ line (active-low) is asserted by the SV7241A for every successfully transmitted packet (TX_DS interrupt):
| Observation | Value |
|---|---|
| Total IRQ assertions | 1,340 (matching 1,340 normal CE pulses) |
| IRQ pulse duration (typical) | ~600 µs |
| IRQ source | TX_DS only (TX complete) |
| RX_DR interrupts | Zero — no data ever received |
The SPI confirms there are no R_RX_PAYLOAD (0x61) commands in either 01b or 02b.
Combined with EN_RXADDR = 0x00 (all RX pipes disabled), it is impossible for the TX to
receive data from the RX. This is confirmed by the STATUS register never showing bit6
(RX_DR) = 1.
One extended IRQ assertion of 15.9 ms occurs at t=1.160476 s (immediately after the 400th
bind packet). This is not an RX event — it is simply the TX_DS interrupt remaining uncleared
while the firmware processes the bind-completion event and updates TX_ADDR. The IRQ is
cleared when W_STATUS [70] is written at t=1.176254 s (pid=1626).
4.7 CE Pulse Timing (from 02a)
| Measurement | Value |
|---|---|
| Normal CE pulse duration | min 1,165 µs, max 1,178 µs, avg 1,172 µs |
| CE inter-pulse gap (end to start) | ~896 µs |
| Total cycle (CE high + gap) | ~2,068 µs |
| Pulse-to-pulse interval (high to high) | min 2.063 ms, max 20.686 ms, avg 2.084 ms |
The 20.686 ms outlier corresponds to the bind-to-normal transition pause (~16.6 ms).
5. Bind vs No-RX Comparison (01b vs 02b)
Finding: The TX transmits identically whether or not an RX is present.
| Metric | 01b (No RX) | 02b (With RX) |
|---|---|---|
| Total TX payloads | 1,352 | 1,345 |
| Bind packets | 400 | 400 |
| Bind packet content | 20 14 07 03 91 05 05 9B |
20 14 07 03 91 05 05 9B (identical) |
| TX_ADDR change time | t=1.175899 s | t=1.175882 s |
| TX_ADDR after bind | [91 05 05 E7 67] |
[91 05 05 E7 67] (identical) |
| Normal data payload (idle) | E1 70 70 70 20 20 00 71 |
E1 70 70 70 20 20 00 71 (identical) |
| Time offset between captures | — | ~7 µs per packet |
The TX performs an automatic timed bind sequence (400 packets) and then switches to data mode unconditionally, regardless of RX acknowledgment. The TX never knows whether the RX accepted the bind. Bind is one-sided: the RX passively listens for the bind packet on ch=0x00 (or the cycling channels), extracts the TX_ID and hop channels, and then follows the TX's normal data transmissions.
6. Normal Data Packet Format
6.1 Packet Structure (8 bytes)
Byte 0 1 2 3 4 5 6 7
[Throttle Rudder Aileron Elevator Flags1 Flags2 Flags3 CKSUM]
Checksum (B7): B7 = (B0 + B1 + B2 + B3 + B4 + B5 + B6) & 0xFF
All 8-byte checksum values have been verified against this formula across all capture files.
6.2 Stick Channels (B0–B3)
| Byte | Channel | Min | Center | Max | Notes |
|---|---|---|---|---|---|
| B0 | Throttle | 0xE1 |
0x70 |
0x00 |
Non-return throttle |
| B1 | Rudder (Yaw) | 0x00 |
0x70 |
0xE1 |
|
| B2 | Aileron (Roll) | 0xE1 |
0x70 |
0x00 |
|
| B3 | Elevator (Pitch) | 0xE1 |
0x70 |
0x00 |
All analog channels use the same range: 0x00 – 0xE1 (0 – 225 decimal) with center at 0x70 (112 decimal).
- Throttle, Aileron, and Elevator use reversed state (0xE1...0x70...0x00).
6.3 Flags Byte 1 (B4)
| Value | Meaning | Description |
|---|---|---|
0x20 |
Trim Elevator Center | Resets at TX start |
0x21 up to 0x40 |
Trim elevator forward | 32 Clicks to max |
0x1F downto 0x01 |
Trim elevator back | 31 Clicks to max |
6.4 Flags Byte 2 (B5)
| Value | Meaning | Description |
|---|---|---|
0x20 |
Trim Aileron Center | Resets at TX start |
0x21 up to 0x40 |
Trim aileron left | 32 Clicks to max |
0x1F down to 0x01 |
Trim aileron right | 31 Clicks to max |
0x80 |
OK button | OK pressed (0x20 → 0xA0) |
B5 bit7 is an OR mask on top of trim value (B5_with_OK = B5_trim | 0x80), e.g.
center 0x20 -> 0xA0, right-max 0x01 -> 0x81.
6.5 Flags Byte 3 (B6)
| Bits | Mask | Name | Description |
|---|---|---|---|
| [1:0] | 0x03 |
Rate mode | 0x00=rate 1 (slow), 0x01=rate 2, 0x02=rate 3 (fast) |
| 2 | 0x04 |
LED off | 0=LED lights ON, 1=LED lights OFF |
| 3 | 0x08 |
RTH | 1=return-to-home active |
| 4 | 0x10 |
Headless | 1=headless mode active |
| 5 | 0x20 |
Video | 1=video recording active |
| 6 | 0x40 |
Picture | 1=photo capture triggered |
| 7 | 0x80 |
Flip | 1=3D flip command |
Normal state: B6 = 0x00 (rate 1, LED on, no special modes)
6.6 Return-to-Home (RTH)
Updated analysis of file 12b (RTH OFF in first half, ON in second half):
- RTH-OFF payload is steady at
7E 70 70 70 20 20 00 0E. - After the OFF→ON transition, the payload changes to
7E 70 70 70 20 20 08 16. - The persistent functional change in this capture is
B6: 0x00 -> 0x08(bit3 asserted when RTH is ON).
In this export, RTH is represented in the steady 8-byte payload by B6 bit3.
6.7 Example Payloads
| Payload | Meaning |
|---|---|
E1 70 70 70 20 20 00 71 |
Throttle low 0xE1, all sticks center 0x70, LED on, no special modes 0x00 |
82 70 70 70 20 20 00 12 |
Throttle ~mid 0x82, all sticks center, LED on, no special modes |
00 70 70 70 20 20 00 90 |
Throttle max 0x00, all sticks center, LED on, no special modes |
E1 70 00 70 20 20 00 01 |
Throttle low, aileron full right 0x00, LED on, no special modes |
E1 70 E1 70 20 20 00 E2 |
Throttle low, aileron full left 0xE1, LED on, no special modes |
E1 70 70 00 20 20 00 01 |
Throttle low, elevator full back 0x00,LED on, no special modes |
82 70 70 70 20 20 80 92 |
Throttle ~mid, all sticks center, flip command active 0x80 |
82 70 70 70 20 20 10 22 |
Throttle ~mid, all sticks center, headless mode active 0x10 |
82 70 70 70 20 20 01 13 |
Throttle ~mid, all sticks center, rate mode 2 0x01 |
82 70 70 70 20 20 02 14 |
Throttle ~mid, all sticks center, rate mode 3 0x02 |
00 70 70 70 20 A0 00 F1 |
Throttle max, all sticks center, OK button pressed 0xA0 |
00 70 70 70 20 20 04 75 |
Throttle max, all sticks center, LED lights OFF 0x04 |
7. Per-Channel Control Analysis (03b – 24b)
All control captures were taken in post-bind normal mode. Hopping channels confirmed active:
0x07, 0x49, 0x34, 0x26 (cyclic). Average packet interval: ~2.07 ms across all files.
03b – Aileron (Roll)
- Affected byte: B2
- Range observed:
0xE1(full left) →0x70(center) →0x00(full right)
04b – Elevator (Pitch)
- Affected byte: B3
- Range observed:
0x00(full back) →0x70(center) →0xE1(full forward)
05b – Throttle
- Affected byte: B0
- Range observed:
0xE1(low minimum) ↔0x70(center) ↔0x00(full maximum) - Non-return throttle (stick does not spring back to center)
06b – Rudder (Yaw)
- Affected byte: B1
- Range observed:
0x00(full left) →0x70(center) →0xE1(full right) - Note: B0 (throttle) also varies in this capture because left stick controls both throttle (up/down) and rudder (left/right) Mode 2 transmitter
07b – Rate Mode Switch
- Affected byte: B6 bits[1:0]
B6 = 0x00: Rate 1 (slowest/beginner)B6 = 0x01: Rate 2 (intermediate)B6 = 0x02: Rate 3 (fastest/expert)
08b – Flip Switch
- Affected byte: B6 bit7
B6 = 0x00: No flip;B6 = 0x80: 3D flip command active- While flip is held: throttle (B0) increments slightly (
0x82→0x83)
09b – Video Switch
- Affected byte: B6 bit5
B6 = 0x00: Video off;B6 = 0x20: Video recording active- Toggle on/off: transitions between these two states
10b – Picture Switch (3×)
- Affected byte: B6 bit6
B6 = 0x00: Idle;B6 = 0x40: Photo capture triggered (momentary)
11b – Headless Switch
- Affected byte: B6 bit4
B6 = 0x00: Headless off;B6 = 0x10: Headless mode active
12b – Return to Home Switch
- Capture split tested: first half RTH OFF, second half RTH ON.
- RTH OFF payload:
7E 70 70 70 20 20 00 0E - RTH ON payload:
7E 70 70 70 20 20 08 16 - Primary byte change:
B6: 0x00 -> 0x08B6 bit3is asserted when RTH is active in this capture
- Minor secondary variation while ON:
B0occasionally increments0x7E -> 0x7F, producing checksum0x16 -> 0x17 - All other payload bytes remain unchanged across the OFF→ON transition in this file.
13b – LED Lights Switch
- Affected byte: B6 bit2
B6 = 0x00: LED lights ON (default)B6 = 0x04: LED lights OFF- Note: Inverted logic — bit2=1 means OFF
14b – OK Switch
- Affected byte: B5 bit7
B5 = 0x20: OK button not pressedB5 = 0xA0: OK button pressed (0x20 | 0x80)
20b – Elevator Trim Center → Forward Max (32 clicks)
- Affected byte: B4 (elevator trim)
- Observed progression:
0x20 -> ... -> 0x40 - Endpoint in this capture:
B4 = 0x40(forward max) B5remains0x20;B6remains0x00
21b – Elevator Trim Center → Back Max (31 clicks)
- Affected byte: B4 (elevator trim)
- Observed progression:
0x20 -> ... -> 0x01 - Endpoint in this capture:
B4 = 0x01(back max) B5remains0x20;B6remains0x00
22b – Aileron Trim Center → Left Max (32 clicks)
- Affected byte: B5 (aileron trim)
- Observed progression:
0x20 -> ... -> 0x40 - Endpoint in this capture:
B5 = 0x40(left max) B4remains0x20;B6remains0x00
23b – Aileron Trim Center → Right Max (31 clicks)
- Affected byte: B5 (aileron trim)
- Observed progression:
0x20 -> ... -> 0x01 - Endpoint in this capture:
B5 = 0x01(right max) B4remains0x20;B6remains0x00
24b – Trim Endpoints + OK Button (2 presses)
- Fixed trim baseline in this capture:
B4 = 0x40(elevator forward max)B5 trim = 0x01(aileron right max)
- OK effect:
B5 bit7toggles over trim baseline:- not pressed:
B5 = 0x01 - pressed:
B5 = 0x81(0x01 | 0x80)
- not pressed:
B6remains0x00throughout.
8. RF Timing Summary
8.1 Per-Packet SPI Cycle (02b decoded)
Each packet transmission consists of 4 SPI transactions:
1. W_STATUS [27] = 0x70 → Clear TX_DS / MAX_RT / RX_DR interrupt flags
2. FLUSH_TX [E1] → Empty TX FIFO
3. W_RF_CH [25] = <ch> → Set hop channel
4. W_TX_PAYLOAD [A0] = 8B → Write 8-byte payload to TX FIFO
[CE pulse ~1,172 µs to transmit]
[~896 µs gap before next cycle]
8.2 Bind Phase Timing
| Event | Timestamp (02b) |
|---|---|
| Init register writes complete | t = 0.186 s |
| Startup wait completes | t = 0.335 s (~150 ms) |
| First bind packet (ch=0x00) | t = 0.335838 s |
| 400th (last) bind packet (ch=0x26) | t = 1.159258 s |
| TX_ADDR change to data address | t = 1.175882 s (+16.6 ms) |
| First normal data packet | t = 1.176619 s |
Total bind phase duration: ~0.824 s (150 ms wait + ~674 ms for 400 packets at 2.07 ms each)
8.3 Normal Data Phase Timing
| Event | Interval |
|---|---|
| Packet 1 → 2 (startup) | 0.918 ms |
| Packet 2 → 3 (startup) | 0.916 ms |
| Packet 3 → 4 (startup) | 1.491 ms |
| Steady state (4+) | ~2.070 ms per packet |
The first 3 packets after bind-to-data transition have shorter intervals, then settle into the steady 2.07 ms period.
8.4 STATUS Byte Values Observed
| STATUS | Meaning | When seen |
|---|---|---|
0x0E |
TX FIFO not full, RX FIFO empty, all interrupts clear | Normal/idle |
0x2E |
TX_DS = 1 (TX complete interrupt), TX FIFO not full | After each TX packet |
RX_DR (bit6) is never set in any STATUS byte — confirming no data is ever received.
9. MPM Implementation Notes
9.1 Comparison with FQ777
The XBM-37 protocol is closely related to FQ777 but has the following differences:
| Property | FQ777 | XBM-37 | Comment |
|---|---|---|---|
| Bind count | 1,000 | 400 | works with either |
| Bind packet B1 | 0x15 |
0x14 |
different |
| Bind packet B2 | 0x05 |
0x07 |
different |
| Bind packet B3 | 0x06 |
0x03 |
different |
| Hop channels | 4D 43 27 07 |
49 34 26 07 |
different |
| Data range | 0x00–0x64 | 0x00–0xE1 | different |
| Bind address | E7 E7 E7 E7 67 |
E7 E7 E7 E7 67 |
(same) |
| Checksum method | Sum B0–B6 | Sum B0–B6 | (same) |
| Bind checksum | Sum B4–B6 | Sum B4–B6 | (same) |
| ssv_pack_dpl encoding | Yes | Yes | (same) |
| Air bitrate (nRF24L01+) | 250 kbps | 250 kbps | (same) |
9.2 Required Implementation Constants
static const uint8_t XBM37_bind_addr[] = {0xE7, 0xE7, 0xE7, 0xE7, 0x67};
static const uint8_t XBM37_hop_channels[] = {0x49, 0x34, 0x26, 0x07};
9.3 Bind Packet Builder
// Bind packet (bytes 4–6 = TX ID; B7 = checksum of B4+B5+B6)
packet[0] = 0x20;
packet[1] = 0x14;
packet[2] = 0x07;
packet[3] = 0x03;
packet[4] = rx_tx_addr[0]; // TX_ID byte 0
packet[5] = rx_tx_addr[1]; // TX_ID byte 1
packet[6] = rx_tx_addr[2]; // TX_ID byte 2
packet[7] = packet[4] + packet[5] + packet[6];
9.4 Data Packet Builder
packet[0] = convert_channel_16b_limit(THROTTLE, 0xE1, 0x00);
packet[1] = convert_channel_16b_limit(RUDDER, 0x00, 0xE1);
packet[2] = convert_channel_16b_limit(AILERON, 0xE1, 0x00);
packet[3] = convert_channel_16b_limit(ELEVATOR, 0xE1, 0x00);
packet[4] = ((convert_channel_8b(CH13) * 63) / 255) + 1; // ele trim (01..20..40)
packet[5] = 64 - (((uint32_t)convert_channel_8b(CH14) * 63 + 127) / 255); // ail trim (40..20..01)
packet[5] |= GET_FLAG(OK_SW, 0x80); // OK button
packet[6] = (rate_mode & 0x03) // bits[1:0] = rate (0/1/2)
| GET_FLAG(LED_SW, 0x04) // bit2=1 = LED off
| GET_FLAG(RTH_SW, 0x08) // bit3 = RTH
| GET_FLAG(HEADLESS_SW, 0x10) // bit4 = headless
| GET_FLAG(VIDEO_SW, 0x20) // bit5 = video
| GET_FLAG(PHOTO_SW, 0x40) // bit6 = picture
| GET_FLAG(FLIP_SW, 0x80); // bit7 = flip
packet[7] = 0;
for (uint8_t i = 0; i < 7; i++) packet[7] += packet[i]; // checksum
9.5 Address Management
// TX init:
rx_tx_addr[2] = 0x00; // original hardcoded value now changes for model match capability (see below)
rx_tx_addr[3] = 0xE7;
rx_tx_addr[4] = 0x67;
// rx_tx_addr[0] and [1] are random/unique to the TX
// rx_tx_addr[2] now varies by changing receiver number (0-63) for model match
// Bind address (used during bind):
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, XBM37_bind_addr, 5);
// After 400th bind packet, switch to data address:
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
9.6 RF Init
void XBM37_RF_init() {
NRF24L01_Initialize();
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, XBM37_bind_addr, 5);
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // no auto-ACK
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x00); // no RX pipes
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x04); // EN_DPL
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x01); // DPL pipe 0
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250 kbps on nRF24L01+
}
9.7 Air Encoding
Because both the XBM-37 TX and RX use SV7241A (a BK2425 derivative), the air packet format is
the SV7241A Enhanced ShockBurst encoding. When implementing with nRF24L01+, the same
ssv_pack_dpl() function used in the FQ777 protocol must be applied to convert the raw 8-byte
payload into the 12-byte packed representation that nRF24L01+ transmits at 250 kbps to produce
a compatible on-air signal.
Analysis completed using Python scripts against the raw CSV captures. All packet checksums, channel sequences, bind counts, and register values verified programmatically.