Files
DIY-Multiprotocol-TX-Module/docs/XBM-37_Analysis.md
2026-07-16 18:25:58 +02:00

28 KiB
Raw Blame History

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

  1. Capture File Inventory
  2. Key Protocol Parameters
  3. SPI Initialization Sequence
  4. Bind Sequence Deep Analysis (02a + 02b)
  5. Bind vs No-RX Comparison (01b vs 02b)
  6. Normal Data Packet Format
  7. Per-Channel Control Analysis (03b 24b)
  8. RF Timing Summary
  9. 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 ID belong to one CSN-low SPI transaction.
  • Files 03b24b 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 024), occurring over the first ~5 ms after the SPI bus becomes active (~150335 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 0x180x1B. 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 1424):

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 B0B6.)

4.3 TX ID

TX ID is 3 bytes embedded in B4B6 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:

  1. Completes the 400th transmission (CE pulse ~1,172 µs, then CE low).
  2. Waits ~16.6 ms (no SPI activity; firmware processing time).
  3. Writes the new TX_ADDR: W_TX_ADDR [91 05 05 E7 67] (SPI pid=1625 at t=1.175882 s).
  4. Immediately clears STATUS and starts normal data mode.
  5. 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 (B0B3)

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 (0x200xA0)

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 (0x820x83)

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 -> 0x08
    • B6 bit3 is asserted when RTH is active in this capture
  • Minor secondary variation while ON: B0 occasionally increments 0x7E -> 0x7F, producing checksum 0x16 -> 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 pressed
  • B5 = 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)
  • B5 remains 0x20; B6 remains 0x00

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)
  • B5 remains 0x20; B6 remains 0x00

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)
  • B4 remains 0x20; B6 remains 0x00

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)
  • B4 remains 0x20; B6 remains 0x00

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 bit7 toggles over trim baseline:
    • not pressed: B5 = 0x01
    • pressed: B5 = 0x81 (0x01 | 0x80)
  • B6 remains 0x00 throughout.

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 0x000x64 0x000xE1 different
Bind address E7 E7 E7 E7 67 E7 E7 E7 E7 67 (same)
Checksum method Sum B0B6 Sum B0B6 (same)
Bind checksum Sum B4B6 Sum B4B6 (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 46 = 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.