Compare commits

...

188 Commits

Author SHA1 Message Date
pascallanger
1772147e5b Update FQ777_nrf24l01.ino 2026-07-16 20:18:54 +02:00
pascallanger
865f6c1597 FQ777/XBM37: new subprotocol 2026-07-16 18:25:58 +02:00
pascallanger
095acb0975 FX/A570: new subprotocol for VTOL QIDI-570 2026-07-16 17:51:34 +02:00
MRC3742
fc8730976c FX: Add telemetry delay for warnings
* telemetry delay for FX protocol warnings

* Update Protocols_Details.md

---------

Co-authored-by: dellclient <dellclient>
2026-07-16 10:11:51 +02:00
pascallanger
699e796a01 FX/BM26: new subprotocol
Missing files
2026-07-14 22:30:14 +02:00
pascallanger
93bade15d0 FX/BM26: new subprotocol 2026-07-14 22:29:07 +02:00
pascallanger
80d08c25e0 Update Protocols_Details.md 2026-07-14 19:52:07 +02:00
pascallanger
debb17a8fc Update Protocols_Details.md 2026-07-14 19:50:43 +02:00
pascallanger
174efa332b XK2/X4: Stunt flag for the Sky Viper Vector 2026-07-14 19:46:38 +02:00
pascallanger
e5f9f8c840 Hontai/XK170: Optical flag
CH11 enable/disable flow sensor for K270
2026-07-14 19:26:55 +02:00
pascallanger
a23716269b Merge branch 'master' of https://github.com/pascallanger/DIY-Multiprotocol-TX-Module 2026-07-14 18:55:06 +02:00
MRC3742
bfef3364fc Fix for DSM Clone
* fix for DSM clone not working
2026-07-14 18:54:36 +02:00
pascallanger
d62f2d0555 Update Protocols_Details.md 2026-07-14 18:52:32 +02:00
pascallanger
bb59582271 Update XN297Dump_nrf24l01.ino 2026-07-14 18:46:54 +02:00
pascallanger
c6c6fa442b Update WPL_nrf24l01.ino 2026-07-14 18:46:40 +02:00
pascallanger
d95012c30b TRAXXAS: attempt to describe TQi 2026-07-14 18:46:09 +02:00
pascallanger
f71768df39 E129 saving few bytes 2026-07-14 18:44:41 +02:00
pascallanger
7bde06ff50 AFHDS2A: Fix for CROSSOVER-RX
CROSSOVER-RX are sending end of telemetry data despite having sensors after...
2026-07-14 18:44:05 +02:00
pascallanger
51a01d1393 KF606 LED
Add LED channel for KF606 boards
2026-07-14 18:42:14 +02:00
pascallanger
bf38415420 Update SGF22_nrf24l01.ino
Fix tx and freq for none CX10
2025-09-26 17:41:06 +02:00
pascallanger
5ce6c6f544 Update Protocols_Details.md 2025-07-28 11:27:57 +02:00
pascallanger
35f284763a SGF22/CX10 new subprotocol
Only 2 IDs
2025-07-28 11:20:57 +02:00
pascallanger
e35c4d3ce8 WPL 1 ID 2025-07-28 01:06:12 +02:00
pascallanger
ba59605b16 Update WPL_nrf24l01.ino 2025-07-27 19:15:55 +02:00
pascallanger
b88f4d40f4 WPL 5 2025-07-27 18:24:24 +02:00
pascallanger
5ade6bc312 WPL 4th 2025-07-27 16:51:13 +02:00
pascallanger
9e099bc486 WPL 3rd try 2025-07-27 16:42:00 +02:00
pascallanger
48b7430e75 WPL try 2 2025-07-27 16:01:52 +02:00
pascallanger
6385e822c2 MT99xx/SU35: removed autobind 2025-07-27 12:25:14 +02:00
pascallanger
66f8906273 WPL new protocol
Only 1 ID
2025-07-27 12:24:03 +02:00
Jon Sturm
dcbc557bf7 Enable Telemetry on DumbRC receivers (#1111)
Tested with X6FGv1.1, X6FP, X6DC(G)v1.1

I also tested with multiple X6F receivers and some gave RSSI Telem and some gave nothing.
2 of them had the same PCB as the X6FP and neither gave telemetry.
I had a third with the same PCB with the X6FP which did give RSSI
and of my two with an older PCB one gives RSSI telem and the other does not.

Of the receivers I tried only one set packet_in[6] to match the TX packet[1] and it was the
oldest receiver of the ones I own.
2025-05-10 11:51:48 +02:00
pascallanger
e556f5cc40 SGF22 telem improvement? 2025-04-16 07:53:47 +02:00
pascallanger
9ef2415178 Update Protocols_Details.md (#1101) 2025-04-03 18:39:02 +02:00
pascallanger
bc1a809325 Update Protocols_Details.md 2025-04-03 18:36:31 +02:00
pascallanger
082e96e381 DSMR timing 2025-04-02 18:02:44 +02:00
pascallanger
abd26ee113 Update SGF22_nrf24l01.ino 2025-04-02 14:24:18 +02:00
pascallanger
98518b3c92 Update Protocols_Details.md 2025-04-02 14:11:58 +02:00
pascallanger
d19de99172 SGF22 telemetry 2025-04-02 14:10:54 +02:00
pascallanger
13024e3816 Update SGF22_nrf24l01.ino 2025-04-01 23:01:18 +02:00
pascallanger
90fb2d745f Update AFHDS2A_a7105.ino 2025-03-31 22:21:33 +02:00
pascallanger
fa13b67f3a Update Validate.h 2025-03-31 14:56:46 +02:00
pascallanger
10be906bec Update Validate.h 2025-03-31 14:43:42 +02:00
pascallanger
f538884d48 XK2 low batt telemetry 2025-03-28 19:13:13 +01:00
pascallanger
d3e5acf704 Update Protocols_Details.md 2025-03-28 10:33:39 +01:00
pascallanger
2ac4745c62 FX/QF012 telemetry 2025-03-21 21:48:29 +01:00
pascallanger
889e01c7c6 MouldKg update 2025-03-20 17:28:33 +01:00
pascallanger
db5f1f8899 Added AFHDS2A BS4/BS6 support 2025-03-20 14:49:34 +01:00
pascallanger
d091c57e10 MouldKg swap CH5 and CH6 2025-03-14 10:34:15 +01:00
pascallanger
0fcb4b7f1a FX telemetry 2025-03-13 20:15:57 +01:00
pascallanger
432e2e08a4 FX small changes 2025-03-13 19:13:14 +01:00
pascallanger
77efe467ad MouldKg renamed subprotocols 2025-03-13 18:42:34 +01:00
pascallanger
b9c828c878 MouldKg multi 6 ports brick support 2025-03-13 18:29:56 +01:00
pascallanger
99cd4d34d4 MT99xx Trim channels 2025-03-12 17:20:54 +01:00
pascallanger
a9be2a8644 Update MouldKg_nrf24l01.ino 2025-03-12 14:05:53 +01:00
pascallanger
835facd2c3 XK2 Gyro off 2025-03-12 07:45:05 +01:00
pascallanger
9e20e47c5a FX/QF012 details 2 2025-03-12 07:34:47 +01:00
pascallanger
a916275f0d FX/QF012 details 2025-03-12 07:31:32 +01:00
rdba2k
9beade33de Add FX_QF012 subprotocol (#1091) 2025-03-12 07:17:08 +01:00
pascallanger
5ba70c3571 Update Protocols_Details.md 2025-03-10 17:40:46 +01:00
pascallanger
168aa89c57 New SFC protocol WL91x 2025-03-10 17:38:37 +01:00
pascallanger
fa055e991c ASF try 1 2025-02-27 16:56:58 +01:00
pascallanger
5437d88642 DSMR SR6200 test 1 2025-02-26 21:12:34 +01:00
pascallanger
d800f2c333 SLT2 final 2025-02-26 11:31:26 +01:00
pascallanger
78f6af6448 SLT2 last trial? 2025-02-26 00:00:49 +01:00
pascallanger
b04d4a54f7 SLT2 trial 3 2025-02-25 22:08:50 +01:00
pascallanger
ed65c81add SLT2 trial 2 2025-02-25 20:31:46 +01:00
pascallanger
9bdc24c42a SLT2 trial 2025-02-25 15:39:16 +01:00
pascallanger
7107d46d29 SHENQI2 new protocol 2025-02-24 17:04:07 +01:00
pascallanger
825f560cbb Update Protocols_Details.md 2025-02-22 11:51:55 +01:00
pascallanger
ccaec304d5 KAMTOM telemetry 2025-02-22 11:48:03 +01:00
pascallanger
22d45349cc Update Compiling.md (#1078) 2025-02-22 11:36:23 +01:00
pascallanger
5f26d624ad Update Compiling.md 2025-02-22 11:23:49 +01:00
pascallanger
496c07943f KAMTOM new surface protocol
Missing low batt telem
2025-02-21 21:46:18 +01:00
pascallanger
af87b0f6d1 Update UDIRC_ccnrf.ino 2025-02-19 17:16:04 +01:00
pascallanger
2918e63fb4 XN297Emu add ReSendPayload 2025-02-19 11:54:29 +01:00
pascallanger
ed63ef7efe Update UDIRC_ccnrf.ino 2025-02-19 11:33:17 +01:00
pascallanger
36d25c7773 Update UDIRC_ccnrf.ino 2025-02-19 11:21:26 +01:00
pascallanger
a3ef2b94d4 Update MultiChan.txt 2025-02-18 19:32:19 +01:00
pascallanger
ad8b45773d UDIRC new protocol / WIP 2025-02-18 11:18:13 +01:00
pascallanger
b2dec9b331 Update Protocols_Details.md 2025-02-12 11:57:04 +01:00
pascallanger
565aaed0c7 New XK2/P10 sub protocol 2025-02-09 12:01:28 +01:00
pascallanger
2f520f2e91 Update Protocols_Details.md 2025-02-06 12:03:15 +01:00
pascallanger
f1470a80dd Update Protocols_Details.md 2025-02-06 12:00:07 +01:00
pascallanger
b6f78e93a0 Yuxiang multi IDs/Freqs 2025-02-06 11:57:40 +01:00
pascallanger
58bfd3b8b0 New JIABAILE/Gyro subprotocol 2025-02-06 11:56:24 +01:00
pascallanger
9e5e907f4a Hontai/XKK170 new subprotocol 2025-01-30 23:14:15 +01:00
pascallanger
4b68443c8f Update Multiprotocol.h 2025-01-30 10:37:53 +01:00
pascallanger
f092e137c7 Improve DSM telemetry stability? 2025-01-30 10:28:57 +01:00
pascallanger
7b9941e537 Fix DSMR 2025-01-29 19:48:18 +01:00
pascallanger
d03a8787d1 Update Protocols_Details.md 2025-01-29 10:05:23 +01:00
pascallanger
75f79095ae JIABAILE RX ID in EEPROM
Not an autobind protocol anymore.
2025-01-29 09:42:16 +01:00
pascallanger
b901bedad6 Update H36_nrf24l01.ino 2025-01-28 17:51:06 +01:00
pascallanger
dbbef9181a Update H36_nrf24l01.ino 2025-01-28 14:05:45 +01:00
pascallanger
98a54300e0 New H36 protocol - 1 ID 2025-01-28 14:00:38 +01:00
pascallanger
56fa7e788b Update Protocols_Details.md 2025-01-27 17:54:26 +01:00
pascallanger
7af23017ff Update JIABAILE_nrf24l01.ino 2025-01-27 17:37:00 +01:00
pascallanger
a07c23b25f New car protocol JIABAILE 2025-01-27 16:12:21 +01:00
pascallanger
b5b2dc37d4 SGF22 FX922 flags 2025-01-25 17:49:22 +01:00
pascallanger
756af87ec1 SGF22 Flags trial 2025-01-25 11:31:21 +01:00
pascallanger
e5810a2978 QIDI560 new light flag? 2025-01-23 15:49:35 +01:00
pascallanger
eaf71c2f49 XK2 details 2025-01-21 17:06:21 +01:00
pascallanger
546a962d96 XK2 multi IDs 2025-01-20 19:19:31 +01:00
pascallanger
fd150fa109 XK2 2nd ID 2025-01-20 16:28:48 +01:00
pascallanger
9b66711052 MT99X2/SU35 2025-01-19 16:07:45 +01:00
pascallanger
550754c7f9 Merge branch 'master' of https://github.com/pascallanger/DIY-Multiprotocol-TX-Module 2025-01-19 15:59:42 +01:00
rdba2k
729fdd4719 Add MT99xx2/SU35 protocol (#1033)
* Update Multi.txt

* Update Multi_Protos.ino

* Update _Config.h

* Update Multiprotocol.h

* Update MT99xx_ccnrf.ino
2025-01-19 15:59:13 +01:00
pascallanger
2713b18099 Update Protocols_Details.md 2025-01-19 14:57:51 +01:00
pascallanger
5b5e95066a Traxxas TQ2&TQ1 2025-01-19 14:25:41 +01:00
pascallanger
b1f8560509 Update Yuxiang_nrf24l01.ino 2025-01-19 14:25:21 +01:00
Paul
50c18311c9 Update readme and protocol detals with surface/air info (#1034)
* Update readme and protocol detals with surface/air info

* Update Protocols_Details.md

* Update README.md

---------

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2025-01-19 14:18:20 +01:00
Arild Langseid
95746f80a1 Add RF_SIM sub-protocol to SLT-protocol (#1061)
* Added sub-protocol RF_SIM to the SLT protocol

* fixed typo

---------

Co-authored-by: Arild Langseid <arild@langseid.no>
2025-01-19 14:14:32 +01:00
pascallanger
bb7685e7a5 Update Yuxiang_nrf24l01.ino 2025-01-18 12:24:26 +01:00
pascallanger
a44126583e Yuxiang telemetry
Offset=7, Ratio=3.5
2025-01-18 12:19:33 +01:00
pascallanger
7fbca99bf3 Yuxiang bind ok 2025-01-18 00:36:33 +01:00
pascallanger
8db90fb6fc Yuxiang remove CH8 2025-01-16 11:34:07 +01:00
pascallanger
406f5feca5 Yuxiang TX2 2025-01-15 19:07:03 +01:00
pascallanger
0e5834a457 Update XN297Dump_nrf24l01.ino 2025-01-15 12:46:28 +01:00
pascallanger
75445ee4a7 Update Yuxiang_nrf24l01.ino 2025-01-13 20:28:25 +01:00
pascallanger
e724d6970e YuXiang E190 protocol
Only 1 ID, bind might not work...
2025-01-13 20:24:04 +01:00
pascallanger
190ebaefd4 Update XN297Dump_nrf24l01.ino 2025-01-13 20:22:14 +01:00
pascallanger
3fdf417ac9 V761 Beeper feature on CH10 2025-01-07 21:37:32 +01:00
pascallanger
e05bc7c447 Update XN297Dump_nrf24l01.ino 2025-01-07 15:11:32 +01:00
pascallanger
e2876c7e8c Update SGF22_nrf24l01.ino 2024-10-09 10:09:21 +02:00
pascallanger
7acfebc30e Update Protocols_Details.md 2024-10-09 10:01:08 +02:00
pascallanger
af12b21899 Merge branch 'master' of https://github.com/pascallanger/DIY-Multiprotocol-TX-Module 2024-09-26 10:14:30 +02:00
pascallanger
68a4285b2a Update Protocols_Details.md 2024-09-26 10:14:26 +02:00
rdba2k
f58f7800d2 Update SGF22_nrf24l01.ino (#1023)
add sub protocol "J20" to "SGF22" protocol
2024-09-26 10:11:19 +02:00
rdba2k
10b230d2dd Update Multiprotocol.h (#1022)
add sub protocol "J20" to "SGF22" protocol
2024-09-26 10:05:12 +02:00
rdba2k
58b19a2130 Update Multi_Protos.ino (#1021)
add sub protocol "J20" to "SGF22" protocol
2024-09-26 10:04:45 +02:00
rdba2k
1b79d7f80f Update Multi.txt (#1020)
add sub protocol "J20" to "SGF22" protocol
2024-09-26 10:04:07 +02:00
rdba2k
e0299f6c7d Update _Config.h (#1019)
add sub protocol "J20" to "SGF22" protocol
2024-09-26 10:03:39 +02:00
pascallanger
64ea7111ac SGF22: new F22S sub protocol 2024-09-02 18:14:25 +02:00
pascallanger
edaee63f16 Revert main.yml to use the old version of arduino_cli 2024-07-24 16:20:16 +02:00
Peter Feerick
b449212c34 chore(ci): bump setup-arduino-ci version to remove nodejs warning (#995)
* chore(ci): bump setup-arduino-ci version to remove nodejs warning

Gets rid of the numerous  "The following actions
uses Node.js version which is deprecated and will
be forced to run on node20" warnings

* chore: bump arduino-cli ver
2024-07-18 12:57:48 +02:00
pascallanger
edaad73290 Update Validate.h 2024-07-12 16:06:32 +02:00
pascallanger
3d5e43da3b Update Validate.h 2024-07-12 16:03:30 +02:00
pascallanger
b8c5929521 XK2: 1 TX ID but multiple RX IDs? 2024-07-12 10:00:21 +02:00
pascallanger
8685fcae7f Update Protocols_Details.md 2024-07-11 16:44:53 +02:00
pascallanger
6edd2a14a7 E129/C185 new debug flag 2024-07-11 16:20:32 +02:00
Ben Lye
600ffe87d7 Disable SCANNER ... (#989) 2024-06-14 12:14:55 +02:00
pascallanger
c6ff0e27d9 New protocol XK2 for the XK A160S 2024-06-07 20:28:15 +02:00
pascallanger
7a7b4b2e74 Update Protocols_Details.md 2024-05-21 13:16:41 +02:00
pascallanger
8a30bf1c0a Update FX/Q560 doc 2024-05-21 07:42:59 +02:00
pascallanger
dd82cbec63 Update Protocols_Details.md 2024-05-17 17:08:01 +02:00
pascallanger
be4595fe67 Update FX_nrf24l01.ino 2024-05-17 14:45:30 +02:00
pascallanger
54ae77ed7f FX/Q560 new sub protocol 2024-05-17 11:01:54 +02:00
pascallanger
2bdbd7088c Traxxas TQ 1st gen: try 5 2024-05-04 11:36:14 +02:00
pascallanger
1d3ed78622 J6Pro update 2024-05-04 11:34:59 +02:00
pascallanger
79b1c54007 Traxxas TQ 1st gen: try 4 2024-05-03 00:15:03 +02:00
pascallanger
81eb5dc6bc Traxxas TQ 1st gen: try 2 2024-04-29 19:43:24 +02:00
pascallanger
85a0e4bde8 Traxxas TQ 1st gen: try 2 2024-04-27 11:23:19 +02:00
pascallanger
63dfa316e8 Traxxas TQ 1st gen: try 1 2024-04-26 19:25:23 +02:00
pascallanger
9d383432a5 Update SGF22_nrf24l01.ino 2024-04-24 07:00:44 +02:00
pascallanger
6b181db629 SLT try 1e 2024-04-18 10:40:37 +02:00
pascallanger
873279dbe9 SLT new sub_protocol V1_4CH 2024-04-15 14:13:37 +02:00
pascallanger
f35be2984a SLT try 1d 2024-04-14 13:29:28 +02:00
pascallanger
7ddeb31e95 SLT try 1c 2024-04-14 13:23:50 +02:00
pascallanger
7444c44b48 SLT try 1b 2024-04-13 10:22:39 +02:00
pascallanger
08d1dcbed2 SLT try 1a 2024-04-12 18:24:50 +02:00
pascallanger
3e4f4e36c1 SLT try 1 2024-04-12 18:20:01 +02:00
pascallanger
902419dd3d Update Protocols_Details.md 2024-04-08 18:36:42 +02:00
pascallanger
038b3b9225 Update Protocols_Details.md 2024-04-04 17:13:35 +02:00
pascallanger
45f0154f4b Update Protocols_Details.md 2024-04-04 16:15:27 +02:00
pascallanger
e03864e35f Traxxas TQ 6 channels 2024-04-04 16:13:35 +02:00
pascallanger
de35ee09f5 Realacc doc update 2024-04-02 16:42:19 +02:00
pascallanger
c88952c35e REALACC: IDs
Any ID
2024-04-02 12:34:04 +02:00
pascallanger
ffae7dda1d Update SGF22_nrf24l01.ino 2024-03-29 16:24:25 +01:00
pascallanger
4f17f76b39 SGF22: new flag 2024-03-29 16:19:20 +01:00
pascallanger
31ef090508 ASF try 4 2024-03-29 15:55:54 +01:00
pascallanger
04d4e39b87 Update _Config.h 2024-03-27 19:34:28 +01:00
pascallanger
b3537ea75a DSM2_SFC try 3 2024-03-26 16:18:34 +01:00
pascallanger
bccc050165 DSM2_SFC try 2 2024-03-26 14:41:19 +01:00
pascallanger
0f0df176de DSM2_SFC try 1
Servo refresh rate -> frame rate 16.5/11ms
2024-03-26 14:37:26 +01:00
pascallanger
5542e7005d ASF try 3 2024-03-26 11:43:42 +01:00
pascallanger
eb35fefd3e ASF try 2 2024-03-25 15:17:51 +01:00
pascallanger
5acc3a8d01 CYRF6936: Findbestchannels flag addition 2024-03-25 15:11:31 +01:00
pascallanger
9e9c3958c6 ASF: try 1 2024-03-23 11:23:42 +01:00
pascallanger
7f3a93ca4c Kyosho3: increase bind time to 2.5s 2024-03-22 18:24:31 +01:00
pascallanger
debc9de11a New protocol Kyosho3/ASF
Only 1 ID and 1 frequency for now
2024-03-22 18:17:21 +01:00
pascallanger
f117105124 SGF22: Fix none working IDs 2024-03-21 16:33:16 +01:00
pascallanger
2e3520acad DSM2SFC timing update 2024-03-19 20:52:50 +01:00
pascallanger
91af921d98 Update Protocols_Details.md 2024-03-19 15:47:56 +01:00
pascallanger
ab45ec6d1c Update Validate.h 2024-03-19 15:44:29 +01:00
pascallanger
26b0b6bd20 Update DSM_cyrf6936.ino 2024-03-19 15:44:21 +01:00
pascallanger
1b9ce32e89 Update DSM_cyrf6936.ino 2024-03-19 14:11:40 +01:00
pascallanger
332e55831c Update Protocols_Details.md 2024-03-18 17:11:23 +01:00
pascallanger
7051438e5d Update Protocols_Details.md 2024-03-18 17:00:40 +01:00
pascallanger
28467fac57 DSM/DSM2SFC new subprotocol 2024-03-18 16:57:54 +01:00
49 changed files with 5463 additions and 903 deletions

View File

@@ -68,7 +68,7 @@ jobs:
- uses: actions/checkout@v4
- name: Install Arduino CLI
uses: arduino/setup-arduino-cli@v1.1.2
uses: arduino/setup-arduino-cli@v2
with:
version: "0.32.2"
@@ -257,7 +257,7 @@ jobs:
- uses: actions/checkout@v4
- name: Install Arduino CLI
uses: arduino/setup-arduino-cli@v1.1.2
uses: arduino/setup-arduino-cli@v2
with:
version: "0.32.2"

View File

@@ -35,6 +35,7 @@
6,3,DSM,X_2F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,5,DSM,R_1F,0,AUX3,AUX4,AUX5
6,6,DSM,2SFC,0
70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH
@@ -53,10 +54,9 @@
28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
28,6,Flysky_AFHDS2A,PWM_SB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
28,7,Flysky_AFHDS2A,PPM_SB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
28,4,Flysky_AFHDS2A,Gyro_Off,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,ST_Ga,TH_Ga,Prio,Calib
28,5,Flysky_AFHDS2A,Gyro_On,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,ST_Ga,TH_Ga,Prio,Calib
28,6,Flysky_AFHDS2A,G_On_Rev,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,ST_Ga,TH_Ga,Prio,Calib
56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
53,0,Height,5ch,0,Gear
@@ -86,12 +86,17 @@
55,1,FrSkyRX,CloneTX,0
55,2,FrSkyRX,EraseTX,0
55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
58,0,FX,816,1
58,1,FX,620,1
58,0,FX,FX816,1
58,1,FX,FX620,1
58,2,FX,9630,1,Rate,Gyro,TrimR,TrimA,TrimE
58,3,FX,Q560,1,FLIP,Gyro,LEDs
58,4,FX,QF012,1,FLIP,Gyro,Invert,Reset
58,5,FX,BM26,1,FLIP,Gyro,LEDs,LED,CALIB
58,6,FX,A570,1,STOP,Rate,Color,LED,TrimR,TrimA,TrimE
20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib
20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib
23,0,FY326,FY326,1,Flip,RTH,HLess,Expert
23,0,FQ777,124,1,Flip,RTH,HLess,Expert
23,1,FQ777,XBM37,1,Rate,Flip,HLess,LED,Pict,Video,RTH,OK,ETrim,Atrim
47,0,GD00x,V1,1,Trim,LED,Rate
47,1,GD00x,V2,1,Trim,LED,Rate
32,0,GW008,FY326,1,Flip
@@ -108,6 +113,7 @@
26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,4,Hontai,XKK170,1,Rate,Emerg,TakLan,Calib,TrimA,TrimE,Optic
57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess
@@ -116,7 +122,7 @@
22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3
71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3
49,0,KF606,KF606,1,Trim
49,0,KF606,KF606,1,Trim,LED
49,1,KF606,MIG320,1,Trim,LED
49,2,KF606,ZCZ50,1,Trim,UNK
9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim,Rtrim,Hover
@@ -130,14 +136,16 @@
18,4,MJXQ,E010,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,5,MJXQ,H26WH,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,6,MJXQ,Phoenix,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
17,0,MT99XX,Std,1,Flip,LED,Pict,Video,HLess
17,1,MT99XX,H7,1,Flip,LED,Pict,Video,HLess
17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess
17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess
17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess
17,5,MT99XX,A180,0,3D_6G
17,6,MT99XX,Dragon,0,Mode,RTH
17,7,MT99XX,F949G,0,6G_3D,Light
17,0,MT99XX,Std,1,Flip,LED,Pict,Video,HLess,Atrim,Etrim
17,1,MT99XX,H7,1,Flip,LED,Pict,Video,HLess,Atrim,Etrim
17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess,Atrim,Etrim
17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess,Atrim,Etrim
17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess,Atrim,Etrim
17,5,MT99XX,A180,0,3D_6G,Rate,3D_6G,n-a,n-a,Atrim,Etrim
17,6,MT99XX,Dragon,0,Mode,RTH,n-a,n-a,n-a,Atrim,Etrim
17,7,MT99XX,F949G,0,6G_3D,Light,Rates,Unk1,Unk2,Atrim,Etrim
92,0,MT99xx2,PA18,0,MODE,FLIP,RTH,n-a,n-a,Atrim,Etrim
92,1,MT99xx2,SU35,0,Mode,LED,LED_FH,Invert,Rate,Atrim,Etrim
44,0,NCC1701,Std,1,Warp
77,0,OMP,M2,0,THold,IdleUp,6G_3D
60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8
@@ -157,7 +165,7 @@
74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8
74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8
74,3,RadioLink,RC4G,0,CH5,FS_CH1,FS_CH2,FS_CH3,FS_CH4
76,0,Realacc,Std,1,Flip,Light,Calib,HLess,RTH,UNK
76,0,Realacc,Std,1,Flip,Light,Calib,HLess,RTH,ThCut,Rotat
50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8
@@ -168,13 +176,16 @@
11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib
11,3,SLT,Q200,0,Rates,n-a,CH7,CH8,Mode,VidOn,VidOff,Calib
11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict
11,5,SLT,V1_4CH,0
11,6,SLT,RF_SIM,0,CH5,CH6,CH7,CH8,CH9,CH10
10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess
10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess
43,0,Traxxas,TQ,0
43,0,Traxxas,TQ2,0
43,1,Traxxas,TQ1,0
5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY
5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD
48,0,V761,3CH,0,Gyro,Calib,Flip,RtnAct,Rtn
48,1,V761,4CH,0,Gyro,Calib,Flip,RtnAct,Rtn
48,0,V761,3CH,0,Gyro,Calib,Flip,RtnAct,Rtn,Beep
48,1,V761,4CH,0,Gyro,Calib,Flip,RtnAct,Rtn,Beep
48,2,V761,TOPRC,0,Gyro,Calib,Flip,RtnAct,Rtn
46,0,V911s,V911s,1,Calib,Rate
46,1,V911s,E119,1,Calib,Rate,6G_3D
@@ -188,6 +199,8 @@
62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video,Flip,Light
62,2,XK,Cars,0,FMode,TakeOf,Emerg,3D_6G,Pict,Video,Flip,Light
99,0,XK2,X4,0,Rate,Mode,Hover,Light,Stunt
99,1,XK2,P10,0,Rate,Mode,Hover,Light,Stunt
8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess
8,1,YD717,SkyWlkr,1,Flip,Light,Pict,Video,HLess
8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess
@@ -200,18 +213,29 @@
81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE
82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe
83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR
83,1,E129,C186,1,TakLan,EmStop,TrimA,TrimE,TrimR,Loop,Flip
83,1,E129,C186,1,TakLan,EmStop,TrimA,TrimE,TrimR,Loop,Flip,Debug
84,0,JOYSWAY,Std,0
85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH
87,0,IKEA
89,0,LOSI
90,0,MouldKg,Analog,0
90,1,MouldKg,Digit,0
90,0,MouldKg,A4444,0
90,1,MouldKg,D4444,0
90,2,MouldKg,A664,0
91,0,Xerall,Tank,0,FlTa,TakLan,Rate,HLess,Photo,Video,TrimR,TrimE,TrimA
92,0,MT99xx2,PA18,0,MODE,FLIP,RTH
93,0,Kyosho2,KT-17,0
94,0,Scorpio
95,0,Bluefly,HP100,0,CH5,CH6,CH7,CH8
96,0,BumbleB
97,0,SGF22,Std,1,Mode,Flip,LED,Pict,Video
61,1,EazyRC
97,0,SGF22,F22,1,Mode,Flip,LED,Pict,Video,TrRes,Bal,HiBal
97,1,SGF22,F22S,1,Mode,Flip,LED,Pict,Video,TrRes
97,2,SGF22,J20,1,Mode,Flip,LED,Pict,Video
61,0,EazyRC
98,0,Kyosho3,ASF,0
100,0,YuXiang,Std,0,Lock,Rate,Land,Manual,Flip,Mode,Pitch
102,0,JIABAILE,Std,0,Speed,Light,Flash
102,1,JIABAILE,Gyro,0,Speed,Light,Flash,ST_Tr
103,0,H36,Std,1,Flip,HLess,RTH
104,0,KAMTOM,Std,0,ST_Tr,TH_Tr,TH_DR
105,0,Shenqi2,Std,1
106,0,WL91x,Std,0
107,0,WPL,Std,0,Light,TH_DR,ST_DR

View File

@@ -183,8 +183,18 @@ uint16_t AFHDS2A_RX_callback()
case AFHDS2A_RX_DATA:
if (AFHDS2A_RX_data_ready()) {
A7105_ReadData(AFHDS2A_RX_TXPACKET_SIZE);
if (memcmp(&packet[1], rx_id, 4) == 0 && memcmp(&packet[5], rx_tx_addr, 4) == 0) {
if (packet[0] == 0x58 && packet[37] == 0x00 && (telemetry_link&0x7F) == 0) { // standard packet, send channels to TX
if (memcmp(&packet[1], rx_id, 4) == 0 && memcmp(&packet[5], rx_tx_addr, 4) == 0)
{
#if 0
//if(packet[0] == 0xAA)
{
for(uint8_t i=0;i<AFHDS2A_RX_TXPACKET_SIZE;i++)
debug(" %02X",packet[i]);
debugln("");
}
#endif
if (packet[0] == 0x58 && packet[37] == 0x00 && (telemetry_link&0x7F) == 0)
{ // standard packet, send channels to TX
int rssi = min(A7105_ReadReg(A7105_1D_RSSI_THOLD),160);
RX_RSSI = map16b(rssi, 160, 8, 0, 128);
AFHDS2A_RX_build_telemetry_packet();

View File

@@ -93,15 +93,19 @@ static void AFHDS2A_update_telemetry()
if (option & 0x80)
{// forward 0xAA and 0xAC telemetry to TX, skip rx and tx id to save space
packet_in[0]= TX_RSSI;
debug("T(%02X)=",packet[0]);
//Fix for CROSSOVER-RX,
if(packet[17+8]==0xFF && packet[21+8]==0xFC && packet[25+8]==0xFF)
packet[17+8]=0xFE; // RX_ERR_RATE needed for TX to validate the telem
#if 0
debug("T(%02X)=",packet[0]);
for(uint8_t i=9;i < AFHDS2A_RXPACKET_SIZE; i++)
debug(" %02X",packet[i]);
debugln("");
#endif
for(uint8_t i=9;i < AFHDS2A_RXPACKET_SIZE; i++)
{
packet_in[i-8]=packet[i];
debug(" %02X",packet[i]);
}
packet_in[29]=packet[0]; // 0xAA Normal telemetry, 0xAC Extended telemetry
telemetry_link=2;
debugln("");
return;
}
#endif
@@ -186,16 +190,40 @@ static void AFHDS2A_build_packet(uint8_t type)
case AFHDS2A_PACKET_STICKS:
packet[0] = 0x58;
//16 channels + RX_LQI on channel 17
for(uint8_t ch=0; ch<num_ch; ch++)
for(uint8_t ch=0; ch<17; ch++)
{
if(ch == 16 // CH17=RX_LQI
#ifdef AFHDS2A_LQI_CH
|| ch == (AFHDS2A_LQI_CH-1) // override channel with LQI
#endif
)
val = 2000 - 10*RX_LQI;
val = convert_channel_ppm(sub_protocol<AFHDS2A_GYRO_OFF?CH_AETR[ch]:ch); // No remapping for BS receivers
if(ch<14)
{
packet[9 + ch*2] = val;
packet[10 + ch*2] = (val>>8)&0x0F;
}
else
val = convert_channel_ppm(CH_AETR[ch]);
{
if(ch == 16) //CH17=RX_LQI
val = 2000 - 10*RX_LQI;
packet[10 + (ch-14)*6] |= (val)<<4;
packet[12 + (ch-14)*6] |= (val)&0xF0;
packet[14 + (ch-14)*6] |= (val>>4)&0xF0;
}
}
{
uint8_t next_hop = (hopping_frequency_no+1)&0x0F;
packet[34] |= next_hop<<4;
packet[36] |= next_hop?0x80:0x90;
}
break;
case AFHDS2A_PACKET_FAILSAFE:
packet[0] = 0x56;
for(uint8_t ch=0; ch<16; ch++)
{ // Failsafe values
#ifdef FAILSAFE_ENABLE
val = Failsafe_data[protocol==PROTO_AFHDS2A?CH_AETR[ch]:ch]; // No remapping for BS receivers
if(val!=FAILSAFE_CHANNEL_HOLD && val!=FAILSAFE_CHANNEL_NOPULSES)
val = (((val<<2)+val)>>3)+860;
else
#endif
val = 0x0FFF;
if(ch<14)
{
packet[9 + ch*2] = val;
@@ -209,42 +237,6 @@ static void AFHDS2A_build_packet(uint8_t type)
}
}
break;
case AFHDS2A_PACKET_FAILSAFE:
packet[0] = 0x56;
for(uint8_t ch=0; ch<num_ch; ch++)
{
#ifdef FAILSAFE_ENABLE
if(ch<16)
val = Failsafe_data[CH_AETR[ch]];
else
val = FAILSAFE_CHANNEL_NOPULSES;
if(val!=FAILSAFE_CHANNEL_HOLD && val!=FAILSAFE_CHANNEL_NOPULSES)
{ // Failsafe values
val = (((val<<2)+val)>>3)+860;
if(ch<14)
{
packet[9 + ch*2] = val;
packet[10 + ch*2] = (val>>8)&0x0F;
}
else
{
packet[10 + (ch-14)*6] &= 0x0F;
packet[10 + (ch-14)*6] |= (val)<<4;
packet[12 + (ch-14)*6] &= 0x0F;
packet[12 + (ch-14)*6] |= (val)&0xF0;
packet[14 + (ch-14)*6] &= 0x0F;
packet[14 + (ch-14)*6] |= (val>>4)&0xF0;
}
}
else
#endif
if(ch<14)
{ // no values
packet[9 + ch*2] = 0xff;
packet[10+ ch*2] = 0xff;
}
}
break;
case AFHDS2A_PACKET_SETTINGS:
packet[0] = 0xaa;
packet[9] = 0xfd;
@@ -253,17 +245,43 @@ static void AFHDS2A_build_packet(uint8_t type)
if(val<50 || val>400) val=50; // default is 50Hz
packet[11]= val;
packet[12]= val >> 8;
packet[13] = sub_protocol & 0x01; // 1 -> PPM output enabled
packet[14]= 0x00;
for(uint8_t i=15; i<37; i++)
packet[i] = 0xff;
packet[18] = 0x05; // ?
packet[19] = 0xdc; // ?
packet[20] = 0x05; // ?
if(sub_protocol&2)
packet[21] = 0xdd; // SBUS output enabled
memset(&packet[15],0xFF,22);
#ifndef MULTI_AIR
if(sub_protocol < AFHDS2A_GYRO_OFF)
{
#endif
packet[13] = sub_protocol & 0x01; // 1 -> PPM output enabled
packet[14] = 0x00; // ?
packet[18] = 0x05; // ?
packet[19] = 0xdc; // ?
packet[20] = 0x05; // ?
if(sub_protocol&2)
packet[21] = 0xdd; // SBUS output enabled
else
packet[21] = 0xde; // IBUS
#ifndef MULTI_AIR
}
else
packet[21] = 0xde; // IBUS
{//BS receivers
if(sub_protocol == AFHDS2A_GYRO_OFF)
{
memset(&packet[15],0x00,4);
packet[22] = 0xFC; // ?
}
else
{//AFHDS2A_GYRO_ON & AFHDS2A_GYRO_ON_REV
packet[15] = convert_channel_16b_limit(CH13,0,100); // ST Gain
packet[16] = convert_channel_16b_limit(CH14,0,100); // TH Gain
packet[17] = convert_channel_16b_limit(CH15,0,100); // Priority
if(sub_protocol == AFHDS2A_GYRO_ON_REV)
packet[17] |= 0x80; // Reverse
packet[18] = CH16_SW?(0x32|0x80):0x32; // Calib|50?
packet[19] = 0x64; // 100?
packet[20] = 0x64; // 100?
packet[22] = 0xFE; // ?
}
}
#endif
break;
}
packet[37] = 0x00;
@@ -283,6 +301,9 @@ uint16_t AFHDS2A_callback()
static uint16_t packet_counter;
uint8_t data_rx=0;
uint16_t start;
#ifndef MULTI_AIR
static uint16_t Prev_Channel[4] = { 0,0,0,0 };
#endif
#ifndef FORCE_AFHDS2A_TUNING
A7105_AdjustLOBaseFreq(1);
#endif
@@ -367,10 +388,39 @@ uint16_t AFHDS2A_callback()
AFHDS2A_build_packet(packet_type);
data_rx=A7105_ReadReg(A7105_00_MODE); // Check if something has been received...
A7105_WriteData(AFHDS2A_TXPACKET_SIZE, hopping_frequency[hopping_frequency_no++]);
if(hopping_frequency_no >= AFHDS2A_NUMFREQ)
hopping_frequency_no = 0;
hopping_frequency_no &= 0x0F; // AFHDS2A_NUMFREQ
#if 0
for(uint8_t i=0; i<AFHDS2A_TXPACKET_SIZE; i++)
debug(" %02X",packet[i]);
debugln("");
#endif
#ifndef MULTI_AIR
if(sub_protocol > AFHDS2A_GYRO_OFF)
{//Gyro is on
//Check if gyro settings have changed
uint16_t val;
for(uint8_t i=0;i<4;i++)
{
val = Channel_data[CH13+i] - Prev_Channel[i];
if(val&0x8000) val ^= 0xFFFF;
if(val > 10)
{//This setting has significantly changed
Prev_Channel[i] = Channel_data[CH13+i];
packet_sent = 5;
}
}
}
if(packet_sent && (packet_counter%5)==0)
{//Inform the RX of the change
packet_type = AFHDS2A_PACKET_SETTINGS;
packet_sent--;
}
else
#endif
if(!(packet_counter % 1313))
{//Send settings every 5s
packet_type = AFHDS2A_PACKET_SETTINGS;
}
else
{
#ifdef FAILSAFE_ENABLE
@@ -448,9 +498,6 @@ void AFHDS2A_init()
rx_id[i]=eeprom_read_byte((EE_ADDR)(addr+i));
}
hopping_frequency_no = 0;
if(sub_protocol&0x04)
num_ch=17;
else
num_ch=14;
packet_sent = 0;
}
#endif

View File

@@ -249,7 +249,7 @@ void CYRF_WriteDataPacket(const uint8_t dpbuffer[])
}
*/
//NOTE: This routine will reset the CRC Seed
void CYRF_FindBestChannels(uint8_t *channels, uint8_t len, uint8_t minspace, uint8_t min, uint8_t max)
void CYRF_FindBestChannels(uint8_t *channels, uint8_t len, uint8_t minspace, uint8_t min, uint8_t max, uint8_t forced)
{
#define NUM_FREQ 80
#define FREQ_OFFSET 4
@@ -269,7 +269,12 @@ void CYRF_FindBestChannels(uint8_t *channels, uint8_t len, uint8_t minspace, uin
delayMilliseconds(1);
for(i = 0; i < NUM_FREQ; i++)
{
CYRF_ConfigRFChannel(protocol==PROTO_LOSI?i|1:i);
if(((i&1) && forced == FIND_CHANNEL_EVEN) || (!(i&1) && forced == FIND_CHANNEL_ODD))
{
rssi[i] = 0xFF;
continue;
}
CYRF_ConfigRFChannel(i); //protocol==PROTO_LOSI?i|1:i);
delayMicroseconds(270); //slow channel require 270usec for synthesizer to settle
if( !(CYRF_ReadRegister(CYRF_05_RX_CTRL) & 0x80)) {
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x80); //Prepare to receive
@@ -321,9 +326,7 @@ const uint8_t PROGMEM DEVO_j6pro_sopcodes[][8] = {
{0xB6, 0x31, 0xAE, 0x46, 0x5A, 0xCC, 0xAE, 0x46},
{0x9E, 0x82, 0xDC, 0x3C, 0xA1, 0x78, 0xDC, 0x3C},
{0x6F, 0x65, 0x18, 0x74, 0xB9, 0x8E, 0x19, 0x74},
#endif
#if defined(TRAXXAS_CYRF6936_INO)
{0x62, 0xDF, 0xC1, 0x49, 0xDF, 0xB1, 0xC0, 0x49},
{0x62, 0xDF, 0xC1, 0x49, 0xDF, 0xB1, 0xC0, 0x49}, //j6pro bind
#endif
};
#endif

View File

@@ -267,6 +267,7 @@ uint16_t DSM_RX_callback()
0x01 => 22ms 1024 DSM2 1 packet => number of channels is <8
0x02 => 22ms 1024 DSM2 2 packets => either a number of channel >7
0x12 => 11ms 2048 DSM2 2 packets => can be any number of channels
0x23 => DX3R DSM2 2 surface packets
0xA2 => 22ms 2048 DSMX 1 packet => number of channels is <8
0xB2 => 11ms 2048 DSMX => can be any number of channels
(0x01 or 0xA2) and num_ch < 7 => 22ms else 11ms

View File

@@ -19,11 +19,10 @@
//#define DSM_DEBUG_FWD_PGM
//#define DEBUG_BIND 1
//#define DSM_GR300
#define CLONE_BIT_MASK 0x20
#define DSM_BIND_CHANNEL 0x0d //13 This can be any odd channel
#define CLONE_BIT_MASK 0x20
#define DSM_BIND_CHANNEL 0x0D //13 This can be any odd channel
#define DSM2_SFC_PERIOD 16500
//During binding we will send BIND_COUNT packets
//One packet each 10msec
@@ -32,8 +31,8 @@
// Lemon-RX G2s seems to have a timeout waiting for the channel to get quiet after the
// first good BIND packet.. If using 3s (300), Lemon-RX will not transmit the BIND-Response packet.
#define DSM_BIND_COUNT 180 // About 1.8s
#define DSM_BIND_COUNT_READ 600 // About 4.2s of waiting for Response
#define DSM_BIND_COUNT 180 // About 1.8s
#define DSM_BIND_COUNT_READ 600 // About 4.2s of waiting for Response
enum {
DSM_BIND_WRITE=0,
@@ -95,9 +94,12 @@ static void __attribute__((unused)) DSM_build_bind_packet()
packet[11] = 12;
else
packet[11] = num_ch; // DX5 DSMR sends 0x48...
//packet[11] = 3; // DX3R
if (sub_protocol==DSMR)
packet[12] = 0xa2;
else if (sub_protocol==DSM2_SFC)
packet[12] = 0x23; // DX3R
else if (sub_protocol==DSM2_1F)
packet[12] = num_ch<8?0x01:0x02; // DSM2/1024 1 or 2 packets depending on the number of channels
else if(sub_protocol==DSM2_2F)
@@ -110,7 +112,6 @@ static void __attribute__((unused)) DSM_build_bind_packet()
#endif
else // DSMX_2F && DSM_AUTO
packet[12] = 0xb2; // DSMX/2048 2 packets
packet[13] = 0x00; //???
for(i = 8; i < 14; i++)
@@ -137,8 +138,13 @@ static void __attribute__((unused)) DSM_update_channels()
if(num_ch<3 || num_ch>12)
num_ch=6; // Default to 6 channels if invalid choice...
if(sub_protocol==DSMR && num_ch>7)
num_ch=7; // Max 7 channels in DSMR
#ifndef MULTI_AIR
if(sub_protocol==DSMR && num_ch>7)
num_ch=7; // Max 7 channels in DSMR
if(sub_protocol==DSM2_SFC && num_ch>5)
num_ch=5; // Max 5 channels in DSM2_SFC
#endif
// Create channel map based on number of channels and refresh rate
uint8_t idx=num_ch-3;
@@ -175,13 +181,17 @@ static void __attribute__((unused)) DSM_build_data_packet(uint8_t upper)
}
#ifndef MULTI_AIR
if(sub_protocol == DSMR)
{
if(sub_protocol == DSMR || sub_protocol == DSM2_SFC)
{ // 12 bits, full range, no reassignment
for (uint8_t i = 0; i < 7; i++)
{
uint16_t value = 0x0000;
if(i < num_ch)
{
value=Channel_data[i]<<1;
if(sub_protocol == DSM2_SFC)
value |= i<<12;
}
packet[i*2+2] = (value >> 8) & 0xff;
packet[i*2+3] = (value >> 0) & 0xff;
}
@@ -266,31 +276,33 @@ static uint8_t __attribute__((unused)) DSM_Check_RX_packet()
uint16_t DSM_callback()
{
#if defined MULTI_EU
if(sub_protocol == DSM2_1F || sub_protocol == DSM2_2F)
if(sub_protocol == DSM2_1F || sub_protocol == DSM2_2F || sub_protocol == DSM2_SFC)
{
SUB_PROTO_INVALID;
return 11000;
}
#endif
#if defined MULTI_AIR
if(sub_protocol == DSMR)
if(sub_protocol == DSMR || sub_protocol == DSM2_SFC)
{
SUB_PROTO_INVALID;
return 11000;
}
#endif
#define DSM_CH1_CH2_DELAY 4010 // Time between write of channel 1 and channel 2
#ifdef STM32_BOARD
#define DSM_WRITE_DELAY 1600 // Time after write to verify write complete
#define DSM_READ_DELAY 300 // Time before write to check read phase, and switch channels.
#else
#define DSM_WRITE_DELAY 1950 // Time after write to verify write complete
#define DSM_READ_DELAY 600 // Time before write to check read phase, and switch channels.
#endif
#define DSM_READ_DELAY 600 // Time before write to check read phase, and switch channels. Was 400 but 600 seems what the 328p needs to read a packet
#if defined DSM_TELEMETRY
uint8_t rx_phase;
uint8_t len;
uint8_t length;
#endif
uint8_t start;
#ifdef DSM_GR300
uint16_t timing=5000+(convert_channel_8b(CH13)*100);
debugln("T=%u",timing);
#endif
//debugln("P=%d",phase);
switch(phase)
{
case DSM_BIND_WRITE:
@@ -304,11 +316,11 @@ uint16_t DSM_callback()
return 10000;
#if defined DSM_TELEMETRY
case DSM_BIND_CHECK:
#if DEBUG_BIND
debugln("Bind Check");
#endif
//64 SDR Mode is configured so only the 8 first values are needed
CYRF_ConfigDataCode((const uint8_t *)"\x98\x88\x1B\xE4\x30\x79\x03\x84");
#if DEBUG_BIND
debugln("Bind Check");
#endif
//64 SDR Mode is configured so only the 8 first values are needed
CYRF_ConfigDataCode((const uint8_t *)"\x98\x88\x1B\xE4\x30\x79\x03\x84");
CYRF_SetTxRxMode(RX_EN); //Receive mode
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x87); //Prepare to receive
bind_counter=DSM_BIND_COUNT_READ; //Timeout of 4.2s if no packet received
@@ -371,25 +383,38 @@ uint16_t DSM_callback()
CYRF_SetTxRxMode(TX_EN);
hopping_frequency_no = 0;
phase = DSM_CH1_WRITE_A; // in fact phase++
#ifndef MULTI_AIR
if(sub_protocol == DSMR)
DSM_set_sop_data_crc(false, true);
else
#endif
DSM_set_sop_data_crc(true, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F); //prep CH1
return 10000;
case DSM_CH1_WRITE_A:
#ifdef MULTI_SYNC
telemetry_set_input_sync(11000); // Always request 11ms spacing even if we don't use half of it in 22ms mode
if(sub_protocol!=DSM2_SFC || option&0x40) // option&40 in this case is 16.5ms/11ms frame rate for DSM2_SFC
telemetry_set_input_sync(11000); // Always request 11ms spacing even if we don't use half of it in 22ms mode
else
telemetry_set_input_sync(DSM2_SFC_PERIOD);
#endif
#ifndef MULTI_AIR
if(sub_protocol == DSMR)
CYRF_SetPower(0x08); //Keep transmit power in sync
else
#endif
CYRF_SetPower(0x28); //Keep transmit power in sync
case DSM_CH1_WRITE_B:
DSM_build_data_packet(phase == DSM_CH1_WRITE_B); // build lower or upper channels
case DSM_CH2_WRITE_A:
case DSM_CH2_WRITE_B:
CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS); // clear IRQ flags
CYRF_WriteDataPacket(packet);
//debugln_time("");
#ifndef MULTI_AIR
if(sub_protocol==DSM2_SFC)
CYRF_WriteDataPacketLen(packet,2*(num_ch+1));
else
#endif
CYRF_WriteDataPacket(packet);
#if 0
for(uint8_t i=0;i<16;i++)
debug(" %02X", packet[i]);
@@ -426,41 +451,52 @@ uint16_t DSM_callback()
phase++; // change from CH2_CHECK to CH2_READ
CYRF_SetTxRxMode(RX_EN); //Receive mode
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x87); //0x80??? //Prepare to receive
if(sub_protocol==DSMR)
{
phase = DSM_CH2_READ_B;
return 11000 - DSM_WRITE_DELAY - DSM_READ_DELAY;
}
#ifdef DSM_GR300
if(num_ch==3)
return timing - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY - DSM_READ_DELAY;
#ifndef MULTI_AIR
if(sub_protocol==DSMR || sub_protocol == DSM2_SFC)
{
phase = DSM_CH2_READ_B;
if(sub_protocol == DSM2_SFC)
{
if(option&0x40) // option&40 in this case is 16.5ms/11ms frame rate for DSM2_SFC
return 11000 - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY - DSM_READ_DELAY;
else
return DSM2_SFC_PERIOD - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY - DSM_READ_DELAY;
}
return 10900 - DSM_WRITE_DELAY - DSM_READ_DELAY; //Was 11000 but the SR6200A needs 10900 to report telemetry correctly
}
#endif
return 11000 - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY - DSM_READ_DELAY;
case DSM_CH2_READ_A:
case DSM_CH2_READ_B:
//Read telemetry
rx_phase = CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
//debug("ST1:%02X ",rx_phase);
if((rx_phase & 0x03) == 0x02) // RXC=1, RXE=0 then 2nd check is required (debouncing)
rx_phase |= CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
//debug("ST2:%02X ",rx_phase);
if((rx_phase & 0x07) == 0x02)
{ // good data (complete with no errors)
CYRF_WriteRegister(CYRF_07_RX_IRQ_STATUS, 0x80); // need to set RXOW before data read
len=CYRF_ReadRegister(CYRF_09_RX_COUNT);
if(len>TELEMETRY_BUFFER_SIZE-2)
len=TELEMETRY_BUFFER_SIZE-2;
CYRF_ReadDataPacketLen(packet_in+1, len);
length=CYRF_ReadRegister(CYRF_09_RX_COUNT);
//debug("RX(%d)",length);
if(length>TELEMETRY_BUFFER_SIZE-2)
length=TELEMETRY_BUFFER_SIZE-2;
CYRF_ReadDataPacketLen(packet_in+1, length);
#ifdef DSM_DEBUG_FWD_PGM
//debug(" %02X", packet_in[1]);
if(packet_in[1]==9)
{
for(uint8_t i=0;i<len;i++)
for(uint8_t i=0;i<length;i++)
debug(" %02X", packet_in[i+1]);
debugln("");
}
#endif
packet_in[0]=CYRF_ReadRegister(CYRF_13_RSSI)&0x1F;// store RSSI of the received telemetry signal
//for(uint8_t i=0;i<length+1;i++)
// debug(" %02X", packet_in[i]);
telemetry_link=1;
}
//debugln("");
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Abort RX operation
if (phase == DSM_CH2_READ_A && (sub_protocol==DSM2_1F || sub_protocol==DSMX_1F) && num_ch < 8) // 22ms mode
{
@@ -468,10 +504,6 @@ uint16_t DSM_callback()
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Clear abort RX operation
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x87); //0x80??? //Prepare to receive
phase = DSM_CH2_READ_B;
#ifdef DSM_GR300
if(num_ch==3)
return timing;
#endif
return 11000;
}
if (phase == DSM_CH2_READ_A)
@@ -492,19 +524,20 @@ uint16_t DSM_callback()
else
{ //Normal mode 22ms
phase = DSM_CH1_WRITE_A; // change from CH2_CHECK_A to CH1_WRITE_A (ie no upper)
#ifdef DSM_GR300
if(num_ch==3)
return timing - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY ;
#ifndef MULTI_AIR
if(sub_protocol==DSM2_SFC)
{
if(option&0x40) // option&40 in this case is 16.5ms/11ms frame rate for DSM2_SFC
return 11000 - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY ;
else
return DSM2_SFC_PERIOD - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY ;
}
#endif
return 22000 - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY ;
}
}
else
phase = DSM_CH1_WRITE_A; // change from CH2_CHECK_B to CH1_WRITE_A (upper already transmitted so transmit lower)
#ifdef DSM_GR300
if(num_ch==3)
return timing - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY ;
#endif
return 11000 - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY;
#endif
}
@@ -547,15 +580,13 @@ void DSM_init()
SUB_PROTO_INVALID;
else
{
SUB_PROTO_VALID;
//SUB_PROTO_VALID;
uint8_t row = rx_tx_addr[3]%22;
for(uint8_t i=0; i< 4; i++)
cyrfmfg_id[i] = pgm_read_byte_near(&DSMR_ID_FREQ[row][i]);
for(uint8_t i=0; i< 23; i++)
hopping_frequency[i] = pgm_read_byte_near(&DSMR_ID_FREQ[row][i+4]);
}
#else
SUB_PROTO_INVALID;
#endif
}
else
@@ -568,30 +599,30 @@ void DSM_init()
uint16_t temp = DSM_CLONE_EEPROM_OFFSET;
for(uint8_t i=0;i<4;i++)
cyrfmfg_id[i] = eeprom_read_byte((EE_ADDR)temp++);
#if DEBUG_BIND
debugln("Using cloned ID");
debug("Clone ID=")
for(uint8_t i=0;i<4;i++)
debug("%02x ", cyrfmfg_id[i]);
debugln("");
#endif
cyrfmfg_id[3]^=RX_num; //Model match
#if DEBUG_BIND
debugln("Using cloned ID");
debug("Clone ID=")
for(uint8_t i=0;i<4;i++)
debug("%02x ", cyrfmfg_id[i]);
debugln("");
#endif
}
else
{
SUB_PROTO_INVALID;
#if DEBUG_BIND
#if DEBUG_BIND
debugln("No valid cloned ID");
#endif
#endif
}
}
else
{
SUB_PROTO_VALID;
//SUB_PROTO_VALID;
CYRF_GetMfgData(cyrfmfg_id);
cyrfmfg_id[3]^=RX_num; //Model match
}
}
//Model match
cyrfmfg_id[3]^=RX_num;
//Calc sop_col
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;
@@ -602,8 +633,8 @@ void DSM_init()
//Fix for OrangeRX using wrong DSM_pncodes by preventing access to "Col 8"
if(sop_col==0 && sub_protocol != DSMR)
{
cyrfmfg_id[rx_tx_addr[0]%3]^=0x01; //Change a bit so sop_col will be different from 0
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;
cyrfmfg_id[rx_tx_addr[0]%3]^=0x01; //Change a bit so sop_col will be different from 0
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;
}
}
@@ -616,7 +647,7 @@ void DSM_init()
else if(sub_protocol != DSMR)
{
uint8_t tmpch[10];
CYRF_FindBestChannels(tmpch, 10, 5, 3, 75);
CYRF_FindBestChannels(tmpch, 10, 5, 3, 75, FIND_CHANNEL_ANY);
//
uint8_t idx = random(0xfefefefe) % 10;
hopping_frequency[0] = tmpch[idx];
@@ -639,10 +670,10 @@ void DSM_init()
{
DSM_initialize_bind_phase();
phase = DSM_BIND_WRITE;
bind_counter=DSM_BIND_COUNT;
#if DEBUG_BIND
debugln("Bind Started: write count=%d",bind_counter);
#endif
bind_counter=DSM_BIND_COUNT;
#if DEBUG_BIND
debugln("Bind Started: write count=%d",bind_counter);
#endif
}
else
phase = DSM_CHANSEL;

View File

@@ -365,7 +365,7 @@ static void __attribute__((unused)) DEVO_cyrf_init()
static void __attribute__((unused)) DEVO_set_radio_channels()
{
CYRF_FindBestChannels(hopping_frequency, 3, 4, 4, 80);
CYRF_FindBestChannels(hopping_frequency, 3, 4, 4, 80, FIND_CHANNEL_ANY);
hopping_frequency[3] = hopping_frequency[0];
hopping_frequency[4] = hopping_frequency[1];
}

View File

@@ -41,11 +41,11 @@ static void __attribute__((unused)) E129_build_data_packet()
}
else
{
packet[ 1] = 0xA6;
packet[ 1] = 0xA6; // Set to A5 every few packets??
//Flags
if(sub_protocol == E129_E129)
packet[ 2] = 0xF7; // High rate 0xF7, low 0xF4
else //C186
packet[ 2] = 0xF7; // E129 High rate 0xF7, low 0xF4
if(sub_protocol == E129_C186)
{
packet[ 2] = 0xFA; // High rate 0xFA, medium 0xF7, low 0xF4
packet[13] = bit_reverse(rx_tx_addr[2]);
@@ -53,11 +53,18 @@ static void __attribute__((unused)) E129_build_data_packet()
packet[15] = bit_reverse(rx_tx_addr[0]);
packet[16] = bit_reverse(rx_tx_addr[1]);
}
packet[ 3] = GET_FLAG(CH10_SW, 0x40) // C159 loop flight 0x40, flag 0x04 is also set on this heli
| GET_FLAG(CH11_SW, 0x08); // C129V2 flip
// Other flags in packet[3] => E129 Mode: short press=0x20->0x00->0x20->..., long press=0x10->0x30->0x10->... => C186 throttle trim is doing the same:up=short press and down=long press
packet[ 3] = GET_FLAG(CH10_SW, 0x40) // C159: loop flight 0x40
| GET_FLAG(CH11_SW, 0x08); // C129V2: flip
//Other flags seen in packet[3]
// Flag 0x04 is set on some helis (C159/C190)
// E129 Mode: short press=0x20->0x00->0x20->..., long press=0x10->0x30->0x10->... => C186 throttle trim is doing the same:up=short press and down=long press
packet[ 4] = GET_FLAG(CH5_SW, 0x20) // Take off/Land 0x20
| GET_FLAG(CH6_SW, 0x04); // Emergency stop 0x04
| GET_FLAG(CH6_SW, 0x04) // Emergency stop 0x04
| GET_FLAG(CH12_SW, 0x80); // C190: debug mode->remote THR trim down sets 0x80
//Other flags seen in packet[4]
// C190 remote LANDING sets 0x10
// C190 remote THR trim down sets 0x80
//Channels and trims
uint16_t val = convert_channel_10b(AILERON,false);
uint8_t trim = convert_channel_8b(CH7) & 0xFC;

View File

@@ -31,6 +31,19 @@ enum {
FQ777_FLAG_FLIP = 0x80,
};
enum {
XBM37_B5_OK = 0x80,
XBM37_B6_RATE_LOW = 0x00,
XBM37_B6_RATE_MID = 0x01,
XBM37_B6_RATE_HIGH = 0x02,
XBM37_B6_LED_OFF = 0x04,
XBM37_B6_RTH = 0x08,
XBM37_B6_HEADLESS = 0x10,
XBM37_B6_VIDEO = 0x20,
XBM37_B6_PICTURE = 0x40,
XBM37_B6_FLIP = 0x80,
};
const uint8_t ssv_xor[] = {0x80,0x44,0x64,0x75,0x6C,0x71,0x2A,0x36,0x7C,0xF1,0x6E,0x52,0x9,0x9D,0x1F,0x78,0x3F,0xE1,0xEE,0x16,0x6D,0xE8,0x73,0x9,0x15,0xD7,0x92,0xE7,0x3,0xBA};
uint8_t FQ777_bind_addr [] = {0xe7,0xe7,0xe7,0xe7,0x67};
@@ -87,12 +100,21 @@ static void __attribute__((unused)) FQ777_send_packet()
uint8_t packet_ori[8];
if (IS_BIND_IN_PROGRESS)
{
// 4,5,6 = address fields
// last field is checksum of address fields
// Byte 0 is always 0x20; bytes 1-3 are sub-protocol specific
packet_ori[0] = 0x20;
packet_ori[1] = 0x15;
packet_ori[2] = 0x05;
packet_ori[3] = 0x06;
if (sub_protocol == XBM37)
{
packet_ori[1] = 0x14;
packet_ori[2] = 0x07;
packet_ori[3] = 0x03;
}
else // FQ777
{
packet_ori[1] = 0x15;
packet_ori[2] = 0x05;
packet_ori[3] = 0x06;
}
// Bytes 4-6: address fields, byte 7: checksum of address fields (shared by both protocols)
packet_ori[4] = rx_tx_addr[0];
packet_ori[5] = rx_tx_addr[1];
packet_ori[6] = rx_tx_addr[2];
@@ -100,36 +122,64 @@ static void __attribute__((unused)) FQ777_send_packet()
}
else
{
// throt, yaw, pitch, roll, trims, flags/left button,00,right button
//0-3 0x00-0x64
//4 roll/pitch/yaw trims. cycles through one trim at a time - 0-40 trim1, 40-80 trim2, 80-C0 trim3 (center: A0 20 60)
//5 flags for throttle button, two buttons above throttle - def: 0x40
//6 00 ??
//7 checksum - add values in other fields
if (sub_protocol == XBM37)
{
packet_ori[0] = convert_channel_16b_limit(THROTTLE,0xE1,0); // reverse channel
packet_ori[1] = convert_channel_16b_limit(RUDDER,0,0xE1);
packet_ori[2] = convert_channel_16b_limit(AILERON,0xE1,0); // reverse channel
packet_ori[3] = convert_channel_16b_limit(ELEVATOR,0xE1,0); // reverse channel
packet_ori[4] = (convert_channel_8b(CH13) >> 2) + 1; // ele trim (01..20..40) front ^ plus 0x01, back - minus 0x01 per click
packet_ori[5] = 0x3F - (convert_channel_8b(CH14) >> 2) // ail trim (40..20..01) left <- plus 0x01, right -> minus 0x01 per click
| GET_FLAG(CH12_SW, XBM37_B5_OK); // bit7 = OK button ...unknown use for this model?
// Trims are usually done through the radio configuration but leaving the code here just in case...
uint8_t trim_mod = packet_count % 144;
uint8_t trim_val = 0;
if (36 <= trim_mod && trim_mod < 72) // yaw
trim_val = 0x20; // don't modify yaw trim
else
if (108 < trim_mod && trim_mod) // pitch
trim_val = 0xA0;
else // roll
trim_val = 0x60;
uint8_t rate_bits;
if (CH5_SW)
rate_bits = XBM37_B6_RATE_HIGH; // high rate
else if (Channel_data[CH5] < CHANNEL_MIN_COMMAND)
rate_bits = XBM37_B6_RATE_LOW; // low rate
else
rate_bits = XBM37_B6_RATE_MID; // medium rate
packet_ori[0] = convert_channel_16b_limit(THROTTLE,0,0x64);
packet_ori[1] = convert_channel_16b_limit(RUDDER,0,0x64);
packet_ori[2] = convert_channel_16b_limit(ELEVATOR,0,0x64);
packet_ori[3] = convert_channel_16b_limit(AILERON,0,0x64);
packet_ori[4] = trim_val; // calculated above
packet_ori[5] = GET_FLAG(CH5_SW, FQ777_FLAG_FLIP)
| GET_FLAG(CH7_SW, FQ777_FLAG_HEADLESS)
| GET_FLAG(!CH6_SW, FQ777_FLAG_RETURN)
| GET_FLAG(CH8_SW,FQ777_FLAG_EXPERT);
packet_ori[6] = 0x00;
// calculate checksum
packet_ori[6] = rate_bits // bits[1:0] = rate
| GET_FLAG(CH8_SW, XBM37_B6_LED_OFF) // bit2 = LED off
| GET_FLAG(CH11_SW, XBM37_B6_RTH) // bit3 = RTH
| GET_FLAG(CH7_SW, XBM37_B6_HEADLESS) // bit4 = headless
| GET_FLAG(CH10_SW, XBM37_B6_VIDEO) // bit5 = video
| GET_FLAG(CH9_SW, XBM37_B6_PICTURE) // bit6 = picture
| GET_FLAG(CH6_SW, XBM37_B6_FLIP); // bit7 = flip
}
else // FQ777
{
// throt, yaw, pitch, roll, trims, flags/left button,00,right button
//0-3 0x00-0x64
//4 roll/pitch/yaw trims. cycles through one trim at a time - 0-40 trim1, 40-80 trim2, 80-C0 trim3 (center: A0 20 60)
//5 flags for throttle button, two buttons above throttle - def: 0x40
//6 00 ??
//7 checksum - add values in other fields
// Trims are usually done through the radio configuration but leaving the code here just in case...
uint8_t trim_mod = packet_count % 144;
uint8_t trim_val = 0;
if (36 <= trim_mod && trim_mod < 72) // yaw
trim_val = 0x20; // don't modify yaw trim
else
if (108 < trim_mod && trim_mod) // pitch
trim_val = 0xA0;
else // roll
trim_val = 0x60;
packet_ori[0] = convert_channel_16b_limit(THROTTLE,0,0x64);
packet_ori[1] = convert_channel_16b_limit(RUDDER,0,0x64);
packet_ori[2] = convert_channel_16b_limit(ELEVATOR,0,0x64);
packet_ori[3] = convert_channel_16b_limit(AILERON,0,0x64);
packet_ori[4] = trim_val; // calculated above
packet_ori[5] = GET_FLAG(CH5_SW, FQ777_FLAG_FLIP)
| GET_FLAG(CH7_SW, FQ777_FLAG_HEADLESS)
| GET_FLAG(!CH6_SW, FQ777_FLAG_RETURN)
| GET_FLAG(CH8_SW,FQ777_FLAG_EXPERT);
packet_ori[6] = 0x00;
}
// Data packet checksum: sum of bytes 0-6 (not applied to bind packets)
uint8_t checksum = 0;
for (int i = 0; i < 7; ++i)
checksum += packet_ori[i];
@@ -181,12 +231,17 @@ void FQ777_init(void)
BIND_IN_PROGRESS; // autobind protocol
bind_counter = FQ777_BIND_COUNT;
packet_count=0;
hopping_frequency[0] = 0x4D;
hopping_frequency[1] = 0x43;
hopping_frequency[2] = 0x27;
// Sub-protocol hop channel table: first 3 channels differ; channel[3]=0x07 is shared
static const uint8_t hop_ch[2][3] = {
{0x4D, 0x43, 0x27}, // FQ777
{0x49, 0x34, 0x26}, // XBM-37
};
hopping_frequency[0] = hop_ch[sub_protocol][0];
hopping_frequency[1] = hop_ch[sub_protocol][1];
hopping_frequency[2] = hop_ch[sub_protocol][2];
hopping_frequency[3] = 0x07;
hopping_frequency_no=0;
rx_tx_addr[2] = 0x00;
rx_tx_addr[2] = (sub_protocol == XBM37) ? (RX_num & 0x3F) : 0x00; // XBM-37 uses receiver number (0-63) for model match capability
rx_tx_addr[3] = 0xe7;
rx_tx_addr[4] = 0x67;
FQ777_RF_init();

View File

@@ -33,15 +33,30 @@ Multiprotocol is distributed in the hope that it will be useful,
#define FX620_PAYLOAD_SIZE 7
#define FX620_CH_OFFSET 1
#define FX9630_PACKET_PERIOD 8124
#define FX9630_BIND_PACKET_PERIOD 8124
#define FX9630_PACKET_PERIOD 8124 //8156 on QIDI-560
#define FX9630_BIND_CHANNEL 51
#define FX9630_PAYLOAD_SIZE 8
#define FX9630_NUM_CHANNELS 3
#define FX9630_WRITE_TIME 500
#define FX_QF012_PACKET_PERIOD 12194
#define FX_QF012_RX_PAYLOAD_SIZE 3
#define FX_BM26_BIND_CHANNEL 40
#define FX_BM26_PACKET_PERIOD 14066
#define FX_A570_PACKET_PERIOD 10270
//#define FORCE_FX620_ID
//#define FORCE_FX9630_ID
//#define FORCE_QIDI_ID
//#define FORCE_FX_A570_ID
enum
{
FX_DATA=0,
FX_RX,
};
static void __attribute__((unused)) FX_send_packet()
{
@@ -51,17 +66,21 @@ static void __attribute__((unused)) FX_send_packet()
if(IS_BIND_DONE)
{
XN297_Hopping(hopping_frequency_no++);
if(sub_protocol == FX9630)
{
XN297_SetTXAddr(rx_tx_addr, 4);
if(sub_protocol >= FX9630)
{ // FX9630 & FX_Q560 & FX_QF012 & FX_A570
if (hopping_frequency_no >= FX9630_NUM_CHANNELS)
{
hopping_frequency_no = 0;
trim_ch++;
if(trim_ch > 3) trim_ch = 0;
if(sub_protocol == FX9630)
{
trim_ch++;
trim_ch &= 3;
}
else // FX_Q560 & FX_QF012 & FX_A570
trim_ch = 0;
}
}
else // FX816 and FX620
else // FX816 & FX620
{
hopping_frequency_no &= 0x03;
}
@@ -71,21 +90,7 @@ static void __attribute__((unused)) FX_send_packet()
//Channels
uint8_t val;
if (sub_protocol == FX9630)
{
packet[0] = convert_channel_8b(THROTTLE);
packet[1] = convert_channel_8b(AILERON);
packet[2] = 0xFF - convert_channel_8b(ELEVATOR);
packet[3] = convert_channel_8b(RUDDER);
val = trim_ch==0 ? 0x20 : (convert_channel_8b(trim_ch + CH6) >> 2); // no trim on Throttle
packet[4] = val; // Trim for channel x 0C..20..34
packet[5] = (trim_ch << 4) // channel x << 4
| GET_FLAG(CH5_SW, 0x01) // DR toggle swich: 0 small throw, 1 large throw
// FX9630 =>0:6G small throw, 1:6G large throw, 2:3D
// QIDI-550=>0:3D, 1:6G, 2:Torque
| ((Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x00 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x02 : 0x01)) << 1);
}
else // FX816 and FX620
if (sub_protocol == FX816 || sub_protocol == FX620)
{
uint8_t offset=sub_protocol == FX816 ? FX816_CH_OFFSET:FX620_CH_OFFSET;
val=convert_channel_8b(AILERON);
@@ -97,6 +102,60 @@ static void __attribute__((unused)) FX_send_packet()
packet[offset] = sub_protocol == FX816 ? 0:0x7F;
packet[offset+1] = convert_channel_16b_limit(THROTTLE,0,100); //FX816:0x00..0x63, FX620:0x00..0x5E but that should work
}
else if (sub_protocol == FX_A570)
{
packet[0] = convert_channel_16b_limit(THROTTLE,0x1C,0xE4);
packet[1] = convert_channel_16b_limit(AILERON,0xE4,0x1C);
packet[2] = convert_channel_16b_limit(ELEVATOR,0xE4,0x1C);
packet[3] = convert_channel_16b_limit(RUDDER,0x1C,0xE4);
// Trims - Ruddder L:01 R:02, Aileron L:03 R:04, Elevator Fwd:05 Back:06
packet[4] = (Channel_data[CH10] < CHANNEL_MIN_COMMAND ? 0x01 : GET_FLAG(CH10_SW, 0x02)) // Rudder trim
| (Channel_data[CH11] < CHANNEL_MIN_COMMAND ? 0x03 : GET_FLAG(CH11_SW, 0x04)) // Aileron trim
| (Channel_data[CH12] < CHANNEL_MIN_COMMAND ? 0x06 : GET_FLAG(CH12_SW, 0x05)); // Elevator trim
packet[5] = GET_FLAG(CH5_SW, 0x40) // motors stop
// A570 flight modes = 0>vertical flight(3 axis mode):00, 1>flat flight(6G):02, 2>vertical flight(2 axis mode):04
| (Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x00 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x04 : 0x02))
| GET_FLAG(CH7_SW, 0x01) // 0:low rate, 1:high rate
| GET_FLAG(CH8_SW, 0x08) // LED color change
| GET_FLAG(CH9_SW, 0x10); // LED off
}
else
{ // FX9630 & FX_Q560 & FX_QF012 & FX_BM26
packet[0] = convert_channel_8b(THROTTLE);
packet[1] = convert_channel_8b(AILERON);
packet[2] = 0xFF - convert_channel_8b(ELEVATOR);
packet[3] = convert_channel_8b(RUDDER);
if ( sub_protocol == FX_BM26 ) // BM26 Ail, Elev, Rud range between 0x00-0x3F-0x7F
{
packet[1] >>= 1;
packet[2] >>= 1;
packet[3] >>= 1;
}
val = trim_ch==0 ? 0x20 : (convert_channel_8b(trim_ch + CH6) >> 2); // no trim on Throttle
packet[4] = val; // Trim for channel x 0C..20..34
packet[5] = (trim_ch << 4) // channel x << 4
| GET_FLAG(CH5_SW, (sub_protocol == FX_QF012 ? 0x08 : 0x01)) // DR toggle swich: 0 small throw, 1 large throw / Q560 acrobatic / QF012 Special effects
// FX9630 =>0:6G small throw, 1:6G large throw, 2:3D
// QIDI-550=>0:3D, 1:6G, 2:Torque
// QF012=>0:beginner(6G), 1:mid(3D), 2:expert(Gyro off)
| (Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x00 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x04 : 0x02));
if(sub_protocol == FX_Q560)
packet[5] |= GET_FLAG(CH7_SW, 0x18); // Q560 LED flag 0x10 conflicting with trim_ch... Corrected on new boards using 0x08 instead
else if (sub_protocol == FX_QF012)
packet[5] |= GET_FLAG(CH7_SW, 0x40) // QF012 invert flight
| GET_FLAG(CH8_SW, 0x80); // QF012 Restore fine tunning midpoint
else if ( sub_protocol == FX_BM26 )
{
packet[5] &= 0xC0;
packet[5] += 0x40;
// BM26 P51=>0:beginner(6G), 1:mid(3D), 2:expert(Gyro off)
packet[5] |= (Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x08 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x0C : 0x0A));
packet[6] = GET_FLAG( CH5_SW, 0x20) // 6G Roll
| GET_FLAG(!CH7_SW, 0x08) // Light control switch, default on
| GET_FLAG(!CH8_SW, 0x10) // Turn signal switch, default on
| GET_FLAG( CH9_SW, 0x01); // Gyro Calibration
}
}
//Bind and specifics
if(sub_protocol == FX816)
@@ -123,7 +182,7 @@ static void __attribute__((unused)) FX_send_packet()
packet[5] = 0xAB; // Is it based on ID??
}
}
else // FX9630
else // FX9630 & FX_Q560 & FX_QF012 & FX_BM26 & FX_A570
{
if(IS_BIND_IN_PROGRESS)
{
@@ -136,15 +195,17 @@ static void __attribute__((unused)) FX_send_packet()
//Check
uint8_t last_packet_idx = packet_length-1;
if (sub_protocol == FX9630 && IS_BIND_IN_PROGRESS)
if (sub_protocol >= FX9630 && IS_BIND_IN_PROGRESS)
last_packet_idx--;
val=0;
for(uint8_t i=0;i<last_packet_idx;i++)
val+=packet[i];
if (sub_protocol == FX9630)
if (sub_protocol >= FX9630)
val = val ^ 0xFF;
packet[last_packet_idx]=val;
if ( IS_BIND_IN_PROGRESS && sub_protocol == FX_BM26 ) packet[7] = packet[6];
//Debug
#if 0
for(uint8_t i=0;i<packet_length;i++)
@@ -161,6 +222,7 @@ static void __attribute__((unused)) FX_send_packet()
static void __attribute__((unused)) FX_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
packet_length = FX9630_PAYLOAD_SIZE; // default
if(sub_protocol == FX816)
{
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc", 5);
@@ -175,12 +237,23 @@ static void __attribute__((unused)) FX_RF_init()
packet_period = FX620_BIND_PACKET_PERIOD;
packet_length = FX620_PAYLOAD_SIZE;
}
else // FX9630
else if (sub_protocol == FX_BM26)
{
XN297_SetTXAddr((uint8_t *)"\x12\x34\x10\x10", 4);
XN297_RFChannel(FX_BM26_BIND_CHANNEL);
packet_period = FX_BM26_PACKET_PERIOD;
}
else if(sub_protocol == FX_A570)
{
XN297_SetTXAddr((uint8_t *)"\x56\x78\x92\x13", 4);
XN297_RFChannel(FX9630_BIND_CHANNEL);
packet_period = FX_A570_PACKET_PERIOD;
}
else // FX9630 & FX_Q560 & FX_QF012
{
XN297_SetTXAddr((uint8_t *)"\x56\x78\x90\x12", 4);
XN297_RFChannel(FX9630_BIND_CHANNEL);
packet_period = FX9630_BIND_PACKET_PERIOD;
packet_length = FX9630_PAYLOAD_SIZE;
packet_period = sub_protocol == FX_QF012 ? FX_QF012_PACKET_PERIOD : FX9630_PACKET_PERIOD;
}
}
@@ -207,40 +280,121 @@ static void __attribute__((unused)) FX_initialize_txid()
for(uint8_t i=1;i<FX_NUM_CHANNELS;i++)
hopping_frequency[i] = i*10 + hopping_frequency[0];
}
else // FX9630
else // FX9630 & FX_Q560 & FX_QF012 & FX_BM26 & FX_A570
{
//??? Need to find out how the first RF channel is calculated ???
hopping_frequency[0] = sub_protocol == FX_BM26? 0x0C : 0x13;
if (sub_protocol == FX_A570) hopping_frequency[0] = 0x0E;
//Other 2 RF channels are sent during the bind phase so they can be whatever
hopping_frequency[1] = RX_num & 0x0F + 0x1A;
hopping_frequency[2] = rx_tx_addr[3] & 0x0F + 0x38;
#ifdef FORCE_FX9630_ID
memcpy(rx_tx_addr,(uint8_t*)"\xCE\x31\x9B\x73", 4);
memcpy(hopping_frequency,"\x13\x1A\x38", FX9630_NUM_CHANNELS); //Original dump=>19=0x13,26=0x1A,56=0x38
#else
hopping_frequency[0] = 0x13; // constant???
hopping_frequency[1] = RX_num & 0x0F + 0x1A;
hopping_frequency[2] = rx_tx_addr[3] & 0x0F + 0x38;
#endif
#ifdef FORCE_QIDI_ID
memcpy(rx_tx_addr,(uint8_t*)"\x23\xDC\x76\xA2", 4);
memcpy(hopping_frequency,"\x08\x25\x33", FX9630_NUM_CHANNELS); //Original dump=>08=0x08,37=0x25,51=0x33
//QIDI-560 #1
//memcpy(rx_tx_addr,(uint8_t*)"\x38\xC7\x6D\x8D", 4);
//memcpy(hopping_frequency,"\x0D\x20\x3A", FX9630_NUM_CHANNELS);
#endif
//??? Need to find out how the first RF channel is calculated ???
}
// BM26 rx_tx_addr: "\x17\x03\x00\x00", hopping_frequency: 0x0C,0x30,0x43(12,48,67)
#ifdef FORCE_FX_A570_ID
memcpy(rx_tx_addr,(uint8_t*)"\xA9\x56\xFC\x1C", 4);
memcpy(hopping_frequency,"\x0E\x1F\x39", FX9630_NUM_CHANNELS); //Original dump=>14=0x0E,31=0x1F,57=0x39
#endif
}
}
uint16_t FX_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(packet_period);
#endif
if(bind_counter)
if(--bind_counter==0)
#ifdef FX_HUB_TELEMETRY
bool rx=false;
static uint8_t telem_count = 0;
switch(phase)
{
BIND_DONE;
if(sub_protocol == FX620)
{
XN297_SetTXAddr(rx_tx_addr, 3);
packet_period = FX620_PACKET_PERIOD;
}
case FX_DATA:
rx = XN297_IsRX();
XN297_SetTxRxMode(TXRX_OFF);
#endif
#ifdef MULTI_SYNC
telemetry_set_input_sync(packet_period);
#endif
if(bind_counter)
if(--bind_counter==0)
{
BIND_DONE;
if(sub_protocol == FX620)
{
XN297_SetTXAddr(rx_tx_addr, 3);
packet_period = FX620_PACKET_PERIOD;
}
else if(sub_protocol >= FX9630)
{ // FX9630 & FX_Q560 & FX_QF012
XN297_SetTXAddr(rx_tx_addr, 4);
#ifdef FX_HUB_TELEMETRY
XN297_SetRXAddr(rx_tx_addr, FX_QF012_RX_PAYLOAD_SIZE);
#endif
}
}
FX_send_packet();
#ifdef FX_HUB_TELEMETRY
if (sub_protocol == FX816 || sub_protocol == FX620 || sub_protocol == FX_A570)
break;
if(rx)
{
debug("RX");
if(XN297_ReadPayload(packet_in, FX_QF012_RX_PAYLOAD_SIZE))
{//Good CRC
telemetry_link = 1;
telemetry_lost = 0;
telem_count = 0;
if ( sub_protocol == FX_BM26 )
v_lipo1 = packet_in[0] < packet_in[2] ? 60:81; // packets: AA 00 55 -> 55 00 AA = low voltage 3.7V
else
v_lipo1 = packet_in[1] ? 60:81; // packets: A5 00 11 -> A5 01 11 = low voltage 3.7V
#if 0
for(uint8_t i=0; i < FX_QF012_RX_PAYLOAD_SIZE; i++)
debug(" %02X", packet_in[i]);
#endif
}
debugln();
}
if(telem_count > 4*63) // Around 3.5 sec with no telemetry
telemetry_lost = 1;
else
{
telem_count++;
if(!telemetry_lost && (telem_count & 0x3F) == 0)
{// Should have received a telem packet but... Send telem to the radio to keep it alive
telemetry_link = 1;
#if 0
debugln("Miss");
#endif
}
}
phase++;
return FX9630_WRITE_TIME;
default: //FX_RX
/* { // Wait for packet to be sent before switching to receive mode
uint16_t start=(uint16_t)micros(), count=0;
while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 500)
{
if(XN297_IsPacketSent())
break;
count++;
}
debug("%d",count);
} */
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = FX_DATA;
return packet_period - FX9630_WRITE_TIME;
}
FX_send_packet();
#endif
return packet_period;
}
@@ -251,6 +405,11 @@ void FX_init()
FX_RF_init();
hopping_frequency_no = 0;
bind_counter=FX_BIND_COUNT;
#ifdef FX_HUB_TELEMETRY
RX_RSSI = 100; // Dummy value
telemetry_lost = 1;
phase = FX_DATA;
#endif
}
#endif

View File

@@ -0,0 +1,132 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(H36_NRF24L01_INO)
#include "iface_xn297.h"
//#define FORCE_H36_ORIGINAL_ID
#define H36_PAYLOAD_SIZE 13
#define H36_RF_NUM_CHANNELS 4
#define H36_BIND_PACKET_PERIOD 10285
#define H36_BIND_COUNT 648 //3sec
enum {
H36_DATA1=0,
H36_DATA2,
H36_DATA3,
H36_DATA4,
};
static void __attribute__((unused)) H36_send_packet()
{
if(IS_BIND_DONE && phase == H36_DATA1)
{
hopping_frequency_no++;
hopping_frequency_no&=3;
XN297_Hopping(hopping_frequency_no);
}
packet[0] = 0x2E; // constant?
memcpy(&packet[2],rx_tx_addr,3);
if(IS_BIND_IN_PROGRESS)
{//Bind
memcpy(&packet[5],hopping_frequency,4);
memset(&packet[9], 0x00, 3);
packet[12] = 0xED; // constant?
bind_counter--;
if(bind_counter == 0)
BIND_DONE;
}
else
{//Normal
packet[5] = convert_channel_8b(THROTTLE);
packet[6] = convert_channel_8b(RUDDER);
packet[7] = convert_channel_8b(ELEVATOR);
packet[8] = convert_channel_8b(AILERON);
packet[9] = GET_FLAG(CH6_SW, 0x02) //Headless
|GET_FLAG(CH7_SW, 0x04); //RTH(temporary)
packet[10] = 0x20; //Trim A centered(0x20)
packet[11] = CH5_SW?0x60:0x20; //Flip(0x40)|Trim E centered(0x20)
packet[12] = 0xA0; //High(0x80)/Low(0x40) rates|Trim R centered(0x20)?
}
//crc
packet[1]=0xAA;
for(uint8_t i=5;i<12;i++)
packet[1] ^= packet[i];
//Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, H36_PAYLOAD_SIZE);
#ifdef DEBUG_SERIAL
debug("H%d P",hopping_frequency_no);
for(uint8_t i=0; i < H36_PAYLOAD_SIZE; i++)
debug(" %02X", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) H36_initialize_txid()
{
rx_tx_addr[0] = rx_tx_addr[3];
calc_fh_channels(4);
#ifdef FORCE_H36_ORIGINAL_ID
if(!RX_num)
{
memcpy(rx_tx_addr,(uint8_t *)"\x00\x11\x00",3);
memcpy(hopping_frequency,(uint8_t *)"\x36\x3A\x31\x2B",4); //54, 58, 49, 43
}
#endif
}
static void __attribute__((unused)) H36_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t*)"\xCC\x6C\x47\x90\x53", 5);
XN297_RFChannel(50); //Bind channel
}
uint16_t H36_callback()
{
H36_send_packet();
switch(phase)
{
case H36_DATA1:
phase++;
return 1830;
case H36_DATA2:
case H36_DATA3:
phase++;
return 3085;
default://DATA4
#ifdef MULTI_SYNC
telemetry_set_input_sync(18500);
#endif
phase = H36_DATA1;
break;
}
return 10500;
}
void H36_init()
{
BIND_IN_PROGRESS; // Autobind protocol
H36_initialize_txid();
H36_RF_init();
phase = H36_DATA1;
hopping_frequency_no = 0;
bind_counter = H36_BIND_COUNT;
}
#endif

View File

@@ -17,21 +17,25 @@
#include "iface_xn297.h" // mix of nrf and xn297 at 1Mb...
#define HONTAI_BIND_COUNT 80
#define HONTAI_PACKET_PERIOD 13500
#define FQ777_951_PACKET_PERIOD 10000
#define HONTAI_INITIAL_WAIT 500
#define HONTAI_BIND_PACKET_SIZE 10
#define HONTAI_PACKET_SIZE 12
#define HONTAI_RF_BIND_CHANNEL 0
#define HONTAI_BIND_COUNT 80
#define HONTAI_PACKET_PERIOD 13500
#define FQ777_951_PACKET_PERIOD 10000
#define HONTAI_INITIAL_WAIT 500
#define HONTAI_BIND_PACKET_SIZE 10
#define HONTAI_PACKET_SIZE 12
#define HONTAI_RF_BIND_CHANNEL 0
#define HONTAI_XKK170_RF_BIND_CHANNEL 20
#define HONTAI_XKK170_PACKET_PERIOD 8085
//#define FORCE_HONTAI_XKK170_ORIGINAL_ID
enum{
HONTAI_FLAG_FLIP = 0x01,
HONTAI_FLAG_PICTURE = 0x02,
HONTAI_FLAG_VIDEO = 0x04,
HONTAI_FLAG_HEADLESS = 0x08,
HONTAI_FLAG_RTH = 0x10,
HONTAI_FLAG_CALIBRATE = 0x20,
HONTAI_FLAG_FLIP = 0x01,
HONTAI_FLAG_PICTURE = 0x02,
HONTAI_FLAG_VIDEO = 0x04,
HONTAI_FLAG_HEADLESS = 0x08,
HONTAI_FLAG_RTH = 0x10,
HONTAI_FLAG_CALIBRATE = 0x20,
};
static void __attribute__((unused)) HONTAI_send_packet()
@@ -40,6 +44,8 @@ static void __attribute__((unused)) HONTAI_send_packet()
{
memcpy(packet, rx_tx_addr, 5);
memset(&packet[5], 0, 3);
//if(sub_protocol == HONTAI_XKK170)
// packet[6] = 0xD2;
packet_length = HONTAI_BIND_PACKET_SIZE;
}
else
@@ -50,58 +56,88 @@ static void __attribute__((unused)) HONTAI_send_packet()
XN297_Hopping(hopping_frequency_no++);
hopping_frequency_no %= 3;
memset(packet,0,HONTAI_PACKET_SIZE);
packet[3] = convert_channel_16b_limit(THROTTLE, 0, 127) << 1; // Throttle
packet[4] = convert_channel_16b_limit(AILERON, 63, 0); // Aileron
packet[5] = convert_channel_16b_limit(ELEVATOR, 0, 63); // Elevator
packet[6] = convert_channel_16b_limit(RUDDER, 0, 63); // Rudder
if(sub_protocol == X5C1)
packet[7] = convert_channel_16b_limit(AILERON, 0, 63)-31; // Aileron trim
for(uint8_t i=0; i<4; i++)
packet[i+3] = convert_channel_8b(CH_TAER[i]);
if(sub_protocol != HONTAI_XKK170)
{
//Drive trims
packet[7] = (packet[4]>>3)-16;
packet[8] = (packet[6]>>3)-16;
packet[9] = (packet[5]>>3)-16;
//Reverse aileron
packet[4] ^= 0xFF;
//Limit range
for(uint8_t i=3; i<7; i++)
packet[i] >>= 2; //00..63
}
else
packet[7] = convert_channel_16b_limit(AILERON, 0, 32)-16; // Aileron trim
packet[8] = convert_channel_16b_limit(RUDDER, 0, 32)-16; // Rudder trim
if (sub_protocol == X5C1)
packet[9] = convert_channel_16b_limit(ELEVATOR, 0, 63)-31; // Elevator trim
else
packet[9] = convert_channel_16b_limit(ELEVATOR, 0, 32)-16; // Elevator trim
{//K170
//packet[2] = 0xAB; //This value keeps changing when touching any button... Left over from debug?
//Sticks
for(uint8_t i=1; i<4; i++)
packet[i+3] = convert_channel_16b_limit(CH_TAER[i],0x28,0xD8);
packet[6] ^= 0xFF; //Reverse rudder
//flags
packet[1] = GET_FLAG(CH8_SW, 0x04) //Gyro calibration (momentary)
|GET_FLAG(CH11_SW, 0x10); //XK K270 optical flow switch
// |GET_FLAG(CH_SW, 0x08) //Unk long press second top right button (momentary)
// |GET_FLAG(CH_SW, 0x10) //Unk short press second top right button (toggle)
// |GET_FLAG(CH_SW, 0x40) //Unk short press second top left button (momentary)
// |GET_FLAG(CH_SW, 0x80); //Unk long press second top left button (momentary)
uint8_t rate = 0x80; //Mid rate
if(CH5_SW)
rate = 0xC0; //High rate
else if(Channel_data[CH5] < CHANNEL_MIN_COMMAND)
rate = 0x40; //Low rate
packet[8] = rate
|GET_FLAG(CH7_SW, 0x04) //Take-off/Landing (momentary)
|GET_FLAG(CH6_SW, 0x10); //Emergency (momentary)
//Trims
packet[7] = ((convert_channel_8b(CH9)^0xFF)>>2)-31; // Trim Aileron
packet[9] = ( convert_channel_8b(CH10) >>2)-32; // Trim Elevator
}
switch(sub_protocol)
{
case HONTAI:
packet[0] = 0x0B;
packet[3] |= GET_FLAG(CH7_SW, 0x01); // Picture
packet[4] |= GET_FLAG(CH10_SW, 0x80) // RTH
packet[4] |= GET_FLAG(CH10_SW, 0x80) // RTH
| GET_FLAG(CH9_SW, 0x40); // Headless
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
| GET_FLAG(CH5_SW, 0x40); // Flip
packet[6] |= GET_FLAG(CH8_SW, 0x80); // Video
break;
case JJRCX1:
packet[0] = GET_FLAG(CH6_SW, 0x02); // Arm
packet[3] |= GET_FLAG(CH7_SW, 0x01); // Picture
packet[4] |= 0x80; // unknown
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
packet[4] |= 0x80; // unknown
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
| GET_FLAG(CH5_SW, 0x40); // Flip
packet[6] |= GET_FLAG(CH8_SW, 0x80); // Video
packet[8] = 0xC0 // high rate, no rudder trim
| GET_FLAG(CH10_SW, 0x02) // RTH
packet[8] = 0xC0 // high rate, no rudder trim
| GET_FLAG(CH10_SW, 0x02) // RTH
| GET_FLAG(CH9_SW, 0x01); // Headless
break;
case X5C1:
packet[0] = 0x0B;
packet[3] |= GET_FLAG(CH7_SW, 0x01); // Picture
packet[4] = 0x80 // unknown
packet[4] = 0x80 // unknown
| GET_FLAG(CH6_SW, 0x40); // Lights
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
| GET_FLAG(CH5_SW, 0x40); // Flip
packet[6] |= GET_FLAG(CH8_SW, 0x80); // Video
packet[8] = 0xC0 // high rate, no rudder trim
| GET_FLAG(CH10_SW, 0x02) // RTH
packet[7] <<= 1; // Aileron trim
packet[8] = 0xC0 // high rate, no rudder trim
| GET_FLAG(CH10_SW, 0x02) // RTH
| GET_FLAG(CH9_SW, 0x01); // Headless
packet[9] <<= 1; // Elevator trim
break;
case FQ777_951:
packet[0] = GET_FLAG(CH7_SW, 0x01) // Picture
| GET_FLAG(CH8_SW, 0x02); // Video
packet[3] |= GET_FLAG(CH5_SW, 0x01); // Flip
packet[4] |= 0xC0; // High rate (mid=0xa0, low=0x60)
packet[4] |= 0xC0; // High rate (mid=0xa0, low=0x60)
packet[5] |= GET_FLAG(CH11_SW, 0x80); // Calibrate
packet[6] |= GET_FLAG(CH9_SW, 0x40); // Headless
break;
@@ -109,13 +145,18 @@ static void __attribute__((unused)) HONTAI_send_packet()
packet_length = HONTAI_PACKET_SIZE;
}
// CRC 16 bits reflected in and out
crc=0xFFFF;
for(uint8_t i=0; i< packet_length-2; i++)
crc16_update(bit_reverse(packet[i]),8);
crc ^= 0xFFFF;
packet[packet_length-2]=bit_reverse(crc>>8);
packet[packet_length-1]=bit_reverse(crc);
if(sub_protocol != HONTAI_XKK170)
{
// CRC 16 bits reflected in and out
crc=0xFFFF;
for(uint8_t i=0; i< packet_length-2; i++)
crc16_update(bit_reverse(packet[i]),8);
crc ^= 0xFFFF;
packet[packet_length-2]=bit_reverse(crc>>8);
packet[packet_length-1]=bit_reverse(crc);
}
else
memset(&packet[packet_length-2], 0xAA, 2);
// Power on, TX mode, 2byte CRC
/*if(sub_protocol == JJRCX1)
@@ -135,6 +176,11 @@ static void __attribute__((unused)) HONTAI_send_packet()
NRF24L01_WritePayload(packet, packet_length);
else
XN297_WritePayload(packet, packet_length);
#ifdef DEBUG_SERIAL
for(uint8_t i=0; i < packet_length; i++)
debug("%02X ", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) HONTAI_RF_init()
@@ -152,15 +198,16 @@ static void __attribute__((unused)) HONTAI_RF_init()
}
else
{
XN297_SetTXAddr((const uint8_t*)"\xd2\xb5\x99\xb3\x4a", 5);
memcpy(rx_id,(const uint8_t*)"\xD2\xB5\x99\xB3\x41",5);
if(sub_protocol == HONTAI_XKK170)
rx_id[4] = 0x4A;
XN297_SetTXAddr(rx_id, 5);
//XN297_HoppingCalib(3);
}
XN297_RFChannel(HONTAI_RF_BIND_CHANNEL);
XN297_RFChannel(sub_protocol==HONTAI_XKK170?HONTAI_XKK170_RF_BIND_CHANNEL:HONTAI_RF_BIND_CHANNEL);
}
const uint8_t PROGMEM HONTAI_hopping_frequency_nonels[][3] = {
{0x05, 0x19, 0x28}, // Hontai
{0x0a, 0x1e, 0x2d}}; // JJRC X1
const uint8_t PROGMEM HONTAI_hopping_frequency[3] = { 0x05, 0x19, 0x28 };
const uint8_t PROGMEM HONTAI_addr_vals[4][16] = {
{0x24, 0x26, 0x2a, 0x2c, 0x32, 0x34, 0x36, 0x4a, 0x4c, 0x4e, 0x54, 0x56, 0x5a, 0x64, 0x66, 0x6a},
@@ -178,32 +225,69 @@ static void __attribute__((unused)) HONTAI_init2()
data_tx_addr[2] = pgm_read_byte_near( &HONTAI_addr_vals[2][ rx_tx_addr[4] & 0x0f]);
data_tx_addr[3] = pgm_read_byte_near( &HONTAI_addr_vals[3][(rx_tx_addr[4] >> 4) & 0x0f]);
data_tx_addr[4] = 0x24;
#ifdef DEBUG_SERIAL
debug("A N");
for(uint8_t i=0; i < 5; i++)
debug(" %02X", data_tx_addr[i]);
debugln();
#endif
if(sub_protocol == JJRCX1)
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, data_tx_addr, 5);
else
XN297_SetTXAddr(data_tx_addr, 5);
//Hopping frequency table
uint8_t val;
for(uint8_t i=0;i<3;i++)
hopping_frequency[i]=pgm_read_byte_near( &HONTAI_hopping_frequency_nonels[sub_protocol == JJRCX1?1:0][i] );
{
if(sub_protocol==HONTAI_XKK170)
val = 60+10*i;
else
{
val = pgm_read_byte_near( &HONTAI_hopping_frequency[i] );
if(sub_protocol == JJRCX1)
val += 5;
}
hopping_frequency[i] = val;
}
hopping_frequency_no=0;
#ifdef DEBUG_SERIAL
debug("H");
for(uint8_t i=0; i < 3; i++)
debug(" %d(%02X)", hopping_frequency[i], hopping_frequency[i]);
debugln();
#endif
}
static void __attribute__((unused)) HONTAI_initialize_txid()
{
rx_tx_addr[4] = rx_tx_addr[2];
if(sub_protocol == HONTAI || sub_protocol == FQ777_951)
{
rx_tx_addr[0] = 0x4c; // first three bytes some kind of model id? - set same as stock tx
rx_tx_addr[1] = 0x4b;
rx_tx_addr[2] = 0x3a;
// First three bytes some kind of model id? - set same as stock tx
if(sub_protocol == JJRCX1 || sub_protocol == X5C1)
{//JJRCX1 & X5C1
rx_tx_addr[0] = 0x4B;
rx_tx_addr[1] = 0x59;
rx_tx_addr[2] = 0x3A;
}
else
{
rx_tx_addr[0] = 0x4b; // JJRC X1
rx_tx_addr[1] = 0x59;
rx_tx_addr[2] = 0x3a;
{//HONTAI, FQ777_951, HONTAI_XKK170
rx_tx_addr[0] = 0x4C;
rx_tx_addr[1] = 0x4B;
rx_tx_addr[2] = 0x3A;
#ifdef FORCE_HONTAI_XKK170_ORIGINAL_ID
if(sub_protocol == HONTAI_XKK170)
{
rx_tx_addr[3] = 0x5A;
rx_tx_addr[4] = 0x06;
}
#endif
}
#ifdef DEBUG_SERIAL
debug("A B");
for(uint8_t i=0; i < 5; i++)
debug(" %02X", rx_tx_addr[i]);
debugln();
#endif
}
uint16_t HONTAI_callback()
@@ -228,8 +312,14 @@ void HONTAI_init()
{
BIND_IN_PROGRESS; // autobind protocol
bind_counter = HONTAI_BIND_COUNT;
HONTAI_initialize_txid();
HONTAI_RF_init();
packet_period = sub_protocol == FQ777_951 ? FQ777_951_PACKET_PERIOD : HONTAI_PACKET_PERIOD;
HONTAI_initialize_txid();
if(sub_protocol == FQ777_951)
packet_period = FQ777_951_PACKET_PERIOD;
else if(sub_protocol == HONTAI_XKK170)
packet_period = HONTAI_XKK170_PACKET_PERIOD;
else
packet_period = HONTAI_PACKET_PERIOD;
}
#endif

View File

@@ -35,7 +35,6 @@ enum PktState {
J6PRO_CHAN_4,
};
const uint8_t PROGMEM j6pro_bind_sop_code[] = {0x62, 0xdf, 0xc1, 0x49, 0xdf, 0xb1, 0xc0, 0x49};
const uint8_t j6pro_data_code[] = {0x02, 0xf9, 0x93, 0x97, 0x02, 0xfa, 0x5c, 0xe3, 0x01, 0x2b, 0xf1, 0xdb, 0x01, 0x32, 0xbe, 0x6f}; // unneeded since this is the default table after a reset
static void __attribute__((unused)) j6pro_build_bind_packet()
@@ -99,7 +98,7 @@ static void __attribute__((unused)) cyrf_bindinit()
/* Use when binding */
CYRF_SetPower(0x28); //Deviation using max power, replaced by bind power...
//CYRF_ConfigRFChannel(0x52);
CYRF_PROGMEM_ConfigSOPCode(j6pro_bind_sop_code);
CYRF_PROGMEM_ConfigSOPCode(DEVO_j6pro_sopcodes[19]);
CYRF_ConfigCRCSeed(0x0000);
//CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4a);
//CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x80);
@@ -124,7 +123,7 @@ static void __attribute__((unused)) j6pro_set_radio_channels()
{
//FIXME: Query free channels
//lowest channel is 0x08, upper channel is 0x4d?
CYRF_FindBestChannels(hopping_frequency, 3, 5, 8, 77);
CYRF_FindBestChannels(hopping_frequency, 3, 5, 8, 77, FIND_CHANNEL_ANY);
hopping_frequency[3] = hopping_frequency[0];
}

View File

@@ -0,0 +1,450 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(JIABAILE_NRF24L01_INO)
#include "iface_xn297.h"
//#define FORCE_JIABAILE_ORIGINAL_ID
//#define FORCE_JIABAILE_GYRO_ORIGINAL_ID
#define JIABAILE_PAYLOAD_SIZE 8
#define JIABAILE_RX_PAYLOAD_SIZE 7
#define JIABAILE_RF_NUM_CHANNELS 3
#define JIABAILE_BIND_PACKET_PERIOD 12700
#define JIABAILE_PACKET_PERIOD 2408
#define JIABAILE_GYRO_PACKET_PERIOD 8205
#define JIABAILE_BIND_COUNT 160 //2 sec
#define JIABAILE_WRITE_TIME 1000
enum {
JIABAILE_BIND=0,
JIABAILE_RX,
JIABAILE_PREP_DATA,
JIABAILE_DATA,
};
static uint8_t __attribute__((unused)) JIABAILE_channel(uint8_t num)
{
uint8_t val=convert_channel_16b_limit(num,0,100);
if(val > 50+num)
{
packet[3] |= 0x01;
return val-50;
}
if(val < 50-num)
{
packet[3] |= 0x02;
return 50-val;
}
return 0;
}
static void __attribute__((unused)) JIABAILE_send_packet()
{
if(!(sub_protocol == JIABAILE_GYRO && IS_BIND_IN_PROGRESS))
{
hopping_frequency_no++;
if(hopping_frequency_no > 2)
hopping_frequency_no = 0;
XN297_Hopping(hopping_frequency_no);
}
if(sub_protocol == JIABAILE_STD)
{//Std
memcpy(packet,rx_tx_addr,3);
memset(&packet[3], 0x00, 4);
if(IS_BIND_DONE)
{//Normal
packet[4] = convert_channel_16b_limit(RUDDER,0,50)-25; //ST Trim
packet[6] = JIABAILE_channel(AILERON); //ST
packet[3] ^= 0x03; //Reverse ST channel
packet[3] <<= 2; //Move ST channel where it should be
packet[5] = JIABAILE_channel(ELEVATOR); //TH
packet[3] |= GET_FLAG(CH5_SW, 0x20) //Low speed
|GET_FLAG(CH7_SW, 0x40) //Flash light
|GET_FLAG(!CH6_SW, 0x80); //Light
if(!CH5_SW && Channel_data[CH5] > CHANNEL_MIN_COMMAND)
packet[3] |= 0x10; //Mid speed
}
else
{
bind_counter--;
if(!bind_counter)
{
BIND_DONE;
phase = JIABAILE_PREP_DATA;
}
}
packet[7] = 0x55 + hopping_frequency[hopping_frequency_no];
for(uint8_t i=0;i<JIABAILE_PAYLOAD_SIZE-1;i++)
packet[7] += packet[i];
}
else
{//Gyro
if(bind_counter)
{
bind_counter--;
if(!bind_counter)
{
BIND_DONE;
XN297_SetTXAddr(rx_tx_addr, 4);
}
}
uint8_t crc_pos;
if(IS_BIND_IN_PROGRESS)
{
memcpy(packet,rx_tx_addr,4);
packet[4] = hopping_frequency[1];
packet[5] = hopping_frequency[2];
crc_pos = 6;
packet[7] = 0x55;
}
else
{
packet[0] = convert_channel_16b_limit(CH2,0x60,0xA0); //Throttle
packet[1] = convert_channel_16b_limit(CH1,0x40,0xC0); //Steering
if(Channel_data[CH5] < CHANNEL_MIN_COMMAND)
packet[2] = 0x02; //High speed
else if(CH5_SW)
packet[2] = 0x00; //Low speed
else
packet[2] = 0x01; //Mid speed
packet[3] = convert_channel_8b(CH3) ^0xFF; //Gyro
uint8_t val = GET_FLAG(CH6_SW, 0x04) //Light
|GET_FLAG(CH7_SW, 0x08); //Flash
if(Channel_data[CH4] > CHANNEL_MAX_COMMAND)
val |= 0x01; //Trim right
else if(Channel_data[CH4] < CHANNEL_MIN_COMMAND)
val |= 0x02; //Trim left
packet[4] = val;
packet[5] = packet[6] = 0x00; //?
crc_pos = 7;
}
uint8_t sum=0;
for(uint8_t i=0; i<crc_pos; i++)
sum += packet[i];
sum ^= 0xFF;
packet[crc_pos] = sum;
}
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, JIABAILE_PAYLOAD_SIZE);
#if 0
debug("B%d ",bind_counter);
debug("H%d RF%d",hopping_frequency_no,hopping_frequency[hopping_frequency_no]);
for(uint8_t i=0; i < JIABAILE_PAYLOAD_SIZE; i++)
debug(" %02X", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) JIABAILE_initialize_txid()
{
if(sub_protocol == JIABAILE_STD)
{//Std
rx_tx_addr[0] = rx_tx_addr[3];
#ifdef FORCE_JIABAILE_ORIGINAL_ID
memcpy(rx_tx_addr,(uint8_t *)"\xCB\x03\xA5",3);
//memcpy(rx_tx_addr,(uint8_t *)"\x3D\x08\xA2",3);
//Normal frequencies are calculated from the car ID...
//memcpy(&hopping_frequency[3],(uint8_t *)"\x23\x2D\x4B",3); //35,45,75
memcpy(&hopping_frequency[3],(uint8_t *)"\x24\x43\x4C",3); //36,67,76
#endif
//Bind frequencies
memcpy(hopping_frequency,(uint8_t *)"\x07\x27\x45",3); //7,39,69
}
else
{//Gyro
rx_tx_addr[0] += RX_num;
uint8_t val = (rx_tx_addr[0] & 0x0F) + 5; //5..20
for(uint8_t i=0; i<3; i++)
hopping_frequency[i] = val+ 20*i; //hopping_frequency[1,2] could be whatever but...
#ifdef FORCE_JIABAILE_GYRO_ORIGINAL_ID
if(RX_num)
{
memcpy(rx_tx_addr,(uint8_t *)"\x7D\x82\x28\xC2",4);
memcpy(hopping_frequency,(uint8_t *)"\x12\x1B\x35",3); //18,27,53
}
else
{
memcpy(rx_tx_addr,(uint8_t *)"\x0C\xF3\x59\xB3",4);
memcpy(hopping_frequency,(uint8_t *)"\x11\x1C\x36",3); //17,28,54
}
#endif
debugln("ID: %02X %02X %02X %02X, HOP: %2d %2d %2d",rx_tx_addr[0],rx_tx_addr[1],rx_tx_addr[2],rx_tx_addr[3],hopping_frequency[0],hopping_frequency[1],hopping_frequency[2]);
}
}
static void __attribute__((unused)) JIABAILE_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
//Bind address
if(sub_protocol == JIABAILE_STD)
{//Std
memcpy(rx_id,(uint8_t*)"\xA7\x07\x57\xA7\x26", 5);
XN297_SetTXAddr(rx_id, 5);
XN297_SetRXAddr(rx_id, JIABAILE_RX_PAYLOAD_SIZE);
}
else
{//Gyro
XN297_SetTXAddr((uint8_t*)"\x14\x41\x11\x13", 4);
XN297_RFChannel(0x29); //41
}
}
uint16_t JIABAILE_callback()
{
uint8_t sum;
uint16_t addr;
switch(phase)
{
case JIABAILE_BIND:
phase++; // JIABAILE_RX but is overwritten if RX or bind timeout
if(XN297_IsRX())
{
if(XN297_ReadPayload(packet_in, JIABAILE_RX_PAYLOAD_SIZE))
{//CRC OK
#ifdef DEBUG_SERIAL
debug("RX");
for(uint8_t i=0; i < JIABAILE_RX_PAYLOAD_SIZE; i++)
debug(" %02X", packet_in[i]);
debugln();
#endif
//RX: CB 03 A5 9D 05 A2 68
if(memcmp(packet_in,rx_tx_addr,3)==0)
{//TX ID match
//Check packet
sum=0xAA + hopping_frequency[hopping_frequency_no];
for(uint8_t i=0; i < JIABAILE_RX_PAYLOAD_SIZE-1; i++)
sum+=packet_in[i];
if(sum==packet_in[6])
{
//Write the RXID
#ifdef DEBUG_SERIAL
debug("RXID ");
for(uint8_t i=0; i < 3; i++)
debug(" %02X", packet_in[3+i]);
debugln();
#endif
addr=JIABAILE_EEPROM_OFFSET+RX_num*3;
for(uint8_t i=0;i<3;i++)
eeprom_write_byte((EE_ADDR)(addr+i),packet_in[3+i]);
//Switch to normal mode
BIND_DONE;
phase = JIABAILE_PREP_DATA;
}
#ifdef DEBUG_SERIAL
else
debug("Wrong Sum");
}
else
debug("Wrong TX ID");
}
else
debug("Bad CRC");
debugln("");
#else
}
}
#endif
}
XN297_SetTxRxMode(TXRX_OFF);
JIABAILE_send_packet();
return JIABAILE_WRITE_TIME;
case JIABAILE_RX:
//Wait for the packet transmission to finish
while(XN297_IsPacketSent()==false);
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = JIABAILE_BIND;
return JIABAILE_BIND_PACKET_PERIOD - JIABAILE_WRITE_TIME;
case JIABAILE_PREP_DATA:
//Read the RXID
addr=JIABAILE_EEPROM_OFFSET+RX_num*3;
for(uint8_t i=0;i<3;i++)
rx_id[i+1] = eeprom_read_byte((EE_ADDR)(addr+i));
#ifdef DEBUG_SERIAL
debug("RXID ");
for(uint8_t i=0; i < 3; i++)
debug(" %02X", rx_id[i+1]);
#endif
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTXAddr(rx_id, 5);
//Set the normal frequencies
sum=rx_id[1]&0x07;
hopping_frequency[0] = (sum>4?30:8) + sum;
if(sum==4 || sum ==7)
hopping_frequency[0]++;
hopping_frequency[1] = 40 + sum;
if((sum & 0x06) == 0x06)
hopping_frequency[1] += 21;
hopping_frequency[2] = 70 + sum;
#ifdef DEBUG_SERIAL
debug(" RF");
for(uint8_t i=0; i < 3; i++)
debug(" %d", hopping_frequency[i]);
debugln();
#endif
phase++;
default: //JIABAILE_DATA
#ifdef MULTI_SYNC
telemetry_set_input_sync(packet_period);
#endif
JIABAILE_send_packet();
break;
}
return packet_period;
}
void JIABAILE_init()
{
JIABAILE_initialize_txid();
JIABAILE_RF_init();
if(sub_protocol == JIABAILE_STD)
{//Std
if(IS_BIND_IN_PROGRESS)
{
phase = JIABAILE_BIND;
bind_counter = JIABAILE_BIND_COUNT;
}
else
phase = JIABAILE_PREP_DATA;
packet_period = JIABAILE_PACKET_PERIOD;
}
else
{//Gyro
phase = JIABAILE_DATA;
bind_counter = IS_BIND_IN_PROGRESS?JIABAILE_BIND_COUNT>>2:1;
packet_period = JIABAILE_GYRO_PACKET_PERIOD;
}
hopping_frequency_no = 0;
}
#endif
/*
// CAR RX debug code
static void __attribute__((unused)) JIABAILE_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
//Bind address
memcpy(rx_id,(uint8_t*)"\xA7\x07\x57\xA7\x26", 5);
XN297_SetTXAddr(rx_id, 5);
XN297_SetRXAddr(rx_id, JIABAILE_PAYLOAD_SIZE);
XN297_RFChannel(7);
rx_tx_addr[0] = 0x00;
rx_tx_addr[1] = RX_num;
rx_tx_addr[2] = 0x00;
}
uint16_t JIABAILE_callback()
{
switch(phase)
{
case JIABAILE_BIND:
if(XN297_IsRX())
{
if(XN297_ReadPayload(packet_in, JIABAILE_PAYLOAD_SIZE))
{//CRC OK
XN297_SetTxRxMode(TXRX_OFF);
#ifdef DEBUG_SERIAL
debug("RX Bind");
for(uint8_t i=0; i < JIABAILE_PAYLOAD_SIZE; i++)
debug(" %02X", packet_in[i]);
debugln();
#endif
memcpy(packet,packet_in,3);
memcpy(&packet[3],rx_tx_addr,3);
packet[6]=0xAA + 7;
for(uint8_t i=0; i < JIABAILE_RX_PAYLOAD_SIZE-1; i++)
packet[6]+=packet[i];
XN297_SetTxRxMode(TX_EN);
memcpy(&rx_id[1],rx_tx_addr,3);
bind_counter = 10;
phase = JIABAILE_RX;
}
}
return JIABAILE_WRITE_TIME;
case JIABAILE_RX:
if(bind_counter)
{
bind_counter--;
XN297_WritePayload(packet, JIABAILE_RX_PAYLOAD_SIZE);
#ifdef DEBUG_SERIAL
debug("TX Bind");
for(uint8_t i=0; i < JIABAILE_RX_PAYLOAD_SIZE; i++)
debug(" %02X", packet[i]);
debugln();
#endif
return JIABAILE_PACKET_PERIOD;
}
//Wait for the packet transmission to finish
//while(XN297_IsPacketSent()==false);
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetRXAddr(rx_id, JIABAILE_PAYLOAD_SIZE);
hopping_frequency_no++;
if(hopping_frequency_no>84)
hopping_frequency_no = 0;
debug(".");
XN297_RFChannel(hopping_frequency_no);
XN297_SetTxRxMode(RX_EN);
phase = JIABAILE_DATA;
return JIABAILE_BIND_PACKET_PERIOD;
case JIABAILE_DATA:
if(XN297_IsRX())
{
debugln("");
if(XN297_ReadPayload(packet_in, JIABAILE_PAYLOAD_SIZE))
{//CRC OK
#ifdef DEBUG_SERIAL
debug("CH=%d=%02X P:",hopping_frequency_no,hopping_frequency_no);
for(uint8_t i=0; i < JIABAILE_PAYLOAD_SIZE; i++)
debug(" %02X", packet_in[i]);
#endif
//Check packet
uint8_t sum=0x55 + hopping_frequency_no;
for(uint8_t i=0; i < JIABAILE_PAYLOAD_SIZE-1; i++)
sum+=packet_in[i];
if(sum==packet_in[7])
{
debug("Good channel");
}
else
debug("Wrong Sum");
}
else
debug("Bad CRC");
debugln("");
}
phase = JIABAILE_RX;
break;
}
return JIABAILE_PACKET_PERIOD;
}
void JIABAILE_init()
{
JIABAILE_RF_init();
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = JIABAILE_BIND;
hopping_frequency_no = 7;
}
*/

View File

@@ -48,32 +48,19 @@ static void __attribute__((unused)) KF606_send_packet()
hopping_frequency_no ^= 1; // 2 RF channels
packet[0] = 0x55;
packet[1] = convert_channel_8b(THROTTLE); // 0..255
if(sub_protocol == KF606_ZCZ50) len--;
packet[len-3] = convert_channel_8b(THROTTLE); // 0..255
// Deadband is needed on aileron, 40 gives +-6%
switch(sub_protocol)
{
case KF606_KF606:
packet[2] = convert_channel_8b_limit_deadband(AILERON,0x20,0x80,0xE0,40); // Aileron: Max values:20..80..E0, Low rates:50..80..AF, High rates:3E..80..C1
packet[3] = convert_channel_16b_limit(CH5,0xC1,0xDF); // Aileron trim must be on a separated channel C1..D0..DF
break;
case KF606_MIG320:
packet[2] = convert_channel_8b_limit_deadband(AILERON,0x00,0x80,0xFF,40); // Aileron: High rate:2B..80..DA
packet[3] = convert_channel_16b_limit(CH5,0x01,0x1F); // Aileron trim must be on a separated channel 01..10..1F
packet[3] += (packet[2]-0x80)>>3; // Drive trims for more aileron authority
if(packet[3] > 0x80)
packet[3] = 0x01;
else if(packet[3] > 0x1F)
packet[3] = 0x1F;
packet[3] |= GET_FLAG(CH6_SW, 0xC0); // 0xC0 and 0xE0 are both turning the LED off, not sure if there is another hidden feature
break;
case KF606_ZCZ50:
len--; // uses only 3 bytes of payload
packet[0] = packet[1]; // Throttle: 0x00..0xFF
packet[1] = convert_channel_8b_limit_deadband(AILERON,0x20,0x80,0xE0,40); // Aileron: Max values:20..80..E0, low rate 0x52..0x80..0xB1, high rate: 0x41..0x80..0xC3.
packet[2] = convert_channel_16b_limit(CH5,0x01,0x1F); // Trim: 0x01..0x10..0x1F
packet[2] |= GET_FLAG(CH6_SW, 0xC0); // Unknown: 0x00 or 0xC0. Left top switch on original TX changes nothing on my plane. Maybe ON/OFF for main motor?
break;
}
packet[len-2] = convert_channel_8b_limit_deadband(AILERON,0x00,0x80,0xFF,40); // Aileron: High rate:2B..80..DA
uint8_t p3;
p3 = convert_channel_8b(CH5)>>3; // Aileron trim must be on a separated channel 01..10..1F
p3 += (packet[2]-0x80)>>3; // Drive trims for more aileron authority
if(p3 > 0x80)
p3 = 0x01;
else if(p3 > 0x1F)
p3 = 0x1F;
p3 |= GET_FLAG(CH6_SW, 0xC0); // 0xC0 and 0xE0 are both turning the LED off, not sure if there is another hidden feature
packet[len-1] = p3;
}
if(sub_protocol == KF606_MIG320)

View File

@@ -0,0 +1,185 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(KAMTOM_NRF24L01_INO)
#include "iface_xn297.h"
//#define FORCE_KAMTOM_ORIGINAL_ID
#define KAMTOM_PAYLOAD_SIZE 16
#define KAMTOM_RF_NUM_CHANNELS 4
#define KAMTOM_BIND_COUNT 2000
#define KAMTOM_WRITE_TIME 650
#define KAMTOM_BIND_CHANNEL 0x28 //40
#define KAMTOM_PACKET_PERIOD 3585
enum {
KAMTOM_DATA,
KAMTOM_RX,
};
static void __attribute__((unused)) KAMTOM_send_packet()
{
if(bind_counter)
{
bind_counter--;
if(!bind_counter)
BIND_DONE;
}
memset(packet, 0x00, 16);
if(IS_BIND_DONE)
{//Normal
XN297_Hopping(hopping_frequency_no);
hopping_frequency_no++;
hopping_frequency_no &= 3;
//RXID
packet[0] = rx_tx_addr[0];
packet[2] = rx_tx_addr[1];
//Next RF channel
packet[1] = hopping_frequency[hopping_frequency_no];
//Channels and trims
for(uint8_t i=0; i<6; i++)
{
packet[4+i] = convert_channel_s8b(CH_TAER[i]);
if(i>3) //ST_TR and TH_TR
packet[4+i] >>= 2;
}
//packet[11] = 0x00; //??
//TH_DR
packet[12] = convert_channel_16b_limit(CH7,0x25,0x64);
}
else
{
packet[1] = KAMTOM_BIND_CHANNEL;
memcpy(&packet[4],hopping_frequency,4);
packet[12] = 0xA5;
}
packet[10] = 0x40; //??
//Checksum
uint16_t sum = packet[1];
for(uint8_t i=4;i<13;i++)
sum += packet[i];
packet[13] = sum;
packet[3] = (sum>>6) & 0xFC;
//TXID
packet[14] = rx_tx_addr[2];
packet[15] = rx_tx_addr[3];
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WriteEnhancedPayload(packet, KAMTOM_PAYLOAD_SIZE,false);
#if 0
//def DEBUG_SERIAL
for(uint8_t i=0; i < KAMTOM_PAYLOAD_SIZE; i++)
debug("%02X ", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) KAMTOM_initialize_txid()
{
calc_fh_channels(4);
#ifdef FORCE_KAMTOM_ORIGINAL_ID
rx_tx_addr[0] = 0xC7;
rx_tx_addr[1] = 0x78;
rx_tx_addr[2] = 0x2C;
rx_tx_addr[3] = 0x25;
hopping_frequency[0] = 59;
hopping_frequency[1] = 59;
hopping_frequency[2] = 71;
hopping_frequency[3] = 65;
#endif
}
static void __attribute__((unused)) KAMTOM_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
//Address
XN297_SetTXAddr((uint8_t*)"\xCC\xDD\xEE\xDD", 4);
XN297_SetRXAddr((uint8_t*)"\xCC\xDD\xEE\xDD", KAMTOM_PAYLOAD_SIZE);
XN297_RFChannel(KAMTOM_BIND_CHANNEL);
}
uint16_t KAMTOM_callback()
{
static bool rx=false;
switch(phase)
{
case KAMTOM_DATA:
rx = XN297_IsRX();
XN297_SetTxRxMode(TXRX_OFF);
#ifdef MULTI_SYNC
telemetry_set_input_sync(KAMTOM_PACKET_PERIOD);
#endif
KAMTOM_send_packet();
if(rx)
{
uint8_t val=XN297_ReadEnhancedPayload(packet_in, KAMTOM_PAYLOAD_SIZE);
if(val==KAMTOM_PAYLOAD_SIZE)
{
BIND_DONE;
if(packet_in[0] == 0xA0 && packet_in[14] == rx_tx_addr[2] && packet_in[15] == rx_tx_addr[3])
{//Good packet with our TXID
rx_tx_addr[0] = packet_in[9];
rx_tx_addr[1] = packet_in[10];
#ifdef KAMTOM_HUB_TELEMETRY
v_lipo1 = packet_in[1] == 0x03 ? 0x00:0xFF; // low voltage
telemetry_link = 1;
#endif
}
#if 0
for(uint8_t i=0; i < KAMTOM_PAYLOAD_SIZE; i++)
debug(" %02X", packet_in[i]);
debugln();
#endif
}
}
phase++;
return KAMTOM_WRITE_TIME;
default: //KAMTOM_RX
//{ // Wait for packet to be sent before switching to receive mode
// uint16_t start=(uint16_t)micros();
// while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 500)
// if(XN297_IsPacketSent())
// break;
//}
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = KAMTOM_DATA;
return KAMTOM_PACKET_PERIOD - KAMTOM_WRITE_TIME;
}
return 0;
}
void KAMTOM_init()
{
KAMTOM_initialize_txid();
KAMTOM_RF_init();
bind_counter = KAMTOM_BIND_COUNT;
phase = KAMTOM_DATA;
hopping_frequency_no = 0;
#ifdef KAMTOM_HUB_TELEMETRY
RX_RSSI = 100; // Dummy value
#endif
}
#endif

View File

@@ -0,0 +1,124 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(KYOSHO3_CYRF6936_INO)
#include "iface_cyrf6936.h"
//#define KYOSHO3_FORCE_ID
//#define KYOSHO3_DEBUG
#define KYOSHO3_BIND_PACKET_SIZE 4
#define KYOSHO3_PACKET_SIZE 9
const uint8_t PROGMEM KYOSHO3_init_vals[][2] = {
//Init from dump
{CYRF_0B_PWR_CTRL, 0x00}, // PMU
{CYRF_32_AUTO_CAL_TIME, 0x3C}, // Default init value
{CYRF_35_AUTOCAL_OFFSET, 0x14}, // Default init value
{CYRF_03_TX_CFG, 0x28 | CYRF_BIND_POWER}, // 8DR Mode, 64 chip codes
{CYRF_10_FRAMING_CFG, 0xA4}, // SOP and LEN enable
{CYRF_1F_TX_OVERRIDE, 0x05}, // Disable CRC, Data invert
{CYRF_1E_RX_OVERRIDE, 0x04}, // CRC check disabled
//{CYRF_11_DATA32_THOLD, 0x04}, // ???Using 64 chip...
{CYRF_12_DATA64_THOLD, 0x0E}, // Default
{CYRF_06_RX_CFG, 0x52}, // AGC disabled, LNA enabled, override enabled
};
static uint16_t __attribute__((unused)) KYOSHO3_send_packet()
{
CYRF_SetPower(0x28);
if(IS_BIND_IN_PROGRESS)
{
if(--bind_counter==0)
BIND_DONE;
packet[0] = 0xAA;
//ID
memcpy(&packet[1],&rx_tx_addr[1],3);
CYRF_WriteDataPacketLen(packet, KYOSHO3_BIND_PACKET_SIZE);
#ifdef KYOSHO3_DEBUG
debug("P:");
for(uint8_t i=0;i<KYOSHO3_BIND_PACKET_SIZE;i++)
debug(" %02X",packet[i]);
debugln("");
#endif
return 2434;
}
else
{
//ID
memcpy(packet,&rx_tx_addr[1],3);
//Channels
for(uint8_t i=0;i<4;i++)
{
packet[3] >>= 2;
packet[3] |= Channel_data[i]<<6;
packet[4+i] = Channel_data[i]>>3;
}
//Checksum
packet[8] = packet[3];
for(uint8_t i=4;i<8;i++)
packet[8] += packet[i];
//Timing
phase ^= 0x01;
CYRF_WriteDataPacketLen(packet, KYOSHO3_PACKET_SIZE);
#ifdef KYOSHO3_DEBUG
debug("P:");
for(uint8_t i=0;i<KYOSHO3_PACKET_SIZE;i++)
debug(" %02X",packet[i]);
debugln("");
#endif
if(phase)
return 9047;
}
return 6957;
}
uint16_t KYOSHO3_callback()
{
return KYOSHO3_send_packet();
}
void KYOSHO3_init()
{
//Config CYRF registers
for(uint8_t i = 0; i < sizeof(KYOSHO3_init_vals) / 2; i++)
CYRF_WriteRegister(pgm_read_byte_near(&KYOSHO3_init_vals[i][0]), pgm_read_byte_near(&KYOSHO3_init_vals[i][1]));
CYRF_WritePreamble(0x333304);
//Find a free even channel
CYRF_FindBestChannels(hopping_frequency,1,1,0x04,0x50, FIND_CHANNEL_EVEN);
#ifdef KYOSHO3_FORCE_ID // data taken from TX dump
rx_tx_addr[1] = 0x01;
rx_tx_addr[2] = 0xAB;
rx_tx_addr[3] = 0x31;
hopping_frequency[0] = 0x04;
#endif
#ifdef KYOSHO3_DEBUG
debugln("ID: %02X %02X %02X",rx_tx_addr[1],rx_tx_addr[2],rx_tx_addr[3]);
debugln("RF CH: %02X",hopping_frequency[0]);
#endif
if(IS_BIND_IN_PROGRESS)
CYRF_ConfigRFChannel(0x04);
else
CYRF_ConfigRFChannel(hopping_frequency[0]);
bind_counter = 8217;
phase = 0;
}
#endif

View File

@@ -169,8 +169,7 @@ void LOSI_init()
{
LOSI_cyrf_init();
CYRF_FindBestChannels(hopping_frequency, 1, 0, 0x07, 0x4F); // 0x07 and 0x4F are unknown limits, this routine resets the CRC Seed to 0
hopping_frequency[0] |= 1; // Only odd channels are used, integrated in CYRF code...
CYRF_FindBestChannels(hopping_frequency, 1, 0, 0x07, 0x4F, FIND_CHANNEL_ODD); // 0x07 and 0x4F are unknown limits, this routine resets the CRC Seed to 0
crc8 = 0;
crc8 = (uint16_t)LOSI_check(((rx_tx_addr[2]&0x0F) << 8) + rx_tx_addr[3]) >> 12;

View File

@@ -12,7 +12,7 @@
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// compatible with MT99xx, Eachine H7, Yi Zhan i6S and LS114/124
// compatible with MT99xx, Eachine H7, Yi Zhan i6S, LS114/124, QF009 Su35
// Last sync with Goebish mt99xx_nrf24l01.c dated 2016-01-29
#if defined(MT99XX_CCNRF_INO)
@@ -96,6 +96,16 @@ enum{
FLAG_PA18_FLIP = 0x80,
};
enum{
// flags going to packet[6] (QF009 Su35)
FLAG_SU35_6G = 0x00,
FLAG_SU35_3D = 0x40,
FLAG_SU35_HIRATE = 0x01,
FLAG_SU35_LED = 0x02,
FLAG_SU35_FLASH = 0x04,
FLAG_SU35_INVERT = 0x08,
};
const uint8_t h7_mys_byte[] = {
0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14,
0x05, 0x15, 0x06, 0x16, 0x07, 0x17, 0x00, 0x10
@@ -179,8 +189,8 @@ static void __attribute__((unused)) MT99XX_send_packet()
packet[1] = convert_channel_16b_limit(RUDDER ,0x00,0xE1); // rudder
packet[2] = convert_channel_16b_limit(AILERON ,0xE1,0x00); // aileron
packet[3] = convert_channel_16b_limit(ELEVATOR,0x00,0xE1); // elevator
packet[4] = 0x20; // pitch trim (0x3f-0x20-0x00)
packet[5] = 0x20; // roll trim (0x00-0x20-0x3f)
packet[4] = (convert_channel_8b(CH10) ^ 0xFF) >> 2; // aileron trim (3F..20..00)
packet[5] = convert_channel_8b(CH11) >> 2; // elevator trim (00..20..3F)
packet[6] = GET_FLAG( CH5_SW, FLAG_MT_FLIP );
if(sub_protocol != PA18+8)
packet[7] = h7_mys_byte[hopping_frequency_no]; // next rf channel index ?
@@ -271,6 +281,14 @@ static void __attribute__((unused)) MT99XX_send_packet()
if(hopping_frequency_no == 0)
packet[7] ^= 0x40;
break;
case SU35+8:
packet[6] = FLAG_SU35_6G
| GET_FLAG( CH5_SW, FLAG_SU35_3D )
| GET_FLAG( !CH6_SW, FLAG_SU35_LED )
| GET_FLAG( CH7_SW, FLAG_SU35_FLASH )
| GET_FLAG( CH8_SW, FLAG_SU35_INVERT )
| GET_FLAG( CH9_SW, FLAG_SU35_HIRATE );
break;
}
uint8_t result=crc8;
for(uint8_t i=0; i<8; i++)
@@ -319,7 +337,7 @@ static void __attribute__((unused)) MT99XX_send_packet()
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, MT99XX_PACKET_SIZE);
#if 0
#if 1
for(uint8_t i=0; i<MT99XX_PACKET_SIZE; i++)
debug(" %02X",packet[i]);
debugln();
@@ -512,7 +530,7 @@ void MT99XX_init(void)
bind_counter = MT99XX_BIND_COUNT;
if(IS_BIND_DONE)
{
if(sub_protocol != A180 && sub_protocol != DRAGON && sub_protocol != F949G && sub_protocol != PA18+8)
if(sub_protocol != A180 && sub_protocol != DRAGON && sub_protocol != F949G && sub_protocol != PA18+8 && sub_protocol != SU35+8)
BIND_IN_PROGRESS; // autobind protocol
else
bind_counter = 1;

View File

@@ -50,12 +50,19 @@ static void __attribute__((unused)) MOULDKG_send_packet()
else
{
uint8_t n = num_ch<<2;
if(sub_protocol == MOULDKG_ANALOG)
if(sub_protocol == MOULDKG_ANALOG4 || sub_protocol == MOULDKG_ANALOG6 )
{
packet[0] = 0x36;
uint8_t ch[]={ 1,0,2,3 };
for(uint8_t i=0;i<4;i++)
packet[i+4] = convert_channel_8b(ch[i]+n);
const uint8_t ch[]={ 1,0,2,3,5,4 };
if(sub_protocol == MOULDKG_ANALOG6)
n += num_ch<<1;
for(uint8_t i=0;i<6;i++)
{
if( (i > 3 && sub_protocol == MOULDKG_ANALOG4) || i + n > 15)
packet[i+4] = 0x80; //Centered channel
else
packet[i+4] = convert_channel_8b(ch[i]+n);
}
len = MOULDKG_PAYLOAD_SIZE_ANALOG;
}
else

View File

@@ -3,12 +3,12 @@
3,FrskyD,D8,Cloned
4,Hisky,Hisky,HK310
5,V2x2,V2x2,JXD506,MR101
6,DSM,DSM2_1F,DSM2_2F,DSMX_1F,DSMX_2F,AUTO,DSMR_1F
6,DSM,DSM2_1F,DSM2_2F,DSMX_1F,DSMX_2F,AUTO,DSMR_1F,DSMR,DSM2_SFC
7,Devo,8CH,10CH,12CH,6CH,7CH
8,YD717,YD717,SKYWLKR,SYMAX4,XINXUN,NIHUI
9,KN,WLTOYS,FEILUN
10,SymaX,SYMAX,SYMAX5C
11,SLT,SLT_V1,SLT_V2,Q100,Q200,MR100
11,SLT,SLT_V1,SLT_V2,Q100,Q200,MR100,V1_4CH,RF_SIM
12,CX10,GREEN,BLUE,DM007,---,J3015_1,J3015_2,MK33041
13,CG023,CG023,YD829
14,Bayang,Bayang,H8S3D,X16_AH,IRDRONE,DHD_D4,QX100
@@ -20,12 +20,12 @@
20,FY326,FY326,FY319
21,Futaba,SFHSS
22,J6PRO
23,FQ777
23,FQ777,124,XBM37
24,ASSAN
25,FrskyV
26,HONTAI,HONTAI,JJRCX1,X5C1,FQ777_951
26,HONTAI,HONTAI,JJRCX1,X5C1,FQ777_951,XKK170
27,OpnLrs
28,AFHDS2A,PWM_IBUS,PPM_IBUS,PWM_SBUS,PPM_SBUS,PWM_IB16,PPM_IB16,PWM_SB16,PPM_SB16
28,AFHDS2A,PWM_IBUS,PPM_IBUS,PWM_SBUS,PPM_SBUS,Gyro_Off,Gyro_On,G_On_Rev
29,Q2X2,Q222,Q242,Q282
30,WK2x01,WK2801,WK2401,W6_5_1,W6_6_1,W6_HEL,W6_HEL_I
31,Q303,Q303,CX35,CX10D,CX10WD
@@ -55,7 +55,7 @@
55,Frsky_RX,Multi,CloneTX,EraseTX,CPPM
56,AFHDS2A_RX,Multi,CPPM
57,HoTT,Sync,No_Sync
58,FX,816,620,9630
58,FX,FX816,FX620,9630,Q560,QF012,BM26,A570
59,Bayang_RX,Multi,CPPM
60,Pelikan,Pro,Lite,SCX24
61,EazyRC
@@ -86,11 +86,20 @@
87,IKEA
88,WILLIFM
89,Losi
90,MouldKg,Analog,Digit
90,MouldKg,A4444,D4444,A664
91,Xerall
92,MT99xx,PA18
92,MT99xx,PA18,SU35
93,Kyosho2,KT-17
94,Scorpio
95,BlueFly
96,BumbleB
97,SGF22
97,SGF22,F22,F22S,J20
98,Kyosho3
99,XK2,X4,P10
100,YuXiang
102,JIABAILE,STD,GYRO
103,H36
104,KAMTOM
105,Shenqi2
106,WL91x
107,WPL

View File

@@ -36,6 +36,7 @@ const char STR_MT99XX[] ="MT99XX";
const char STR_MT99XX2[] ="MT99XX2";
const char STR_MJXQ[] ="MJXq";
const char STR_SHENQI[] ="Shenqi";
const char STR_SHENQI2[] ="Shenqi2";
const char STR_FY326[] ="FY326";
const char STR_FUTABA[] ="Futaba";
const char STR_J6PRO[] ="J6 Pro";
@@ -55,6 +56,7 @@ const char STR_DM002[] ="DM002";
const char STR_CABELL[] ="Cabell";
const char STR_ESKY150[] ="Esky150";
const char STR_ESKY150V2[] ="EskyV2";
const char STR_H36[] ="H36";
const char STR_H8_3D[] ="H8 3D";
const char STR_CORONA[] ="Corona";
const char STR_CFLIE[] ="CFlie";
@@ -82,6 +84,7 @@ const char STR_FX[] ="FX";
const char STR_BAYANG_RX[] ="BayanRX";
const char STR_PELIKAN[] ="Pelikan";
const char STR_XK[] ="XK";
const char STR_XK2[] ="XK2";
const char STR_XN297DUMP[] ="XN297DP";
const char STR_FRSKYR9[] ="FrSkyR9";
const char STR_PROPEL[] ="Propel";
@@ -109,6 +112,13 @@ const char STR_BLUEFLY[] ="BlueFly";
const char STR_BUMBLEB[] ="BumbleB";
const char STR_SGF22[] ="SGF22";
const char STR_EAZYRC[] ="EazyRC";
const char STR_KYOSHO3[] ="Kyosho3";
const char STR_YUXIANG[] ="YuXiang";
const char STR_UDIRC[] ="UDIRC";
const char STR_JIABAILE[] ="JIABAILE";
const char STR_KAMTOM[] ="KAMTOM";
const char STR_WL91X[] ="WL91x";
const char STR_WPL[] ="WPL";
const char STR_SUBTYPE_FLYSKY[] = "\x04""Std\0""V9x9""V6x6""V912""CX20";
const char STR_SUBTYPE_HUBSAN[] = "\x04""H107""H301""H501";
@@ -121,24 +131,25 @@ const char STR_SUBTYPE_FRSKYD[] = "\x06""D8\0 ""Cloned";
const char STR_SUBTYPE_HISKY[] = "\x05""Std\0 ""HK310";
const char STR_SUBTYPE_V2X2[] = "\x06""Std\0 ""JXD506""MR101\0";
#ifndef MULTI_EU
const char STR_SUBTYPE_DSM[] = "\x04""2 1F""2 2F""X 1F""X 2F""Auto""R 1F";
const char STR_SUBTYPE_DSM[] = "\x04""2 1F""2 2F""X 1F""X 2F""Auto""R 1F""2SFC";
#else
const char STR_SUBTYPE_DSM[] = "\x04""--->""--->""X 1F""X 2F""Auto""R 1F";
const char STR_SUBTYPE_DSM[] = "\x04""--->""--->""X 1F""X 2F""Auto""R 1F""----";
#endif
const char STR_SUBTYPE_DEVO[] = "\x04""8ch\0""10ch""12ch""6ch\0""7ch\0";
const char STR_SUBTYPE_YD717[] = "\x07""Std\0 ""SkyWlkr""Syma X4""XINXUN\0""NIHUI\0 ";
const char STR_SUBTYPE_KN[] = "\x06""WLtoys""FeiLun";
const char STR_SUBTYPE_SYMAX[] = "\x03""Std""X5C";
const char STR_SUBTYPE_SLT[] = "\x06""V1_6ch""V2_8ch""Q100\0 ""Q200\0 ""MR100\0";
const char STR_SUBTYPE_SLT[] = "\x06""V1_6ch""V2_8ch""Q100\0 ""Q200\0 ""MR100\0""V1_4ch""RF_SIM";
const char STR_SUBTYPE_CX10[] = "\x07""Green\0 ""Blue\0 ""DM007\0 ""-\0 ""JC3015a""JC3015b""MK33041";
const char STR_SUBTYPE_CG023[] = "\x05""Std\0 ""YD829";
const char STR_SUBTYPE_BAYANG[] = "\x07""Std\0 ""H8S3D\0 ""X16 AH\0""IRDrone""DHD D4\0""QX100\0 ";
const char STR_SUBTYPE_MT99[] = "\x06""MT99\0 ""H7\0 ""YZ\0 ""LS\0 ""FY805\0""A180\0 ""Dragon""F949G\0";
const char STR_SUBTYPE_MT992[] = "\x04""PA18";
const char STR_SUBTYPE_MT992[] = "\x04""PA18""SU35";
const char STR_SUBTYPE_MJXQ[] = "\x07""WLH08\0 ""X600\0 ""X800\0 ""H26D\0 ""E010\0 ""H26WH\0 ""Phoenix";
const char STR_SUBTYPE_FY326[] = "\x05""Std\0 ""FY319";
const char STR_SUBTYPE_HONTAI[] = "\x07""Std\0 ""JJRC X1""X5C1\0 ""FQ_951";
const char STR_SUBTYPE_AFHDS2A[] = "\x08""PWM,IBUS""PPM,IBUS""PWM,SBUS""PPM,SBUS""PWM,IB16""PPM,IB16""PWM,SB16""PPM,SB16";
const char STR_SUBTYPE_FQ777[] = "\x05""124\0 ""XBM37";
const char STR_SUBTYPE_HONTAI[] = "\x06""Std\0 ""JJRCX1""X5C1\0 ""FQ_951""XKK170";
const char STR_SUBTYPE_AFHDS2A[] = "\x08""PWM,IBUS""PPM,IBUS""PWM,SBUS""PPM,SBUS""Gyro_Off""Gyro_On\0""G_On_Rev";
const char STR_SUBTYPE_Q2X2[] = "\x04""Q222""Q242""Q282";
const char STR_SUBTYPE_WK2x01[] = "\x06""WK2801""WK2401""W6_5_1""W6_6_1""W6_HeL""W6_HeI";
const char STR_SUBTYPE_Q303[] = "\x06""Std\0 ""CX35\0 ""CX10D\0""CX10WD";
@@ -147,18 +158,19 @@ const char STR_SUBTYPE_H83D[] = "\x07""Std\0 ""H20H\0 ""H20Mini""H30Min
const char STR_SUBTYPE_CORONA[] = "\x05""V1\0 ""V2\0 ""FD V3";
const char STR_SUBTYPE_HITEC[] = "\x07""Optima\0""Opt Hub""Minima\0";
const char STR_SUBTYPE_BUGS_MINI[] = "\x06""Std\0 ""Bugs3H";
const char STR_SUBTYPE_TRAXXAS[] = "\x02""TQ";
const char STR_SUBTYPE_TRAXXAS[] = "\x03""TQ2""TQ1";
const char STR_SUBTYPE_E01X[] = "\x05""E012\0""E015\0";
const char STR_SUBTYPE_GD00X[] = "\x05""GD_V1""GD_V2";
const char STR_SUBTYPE_REDPINE[] = "\x04""Fast""Slow";
const char STR_SUBTYPE_POTENSIC[] = "\x03""A20";
const char STR_SUBTYPE_ZSX[] = "\x07""280JJRC";
const char STR_SUBTYPE_HEIGHT[] = "\x03""5ch""8ch";
const char STR_SUBTYPE_XN297DUMP[] = "\x07""250Kbps""1Mbps\0 ""2Mbps\0 ""Auto\0 ""NRF\0 ""CC2500\0";
const char STR_SUBTYPE_XN297DUMP[] = "\x07""250Kbps""1Mbps\0 ""2Mbps\0 ""Auto\0 ""NRF\0 ""CC2500\0""XN297\0 ";
const char STR_SUBTYPE_ESKY150[] = "\x03""4ch""7ch";
const char STR_SUBTYPE_ESKY150V2[] = "\x05""150V2";
const char STR_SUBTYPE_V911S[] = "\x05""V911S""E119\0";
const char STR_SUBTYPE_XK[] = "\x04""X450""X420""Cars";
const char STR_SUBTYPE_XK2[] = "\x03""X4\0""P10";
const char STR_SUBTYPE_FRSKYR9[] = "\x07""915MHz\0""868MHz\0""915 8ch""868 8ch""FCC\0 ""--\0 ""FCC 8ch""-- 8ch\0";
const char STR_SUBTYPE_ESKY[] = "\x03""Std""ET4";
const char STR_SUBTYPE_PROPEL[] = "\x04""74-Z";
@@ -171,12 +183,15 @@ const char STR_SUBTYPE_V761[] = "\x05""3ch\0 ""4ch\0 ""TOPRC";
const char STR_SUBTYPE_RLINK[] = "\x07""Surface""Air\0 ""DumboRC""RC4G\0 ";
const char STR_SUBTYPE_KYOSHO[] = "\x04""FHSS""Hype";
const char STR_SUBTYPE_KYOSHO2[] = "\x05""KT-17";
const char STR_SUBTYPE_KYOSHO3[] = "\x03""ASF";
const char STR_SUBTYPE_FUTABA[] = "\x05""SFHSS";
const char STR_SUBTYPE_JJRC345[] = "\x08""JJRC345\0""SkyTmblr";
const char STR_SUBTYPE_MOULDKG[] = "\x06""Analog""Digit\0";
const char STR_SUBTYPE_MOULDKG[] = "\x05""A4444""D4444""A664\0";
const char STR_SUBTYPE_KF606[] = "\x06""KF606\0""MIG320""ZCZ50\0";
const char STR_SUBTYPE_E129[] = "\x04""E129""C186";
const char STR_SUBTYPE_FX[] = "\x04""816\0""620\0""9630";
const char STR_SUBTYPE_FX[] = "\x05""FX816""FX620""9630\0""Q560\0""QF012""BM26\0""A570\0";
const char STR_SUBTYPE_SGF22[] = "\x04""F22\0""F22S""J20\0""CX10";
const char STR_SUBTYPE_JIABAILE[] = "\x04""Std\0""Gyro";
#define NO_SUBTYPE nullptr
#ifdef SEND_CPPM
@@ -258,7 +273,7 @@ const mm_protocol_definition multi_protocols[] = {
{PROTO_DM002, STR_DM002, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, DM002_init, DM002_callback },
#endif
#if defined(DSM_CYRF6936_INO)
{PROTO_DSM, STR_DSM, STR_SUBTYPE_DSM, 6, OPTION_MAXTHR, 0, 1, SW_CYRF, DSM_init, DSM_callback },
{PROTO_DSM, STR_DSM, STR_SUBTYPE_DSM, 7, OPTION_MAXTHR, 0, 1, SW_CYRF, DSM_init, DSM_callback },
#endif
#if defined(DSM_RX_CYRF6936_INO)
{PROTO_DSM_RX, STR_DSM_RX, STR_SUB_DSM_RX, DSMCPPM, OPTION_NONE, 0, 1, SW_CYRF, DSM_RX_init, DSM_RX_callback },
@@ -294,13 +309,13 @@ const mm_protocol_definition multi_protocols[] = {
{PROTO_FLYSKY, STR_FLYSKY, STR_SUBTYPE_FLYSKY, 5, OPTION_NONE, 0, 1, SW_A7105, FLYSKY_init, FLYSKY_callback },
#endif
#if defined(AFHDS2A_A7105_INO)
{PROTO_AFHDS2A, STR_AFHDS2A, STR_SUBTYPE_AFHDS2A, 8, OPTION_SRVFREQ, 1, 1, SW_A7105, AFHDS2A_init, AFHDS2A_callback },
{PROTO_AFHDS2A, STR_AFHDS2A, STR_SUBTYPE_AFHDS2A, 7, OPTION_SRVFREQ, 1, 1, SW_A7105, AFHDS2A_init, AFHDS2A_callback },
#endif
#if defined(AFHDS2A_RX_A7105_INO)
{PROTO_AFHDS2A_RX, STR_AFHDS2A_RX,STR_CPPM, NBR_CPPM, OPTION_NONE, 0, 0, SW_A7105, AFHDS2A_RX_init, AFHDS2A_RX_callback },
#endif
#if defined(FQ777_NRF24L01_INO)
{PROTO_FQ777, STR_FQ777, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, FQ777_init, FQ777_callback },
{PROTO_FQ777, STR_FQ777, STR_SUBTYPE_FQ777, 2, OPTION_NONE, 0, 0, SW_NRF, FQ777_init, FQ777_callback },
#endif
//OpenTX 2.3.x issue: DO NOT CHANGE ORDER below
#if defined(FRSKY_RX_CC2500_INO)
@@ -331,7 +346,7 @@ const mm_protocol_definition multi_protocols[] = {
{PROTO_FUTABA, STR_FUTABA, STR_SUBTYPE_FUTABA, 1, OPTION_RFTUNE, 1, 1, SW_CC2500, SFHSS_init, SFHSS_callback },
#endif
#if defined(FX_NRF24L01_INO)
{PROTO_FX, STR_FX, STR_SUBTYPE_FX, 3, OPTION_NONE, 0, 0, SW_NRF, FX_init, FX_callback },
{PROTO_FX, STR_FX, STR_SUBTYPE_FX, 7, OPTION_NONE, 0, 0, SW_NRF, FX_init, FX_callback },
#endif
#if defined(FY326_NRF24L01_INO)
{PROTO_FY326, STR_FY326, STR_SUBTYPE_FY326, 2, OPTION_NONE, 0, 0, SW_NRF, FY326_init, FY326_callback },
@@ -342,6 +357,9 @@ const mm_protocol_definition multi_protocols[] = {
#if defined(GW008_NRF24L01_INO)
{PROTO_GW008, STR_GW008, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, GW008_init, GW008_callback },
#endif
#if defined(H36_NRF24L01_INO)
{PROTO_H36, STR_H36, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, H36_init, H36_callback },
#endif
#if defined(H8_3D_NRF24L01_INO)
{PROTO_H8_3D, STR_H8_3D, STR_SUBTYPE_H83D, 4, OPTION_NONE, 0, 0, SW_NRF, H8_3D_init, H8_3D_callback },
#endif
@@ -355,7 +373,7 @@ const mm_protocol_definition multi_protocols[] = {
{PROTO_HITEC, STR_HITEC, STR_SUBTYPE_HITEC, 3, OPTION_RFTUNE, 0, 0, SW_CC2500, HITEC_init, HITEC_callback },
#endif
#if defined(HONTAI_NRF24L01_INO)
{PROTO_HONTAI, STR_HONTAI, STR_SUBTYPE_HONTAI, 4, OPTION_NONE, 0, 0, SW_NRF, HONTAI_init, HONTAI_callback },
{PROTO_HONTAI, STR_HONTAI, STR_SUBTYPE_HONTAI, 5, OPTION_NONE, 0, 0, SW_NRF, HONTAI_init, HONTAI_callback },
#endif
#if defined(HOTT_CC2500_INO)
{PROTO_HOTT, STR_HOTT, STR_SUBTYPE_HOTT, 2, OPTION_RFTUNE, 1, 0, SW_CC2500, HOTT_init, HOTT_callback },
@@ -369,12 +387,18 @@ const mm_protocol_definition multi_protocols[] = {
#if defined(J6PRO_CYRF6936_INO)
{PROTO_J6PRO, STR_J6PRO, NO_SUBTYPE, 0, OPTION_NONE, 0, 1, SW_CYRF, J6PRO_init, J6PRO_callback },
#endif
#if defined(JIABAILE_NRF24L01_INO)
{PROTO_JIABAILE, STR_JIABAILE, STR_SUBTYPE_JIABAILE, 2, OPTION_NONE, 0, 0, SW_NRF, JIABAILE_init, JIABAILE_callback },
#endif
#if defined(JJRC345_NRF24L01_INO)
{PROTO_JJRC345, STR_JJRC345, STR_SUBTYPE_JJRC345, 2, OPTION_NONE, 0, 0, SW_NRF, JJRC345_init, JJRC345_callback },
#endif
#if defined(JOYSWAY_A7105_INO)
{PROTO_JOYSWAY, STR_JOYSWAY, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_A7105, JOYSWAY_init, JOYSWAY_callback },
#endif
#if defined(KAMTOM_NRF24L01_INO)
{PROTO_KAMTOM, STR_KAMTOM, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, KAMTOM_init, KAMTOM_callback },
#endif
#if defined(KF606_CCNRF_INO)
{PROTO_KF606, STR_KF606, STR_SUBTYPE_KF606, 3, OPTION_RFTUNE, 0, 0, SW_NRF, KF606_init, KF606_callback },
#endif
@@ -387,6 +411,9 @@ const mm_protocol_definition multi_protocols[] = {
#if defined(KYOSHO2_NRF24L01_INO)
{PROTO_KYOSHO2, STR_KYOSHO2, STR_SUBTYPE_KYOSHO2, 1, OPTION_NONE, 0, 0, SW_NRF, KYOSHO2_init, KYOSHO2_callback },
#endif
#if defined(KYOSHO3_CYRF6936_INO)
{PROTO_KYOSHO3, STR_KYOSHO3, STR_SUBTYPE_KYOSHO3, 1, OPTION_NONE, 0, 0, SW_CYRF, KYOSHO3_init, KYOSHO3_callback },
#endif
#if defined(LOLI_NRF24L01_INO)
{PROTO_LOLI, STR_LOLI, NO_SUBTYPE, 0, OPTION_NONE, 1, 0, SW_NRF, LOLI_init, LOLI_callback },
#endif
@@ -400,13 +427,13 @@ const mm_protocol_definition multi_protocols[] = {
{PROTO_MLINK, STR_MLINK, NO_SUBTYPE, 0, OPTION_NONE, 1, 0, SW_CYRF, MLINK_init, MLINK_callback },
#endif
#if defined(MOULDKG_NRF24L01_INO)
{PROTO_MOULDKG, STR_MOULDKG, STR_SUBTYPE_MOULDKG, 2, OPTION_OPTION, 0, 0, SW_NRF, MOULDKG_init, MOULDKG_callback },
{PROTO_MOULDKG, STR_MOULDKG, STR_SUBTYPE_MOULDKG, 3, OPTION_OPTION, 0, 0, SW_NRF, MOULDKG_init, MOULDKG_callback },
#endif
#if defined(MT99XX_CCNRF_INO)
{PROTO_MT99XX, STR_MT99XX, STR_SUBTYPE_MT99, 8, OPTION_NONE, 0, 0, SW_NRF, MT99XX_init, MT99XX_callback },
#endif
#if defined(MT99XX_CCNRF_INO)
{PROTO_MT99XX2, STR_MT99XX2, STR_SUBTYPE_MT992, 1, OPTION_NONE, 0, 0, SW_NRF, MT99XX_init, MT99XX_callback },
{PROTO_MT99XX2, STR_MT99XX2, STR_SUBTYPE_MT992, 2, OPTION_NONE, 0, 0, SW_NRF, MT99XX_init, MT99XX_callback },
#endif
#if defined(NCC1701_NRF24L01_INO)
{PROTO_NCC1701, STR_NCC1701, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, NCC_init, NCC_callback },
@@ -448,22 +475,28 @@ const mm_protocol_definition multi_protocols[] = {
{PROTO_SCORPIO, STR_SCORPIO, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_CYRF, SCORPIO_init, SCORPIO_callback },
#endif
#if defined(SGF22_NRF24L01_INO)
{PROTO_SGF22, STR_SGF22, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, SGF22_init, SGF22_callback },
{PROTO_SGF22, STR_SGF22, STR_SUBTYPE_SGF22, 4, OPTION_NONE, 0, 0, SW_NRF, SGF22_init, SGF22_callback },
#endif
#if defined(SHENQI_NRF24L01_INO)
{PROTO_SHENQI, STR_SHENQI, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, SHENQI_init, SHENQI_callback },
#endif
#if defined(SHENQI2_NRF24L01_INO)
{PROTO_SHENQI2, STR_SHENQI2, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, SHENQI2_init, SHENQI2_callback },
#endif
#if defined(SKYARTEC_CC2500_INO)
{PROTO_SKYARTEC, STR_SKYARTEC, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 1, SW_CC2500, SKYARTEC_init, SKYARTEC_callback },
#endif
#if defined(SLT_CCNRF_INO)
{PROTO_SLT, STR_SLT, STR_SUBTYPE_SLT, 5, OPTION_RFTUNE, 0, 1, SW_NRF, SLT_init, SLT_callback },
{PROTO_SLT, STR_SLT, STR_SUBTYPE_SLT, 7, OPTION_RFTUNE, 0, 1, SW_NRF, SLT_init, SLT_callback },
#endif
#if defined(SYMAX_NRF24L01_INO)
{PROTO_SYMAX, STR_SYMAX, STR_SUBTYPE_SYMAX, 2, OPTION_NONE, 0, 0, SW_NRF, SYMAX_init, SYMAX_callback },
#endif
#if defined(TRAXXAS_CYRF6936_INO)
{PROTO_TRAXXAS, STR_TRAXXAS, STR_SUBTYPE_TRAXXAS, 1, OPTION_NONE, 0, 0, SW_CYRF, TRAXXAS_init, TRAXXAS_callback },
{PROTO_TRAXXAS, STR_TRAXXAS, STR_SUBTYPE_TRAXXAS, 2, OPTION_NONE, 0, 0, SW_CYRF, TRAXXAS_init, TRAXXAS_callback },
#endif
#if defined(UDIRC_CCNRF_INO)
{PROTO_UDIRC, STR_UDIRC, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, UDIRC_init, UDIRC_callback },
#endif
#if defined(V2X2_NRF24L01_INO)
{PROTO_V2X2, STR_V2X2, STR_SUBTYPE_V2X2, 3, OPTION_NONE, 0, 0, SW_NRF, V2X2_init, V2X2_callback },
@@ -474,9 +507,6 @@ const mm_protocol_definition multi_protocols[] = {
#if defined(V911S_CCNRF_INO)
{PROTO_V911S, STR_V911S, STR_SUBTYPE_V911S, 2, OPTION_RFTUNE, 0, 0, SW_NRF, V911S_init, V911S_callback },
#endif
#if defined(WK2x01_CYRF6936_INO)
{PROTO_WK2x01, STR_WK2x01, STR_SUBTYPE_WK2x01, 6, OPTION_NONE, 1, 1, SW_CYRF, WK_init, WK_callback },
#endif
#if defined(WFLY_CYRF6936_INO)
{PROTO_WFLY, STR_WFLY, STR_SUBTYPE_WFLY, 1, OPTION_NONE, 1, 0, SW_CYRF, WFLY_init, WFLY_callback },
#endif
@@ -484,18 +514,33 @@ const mm_protocol_definition multi_protocols[] = {
{PROTO_WFLY2, STR_WFLY2, STR_SUBTYPE_WFLY2, 1, OPTION_OPTION, 1, 0, SW_A7105, WFLY2_init, WFLY2_callback },
// {PROTO_WFLY2, STR_WFLY2, STR_SUBTYPE_WFLY2, 1, OPTION_WBUS, 1, 0, SW_A7105, WFLY2_init, WFLY2_callback },// crash OpenTX...
#endif
#if defined(WK2x01_CYRF6936_INO)
{PROTO_WK2x01, STR_WK2x01, STR_SUBTYPE_WK2x01, 6, OPTION_NONE, 1, 1, SW_CYRF, WK_init, WK_callback },
#endif
#if defined(WL91X_CCNRF_INO)
{PROTO_WL91X, STR_WL91X, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, WL91X_init, WL91X_callback },
#endif
#if defined(WPL_NRF24L01_INO)
{PROTO_WPL, STR_WPL, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, WPL_init, WPL_callback },
#endif
#if defined(XERALL_NRF24L01_INO)
{PROTO_XERALL, STR_XERALL, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, XERALL_init, XERALL_callback },
#endif
#if defined(XK_CCNRF_INO)
{PROTO_XK, STR_XK, STR_SUBTYPE_XK, 3, OPTION_RFTUNE, 0, 0, SW_NRF, XK_init, XK_callback },
#endif
#if defined(XK2_CCNRF_INO)
{PROTO_XK2, STR_XK2, STR_SUBTYPE_XK2, 2, OPTION_RFTUNE, 0, 0, SW_NRF, XK2_init, XK2_callback },
#endif
#if defined(XN297DUMP_NRF24L01_INO)
{PROTO_XN297DUMP, STR_XN297DUMP, STR_SUBTYPE_XN297DUMP, 6, OPTION_RFCHAN, 0, 0, SW_NRF, XN297Dump_init, XN297Dump_callback },
{PROTO_XN297DUMP, STR_XN297DUMP, STR_SUBTYPE_XN297DUMP, 7, OPTION_RFCHAN, 0, 0, SW_NRF, XN297Dump_init, XN297Dump_callback },
#endif
#if defined(YD717_NRF24L01_INO)
{PROTO_YD717, STR_YD717, STR_SUBTYPE_YD717, 5, OPTION_NONE, 0, 0, SW_NRF, YD717_init, YD717_callback },
#endif
#if defined(YUXIANG_NRF24L01_INO)
{PROTO_YUXIANG, STR_YUXIANG, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, YUXIANG_init, YUXIANG_callback },
#endif
#if defined(ZSX_NRF24L01_INO)
{PROTO_ZSX, STR_ZSX, STR_SUBTYPE_ZSX, 1, OPTION_NONE, 0, 0, SW_NRF, ZSX_init, ZSX_callback },
#endif
@@ -550,7 +595,10 @@ uint16_t PROTOLIST_callback()
Serial_write(multi_protocols[option].protocol);
//Protocol name
for(uint8_t i=0;i<proto_len;i++)
{
Serial_write(multi_protocols[option].ProtoString[i]);
//debug("%c",multi_protocols[option].ProtoString[i]);
}
//Flags
uint8_t flags=0;
#ifdef FAILSAFE_ENABLE
@@ -560,16 +608,21 @@ uint16_t PROTOLIST_callback()
if(multi_protocols[option].chMap)
flags |= 0x02; //Disable_ch_mapping supported
Serial_write( flags | (multi_protocols[option].optionType<<4)); // flags && option type
//debug(" Flag=%02X",flags | (multi_protocols[option].optionType<<4));
//Number of sub protocols
Serial_write(nbr_sub);
//debug(" NSub=%02X ",nbr_sub);
if(nbr_sub !=0 )
{//Sub protocols length and texts
for(uint8_t i=0;i<=nbr_sub*sub_len;i++)
{
Serial_write(multi_protocols[option].SubProtoString[i]);
//debug("%c",multi_protocols[option].SubProtoString[i]);
}
}
//debugln("");
}
}
return 1000;
}
#endif
#endif

View File

@@ -19,7 +19,7 @@
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_REVISION 4
#define VERSION_PATCH_LEVEL 0
#define VERSION_PATCH_LEVEL 58
#define MODE_SERIAL 0
@@ -75,7 +75,7 @@ enum PROTOCOLS
PROTO_NCC1701 = 44, // =>NRF24L01
PROTO_E01X = 45, // =>CYRF6936
PROTO_V911S = 46, // =>NRF24L01
PROTO_GD00X = 47, // =>NRF24L01
PROTO_GD00X = 47, // =>CC2500 & NRF24L01
PROTO_V761 = 48, // =>NRF24L01
PROTO_KF606 = 49, // =>NRF24L01
PROTO_REDPINE = 50, // =>CC2500
@@ -125,7 +125,16 @@ enum PROTOCOLS
PROTO_BLUEFLY = 95, // =>CC2500 & NRF24L01
PROTO_BUMBLEB = 96, // =>CC2500 & NRF24L01
PROTO_SGF22 = 97, // =>NRF24L01
PROTO_KYOSHO3 = 98, // =>CYRF6936
PROTO_XK2 = 99, // =>CC2500 & NRF24L01
PROTO_YUXIANG = 100, // =>NRF24L01
PROTO_UDIRC = 101, // =>CC2500 & NRF24L01
PROTO_JIABAILE = 102, // =>NRF24L01
PROTO_H36 = 103, // =>NRF24L01
PROTO_KAMTOM = 104, // =>NRF24L01
PROTO_SHENQI2 = 105, // =>NRF24L01
PROTO_WL91X = 106, // =>CC2500 & NRF24L01
PROTO_WPL = 107, // =>NRF24L01
PROTO_NANORF = 126, // =>NRF24L01
PROTO_TEST = 127, // =>CC2500
@@ -151,14 +160,13 @@ enum Hubsan
};
enum AFHDS2A
{
PWM_IBUS = 0,
PPM_IBUS = 1,
PWM_SBUS = 2,
PPM_SBUS = 3,
PWM_IB16 = 4,
PPM_IB16 = 5,
PWM_SB16 = 6,
PPM_SB16 = 7,
PWM_IBUS = 0,
PPM_IBUS = 1,
PWM_SBUS = 2,
PPM_SBUS = 3,
AFHDS2A_GYRO_OFF = 4,
AFHDS2A_GYRO_ON = 5,
AFHDS2A_GYRO_ON_REV = 6,
};
enum Hisky
{
@@ -173,6 +181,7 @@ enum DSM
DSMX_2F = 3,
DSM_AUTO = 4,
DSMR = 5,
DSM2_SFC = 6,
};
enum DSM_RX
{
@@ -200,11 +209,13 @@ enum SYMAX
};
enum SLT
{
SLT_V1 = 0,
SLT_V2 = 1,
Q100 = 2,
Q200 = 3,
MR100 = 4,
SLT_V1 = 0,
SLT_V2 = 1,
Q100 = 2,
Q200 = 3,
MR100 = 4,
SLT_V1_4 = 5,
RF_SIM = 6,
};
enum CX10
{
@@ -251,7 +262,8 @@ enum MT99XX
};
enum MT99XX2
{
PA18 = 0,
PA18 = 0,
SU35 = 1,
};
enum MJXQ
{
@@ -279,10 +291,11 @@ enum FRSKYX
};
enum HONTAI
{
HONTAI = 0,
JJRCX1 = 1,
X5C1 = 2,
FQ777_951 =3,
HONTAI = 0,
JJRCX1 = 1,
X5C1 = 2,
FQ777_951 = 3,
HONTAI_XKK170 = 4,
};
enum V2X2
{
@@ -360,7 +373,8 @@ enum REDPINE
};
enum TRAXXAS
{
TRAXXAS_TQ = 0,
TRAXXAS_TQ2 = 0,
TRAXXAS_TQ1 = 1,
};
enum ESKY150
{
@@ -386,6 +400,7 @@ enum XN297DUMP
XN297DUMP_AUTO = 3,
XN297DUMP_NRF = 4,
XN297DUMP_CC2500 = 5,
XN297DUMP_XN297 = 6,
};
enum FRSKY_R9
{
@@ -403,6 +418,11 @@ enum ESKY
ESKY_STD = 0,
ESKY_ET4 = 1,
};
enum FQ777
{
FQ777 = 0,
XBM37 = 1,
};
enum FRSKY_RX
{
FRSKY_RX = 0,
@@ -456,8 +476,9 @@ enum RLINK
};
enum MOULDKG
{
MOULDKG_ANALOG = 0,
MOULDKG_DIGIT = 1,
MOULDKG_ANALOG4 = 0,
MOULDKG_DIGIT4 = 1,
MOULDKG_ANALOG6 = 2,
};
enum KF606
{
@@ -475,6 +496,27 @@ enum FX
FX816 = 0,
FX620 = 1,
FX9630 = 2,
FX_Q560 = 3,
FX_QF012 = 4,
FX_BM26 = 5,
FX_A570 = 6,
};
enum SGF22
{
SGF22_F22 = 0,
SGF22_F22S = 1,
SGF22_J20 = 2,
SGF22_CX10 = 3,
};
enum JIABAILE
{
JIABAILE_STD = 0,
JIABAILE_GYRO = 1,
};
enum XK2
{
XK2_X4 = 0,
XK2_P10 = 1,
};
#define NONE 0
@@ -837,7 +879,9 @@ enum {
#define MOULDKG_EEPROM_OFFSET 882 // RX ID, 3 bytes per model, end is 882+64*3=1074
#define DSM_CLONE_EEPROM_OFFSET 1074 // (4) TX ID, (1) Initialized, end is 1079
#define TRAXXAS_EEPROM_OFFSET 1079 // RX ID and SOP index, 3 bytes per model id, end is 1079+192=1271
//#define CONFIG_EEPROM_OFFSET 1271 // Current configuration of the multimodule
#define XK2_EEPROM_OFFSET 1271 // RX ID checksum, 1 byte per model, end is 1271+64=1335
#define JIABAILE_EEPROM_OFFSET 1335 // RX ID, 3 bytes per model, end is 1335+64*3=1527
//#define CONFIG_EEPROM_OFFSET 1527 // Current configuration of the multimodule
/* STM32 Flash Size */
#ifndef DISABLE_FLASH_SIZE_CHECK
@@ -1096,6 +1140,8 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p --
Q100 2
Q200 3
MR100 4
SLT_V1_4CH 5
RF_SIM 6
sub_protocol==E01X
E012 0
E015 1

View File

@@ -18,7 +18,7 @@ Multiprotocol is distributed in the hope that it will be useful,
#include "iface_xn297.h"
#define FORCE_REALACC_ORIGINAL_ID
//#define FORCE_REALACC_ORIGINAL_ID
#define REALACC_INITIAL_WAIT 500
#define REALACC_PACKET_PERIOD 2268
@@ -30,7 +30,7 @@ Multiprotocol is distributed in the hope that it will be useful,
static void __attribute__((unused)) REALACC_send_packet()
{
packet[ 0]= 0xDC;
packet[ 0]= 0xDC; // DC/D6/DE
packet[ 1]= convert_channel_8b(AILERON); // 00..80..FF
packet[ 2]= convert_channel_8b(ELEVATOR); // 00..80..FF
packet[ 3]= convert_channel_8b(THROTTLE); // 00..FF
@@ -39,16 +39,17 @@ static void __attribute__((unused)) REALACC_send_packet()
packet[ 6]= 0x20; // Trim
packet[ 7]= 0x20; // Trim
packet[ 8]= 0x20; // Trim
packet[ 9]= num_ch; // Change at each power up
packet[10]= 0x04 // Flag1
packet[ 9]= 0x88; // Change at each power up: C5 A2 77 F0 84 58, fixed for the E017 = 88
packet[10]= 0x04 // Flag1: R11=04, E017=0C
| 0x02 // Rate1=0, Rate2=1, Rate3=2
| GET_FLAG(CH8_SW, 0x20); // Headless
packet[11]= 0x00 // Flag2
| GET_FLAG(CH7_SW, 0x01) // Calib
| GET_FLAG(CH9_SW, 0x20) // Return
| GET_FLAG(CH10_SW,0x80); // Unknown
| GET_FLAG(CH10_SW,0x80); // Throttle cut
packet[12]= 0x00 // Flag3
| GET_FLAG(CH5_SW, 0x01) // Flip
| GET_FLAG(CH11_SW,0x02) // Rotating
| GET_FLAG(CH6_SW, 0x80); // Light
XN297_Hopping(hopping_frequency_no);
@@ -59,31 +60,50 @@ static void __attribute__((unused)) REALACC_send_packet()
static void __attribute__((unused)) REALACC_send_bind_packet()
{
packet[0] = 0xB1;
memcpy(&packet[1],rx_tx_addr,4);
memcpy(&packet[5],hopping_frequency,5);
packet[0] = 0xB1; // B0/B1
memcpy(&packet[1],rx_tx_addr,4); // Address
memcpy(&packet[5],hopping_frequency,5); // RF frequencies
XN297_WriteEnhancedPayload(packet, REALACC_BIND_PAYLOAD_SIZE,1);
}
static void __attribute__((unused)) REALACC_initialize_txid()
{
rx_tx_addr[3] &= 0x3F;
calc_fh_channels(REALACC_RF_NUM_CHANNELS);
num_ch=random(0xfefefefe); // 00..FF
#ifdef FORCE_REALACC_ORIGINAL_ID
//Dump
rx_tx_addr[0]=0x99;
rx_tx_addr[1]=0x06;
rx_tx_addr[2]=0x00;
rx_tx_addr[3]=0x00;
hopping_frequency[0]=0x55;
hopping_frequency[1]=0x59;
hopping_frequency[2]=0x5A;
hopping_frequency[3]=0x5A;
hopping_frequency[4]=0x62;
num_ch=0xC5; // Value in dumps: C5 A2 77 F0 84 58
if(RX_num==0)
{//TX1
rx_tx_addr[0]=0x99;
rx_tx_addr[1]=0x06;
rx_tx_addr[2]=0x00;
rx_tx_addr[3]=0x00; // 00..3F:OK, 40..:NOK
hopping_frequency[0]=0x55;
hopping_frequency[1]=0x59;
hopping_frequency[2]=0x5A;
hopping_frequency[3]=0x5A;
hopping_frequency[4]=0x62;
}
else
{//TX2
rx_tx_addr[0]=0x4F;
rx_tx_addr[1]=0xB9;
rx_tx_addr[2]=0xA1;
rx_tx_addr[3]=0x17;
hopping_frequency[0]=0x45;
hopping_frequency[1]=0x38;
hopping_frequency[2]=0x3C;
hopping_frequency[3]=0x41;
hopping_frequency[4]=0x3F;
}
#endif
#if 0
debug("ID: %02X %02X %02X %02X, C: ",rx_tx_addr[0],rx_tx_addr[1],rx_tx_addr[2],rx_tx_addr[3]);
for(uint8_t i=0; i<REALACC_RF_NUM_CHANNELS; i++)
debug(" %02X",hopping_frequency[i]);
debugln("");
#endif
}
static void __attribute__((unused)) REALACC_RF_init()
@@ -129,18 +149,39 @@ void REALACC_init()
// Bind
// Address = 4D 41 49 4E = 'MAIN'
// Channel = 80 (most likely from dump)
// P(10) = B1 99 06 00 00 55 59 5A 5A 62
// B1 indicates bind packet
// TX1
// ---
// P(10) = B1 99 06 00 00 55 59 5A 5A 62
// Bx indicates bind packet, why x=1?
// 99 06 00 00 = ID = address of normal packets
// 55 59 5A 5A 62 = 85, 89, 90, 90, 98 = RF channels to be used (kind of match previous dumps)// Normal
// 55 59 5A 5A 62 = 85, 89, 90, 90, 98 = RF channels to be used (kind of match previous dumps)
// TX2
// ---
// P(10) = B0 4F B9 A1 17 45 38 3C 41 3F
// Bx indicates bind packet, why x=0?
// 4F B9 A1 17 = ID = address of normal packets
// 45 38 3C 41 3F = 69, 56, 60, 65, 63 = RF channels to be used
// Normal
// Address = 99 06 00 00
// Channels = 84, 89, 90, 90, 98 (guess from bind)
// P(13)= DC 80 80 32 80 20 20 20 20 58 04 00 00
// DC = normal packet
// 80 80 32 80 : AETR 00..80..FF
// 20 20 20 20 : Trims
// 58 : changing every time the TX restart
// 04 : |0x20=headless, |0x01=rate2, |0x02=rate3
// 00 : |0x01=calib, |0x20=return, |0x80=unknown
// 00 : |0x80=light, |0x01=flip
// TX1
// ---
// Address = 99 06 00 00
// Channels = 84, 89, 90, 90, 98 (guess from bind)
// P(13)= DC 80 80 32 80 20 20 20 20 58 04 00 00
// Dx = normal packet, why C ?
// 80 80 32 80 : AETR 00..80..FF
// 20 20 20 20 : Trims
// 58 : changing every time the TX restart
// 04 : |0x20=headless, |0x01=rate2, |0x02=rate3
// 00 : |0x01=calib, |0x20=return, |0x80=unknown
// 00 : |0x80=light, |0x01=flip
// TX2
// ---
// Address = 4F B9 A1 17
// P(13)= D6/DE 80 80 80 80 20 20 20 20 88 0C 00 00
// Dx = normal packet, why 6/E ?
// 80 80 32 80 : AETR 00..80..FF
// 20 20 20 20 : Trims
// 88 : not changing unknown
// 0C : |0x20=headless, |0x01=rate2, |0x02=rate3
// 00 : |0x01=calib, |0x20=return, |0x80=unknown
// 00 : |0x80=light, |0x01=flip, |0x02=Rotating

View File

@@ -232,7 +232,7 @@ static void __attribute__((unused)) RLINK_send_packet()
packet[1] |= 0x21; //air 0x21 on dump but it looks to support telemetry at least RSSI
break;
case RLINK_DUMBORC:
packet[1] = 0x00; //always 0x00 on dump
packet[1] |= 0x01; //always 0x00 on dump but does appear to support telemtry on newer transmitters
break;
}
@@ -384,8 +384,9 @@ uint16_t RLINK_callback()
debug("Telem:");
#endif
CC2500_ReadData(packet_in, len);
if(packet_in[0]==RLINK_RX_PACKET_LEN && (packet_in[len-1] & 0x80) && memcmp(&packet[2],rx_tx_addr,RLINK_TX_ID_LEN)==0 && packet_in[6]==packet[1])
if(packet_in[0]==RLINK_RX_PACKET_LEN && (packet_in[len-1] & 0x80) && memcmp(&packet[2],rx_tx_addr,RLINK_TX_ID_LEN)==0 && (packet_in[6]==packet[1] || sub_protocol == RLINK_DUMBORC))
{//Correct telemetry received: length, CRC, ID and type
//packet_in[6] is 0x00 on almost all DumboRC RX so assume it is always valid
#ifdef RLINK_DEBUG_TELEM
for(uint8_t i=0;i<len;i++)
debug(" %02X",packet_in[i]);

View File

@@ -12,29 +12,52 @@ Multiprotocol is distributed in the hope that it will be useful,
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// Compatible with SGF22 R11
// Compatible with SGF22, ParkTen F22S, KF700 J20
#if defined(SGF22_NRF24L01_INO)
#include "iface_xn297.h"
//#define FORCE_SGF22_ORIGINAL_ID
#define FORCE_SGF22_CX10_ORIGINAL_ID
#define SGF22_PACKET_PERIOD 11950 //10240
#define SGF22_BIND_RF_CHANNEL 78
#define SGF22_PAYLOAD_SIZE 12
#define SGF22_BIND_COUNT 50
#define SGF22_RF_NUM_CHANNELS 4
#define SGF22_PACKET_PERIOD 11950 //10240
#define SGF22_PAYLOAD_SIZE 12
#define SGF22_BIND_COUNT 50
#define SGF22_RF_NUM_CHANNELS 4
#define SGF22_BIND_RF_CHANNEL 78
#define SGF22_F22S_BIND_RF_CHANNEL 10
#define SGF22_J20_BIND_RF_CHANNEL 28
#define SGF22_CX10_BIND_RF_CHANNEL 48
//packet[8]
#define SGF22_FLAG_3D 0x00
#define SGF22_FLAG_ROLL 0x08
#define SGF22_FLAG_LIGHT 0x04
#define SGF22_FLAG_VIDEO 0x10
#define SGF22_FLAG_6G 0x40
#define SGF22_FLAG_VERTICAL 0xC0
#define SGF22_FLAG_3D 0x00
#define SGF22_FLAG_LIGHT 0x04
#define SGF22_FLAG_ROLL 0x08
#define SGF22_FLAG_VIDEO 0x10
#define SGF22_FLAG_6G 0x40
#define SGF22_FLAG_VERTICAL 0xC0
#define SGF22_J20_FLAG_HORIZONTAL 0x80
//#define SGF22_J20_FLAG_SPEED 0x01 // Up/Down trim, not implemented
#define SGF22_FX922_FLAG_BALANCEHIGH 0x01
#define SGF22_FX922_FLAG_BALANCE 0x02
//packet[9]
#define SGF22_FLAG_PHOTO 0x40
#define SGF22_FLAG_TRIMRESET 0x04
#define SGF22_FLAG_PHOTO 0x40 // #define SGF22_J20_FLAG_INVERT 0x40
#define SGF22_J20_FLAG_FIXHEIGHT 0x80
#define SGF22_WRITE_TIME 1000
enum {
SGF22_DATA1,
SGF22_DATA2,
SGF22_DATA3,
SGF22_RX,
};
static void __attribute__((unused)) SGF22_send_packet()
{
@@ -58,19 +81,43 @@ static void __attribute__((unused)) SGF22_send_packet()
packet_sent = 0;
//packet
packet[0] = 0x1B;
packet[8] = SGF22_FLAG_3D // default
| GET_FLAG(CH6_SW, SGF22_FLAG_ROLL) // roll
| GET_FLAG(CH7_SW, SGF22_FLAG_LIGHT) // push up throttle trim for light
| GET_FLAG(CH9_SW, SGF22_FLAG_VIDEO); // push down throttle trim for video
if(Channel_data[CH5] > CHANNEL_MIN_COMMAND)
packet[8] |= SGF22_FLAG_6G; // mode 1 - 6g
if(Channel_data[CH5] > CHANNEL_MAX_COMMAND)
packet[8] |= SGF22_FLAG_VERTICAL; // mode 0 - vertical
packet[9] = GET_FLAG(CH8_SW, SGF22_FLAG_PHOTO); // press in throttle trim for photo
packet[10] = 0x42; // no fine tune
packet[11] = 0x10; // no fine tune
if (sub_protocol != SGF22_CX10)
{//SGF22_F22,SGF22_F22S,SGF22_J20
packet[8] = SGF22_FLAG_3D // CH5 -100%, F22 & F22S - 3D mode, J20 - Gyro off
| GET_FLAG(CH6_SW, SGF22_FLAG_ROLL) // roll
| GET_FLAG(CH7_SW, SGF22_FLAG_LIGHT) // push up throttle trim for light in the stock TX
| GET_FLAG(CH9_SW, SGF22_FLAG_VIDEO) // push down throttle trim for video in the stock TX
| GET_FLAG(CH11_SW, SGF22_FX922_FLAG_BALANCE)
| GET_FLAG(CH12_SW, SGF22_FX922_FLAG_BALANCEHIGH);
if(Channel_data[CH5] > CHANNEL_MAX_COMMAND)
packet[8] |= SGF22_FLAG_VERTICAL; // CH5 100%, vertical mode (torque)
else if(Channel_data[CH5] > CHANNEL_MIN_COMMAND )
packet[8] |= ( sub_protocol == SGF22_J20 ? SGF22_J20_FLAG_HORIZONTAL : SGF22_FLAG_6G ); // CH5 0%, F22 & F22S - 6G mode, J20 - Horizontal mode
}
else //SGF22_CX10 114548
{
if(CH6_SW)
flags = 0x06; // high rate
else
if(Channel_data[CH6] < CHANNEL_MIN_COMMAND)
flags = 0x04; // low rate
else
flags = 0x05; // mid rate
packet[8] = flags
| GET_FLAG(CH5_SW, 0x08); // flip
}
packet[9] = GET_FLAG(CH8_SW, SGF22_FLAG_PHOTO) // F22: photo, press in throttle trim in the stock TX, J20: invert flight
| GET_FLAG(CH10_SW, ( sub_protocol == SGF22_J20 ? SGF22_J20_FLAG_FIXHEIGHT : SGF22_FLAG_TRIMRESET )) ; // F22: Both sticks down inwards in the stock TX, J20: Altitude hold
packet[10] = 0x42; // no fine tune
packet[11] = 0x10; // no fine tune
}
packet[1] = packet_count; // sequence
if(sub_protocol == SGF22_F22S)
packet[0] += 6;
else if (sub_protocol == SGF22_J20)
packet[0] += 3;
else if (sub_protocol == SGF22_CX10)
packet[0] += 0x6A;
packet[1] = packet_count; // sequence
packet[2] = rx_tx_addr[2];
packet[3] = rx_tx_addr[3];
packet[4] = convert_channel_8b(THROTTLE);
@@ -80,7 +127,10 @@ static void __attribute__((unused)) SGF22_send_packet()
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WriteEnhancedPayload(packet, SGF22_PAYLOAD_SIZE,0);
if (sub_protocol != SGF22_CX10)
XN297_WriteEnhancedPayload(packet, SGF22_PAYLOAD_SIZE,0);
else
XN297_WritePayload(packet, SGF22_PAYLOAD_SIZE);
#if 0
debug_time("");
for(uint8_t i=0; i<SGF22_PAYLOAD_SIZE; i++)
@@ -91,7 +141,10 @@ static void __attribute__((unused)) SGF22_send_packet()
static void __attribute__((unused)) SGF22_initialize_txid()
{
uint8_t val = (( (uint16_t) rx_tx_addr[2] << 8 ) | rx_tx_addr[3])%5;
uint16_t val = ( rx_tx_addr[2] << 8 ) | rx_tx_addr[3];
if ( rx_tx_addr[2] > ( 0xFF - rx_tx_addr[3]) )
val--;
val %= 5;
const uint8_t hop[5][4] =
{ { 0x0C, 0x2A, 0x1B, 0x39 },
@@ -100,8 +153,8 @@ static void __attribute__((unused)) SGF22_initialize_txid()
{ 0x15, 0x34, 0x24, 0x44 },
{ 0x18, 0x37, 0x27, 0x47 } };
memcpy(hopping_frequency, &hop[val], SGF22_RF_NUM_CHANNELS);
/*//Same code sze...
/*//Same code size...
hopping_frequency[0] = 0x0C + 3 * val;
hopping_frequency[1] = hopping_frequency[0] + 0x1E;
if(val > 1) hopping_frequency[1]++;
@@ -110,10 +163,27 @@ static void __attribute__((unused)) SGF22_initialize_txid()
if(val ) hopping_frequency[3]++;*/
#ifdef FORCE_SGF22_ORIGINAL_ID
rx_tx_addr[2] = 0x1F;
rx_tx_addr[3] = 0x61;
rx_tx_addr[2] = 0x1F; // TX2:27 TX3:2B
rx_tx_addr[3] = 0x61; // TX2:51 TX3:0C
memcpy(hopping_frequency,"\x15\x34\x24\x44", SGF22_RF_NUM_CHANNELS); //Original dump=>21=0x15,52=0x34,36=0x24,68=0x44
#endif
#ifdef FORCE_SGF22_CX10_ORIGINAL_ID
if(sub_protocol == SGF22_CX10)
{
if(rx_tx_addr[3] & 1)
{
rx_tx_addr[2] = 0x4C;
rx_tx_addr[3] = 0xD7;
memcpy(hopping_frequency, "\x37\x42\x47\x3c", SGF22_RF_NUM_CHANNELS);
}
else
{
rx_tx_addr[2] = 0x50;
rx_tx_addr[3] = 0xE1;
memcpy(hopping_frequency, "\x3b\x4b\x46\x41", SGF22_RF_NUM_CHANNELS);
}
}
#endif
#if 0
debug("ID: %02X %02X, C: ",rx_tx_addr[2],rx_tx_addr[3]);
for(uint8_t i=0; i<SGF22_RF_NUM_CHANNELS; i++)
@@ -126,33 +196,97 @@ static void __attribute__((unused)) SGF22_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t*)"\xC7\x95\x3C\xBB\xA5", 5);
XN297_RFChannel(SGF22_BIND_RF_CHANNEL); // Set bind channel
#ifdef SGF22_HUB_TELEMETRY
XN297_SetRXAddr((uint8_t*)"\xC7\x95\x3C\xBB\xA5", SGF22_PAYLOAD_SIZE);
#endif
const uint8_t bind_chan[] = {SGF22_BIND_RF_CHANNEL, SGF22_F22S_BIND_RF_CHANNEL, SGF22_J20_BIND_RF_CHANNEL, SGF22_CX10_BIND_RF_CHANNEL};
XN297_RFChannel(bind_chan[sub_protocol]); // Set bind channel
}
uint16_t SGF22_callback()
{
if(phase == 0)
#ifdef SGF22_HUB_TELEMETRY
bool rx = false;
static uint8_t telem_count = 0;
#endif
switch(phase)
{
phase++;
#ifdef MULTI_SYNC
telemetry_set_input_sync(SGF22_PACKET_PERIOD);
#endif
SGF22_send_packet();
if(IS_BIND_IN_PROGRESS)
{
if(--bind_counter==0)
BIND_DONE;
}
}
else
{//send 3 times in total the same packet
NRF24L01_Strobe(REUSE_TX_PL);
phase++;
if(phase > 2)
{
phase = 0;
return SGF22_PACKET_PERIOD - 2*1550;
}
case SGF22_DATA1:
#ifdef MULTI_SYNC
telemetry_set_input_sync(SGF22_PACKET_PERIOD);
#endif
#ifdef SGF22_HUB_TELEMETRY
rx = XN297_IsRX();
XN297_SetTxRxMode(TXRX_OFF);
#endif
SGF22_send_packet();
if(IS_BIND_IN_PROGRESS)
{
if(--bind_counter==0)
BIND_DONE;
}
#ifdef SGF22_HUB_TELEMETRY
if(rx)
{
uint8_t p_len = XN297_ReadEnhancedPayload(packet_in, SGF22_PAYLOAD_SIZE);
if(p_len == 3 && packet_in[0] == rx_tx_addr[2] && packet_in[1] == rx_tx_addr[3])
{//packets: 00 0B 00 -> 00 0B 01
telemetry_link = 1;
v_lipo1 = packet_in[2] ? 0 : 255; //2.9V for 1S, 7.0V for 2S
telemetry_lost = 0;
telem_count = 0;
}
#if 0
debug("L %d ",p_len);
debug("RX");
for(uint8_t i=0; i<SGF22_PAYLOAD_SIZE; i++)
debug(" %02X",packet_in[i]);
debugln("");
#endif
}
if(telem_count > 4*63) // Around 3.5sec with no telemetry
telemetry_lost = 1;
else
{
telem_count++;
if(!telemetry_lost && (telem_count & 0x3F) == 0)
{// Should have received a telem packet but... Send telem to the radio to keep it alive
telemetry_link = 1;
#if 0
debugln("Miss");
#endif
}
}
#endif
phase++;
break;
case SGF22_DATA2:
case SGF22_DATA3:
//send 3 times in total the same packet
XN297_ReSendPayload();
phase++;
break;
default: //SGF22_RX
#ifdef SGF22_HUB_TELEMETRY
/*{ // Wait for packet to be sent before switching to receive mode
uint16_t start=(uint16_t)micros(), count=0;
while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 500)
{
if(XN297_IsPacketSent())
break;
count++;
}
debugln("%d",count);
}*/
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
#endif
phase = SGF22_DATA1;
return SGF22_PACKET_PERIOD - 3*1550;
}
return 1550;
}
@@ -163,8 +297,12 @@ void SGF22_init()
SGF22_initialize_txid();
SGF22_RF_init();
bind_counter=SGF22_BIND_COUNT;
packet_sent = packet_count = 0x26;
phase = 0;
packet_sent = packet_count = 0x26; // TX2:26 TX3:26
phase = SGF22_DATA1;
#ifdef SGF22_HUB_TELEMETRY
RX_RSSI = 100; // Dummy value
telemetry_lost = 1;
#endif
}
#endif

View File

@@ -0,0 +1,232 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(SHENQI2_NRF24L01_INO)
#include "iface_xn297.h"
//#define FORCE_SHENQI2_ORIGINAL_ID
#define SHENQI2_PAYLOAD_SIZE 8
#define SHENQI2_RF_NUM_CHANNELS 16
#define SHENQI2_BIND_COUNT 2000
#define SHENQI2_WRITE_TIME 650
#define SHENQI2_BIND_CHANNEL 44
#define SHENQI2_PACKET_PERIOD 8210
enum {
SHENQI2_BIND = 0,
SHENQI2_BIND_RX,
SHENQI2_DATA,
};
static void __attribute__((unused)) SHENQI2_send_packet()
{
if(bind_counter)
{
bind_counter--;
if(!bind_counter)
BIND_DONE;
}
memset(packet, 0x00, SHENQI2_PAYLOAD_SIZE);
packet_count &= 0x0F;
packet[0] = packet_count;
memcpy(&packet[1],rx_tx_addr,5);
if(IS_BIND_DONE)
{//Normal
uint8_t val = convert_channel_8b(CH2);
if(val < 0x70)
val = 0x30;
else if(val < 0x80)
val = 0x00;
else
{
val &= 0x7F;
val >>= 2;
}
if(Channel_data[CH1] > 1024+50)
val |= 0x40;
else if(Channel_data[CH1] < 1024-50)
val |= 0x80;
packet[6] = val;
//packet[7] = 0x00; // ??
}
else
packet[0] |= 0x30;
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WriteEnhancedPayload(packet, SHENQI2_PAYLOAD_SIZE, false);
#ifdef DEBUG_SERIAL
for(uint8_t i=0; i < SHENQI2_PAYLOAD_SIZE; i++)
debug("%02X ", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) SHENQI2_initialize_txid()
{
#ifdef FORCE_SHENQI2_ORIGINAL_ID
//TXID
rx_tx_addr[0] = 0x51;
rx_tx_addr[1] = 0x70;
rx_tx_addr[2] = 0x02;
//RXID
rx_tx_addr[3] = 0x46;
rx_tx_addr[4] = 0xBE;
#endif
rx_tx_addr[3] = 0x00;
rx_tx_addr[4] = 0x00;
//Freq
memcpy(hopping_frequency,(uint8_t*)"\x05\x09\x0E\x0F\x17\x1C\x21\x27\x2A\x2C\x33\x39\x3D\x42\x48\x4C",16);
}
static void __attribute__((unused)) SHENQI2_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
//Address
XN297_SetTXAddr((uint8_t*)"\x74\xD1\x3A\xF5\x6C", 5);
XN297_SetRXAddr((uint8_t*)"\x74\xD1\x3A\xF5\x6C", SHENQI2_PAYLOAD_SIZE);
XN297_RFChannel(SHENQI2_BIND_CHANNEL);
}
uint16_t SHENQI2_callback()
{
static bool rx=false;
switch(phase)
{
case SHENQI2_BIND:
rx = XN297_IsRX();
XN297_SetTxRxMode(TXRX_OFF);
SHENQI2_send_packet();
packet_count++;
if(rx)
{
uint8_t val=XN297_ReadEnhancedPayload(packet_in, SHENQI2_PAYLOAD_SIZE);
if(val == SHENQI2_PAYLOAD_SIZE)
{
if(memcmp(rx_tx_addr, packet_in, 3) == 0)
{//Good packet with our TXID
BIND_DONE;
rx_tx_addr[3] = packet_in[3];
rx_tx_addr[4] = packet_in[4];
packet_count = 0;
phase = SHENQI2_DATA;
}
#ifdef DEBUG_SERIAL
for(uint8_t i=0; i < SHENQI2_PAYLOAD_SIZE; i++)
debug(" %02X", packet_in[i]);
debugln();
#endif
}
}
phase++;
return SHENQI2_WRITE_TIME;
case SHENQI2_BIND_RX:
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = SHENQI2_BIND;
return SHENQI2_PACKET_PERIOD - SHENQI2_WRITE_TIME;
default: //SHENQI2_DATA
//Since I don't know the order of the channels, I'm hopping on all the channels quickly
//Refresh rate from the motorcycle perspective is 32ms instead of 8ms...
XN297_Hopping(hopping_frequency_no);
hopping_frequency_no++;
hopping_frequency_no &= 0x0F;
SHENQI2_send_packet();
if(hopping_frequency_no%4 == 0)
packet_count++;
return SHENQI2_PACKET_PERIOD/4;
}
return 0;
}
void SHENQI2_init()
{
BIND_IN_PROGRESS;
SHENQI2_initialize_txid();
SHENQI2_RF_init();
bind_counter = SHENQI2_BIND_COUNT;
phase = SHENQI2_BIND;
hopping_frequency_no = 0;
}
#endif
/*
XN297 1Mb Enhanced,Acked,Scrambled
Bind
---
RX on channel: 44, Time: 2890us P: 34 51 70 02 00 00 00 00
RX on channel: 44, Time: 1780us P: 34 51 70 02 00 00 00 00
RX on channel: 44, Time: 1773us P: 34 51 70 02 00 00 00 00
RX on channel: 44, Time: 1772us P: 34 51 70 02 00 00 00 00
RX on channel: 44, Time: 2889us P: 35 51 70 02 00 00 00 00
RX on channel: 44, Time: 1769us P: 35 51 70 02 00 00 00 00
RX on channel: 44, Time: 1774us P: 35 51 70 02 00 00 00 00
RX on channel: 44, Time: 1771us P: 35 51 70 02 00 00 00 00
RX on channel: 44, Time: 2894us P: 36 51 70 02 00 00 00 00
A= 74 D1 3A F5 6C
RF:44
Timing: 1772µs between the same 4 packets, 2892µs to the next packet, 8208µs between 2 packets
Request ack, if no ack repeat the packet 4 times
P[0] = counter 00..0F | 30 bind
P[1] = TXID[0]
P[2] = TXID[1]
P[3] = TXID[2]
P[4] = RXID[0]
P[5] = RXID[1]
P[6] = TH 00..1F, Break 30, 40 ST_right, 80 ST_left
P[7] = 00?
Answer from motorcycle:
P: 51 70 02 46 BE 00 00 00
P[0] = TXID[0]
P[1] = TXID[1]
P[2] = TXID[2]
P[3] = RXID[0]
P[4] = RXID[1]
P[5] = 00
P[6] = 00
P[7] = 00
Normal packets
---
A= 74 D1 3A F5 6C
RF:5,9,14,15,23,28,33,39,42,44,51,57,61,66,72,76
RF:\x05\x09\x0E\x0F\x17\x1C\x21\x27\x2A\x2C\x33\x39\x3D\x42\x48\x4C
- order of the channels is unknown and vary over time
- send 16 times on each channel and switch (counter 00..0F)
Timing:1772µs between the same 4 packets, 2892µs to the next packet, 8208µs between 2 packets
Timing:8208 between packets if acked
P: 00 51 70 02 46 BE 00 00
P[0] = counter 00..0F
P[1] = TXID[0]
P[2] = TXID[1]
P[3] = TXID[2]
P[4] = RXID[0]
P[5] = RXID[1]
P[6] = TH 00..1F, Break 30, 40 ST_right, 80 ST_left
P[7] = 00?
*/

View File

@@ -19,13 +19,15 @@
#include "iface_nrf250k.h"
//#define SLT_Q200_FORCE_ID
//#define SLT_V1_4_FORCE_ID
// For code readability
#define SLT_PAYLOADSIZE_V1 7
#define SLT_PAYLOADSIZE_V2 11
#define SLT_NFREQCHANNELS 15
#define SLT_TXID_SIZE 4
#define SLT_BIND_CHANNEL 0x50
#define SLT_PAYLOADSIZE_V1 7
#define SLT_PAYLOADSIZE_V1_4 5
#define SLT_PAYLOADSIZE_V2 11
#define SLT_NFREQCHANNELS 15
#define SLT_TXID_SIZE 4
#define SLT_BIND_CHANNEL 0x50
enum{
// flags going to packet[6] (Q200)
@@ -72,12 +74,12 @@ static void __attribute__((unused)) SLT_set_freq(void)
}
// Unique freq
uint8_t max_freq=0x50; //V1 and V2
if(sub_protocol==Q200)
uint8_t max_freq = 0x50; //V1 and V2
if(sub_protocol == Q200)
max_freq=45;
for (uint8_t i = 0; i < SLT_NFREQCHANNELS; ++i)
{
if(sub_protocol==Q200 && hopping_frequency[i] >= max_freq)
if(sub_protocol == Q200 && hopping_frequency[i] >= max_freq)
hopping_frequency[i] = hopping_frequency[i] - max_freq + 0x03;
uint8_t done = 0;
while (!done)
@@ -93,9 +95,15 @@ static void __attribute__((unused)) SLT_set_freq(void)
}
}
}
#ifdef DEBUG_SERIAL
debug("CH:");
for (uint8_t i = 0; i < SLT_NFREQCHANNELS; ++i)
debug(" %02X(%d)", hopping_frequency[i], hopping_frequency[i]);
debugln();
#endif
//Bind channel
hopping_frequency[SLT_NFREQCHANNELS]=SLT_BIND_CHANNEL;
hopping_frequency[SLT_NFREQCHANNELS] = SLT_BIND_CHANNEL;
//Calib all channels
NRF250K_HoppingCalib(SLT_NFREQCHANNELS+1);
@@ -129,44 +137,55 @@ static void __attribute__((unused)) SLT_build_packet()
uint8_t e = 0; // byte where extension 2 bits for every 10-bit channel are packed
for (uint8_t i = 0; i < 4; ++i)
{
uint16_t v = convert_channel_10b(CH_AETR[i], false);
if(sub_protocol>SLT_V2 && (i==CH2 || i==CH3) )
uint16_t v = convert_channel_10b(sub_protocol != SLT_V1_4 ? CH_AETR[i] : i, false);
if(sub_protocol>SLT_V2 && (i==CH2 || i==CH3) && sub_protocol != SLT_V1_4 && sub_protocol != RF_SIM)
v=1023-v; // reverse throttle and elevator channels for Q100/Q200/MR100 protocols
packet[i] = v;
e = (e >> 2) | (uint8_t) ((v >> 2) & 0xC0);
}
// Extra bits for AETR
packet[4] = e;
//->V1_4CH stops here
// 8-bit channels
packet[5] = convert_channel_8b(CH5);
packet[6] = convert_channel_8b(CH6);
if(sub_protocol!=SLT_V1)
//->V1 stops here
if(sub_protocol == Q200)
packet[6] = GET_FLAG(CH9_SW , FLAG_Q200_FMODE)
|GET_FLAG(CH10_SW, FLAG_Q200_FLIP)
|GET_FLAG(CH11_SW, FLAG_Q200_VIDON)
|GET_FLAG(CH12_SW, FLAG_Q200_VIDOFF);
else if(sub_protocol == MR100 || sub_protocol == Q100)
packet[6] = GET_FLAG(CH9_SW , FLAG_MR100_FMODE)
|GET_FLAG(CH10_SW, FLAG_MR100_FLIP)
|GET_FLAG(CH11_SW, FLAG_MR100_VIDEO) // Does not exist on the Q100 but...
|GET_FLAG(CH12_SW, FLAG_MR100_PICTURE); // Does not exist on the Q100 but...
packet[7] = convert_channel_8b(CH7);
packet[8] = convert_channel_8b(CH8);
if(sub_protocol == RF_SIM)
{
if(sub_protocol==Q200)
packet[6] = GET_FLAG(CH9_SW , FLAG_Q200_FMODE)
|GET_FLAG(CH10_SW, FLAG_Q200_FLIP)
|GET_FLAG(CH11_SW, FLAG_Q200_VIDON)
|GET_FLAG(CH12_SW, FLAG_Q200_VIDOFF);
else if(sub_protocol==MR100 || sub_protocol==Q100)
packet[6] = GET_FLAG(CH9_SW , FLAG_MR100_FMODE)
|GET_FLAG(CH10_SW, FLAG_MR100_FLIP)
|GET_FLAG(CH11_SW, FLAG_MR100_VIDEO) // Does not exist on the Q100 but...
|GET_FLAG(CH12_SW, FLAG_MR100_PICTURE); // Does not exist on the Q100 but...
packet[7]=convert_channel_8b(CH7);
packet[8]=convert_channel_8b(CH8);
packet[9]=0xAA; //normal mode for Q100/Q200, unknown for V2/MR100
packet[10]=0x00; //normal mode for Q100/Q200, unknown for V2/MR100
if((sub_protocol==Q100 || sub_protocol==Q200) && CH13_SW)
{//Calibrate
packet[9]=0x77; //enter calibration
if(calib_counter>=20 && calib_counter<=25) // 7 packets for Q100 / 3 packets for Q200
packet[10]=0x20; //launch calibration
calib_counter++;
if(calib_counter>250) calib_counter=250;
}
else
calib_counter=0;
packet[9] = convert_channel_8b(CH9);
packet[10] = convert_channel_8b(CH10);
}
else
{
packet[9] = 0xAA; //normal mode for Q100/Q200, unknown for V2/MR100
packet[10] = 0x00; //normal mode for Q100/Q200, unknown for V2/MR100
}
if((sub_protocol == Q100 || sub_protocol == Q200) && CH13_SW)
{//Calibrate
packet[9] = 0x77; //enter calibration
if(calib_counter >= 20 && calib_counter <= 25) // 7 packets for Q100 / 3 packets for Q200
packet[10] = 0x20; //launch calibration
calib_counter++;
if(calib_counter > 250) calib_counter = 250;
}
else
calib_counter = 0;
}
static void __attribute__((unused)) SLT_send_bind_packet()
@@ -177,8 +196,8 @@ static void __attribute__((unused)) SLT_send_bind_packet()
NRF250K_SetPower();
BIND_DONE;
NRF250K_SetTXAddr((uint8_t *)"\x7E\xB8\x63\xA9", SLT_TXID_SIZE);
memcpy((void*)packet,(void*)rx_tx_addr,SLT_TXID_SIZE);
if(phase==SLT_BIND2)
memcpy((void*)packet, (void*)rx_tx_addr, SLT_TXID_SIZE);
if(phase == SLT_BIND2)
SLT_send_packet(SLT_TXID_SIZE);
else // SLT_BIND1
SLT_send_packet(SLT_PAYLOADSIZE_V2);
@@ -186,6 +205,7 @@ static void __attribute__((unused)) SLT_send_bind_packet()
#define SLT_TIMING_BUILD 1000
#define SLT_V1_TIMING_PACKET 1000
#define SLT_V1_4_TIMING_PACKET 1643
#define SLT_V2_TIMING_PACKET 2042
#define SLT_V1_TIMING_BIND2 1000
#define SLT_V2_TIMING_BIND1 6507
@@ -195,8 +215,9 @@ uint16_t SLT_callback()
switch (phase)
{
case SLT_BUILD:
//debugln_time("b ");
#ifdef MULTI_SYNC
telemetry_set_input_sync(sub_protocol==SLT_V1?20000:13730);
telemetry_set_input_sync(packet_period);
#endif
SLT_build_packet();
NRF250K_SetPower(); //Change power level
@@ -206,42 +227,39 @@ uint16_t SLT_callback()
case SLT_DATA1:
case SLT_DATA2:
phase++;
if(sub_protocol==SLT_V1)
{
SLT_send_packet(SLT_PAYLOADSIZE_V1);
SLT_send_packet(packet_length);
if(sub_protocol == SLT_V1)
return SLT_V1_TIMING_PACKET;
}
else //V2
if(sub_protocol == SLT_V1_4)
{
SLT_send_packet(SLT_PAYLOADSIZE_V2);
return SLT_V2_TIMING_PACKET;
phase++; //Packets are sent two times only
return SLT_V1_4_TIMING_PACKET;
}
//V2
return SLT_V2_TIMING_PACKET;
case SLT_DATA3:
if(sub_protocol==SLT_V1)
SLT_send_packet(SLT_PAYLOADSIZE_V1);
else //V2
SLT_send_packet(SLT_PAYLOADSIZE_V2);
SLT_send_packet(packet_length);
if (++packet_count >= 100)
{// Send bind packet
packet_count = 0;
if(sub_protocol==SLT_V1)
if(sub_protocol == SLT_V1 || sub_protocol == SLT_V1_4)
{
phase=SLT_BIND2;
phase = SLT_BIND2;
return SLT_V1_TIMING_BIND2;
}
else //V2
{
phase=SLT_BIND1;
return SLT_V2_TIMING_BIND1;
}
//V2
phase = SLT_BIND1;
return SLT_V2_TIMING_BIND1;
}
else
{// Continue to send normal packets
phase = SLT_BUILD;
if(sub_protocol==SLT_V1)
return 20000-SLT_TIMING_BUILD;
else //V2
return 13730-SLT_TIMING_BUILD;
if(sub_protocol == SLT_V1)
return 20000 - SLT_TIMING_BUILD;
if(sub_protocol==SLT_V1_4)
return 18000 - SLT_TIMING_BUILD - SLT_V1_4_TIMING_PACKET;
//V2
return 13730 - SLT_TIMING_BUILD;
}
case SLT_BIND1:
SLT_send_bind_packet();
@@ -250,10 +268,12 @@ uint16_t SLT_callback()
case SLT_BIND2:
SLT_send_bind_packet();
phase = SLT_BUILD;
if(sub_protocol==SLT_V1)
return 20000-SLT_TIMING_BUILD-SLT_V1_TIMING_BIND2;
else //V2
return 13730-SLT_TIMING_BUILD-SLT_V2_TIMING_BIND1-SLT_V2_TIMING_BIND2;
if(sub_protocol == SLT_V1)
return 20000 - SLT_TIMING_BUILD - SLT_V1_TIMING_BIND2;
if(sub_protocol == SLT_V1_4)
return 18000 - SLT_TIMING_BUILD - SLT_V1_TIMING_BIND2 - SLT_V1_4_TIMING_PACKET;
//V2
return 13730 - SLT_TIMING_BUILD - SLT_V2_TIMING_BIND1 - SLT_V2_TIMING_BIND2;
}
return 19000;
}
@@ -264,7 +284,36 @@ void SLT_init()
packet_count = 0;
packet_sent = 0;
hopping_frequency_no = 0;
if(sub_protocol==Q200)
if(sub_protocol == SLT_V1)
{
packet_length = SLT_PAYLOADSIZE_V1;
#ifdef MULTI_SYNC
packet_period = 20000+2*SLT_V1_TIMING_PACKET; //22ms
#endif
}
else if(sub_protocol == SLT_V1_4)
{
packet_length = SLT_PAYLOADSIZE_V1_4;
#ifdef MULTI_SYNC
packet_period = 18000; //18ms
#endif
//Force high part of the ID otherwise the RF frequencies do not match, only tested the 2 last bytes...
rx_tx_addr[0]=0xF4;
rx_tx_addr[1]=0x71;
#ifdef SLT_V1_4_FORCE_ID // ID taken from TX dumps
memcpy(rx_tx_addr,"\xF4\x71\x8D\x01",SLT_TXID_SIZE);
#endif
}
else //V2
{
packet_length = SLT_PAYLOADSIZE_V2;
#ifdef MULTI_SYNC
packet_period = 13730+2*SLT_V2_TIMING_PACKET; //~18ms
#endif
}
if(sub_protocol == Q200)
{ //Q200: Force high part of the ID otherwise it won't bind
rx_tx_addr[0]=0x01;
rx_tx_addr[1]=0x02;
@@ -273,9 +322,13 @@ void SLT_init()
/* rx_tx_addr[0]=0x01;rx_tx_addr[1]=0x02;rx_tx_addr[2]=0x0B;rx_tx_addr[3]=0x57;*/
#endif
}
SLT_RF_init();
SLT_set_freq();
phase = SLT_BUILD;
}
#endif
//SLT v1_4ch timing
//268363 + 1643 / 15 = 18000

View File

@@ -19,12 +19,16 @@
#include "iface_cyrf6936.h"
//#define TRAXXAS_FORCE_ID
//#define TRAXXAS_DEBUG
#define TRAXXAS_TQ1_FORCE_ID
//#define TRAXXAS_TQ2_FORCE_ID
#define TRAXXAS_DEBUG
#define TRAXXAS_BIND_CHANNEL 0x2B
#define TRAXXAS_CHECK_CHANNEL 0x22
#define TRAXXAS_PACKET_SIZE 16
#define TRAXXAS_BIND_CHANNEL 0x2B
#define TRAXXAS_CHECK_CHANNEL 0x22
#define TRAXXAS_PACKET_SIZE 16
#define TRAXXAS_TQ1_BIND_CHANNEL 0x04
#define TRAXXAS_TQ1_CHECK_CHANNEL 0x34
enum {
TRAXXAS_BIND_PREP_RX=0,
@@ -34,19 +38,22 @@ enum {
TRAXXAS_RX,
TRAXXAS_PREP_DATA,
TRAXXAS_DATA,
TRAXXAS_TQ1_BIND,
TRAXXAS_TQ1_DATA1,
TRAXXAS_TQ1_DATA2,
};
const uint8_t PROGMEM TRAXXAS_init_vals[][2] = {
//Init from dump
{CYRF_0B_PWR_CTRL, 0x00}, // PMU
{CYRF_32_AUTO_CAL_TIME, 0x3C}, // Default init value
{CYRF_35_AUTOCAL_OFFSET, 0x14}, // Default init value
{CYRF_1B_TX_OFFSET_LSB, 0x55}, // Default init value
{CYRF_1C_TX_OFFSET_MSB, 0x05}, // Default init value
{CYRF_28_CLK_EN, 0x02}, // Force Receive Clock Enable
{CYRF_03_TX_CFG, 0x08 | CYRF_BIND_POWER}, // 8DR Mode, 32 chip codes
{CYRF_0B_PWR_CTRL, 0x00}, // PMU
{CYRF_06_RX_CFG, 0x88 | 0x02}, // AGC enabled, Fast Turn Mode enabled, adding overwrite enable to not lockup RX
{CYRF_1E_RX_OVERRIDE, 0x08}, // Reject packets with 0 seed
{CYRF_03_TX_CFG, 0x08 | CYRF_BIND_POWER}, // 8DR Mode, 32 chip codes
};
static void __attribute__((unused)) TRAXXAS_cyrf_bind_config()
@@ -87,25 +94,58 @@ static void __attribute__((unused)) TRAXXAS_send_data_packet()
memset(&packet[1],0x00,TRAXXAS_PACKET_SIZE-1);
//Next RF channel ? 0x00 -> keep current, 0x0E change to F=15
//packet[1] = hopping_frequency[0] - 1;
//Steering
uint16_t ch = convert_channel_16b_nolimit(RUDDER,500,1000,false);
packet[2]=ch>>8;
packet[3]=ch;
//Throttle
ch = convert_channel_16b_nolimit(THROTTLE,500,1000,false);
packet[4]=ch>>8;
packet[5]=ch;
//AUX3
ch = convert_channel_16b_nolimit(AILERON,500,1000,false);
packet[6]=ch>>8;
packet[7]=ch;
//AUX4???
ch = convert_channel_16b_nolimit(ELEVATOR,500,1000,false);
packet[12]=ch>>8;
packet[13]=ch;
//6 channels
uint16_t ch;
for(uint8_t i=0; i<6; i++)
{
ch = convert_channel_16b_nolimit(i,500,1000,false);
packet[2+i*2]=ch>>8;
packet[3+i*2]=ch;
}
CYRF_SetPower(0x08);
CYRF_WriteDataPacketLen(packet, TRAXXAS_PACKET_SIZE);
CYRF_WriteDataPacket(packet);
}
static void __attribute__((unused)) TRAXXAS_TQ1_send_data_packet()
{
memcpy(&packet[1], cyrfmfg_id, 4);
if(IS_BIND_IN_PROGRESS)
{
packet_length = 8;
packet[0] = 0x2A; // Bind packet
packet[5] = 0xA0; // Bind phase 0
packet[6] = TRAXXAS_TQ1_BIND_CHANNEL-1; // Not sure...
}
else
{
packet_length = 16;
packet[0] = 0x02; // Normal packet
packet[5] = 0xA2; // Bind phase 2 = completed?
//4 channels
uint16_t ch;
for(uint8_t i=0; i<4; i++)
{
ch = convert_channel_ppm(i);
packet[6+i*2]=ch;
packet[7+i*2]=ch>>8;
}
packet[14] = hopping_frequency[0]-1; // Not sure...
}
uint8_t xor_value=0;
for(uint8_t i=0; i<packet_length-1; i++)
xor_value ^= packet[i];
packet[packet_length-1] = xor_value;
CYRF_SetPower(0x08);
CYRF_WriteDataPacketLen(packet, packet_length);
#ifdef TRAXXAS_DEBUG
debug("P:");
for(uint8_t i=0; i<packet_length; i++)
debug(" %02X",packet[i]);
debugln("");
#endif
}
uint16_t TRAXXAS_callback()
@@ -114,6 +154,7 @@ uint16_t TRAXXAS_callback()
switch(phase)
{
//TQ2
case TRAXXAS_BIND_PREP_RX:
case TRAXXAS_PREP_RX:
//debugln("PREP_RX");
@@ -228,6 +269,33 @@ uint16_t TRAXXAS_callback()
TRAXXAS_send_data_packet();
phase = TRAXXAS_PREP_RX;
return 1000;
//TQ1
case TRAXXAS_TQ1_BIND:
if(bind_counter)
{
CYRF_ConfigRFChannel(TRAXXAS_TQ1_BIND_CHANNEL);
TRAXXAS_TQ1_send_data_packet();
bind_counter--;
if(bind_counter == 0)
{
BIND_DONE;
phase++;
}
}
return 10000;
case TRAXXAS_TQ1_DATA1:
#ifdef MULTI_SYNC
telemetry_set_input_sync(19900);
#endif
CYRF_ConfigRFChannel(TRAXXAS_TQ1_CHECK_CHANNEL);
TRAXXAS_TQ1_send_data_packet();
phase++;
return 7100;
case TRAXXAS_TQ1_DATA2:
CYRF_ConfigRFChannel(hopping_frequency[0]);
TRAXXAS_TQ1_send_data_packet();
phase = TRAXXAS_TQ1_DATA1;
return 12800;
}
return 10000;
}
@@ -235,35 +303,87 @@ uint16_t TRAXXAS_callback()
void TRAXXAS_init()
{
//Config CYRF registers
for(uint8_t i = 0; i < sizeof(TRAXXAS_init_vals) / 2; i++)
CYRF_WriteRegister(pgm_read_byte_near(&TRAXXAS_init_vals[i][0]), pgm_read_byte_near(&TRAXXAS_init_vals[i][1]));
uint8_t init;
if(sub_protocol == TRAXXAS_TQ1)
{
//CYRF_WriteRegister(CYRF_06_RX_CFG, 0x48 | 0x02);
//CYRF_WriteRegister(CYRF_26_XTAL_CFG, 0x08);
init = 5;
}
else //TQ2
{
init = sizeof(TRAXXAS_init_vals) / 2;
for(uint8_t i = 0; i < init; i++)
CYRF_WriteRegister(pgm_read_byte_near(&TRAXXAS_init_vals[i][0]), pgm_read_byte_near(&TRAXXAS_init_vals[i][1]));
}
//Read CYRF ID
CYRF_GetMfgData(cyrfmfg_id);
//cyrfmfg_id[0]+=RX_num; // Not needed since the TX and RX have to match
CYRF_FindBestChannels(hopping_frequency,1,1,0x02,0x21);
#ifdef TRAXXAS_FORCE_ID // data taken from TX dump
cyrfmfg_id[0]=0x65; // CYRF MFG ID
cyrfmfg_id[1]=0xE2;
cyrfmfg_id[2]=0x5E;
cyrfmfg_id[3]=0x55;
cyrfmfg_id[4]=0x4D;
cyrfmfg_id[5]=0xFE;
hopping_frequency[0] = 0x05; // seen 05 and 0F
//Find a free channel
if(sub_protocol == TRAXXAS_TQ1)
{
cyrfmfg_id[3]+=RX_num; // Not needed for TQ2 since the TX and RX have to match
//CYRF_FindBestChannels(hopping_frequency,1,1,0x0B,0x30, FIND_CHANNEL_ANY); // Complete guess
}
else //TRAXXAS_TQ2
CYRF_FindBestChannels(hopping_frequency,1,1,0x02,0x21, FIND_CHANNEL_ANY);
#ifdef TRAXXAS_TQ1_FORCE_ID // data taken from TX dump
if(sub_protocol == TRAXXAS_TQ1)
{
cyrfmfg_id[0]=0xD8; // CYRF MFG ID
cyrfmfg_id[1]=0xAA;
cyrfmfg_id[2]=0x59;
cyrfmfg_id[3]=0xE6;
//cyrfmfg_id[4]=0x44; // Unused
//cyrfmfg_id[5]=0xFB; // Unused
hopping_frequency[0] = 0x0B;
}
#endif
#ifdef TRAXXAS_TQ2_FORCE_ID // data taken from TX dump
if(sub_protocol == TRAXXAS_TQ2)
{
cyrfmfg_id[0]=0x65; // CYRF MFG ID
cyrfmfg_id[1]=0xE2;
cyrfmfg_id[2]=0x5E;
cyrfmfg_id[3]=0x55;
cyrfmfg_id[4]=0x4D;
cyrfmfg_id[5]=0xFE;
hopping_frequency[0] = 0x05; // seen 05 and 0F
}
#endif
#ifdef TRAXXAS_DEBUG
debugln("ID: %02X %02X %02X %02X %02X %02X",cyrfmfg_id[0],cyrfmfg_id[1],cyrfmfg_id[2],cyrfmfg_id[3],cyrfmfg_id[4],cyrfmfg_id[5]);
debugln("RF CH: %02X",hopping_frequency[0]);
#endif
if(IS_BIND_IN_PROGRESS)
bind_counter=100;
if(sub_protocol == TRAXXAS_TQ1)
{
bind_counter=100;
phase = TRAXXAS_BIND_PREP_RX;
CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02);
CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3C);
CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14);
CYRF_WriteRegister(CYRF_26_XTAL_CFG, 0x08);
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x48);
CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55);
CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x21);
CYRF_WriteRegister(CYRF_03_TX_CFG, 0x0C);
CYRF_PROGMEM_ConfigSOPCode(DEVO_j6pro_sopcodes[0]);
CYRF_SetTxRxMode(TX_EN);
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x21);
if(IS_BIND_IN_PROGRESS)
phase = TRAXXAS_TQ1_BIND;
else
phase = TRAXXAS_TQ1_DATA1;
}
else
phase = TRAXXAS_PREP_DATA;
{//TRAXXAS_TQ2
if(IS_BIND_IN_PROGRESS)
phase = TRAXXAS_BIND_PREP_RX;
else
phase = TRAXXAS_PREP_DATA;
}
//
// phase = TRAXXAS_BIND_TX1;
// TRAXXAS_cyrf_bind_config();
@@ -273,6 +393,8 @@ void TRAXXAS_init()
}
/*
Traxxas TQ 2nd generation
-------------------------
Packets 0x02: Bind learn TX/RX addresses
CHANNEL: 0x2B
SOP_CODE: 0x3C 0x37 0xCC 0x91 0xE2 0xF8 0xCC 0x91
@@ -282,7 +404,7 @@ RX: 0x02 0x4A 0xA3 0x2D 0x1A 0x49 0xFE 0x06 0x00 0x00 0x02 0x01 0x06 0x06 0x00 0
TX: 0x02 0x65 0xE2 0x5E 0x55 0x4D 0xFE 0xEE 0x00 0x00 0x01 0x01 0x06 0x05 0x00 0x00
Notes:
- RX cyrfmfg_id is 0x4A,0xA3,0x2D,0x1A,0x49,0xFE and TX cyrfmfg_id is 0x65,0xE2,0x5E,0x55,0x4D,0xFE
- P[7] changes from 0x06 to 0xEE but not needed to complete the bind -> doesn't care??
- P[7] = RX SOP code index, changes from 0x06 to 0xEE but not needed to complete the bind -> doesn't care??
- P[8] RF channel - 1 (on packets type 0x03)
- P[9] 0x00 unchanged??
- P[10] needs to be set to 0x01 to complete the bind -> normal packet P[0]??
@@ -317,8 +439,8 @@ RX ID: \x4B\xA3\x2D\x1A\x49\xFE CRC 0x1A 0x3F => CRC: 65-4B=1A E2-A3=3F
RX ID: \x00\x00\x2D\x1A\x49\xFE CRC 0x65 0xE2 => CRC: 65-00=65 E2-00=E2
RX ID: \x00\xFF\x2D\x1A\x49\xFE CRC 0x65 0xE3 => CRC: 65-00=65 E2-FF=E3
RX ID: \xFF\x00\x2D\x1A\x49\xFE CRC 0x66 0xE2 => CRC: 65-FF=66 E2-00=E2
*/
/*
SOP Codes:
RX1: 02 4A A3 2D 1A 49 FE 06 00 00 02 01 06 06 00 00
SOP: A1 78 DC 3C 9E 82 DC 3C
RX2: 02 49 AC 4F 55 4D FE 05 00 00 02 01 06 06 00 00
@@ -347,4 +469,128 @@ Dump of SOP Codes:
20: 00 00 00 33 DE AD BA BE ??over??
*/
/*
Traxxas TQ 1st generation
-------------------------
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/issues/967#issuecomment-2079038576
*/
/*
Traxxas TQi
-------------------------
Unknown 3 first packets:
CHANNEL: 0x2B
SOP_CODE: 0x3C 0x37 0xCC 0x91 0xE2 0xF8 0xCC 0x91
CRC_SEED_LSB: 0x5A
CRC_SEED_MSB: 0x5A
RX: 0xA0 0x1F 0x60 0x58 0x34 0x44 0x1C 0x11 0xA5 0x72 0x8D 0x9D 0xEA 0x49 0x2B 0xE2
RX: 0xD3 0x53 0xD4 0x45 0x1B 0x6F 0x75 0x42 0xEF 0x8A 0xD2 0x24 0x8E 0xF0 0x05 0x6C
RX: 0xAA 0xD7 0xD7 0x43 0x0F 0x2F 0x02 0x8F 0x06 0x54 0x3A 0x35 0xF5 0xCF 0x0F 0x82
Packets 0x02: Bind learn TX/RX addresses same as TQ 2nd:
CHANNEL: 0x2B
SOP_CODE: 0x3C 0x37 0xCC 0x91 0xE2 0xF8 0xCC 0x91
CRC_SEED_LSB: 0x5A
CRC_SEED_MSB: 0x5A
RX: 0x02 0x5B 0xE9 0x7E 0x14 0xF1 0xFE 0x0D 0x00 0x00 0x02 0x02 0x00 0x26 0x00 0x00
TX: 0x02 0xE0 0xAC 0x7F 0x14 0xF1 0xFE 0x00 0x00 0x00 0x01 0x02 0x00 0x51 0x00 0x00
Notes:
- P[1..6] = RX cyrfmfg_id is 0x5B,0xE9,0x7E,0x14,0xF1,0xFE and TX cyrfmfg_id is 0xE0,0xAC,0x7F,0x14,0xF1,0xFE
- P[7] RX SOP code index, changes from 0x0D to 0x00 (but not needed to complete the bind -> doesn't care??)
- P[8] RF channel - 1 (on packets type 0x03)
- P[9] 0x00 unchanged??
- P[10] needs to be set to 0x01 to complete the bind -> normal packet P[0]??
- P[11] unchanged ?? (-> no bind if set to 0x00 or 0x81)
- P[12] unchanged ?? (-> no bind if set to 0x05 or 0x86)
- P[13] changes from 0x26 to 0x51 (but not needed to complete the bind -> doesn't care??)
- P[14..15]=0x00 unchanged??
Next steps:
Packets 0x08: ???
TX: 0x08 0x00 0x03 0x14 0xFD 0x3E 0x28 0xB9 0xCA 0x48 0x76 0x4E 0x00 0x51 0x00 0x00
TX: 0x08 0x00 0x02 0x14 0xFD 0x3E 0x28 0xB9 0xCA 0x48 0x76 0x4E 0x00 0x51 0x00 0x00
TX: 0x08 0x00 0x01 0x14 0xFD 0x3E 0x28 0xB9 0xCA 0x48 0x76 0x4E 0x00 0x51 0x00 0x00
RX: 0x08 0x00 0x03 0x14 0xFD 0x24 0x20 0xB8 0xCA 0x48 0x76 0x4E 0x00 0x26 0x00 0x00
RX: 0x08 0x00 0x01 0x14 0xFD 0x24 0x20 0xB8 0xCA 0x48 0x76 0x4E 0x00 0x26 0x00 0x00
P[1] index
P[2] sequence number
Packets 0x09: ???
TX: 0x09 0x00 0x03 0xEE 0x12 0x49 0x57 0x1F 0x4B 0x60 0x6B 0x4D 0x14 0xEF 0x69 0xC4
TX: 0x09 0x00 0x02 0xEE 0x12 0x49 0x57 0x1F 0x4B 0x60 0x6B 0x4D 0x14 0xEF 0x69 0xC4
TX: 0x09 0x00 0x01 0xEE 0x12 0x49 0x57 0x1F 0x4B 0x60 0x6B 0x4D 0x14 0xEF 0x69 0xC4
TX: 0x09 0x01 0x03 0xB2 0x63 0x43 0x25 0x22 0x4D 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
TX: 0x09 0x01 0x02 0xB2 0x63 0x43 0x25 0x22 0x4D 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
TX: 0x09 0x01 0x01 0xB2 0x63 0x43 0x25 0x22 0x4D 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
TX: 0x09 0x02 0x03 0xCB 0xBE 0xA6 0xD1 0xCD 0x23 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
TX: 0x09 0x02 0x02 0xCB 0xBE 0xA6 0xD1 0xCD 0x23 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
TX: 0x09 0x02 0x01 0xCB 0xBE 0xA6 0xD1 0xCD 0x23 0xA1 0x15 0xFE 0xBA 0x7D 0x67 0x3E
RX: 0x09 0x00 0x03 0x90 0x24 0x93 0x08 0x41 0xC7 0x7F 0x59 0xB4 0xE3 0x25 0x77 0xB5
RX: 0x09 0x00 0x01 0x90 0x24 0x93 0x08 0x41 0xC7 0x7F 0x59 0xB4 0xE3 0x25 0x77 0xB5
RX: 0x09 0x01 0x02 0x88 0xFA 0x4D 0xFB 0x33 0xD4 0xFE 0x5F 0x1C 0xA9 0x0B 0x7A 0xE6
RX: 0x09 0x02 0x03 0x86 0xAF 0xD0 0x91 0xF6 0xDD 0xFE 0x5F 0x1C 0xA9 0x0B 0x7A 0xE6
RX: 0x09 0x02 0x01 0x86 0xAF 0xD0 0x91 0xF6 0xDD 0xFE 0x5F 0x1C 0xA9 0x0B 0x7A 0xE6
P[1] index
P[2] sequence number
Switch to normal mode:
SOP_CODE: 0xD2 0x8F 0xB1 0x2A 0xEF 0x64 0xB0 0x2A
CRC_SEED_LSB: 0x85
CRC_SEED_MSB: 0xC3
CHANNEL: 0x05
TX: 0x01 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x67 0x3E
Normal content except P[14]&P[15] = 0x67&0x3E from previous packet sent
SOP_CODE: 0x97 0xE5 0x14 0x72 0x7F 0x1A 0x14 0x72
CRC_SEED_LSB: 0xA5
CRC_SEED_MSB: 0xA5
CHANNEL: 0x22
RX: 0x04 0x5B 0xE9 0x7E 0x14 0xF1 0xFE 0x0D 0xDD 0xFE 0x02 0x02 0x00 0x26 0x7A 0xE6
RX packets 0x04: RX identity ?
P[1..6] = RX cyrfmfg_id 0x5B,0xE9,0x7E,0x14,0xF1,0xFE
P[7] = SOP index
P[8] = unknown
P[9] = unknown
P[10] = same value as bind 0x02
P[11] = same value as bind 0x02
P[12] = same value as bind 0x00
P[13] = same value as bind 0x26
P[14] = unknown
P[15] = unknown
SOP_CODE: 0xD2 0x8F 0xB1 0x2A 0xEF 0x64 0xB0 0x2A
CRC_SEED_LSB: 0x85
CRC_SEED_MSB: 0xC3
CHANNEL: 0x05
TX: 0x14 0x00 0x03 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x67 0x3E
TX: 0x14 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x67 0x3E
TX: 0x14 0x00 0x01 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x67 0x3E
RX: 0x14 0x00 0x03 0x22 0xEC 0x42 0x9B 0xD7 0x99 0x3B 0xBB 0x85 0x17 0x41 0x67 0xA0
RX: 0x14 0x00 0x01 0x22 0xEC 0x42 0x9B 0xD7 0x99 0x3B 0xBB 0x85 0x17 0x41 0x67 0xA0
RX: 0x14 0x01 0x02 0x22 0xEF 0x42 0x9C 0xCA 0xFB 0x39 0xBB 0x85 0x17 0x41 0x67 0xA0
TX packets 0x14 same as packets 0x01 with a sequence number on P[2]
RX packets 0x14 unknown content
Packets 0x01 & 0x07: normal and telemetry request/answer
TX: 0x01 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
TX: 0x07 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
RX: 0x07 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0xFB 0x00 0x00 0x85 0x17 0x41 0x67 0xA0
TX: 0x01 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
TX: 0x07 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
RX: 0x07 0x01 0x00 0x00 0x00 0x11 0x49 0xC8 0xFB 0x00 0x00 0x85 0x17 0x41 0x67 0xA0
TX: 0x01 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
TX: 0x07 0x00 0x02 0xF5 0x02 0x9E 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x99 0x00 0x00
RX: 0x07 0x01 0x00 0x00 0x00 0x12 0xDC 0x20 0xFB 0x00 0x00 0x85 0x17 0x41 0x67 0xA0
TX packets 0x07: request for telemetry
RX packets 0x07: unknown
CRC calc same as TQ 2nd:
TX ID: 0xE0,0xAC,0x7F,0x14,0xF1,0xFE
RX ID: 0x5B,0xE9,0x7E,0x14,0xF1,0xFE CRC 0x85 0xC3 => CRC: E0-5B=85 AC-E9=C3
SOP index same as TQ 2nd:
SOP index=0x0D, SOP code= 0xD2, 0x8F, 0xB1, 0x2A, 0xEF, 0x64, 0xB0, 0x2A
*/
#endif

View File

@@ -887,7 +887,7 @@ void TelemetryUpdate()
t += TXBUFFER_SIZE - h ;
else
t -= h ;
if ( t < 32 )
if ( t < 48 ) //32 )
{
debugln("TEL_BUF_FULL %d",t);
return ;

View File

@@ -0,0 +1,184 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
//Models: UDIRC UD160x(PRO), Pinecone Models SG-160x, Eachine EAT15
#if defined(UDIRC_CCNRF_INO)
#include "iface_xn297.h"
#define FORCE_UDIRC_ORIGINAL_ID
#define UDIRC_PAYLOAD_SIZE 15
#define UDIRC_RF_NUM_CHANNELS 4
#define UDIRC_PACKET_PERIOD 21000
#define UDIRC_BIND_COUNT 2000
#define UDIRC_P1_P2_TIME 5000
#define UDIRC_WRITE_TIME 1500
enum {
UDIRC_DATA1=0,
UDIRC_DATA2,
UDIRC_DATA3,
UDIRC_RX,
};
static void __attribute__((unused)) UDIRC_send_packet()
{
if(rf_ch_num==0)
{
XN297_Hopping(hopping_frequency_no);
debug("H %d ",hopping_frequency_no);
hopping_frequency_no++;
hopping_frequency_no &= 3;
}
memset(&packet[3], 0x00, 12);
if(bind_counter)
{//Bind in progress
bind_counter--;
if(bind_counter)
{//Bind
packet[0] = 0x01;
memcpy(&packet[1],rx_tx_addr,5);
}
else
{//Switch to normal
rf_ch_num = 1;
BIND_DONE;
XN297_SetTXAddr(rx_tx_addr, 5);
XN297_SetRXAddr(rx_tx_addr, UDIRC_PAYLOAD_SIZE);
}
}
if(!bind_counter)
{//Normal
packet[0] = 0x08;
//Channels SG-16xx: ST/TH/CH4 /CH3 /UNK/UNK/UNK/UNK/GYRO/ST_TRIM/ST_DR
//Channels EAT15 : ST/TH/RATE/LIGHT/UNK/UNK/UNK/UNK/GYRO/ST_TRIM/ST_DR
for(uint8_t i=0; i<12; i++)
packet[i+1] = convert_channel_16b_limit(i,0,200);
//Just for now let's set the additional channels to 0
packet[5] = packet[6] = packet[7] = packet[8] = 0;
}
packet[12] = GET_FLAG(CH12_SW, 0x40) //TH.REV
|GET_FLAG(CH13_SW, 0x80); //ST.REV
//packet[13] = 00; //Unknown, future flags?
for(uint8_t i=0;i<UDIRC_PAYLOAD_SIZE-1;i++)
packet[14] += packet[i];
// Send
XN297_SetFreqOffset();
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WriteEnhancedPayload(packet, UDIRC_PAYLOAD_SIZE,false);
#ifdef DEBUG_SERIAL
for(uint8_t i=0; i < UDIRC_PAYLOAD_SIZE; i++)
debug("%02X ", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) UDIRC_initialize_txid()
{
#ifdef FORCE_UDIRC_ORIGINAL_ID
if(RX_num)
{
rx_tx_addr[0] = 0xD0;
rx_tx_addr[1] = 0x06;
rx_tx_addr[2] = 0x00;
rx_tx_addr[3] = 0x00;
rx_tx_addr[4] = 0x81;
}
else
{
rx_tx_addr[0] = 0xF6;
rx_tx_addr[1] = 0x96;
rx_tx_addr[2] = 0x01;
rx_tx_addr[3] = 0x00;
rx_tx_addr[4] = 0x81;
}
hopping_frequency[0] = 45;
hopping_frequency[1] = 59;
hopping_frequency[2] = 52;
hopping_frequency[3] = 67;
#endif
}
static void __attribute__((unused)) UDIRC_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K);
//Bind address
XN297_SetTXAddr((uint8_t*)"\x01\x03\x05\x07\x09", 5);
XN297_SetRXAddr((uint8_t*)"\x01\x03\x05\x07\x09", UDIRC_PAYLOAD_SIZE);
XN297_HoppingCalib(UDIRC_RF_NUM_CHANNELS);
}
uint16_t UDIRC_callback()
{
bool rx;
switch(phase)
{
case UDIRC_DATA1:
rx = XN297_IsRX();
XN297_SetTxRxMode(TXRX_OFF);
#ifdef MULTI_SYNC
telemetry_set_input_sync(UDIRC_PACKET_PERIOD);
#endif
UDIRC_send_packet();
if(rx)
{
uint8_t val=XN297_ReadEnhancedPayload(packet_in, UDIRC_PAYLOAD_SIZE);
debug("RX(%d):",val);
if(val != 255)
{
rf_ch_num = 1;
if(bind_counter)
bind_counter=1;
#ifdef DEBUG_SERIAL
for(uint8_t i=0; i < UDIRC_PAYLOAD_SIZE; i++)
debug(" %02X", packet_in[i]);
#endif
}
debugln("");
}
phase++;
return UDIRC_P1_P2_TIME;
case UDIRC_DATA2:
//Resend packet
XN297_ReSendPayload();
phase++;
return UDIRC_WRITE_TIME;
default: //UDIRC_RX
//Wait for the packet transmission to finish
while(XN297_IsPacketSent()==false);
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = UDIRC_DATA1;
return UDIRC_PACKET_PERIOD - UDIRC_P1_P2_TIME - UDIRC_WRITE_TIME;
}
return 0;
}
void UDIRC_init()
{
UDIRC_initialize_txid();
UDIRC_RF_init();
bind_counter = IS_BIND_IN_PROGRESS ? UDIRC_BIND_COUNT : 1;
phase = UDIRC_DATA1;
hopping_frequency_no = 0;
rf_ch_num = 0;
}
#endif

View File

@@ -118,9 +118,10 @@ static void __attribute__((unused)) V761_send_packet()
packet[5] |= flags;
packet[6] = GET_FLAG(CH7_SW, 0x20) // Flip
|GET_FLAG(CH8_SW, 0x08) // RTH activation
|GET_FLAG(CH9_SW, 0x10); // RTH on/off
packet[6] = GET_FLAG(CH7_SW, 0x20) // Flip
|GET_FLAG(CH8_SW, 0x08) // RTH activation
|GET_FLAG(CH9_SW, 0x10) // RTH on/off
|GET_FLAG(CH10_SW, 0x40); // Beeper on/off
if(sub_protocol == V761_3CH)
packet[6] |= 0x80; // Unknown, set on original V761-1 dump but not on eachine dumps, keeping for compatibility
}
@@ -243,7 +244,7 @@ uint16_t V761_callback()
else
{
packet_count++;
if(!telemetry_lost && !rx && (packet_count%64) == 0)
if(!telemetry_lost && !rx && (packet_count & 0x3F) == 0)
{// Should have received a telem packet but... Send telem to the radio to keep it alive
telemetry_link = 1;
#ifdef V761_TELEM_DEBUG

View File

@@ -266,6 +266,7 @@
#undef E01X_CYRF6936_INO
#undef E129_CYRF6936_INO
#undef J6PRO_CYRF6936_INO
#undef KYOSHO3_CYRF6936_INO
#undef LOSI_CYRF6936_INO
#undef MLINK_CYRF6936_INO
#undef TRAXXAS_CYRF6936_INO
@@ -320,9 +321,12 @@
#undef FY326_NRF24L01_INO
#undef GW008_NRF24L01_INO
#undef H8_3D_NRF24L01_INO
#undef H36_NRF24L01_INO
#undef HISKY_NRF24L01_INO
#undef HONTAI_NRF24L01_INO
#undef JIABAILE_NRF24L01_INO
#undef JJRC345_NRF24L01_INO
#undef KAMTOM_NRF24L01_INO
#undef KN_NRF24L01_INO
#undef KYOSHO2_NRF24L01_INO
#undef LOLI_NRF24L01_INO
@@ -333,11 +337,14 @@
#undef REALACC_NRF24L01_INO
#undef SGF22_NRF24L01_INO
#undef SHENQI_NRF24L01_INO
#undef SHENQI2_NRF24L01_INO
#undef SYMAX_NRF24L01_INO
#undef V2X2_NRF24L01_INO
#undef V761_NRF24L01_INO
#undef WPL_NRF24L01_INO
#undef XERALL_NRF24L01_INO
#undef YD717_NRF24L01_INO
#undef YUXIANG_NRF24L01_INO
#undef ZSX_NRF24L01_INO
#endif
#if ( not defined(CC2500_INSTALLED) && not defined(NRF24L01_INSTALLED) ) || defined MULTI_EU
@@ -351,8 +358,16 @@
#undef Q303_CCNRF_INO
#undef Q90C_CCNRF_INO
#undef SLT_CCNRF_INO
#undef UDIRC_CCNRF_INO
#undef V911S_CCNRF_INO
#undef WL91X_CCNRF_INO
#undef XK_CCNRF_INO
#undef XK2_CCNRF_INO
#endif
#if defined(MCU_STM32F103C8) // Save flash space...
#undef BUMBLEB_CCNRF_INO
#undef Q303_CCNRF_INO
#undef Q90C_CCNRF_INO
#endif
#if not defined(DSM_CYRF6936_INO)
#undef LOSI_CYRF6936_INO
@@ -376,9 +391,19 @@
#undef LOSI_CYRF6936_INO //Need DSM to be enabled
#undef TRAXXAS_CYRF6936_INO
#undef EAZYRC_NRF24L01_INO
#undef KYOSHO2_NRF24L01_INO
//#undef KYOSHO2_NRF24L01_INO
#undef KYOSHO3_CYRF6936_INO
#undef MOULDKG_NRF24L01_INO
#undef SHENQI_NRF24L01_INO
#undef SHENQI2_NRF24L01_INO
#undef JIABAILE_NRF24L01_INO
#undef UDIRC_CCNRF_INO
#undef KAMTOM_NRF24L01_INO
#undef WL91X_CCNRF_INO
#undef WPL_NRF24L01_INO
//Save flash space...
#undef CABELL_NRF24L01_INO
#undef REDPINE_CC2500_INO
#endif
#ifdef MULTI_SURFACE
@@ -412,6 +437,7 @@
#undef GW008_NRF24L01_INO
#undef HONTAI_NRF24L01_INO
#undef H8_3D_NRF24L01_INO
#undef H36_NRF24L01_INO
#undef JJRC345_NRF24L01_INO
#undef KN_NRF24L01_INO
#undef LOLI_NRF24L01_INO
@@ -432,8 +458,9 @@
#undef OMP_CCNRF_INO
#undef Q303_CCNRF_INO
#undef Q90C_CCNRF_INO
#undef SLT_CCNRF_INO
#undef V911S_CCNRF_INO
#undef SGF22_NRF24L01_INO
#undef YUXIANG_NRF24L01_INO
#endif
//OpenTX 2.3.x issue
@@ -472,6 +499,11 @@
#undef PROPEL_HUB_TELEMETRY
#undef OMP_HUB_TELEMETRY
#undef V761_HUB_TELEMETRY
#undef FX_HUB_TELEMETRY
#undef XK2_HUB_TELEMETRY
#undef SGF22_HUB_TELEMETRY
#undef KAMTOM_HUB_TELEMETRY
#undef YUXIANG_HUB_TELEMETRY
#undef RLINK_HUB_TELEMETRY
#undef DSM_RX_CYRF6936_INO
#undef DSM_FWD_PGM
@@ -510,6 +542,21 @@
#if not defined(V761_NRF24L01_INO)
#undef V761_HUB_TELEMETRY
#endif
#if not defined(FX_NRF24L01_INO)
#undef FX_HUB_TELEMETRY
#endif
#if not defined(XK2_CCNRF_INO)
#undef XK2_HUB_TELEMETRY
#endif
#if not defined(SGF22_NRF24L01_INO)
#undef SGF22_HUB_TELEMETRY
#endif
#if not defined(KAMTOM_NRF24L01_INO)
#undef KAMTOM_HUB_TELEMETRY
#endif
#if not defined(YUXIANG_NRF24L01_INO)
#undef YUXIANG_HUB_TELEMETRY
#endif
#if not defined(PROPEL_NRF24L01_INO)
#undef PROPEL_HUB_TELEMETRY
#endif
@@ -567,7 +614,7 @@
//protocols using FRSKYD user frames
#undef HUB_TELEMETRY
#endif
#if not defined(HOTT_FW_TELEMETRY) && not defined(DSM_TELEMETRY) && not defined(SPORT_TELEMETRY) && not defined(HUB_TELEMETRY) && not defined(HUBSAN_HUB_TELEMETRY) && not defined(BUGS_HUB_TELEMETRY) && not defined(NCC1701_HUB_TELEMETRY) && not defined(BAYANG_HUB_TELEMETRY) && not defined(CABELL_HUB_TELEMETRY) && not defined(RLINK_HUB_TELEMETRY) && not defined(AFHDS2A_HUB_TELEMETRY) && not defined(AFHDS2A_FW_TELEMETRY) && not defined(MULTI_TELEMETRY) && not defined(MULTI_STATUS) && not defined(HITEC_HUB_TELEMETRY) && not defined(HITEC_FW_TELEMETRY) && not defined(SCANNER_TELEMETRY) && not defined(FRSKY_RX_TELEMETRY) && not defined(AFHDS2A_RX_TELEMETRY) && not defined(BAYANG_RX_TELEMETRY) && not defined(DEVO_HUB_TELEMETRY) && not defined(PROPEL_HUB_TELEMETRY) && not defined(OMP_HUB_TELEMETRY) && not defined(V761_HUB_TELEMETRY) && not defined(WFLY2_HUB_TELEMETRY) && not defined(LOLI_HUB_TELEMETRY) && not defined(MLINK_HUB_TELEMETRY) && not defined(MLINK_FW_TELEMETRY) && not defined(MT99XX_HUB_TELEMETRY) && not defined(MULTI_CONFIG_INO)
#if not defined(HOTT_FW_TELEMETRY) && not defined(DSM_TELEMETRY) && not defined(SPORT_TELEMETRY) && not defined(HUB_TELEMETRY) && not defined(HUBSAN_HUB_TELEMETRY) && not defined(BUGS_HUB_TELEMETRY) && not defined(NCC1701_HUB_TELEMETRY) && not defined(BAYANG_HUB_TELEMETRY) && not defined(CABELL_HUB_TELEMETRY) && not defined(RLINK_HUB_TELEMETRY) && not defined(AFHDS2A_HUB_TELEMETRY) && not defined(AFHDS2A_FW_TELEMETRY) && not defined(MULTI_TELEMETRY) && not defined(MULTI_STATUS) && not defined(HITEC_HUB_TELEMETRY) && not defined(HITEC_FW_TELEMETRY) && not defined(SCANNER_TELEMETRY) && not defined(FRSKY_RX_TELEMETRY) && not defined(AFHDS2A_RX_TELEMETRY) && not defined(BAYANG_RX_TELEMETRY) && not defined(DEVO_HUB_TELEMETRY) && not defined(PROPEL_HUB_TELEMETRY) && not defined(OMP_HUB_TELEMETRY) && not defined(V761_HUB_TELEMETRY) && not defined(SGF22_HUB_TELEMETRY) && not defined(XK2_HUB_TELEMETRY) && not defined(FX_HUB_TELEMETRY) && not defined(KAMTOM_HUB_TELEMETRY) && not defined(YUXIANG_HUB_TELEMETRY) && not defined(WFLY2_HUB_TELEMETRY) && not defined(LOLI_HUB_TELEMETRY) && not defined(MLINK_HUB_TELEMETRY) && not defined(MLINK_FW_TELEMETRY) && not defined(MT99XX_HUB_TELEMETRY) && not defined(MULTI_CONFIG_INO)
#undef TELEMETRY
#undef INVERT_TELEMETRY
#undef MULTI_TELEMETRY

View File

@@ -487,7 +487,7 @@ void WK_init()
CYRF_SetTxRxMode(TX_EN);
hopping_frequency_no=0;
CYRF_FindBestChannels(hopping_frequency, 3, 4, 4, 80);
CYRF_FindBestChannels(hopping_frequency, 3, 4, 4, 80, FIND_CHANNEL_ANY);
CYRF_ConfigRFChannel(hopping_frequency[0]);
packet_count = 0;

View File

@@ -0,0 +1,96 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(WL91X_CCNRF_INO)
#include "iface_xn297.h"
//#define FORCE_WL91X_ORIGINAL_ID
#define WL91X_PAYLOAD_SIZE 9
#define WL91X_RF_NUM_CHANNELS 3
#define WL91X_PACKET_PERIOD 2594
static void __attribute__((unused)) WL91X_send_packet()
{
uint8_t val;
//RF freq
XN297_Hopping(hopping_frequency_no++);
hopping_frequency_no %= WL91X_RF_NUM_CHANNELS;
//Sticks
val = convert_channel_16b_limit(CH2,0x21,0xE0); //THR forward 00..5F, backward 80..DF
if(val < 128) val = 127 - val;
packet[0] = val - 0x80;
val = convert_channel_s8b(CH1); //ST right 00..7F, left 80..FF
packet[1] = val - 0x80;
//Trims
val = convert_channel_s8b(CH3); //ST_Trim centered=80, increment/decrement=4, right 04..7C, left 84..FC
packet[2] = val - 0x80;
packet[3] = convert_channel_16b_limit(CH4,0x00,0x70); //TH_Trim increment/decrement=3, 00..39..6F
//TX_ID
memcpy(&packet[4], rx_tx_addr, 4);
//Checksum
val = 0;
for(uint8_t i=0; i<WL91X_PAYLOAD_SIZE-1; i++)
val += packet[i];
packet[8] = val;
//Send
XN297_SetPower();
XN297_SetFreqOffset();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, WL91X_PAYLOAD_SIZE);
#ifdef DEBUG_SERIAL
for(uint8_t i=0; i < WL91X_PAYLOAD_SIZE; i++)
debug("%02X ", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) WL91X_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_UNSCRAMBLED, XN297_250K);
XN297_HoppingCalib(WL91X_RF_NUM_CHANNELS);
XN297_SetTXAddr((uint8_t*)"\x46\x14\x7B\x08", 4);
}
static void __attribute__((unused)) WL91X_initialize_txid()
{
#ifdef FORCE_WL91X_ORIGINAL_ID
memcpy(rx_tx_addr, (uint8_t*)"\x00\x1E\x33\x02",4);
#endif
memcpy(hopping_frequency, (uint8_t*)"\x1A\x3B\x3B",3); //26,59,59
}
uint16_t WL91X_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(WL91X_PACKET_PERIOD);
#endif
WL91X_send_packet();
return WL91X_PACKET_PERIOD;
}
void WL91X_init()
{
BIND_DONE; //No bind for this protocol
WL91X_initialize_txid();
WL91X_RF_init();
hopping_frequency_no = 0;
}
#endif

View File

@@ -0,0 +1,158 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// Compatible with WPL "Basic" TX models D12, D12KM, D22, D32, D42, D14
#if defined(WPL_NRF24L01_INO)
#include "iface_xn297.h"
#define FORCE_WPL_ORIGINAL_ID
#define WPL_PACKET_PERIOD 9875
#define WPL_RF_NUM_CHANNELS 4
#define WPL_PAYLOAD_SIZE 16
#define WPL_BIND_COUNT 303 //3sec
static void __attribute__((unused)) WPL_send_packet()
{
#if 0
debug("no:%d, rf:%d, ",hopping_frequency_no + (IS_BIND_IN_PROGRESS?0:4),hopping_frequency[hopping_frequency_no + (IS_BIND_IN_PROGRESS?0:4)]);
#endif
XN297_Hopping(hopping_frequency_no + (IS_BIND_IN_PROGRESS?0:4) );
hopping_frequency_no++;
hopping_frequency_no &= WPL_RF_NUM_CHANNELS-1; // 4 RF channels
memset(&packet[8],0,7);
packet[0] = 0x94; //??
packet[1] = 0x16; //??
packet[2] = 0xCC; //??
if(IS_BIND_IN_PROGRESS)
{
memcpy(&packet[3],rx_tx_addr,5);
packet[9] = 0x08; // ?? Not bound + Headlights on
}
else
{
packet[3 ] = convert_channel_s8b(CH1); // Throttle
packet[4 ] = convert_channel_s8b(CH2); // Steering
packet[5 ] = convert_channel_16b_limit(CH3,0x22,0x5E); // Steering trim
packet[6 ] = rx_tx_addr[3]; // 0x32??
packet[7 ] = convert_channel_s8b(CH4); // Aux
packet[9 ] = 0x80 // ?? Bound
| GET_FLAG(CH5_SW, 0x08) // Headlights 100%=on
| GET_FLAG(CH6_SW, 0x04) // Throttle rate 100%=high
| GET_FLAG(CH7_SW, 0x02); // Steering rate 100%=high
}
uint8_t sum = 0x66;
for(uint8_t i=0;i<WPL_PAYLOAD_SIZE-1;i++)
sum += packet[i];
packet[WPL_PAYLOAD_SIZE-1] = sum;
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, WPL_PAYLOAD_SIZE);
#if 0
for(uint8_t i=0; i<WPL_PAYLOAD_SIZE; i++)
debug(" %02X",packet[i]);
debugln("");
#endif
}
static void __attribute__((unused)) WPL_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t*)"\x69\xA5\x37\x4D\x8B", 5);
XN297_HoppingCalib(WPL_RF_NUM_CHANNELS*2); // Calibrate bind and normal channels
}
static void __attribute__((unused)) WPL_initialize_txid()
{
//Bind frequencies
memcpy(hopping_frequency ,"\x17\x25\x46\x36", WPL_RF_NUM_CHANNELS); //23=17, 37=25, 70=46, 54=36
#ifdef FORCE_WPL_ORIGINAL_ID
//Original ID
memcpy(rx_tx_addr,"\x96\x2A\xA9\x32\xB4",5);
//Normal frequencies
memcpy(hopping_frequency+4,"\x0C\x2A\x3D\x1D", WPL_RF_NUM_CHANNELS); //12=0C, 42=2A, 61=3D, 29=1D
#endif
}
uint16_t WPL_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(WPL_PACKET_PERIOD);
#endif
if(bind_counter)
if(--bind_counter==0)
{
BIND_DONE;
XN297_SetTXAddr(rx_tx_addr, 5);
}
WPL_send_packet();
return WPL_PACKET_PERIOD;
}
void WPL_init()
{
BIND_IN_PROGRESS; // autobind protocol
WPL_initialize_txid();
WPL_RF_init();
hopping_frequency_no = 0;
bind_counter=WPL_BIND_COUNT;
}
#endif
/* https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/issues/1120
Bind packet
-----------
XN297 1Mb Scrambled
Bind address: 69 A5 37 4D 8B
RF channels: 23, 37, 70, 54
Timing: 9875µs
Payload 16 bytes: 94 16 CC 96 2A A9 32 B4 00 08 00 00 00 00 00 33
P[0] = 94 ??
P[1] = 16 ??
P[2] = CC ??
P[3..7] = Normal address
P[8] = 00 ??
P[9] = 08 ?? not bound?, Throttle and Steering rate low, Headlights on
P[10..14] = 00 ??
P[15] = sum(P[0..14])+66 why 66? 66=(94+16)^CC...
Normal packet
-----------
XN297 1Mb Scrambled
Normal address: 96 2A A9 32 B4
RF channels: 12=0C, 42=2A, 61=3D, 29=1D -> no idea where they come from...
Timing: 9875µs
Payload 16 bytes: 94 16 CC 80 80 38 32 80 00 88 00 00 00 00 00 4E
P[0] = 94 ??
P[1] = 16 ??
P[2] = CC ??
P[3] = Throttle, not enough data on dumps... Same coding as Steering?
P[4] = Steering, not enough data on dumps, looks like one side goes from 7F to 00 and the other 80 to FF which would be s8b
P[5] = Steering trim 22..5E, mid gives 40 not 38... Was the trim centered on the other dumps with value 38?
P[6] = 32 ?? Left over from the bind packet TX_ADDR[3]?
P[7] = 80 ?? Additional channel? It moves at the same time as the trim but my guess is that it is an unconnected channel.
P[8] = 00 ??
P[9] = 80 ?? bound?, Throttle and Steering rate low, Headlights off
|02 -> Steering rate high
|04 -> Throttle rate high
|08 -> Headlights on
P[10..14] = 00 ??
P[15] = sum(P[0..14])+66 why 66? 66=(94+16)^CC...
*/

432
Multiprotocol/XK2_ccnrf.ino Normal file
View File

@@ -0,0 +1,432 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// Compatible with XK TX X4 and model A160S.
#if defined(XK2_CCNRF_INO)
#include "iface_xn297.h"
//#define FORCE_XK2_ID
//#define FORCE_XK2_P10_ID
#define XK2_RF_BIND_CHANNEL 71
#define XK2_P10_RF_BIND_CHANNEL 69
#define XK2_PAYLOAD_SIZE 9
#define XK2_PACKET_PERIOD 4911
#define XK2_RF_NUM_CHANNELS 4
#define XK2_WRITE_TIME 1000
enum {
XK2_BIND1,
XK2_BIND2,
XK2_DATA_PREP,
XK2_DATA,
XK2_RX,
};
static uint8_t __attribute__((unused)) XK2_checksum(uint8_t init)
{
for(uint8_t i=0; i<XK2_PAYLOAD_SIZE-1; i++)
init += packet[i];
if(sub_protocol == XK2_P10)
init += 0x10;
return init;
}
static void __attribute__((unused)) XK2_send_packet()
{
static uint8_t trim_ch=0;
if(IS_BIND_IN_PROGRESS)
{
packet[0] = 0x9D;
//TXID
memcpy(&packet[1], rx_tx_addr, 3);
//RXID
//memcpy(&packet[4], rx_id , 3);
//Unknown
packet[7] = 0x00;
}
else
{
XN297_Hopping(hopping_frequency_no);
hopping_frequency_no++;
hopping_frequency_no &= 0x03;
//Channels
packet[0] = convert_channel_16b_limit(AILERON ,0x00,0x64); //Aileron
packet[1] = convert_channel_16b_limit(ELEVATOR,0x00,0x64); //Elevator
packet[2] = convert_channel_16b_limit(THROTTLE,0x00,0x64); //Throttle
packet[3] = convert_channel_16b_limit(RUDDER ,0x00,0x64); //Rudder
//Center the trims
trim_ch++;
if(trim_ch > 2) trim_ch = 0;
packet[4] = 0x20 + 0x40 * trim_ch; //Trims are A=01..20..3F/E=41..60..7F/R=81..A0..BF, E0 appears when telemetry is received, C1 when p[6] changes from 00->08, C0 when p[6] changes from 08->00
if(trim_ch == 2) //Drive rudder trim since otherwise there is no control...
{
packet[4] = 0x80 + (convert_channel_8b(RUDDER)>>2);
if(packet[4] <= 0x81) packet[4] = 0x81;
}
//Flags
packet[5] = GET_FLAG(CH5_SW, 0x01) //Rate
| GET_FLAG(CH7_SW, 0x20) //Hover
| GET_FLAG(CH8_SW, 0x40); //Light
if(CH6_SW)
packet[5] |= 0x10; //Gyro off (senior mode)
else if(Channel_data[CH6] > CHANNEL_MIN_COMMAND)
packet[5] |= 0x08; //3D
packet[5] |= GET_FLAG(CH9_SW, 0x04); //Sky Viper Vector stunt flag
//Request telemetry flag
packet[6] = 0x01;
//RXID checksum
packet[7] = crc8; //Sum RX_ID[0..2]
}
//Checksum
packet[8] = XK2_checksum(IS_BIND_IN_PROGRESS ? 0xC0 : num_ch);
// Send
XN297_SetFreqOffset();
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, XK2_PAYLOAD_SIZE);
#if 0
debug("P");
for(uint8_t i=0; i<XK2_PAYLOAD_SIZE; i++)
debug(" %02X",packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) XK2_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K);
XN297_SetTXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
XN297_SetRXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", XK2_PAYLOAD_SIZE);
XN297_HoppingCalib(XK2_RF_NUM_CHANNELS);
XN297_RFChannel(sub_protocol==XK2_X4?XK2_RF_BIND_CHANNEL:XK2_P10_RF_BIND_CHANNEL);
}
static void __attribute__((unused)) XK2_initialize_txid()
{
rx_tx_addr[0] = rx_tx_addr[3]; // Use RX_num
num_ch = 0x21 + rx_tx_addr[0] - rx_tx_addr[1] + rx_tx_addr[2];
//RF frequencies for X4: 65=0x41, 69=0x45, 73=0x49, 77=0x4D
//RF frequencies for P10: 67, unknown
uint8_t start = 65;
if(sub_protocol == XK2_P10) start += 2;
for(uint8_t i=0;i<XK2_RF_NUM_CHANNELS;i++)
hopping_frequency[i] = start + i*4;
#ifdef FORCE_XK2_ID
if(rx_tx_addr[3]&1)
{//Pascal
rx_tx_addr[0] = 0x66;
rx_tx_addr[1] = 0x4F;
rx_tx_addr[2] = 0x47;
num_ch = 0x7F;
//hopping frequencies 65=0x41, 69=0x45, 73=0x49, 77=0x4D
}
else
{//Marc
rx_tx_addr[0] = 0x36;
rx_tx_addr[1] = 0x49;
rx_tx_addr[2] = 0x6B;
num_ch = 0x79;
//hopping frequencies 65=0x41, 69=0x45, 73=0x49, 77=0x4D
}
#endif
#ifdef FORCE_XK2_P10_ID
rx_tx_addr[0] = 0xE8;
rx_tx_addr[1] = 0x25;
rx_tx_addr[2] = 0x3B;
num_ch = 0x1F;
//hopping frequencies 67=0x43, =0x, =0x, =0x
#endif
rx_tx_addr[3] = rx_tx_addr[4] = 0xCC;
debugln("ID: %02X %02X %02X %02X %02X, OFFSET: %02X, HOP: %02X %02X %02X %02X",rx_tx_addr[0],rx_tx_addr[1],rx_tx_addr[2],rx_tx_addr[3],rx_tx_addr[4],num_ch,hopping_frequency[0],hopping_frequency[1],hopping_frequency[2],hopping_frequency[3]);
}
uint16_t XK2_callback()
{
static bool rx = false;
switch(phase)
{
case XK2_BIND1:
// switch to RX mode
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase++;
return 5000;
case XK2_BIND2:
if(XN297_IsRX())
{
XN297_ReadPayload(packet, XK2_PAYLOAD_SIZE);
#if 0
debug("RX");
for(uint8_t i=0; i<XK2_PAYLOAD_SIZE; i++)
debug(" %02X",packet[i]);
debugln("");
#endif
if(XK2_checksum(0xBF) != packet[8])
{//Wrong checksum
phase = XK2_BIND1;
return 1000;
}
if(packet[0] == 0x9B)
phase++;
else
{
//checksum of RX_ID
crc8 = packet[4] + packet[5] + packet[6];
debugln("W:RX_ID=%02X",crc8);
eeprom_write_byte((EE_ADDR)(XK2_EEPROM_OFFSET+RX_num),crc8);
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(TX_EN);
bind_counter = 10; //send 10 bind end packets
phase = XK2_DATA;
}
}
return 1000;
case XK2_DATA_PREP:
crc8 = eeprom_read_byte((EE_ADDR)(XK2_EEPROM_OFFSET+RX_num));
debugln("R:RX_ID=%02X",crc8);
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(TX_EN);
XN297_SetTXAddr(rx_tx_addr, 5);
#ifdef XK2_HUB_TELEMETRY
XN297_SetRXAddr(rx_tx_addr, XK2_PAYLOAD_SIZE);
#endif
BIND_DONE;
phase++;
case XK2_DATA:
#ifdef MULTI_SYNC
telemetry_set_input_sync(XK2_PACKET_PERIOD);
#endif
#ifdef XK2_HUB_TELEMETRY
rx = XN297_IsRX();
XN297_SetTxRxMode(TXRX_OFF);
#endif
XK2_send_packet();
#ifdef XK2_HUB_TELEMETRY
if(rx)
{
XN297_ReadPayload(packet, XK2_PAYLOAD_SIZE);
#if 0
debug("RX");
for(uint8_t i=0; i<XK2_PAYLOAD_SIZE; i++)
debug(" %02X",packet[i]);
debugln("");
#endif
if(XK2_checksum(0xCC) == packet[8] && memcmp(packet, rx_tx_addr, 3) == 0)
{//Good checksum and TXID
//packets: E5 20 F2 00 00 00 00 00 C3 -> E5 20 F2 80 00 00 00 00 43
telemetry_link = 1;
v_lipo1 = packet[3] ? 137:162; // low voltage 7.1V
}
}
#endif
if(bind_counter)
{
bind_counter--;
if(bind_counter == 0)
phase = XK2_DATA_PREP;
break;
}
#ifndef XK2_HUB_TELEMETRY
break;
#else
phase++;
return XK2_WRITE_TIME;
default: //XK2_RX
/*{ // Wait for packet to be sent before switching to receive mode
uint16_t start=(uint16_t)micros(), count=0;
while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 500)
{
if(XN297_IsPacketSent())
break;
count++;
}
debugln("%d",count);
}*/
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = XK2_DATA;
return XK2_PACKET_PERIOD-XK2_WRITE_TIME;
#endif
}
return XK2_PACKET_PERIOD;
}
void XK2_init()
{
//BIND_IN_PROGRESS; // autobind protocol
XK2_initialize_txid();
XK2_RF_init();
if(IS_BIND_IN_PROGRESS)
phase = XK2_BIND1;
else
phase = XK2_DATA_PREP;
bind_counter = 0;
hopping_frequency_no = 0;
#ifdef XK2_HUB_TELEMETRY
RX_RSSI = 100; // Dummy value
#endif
}
#endif
/*
XK A160 Piper CUB
Bind
----
Plane sends these packets:
RX: 0us C=71 S=Y A= CC CC CC CC CC P(9)= 9C BB CC DD 38 12 10 00 19
P[0] = 9C bind phase 1
P[1] = Dummy TX_ID
P[2] = Dummy TX_ID
P[3] = Dummy TX_ID
P[4] = RX_ID[0]
P[5] = RX_ID[1]
P[6] = RX_ID[2]
P[7] = 00
P[8] = sum P[0..7] + BF
TX responds to plane:
RX 9D 66 4F 47 38 12 10 00 B3
P[0] = 9D bind phase 2
P[1] = TX_ID[0]
P[2] = TX_ID[1]
P[3] = TX_ID[2]
P[4] = RX_ID[0]
P[5] = RX_ID[1]
P[6] = RX_ID[2]
P[7] = 00
P[8] = sum P[0..7] + C0
Planes ack:
RX: 4299us C=71 S=Y A= CC CC CC CC CC P(9)= 9B 66 4F 47 38 12 10 00 B0
RX: 26222us C=71 S=Y A= CC CC CC CC CC P(9)= 9B 66 4F 47 38 12 10 00 B0
RX: 8743us C=71 S=Y A= CC CC CC CC CC P(9)= 9B 66 4F 47 38 12 10 00 B0
P[0] = 9B bind phase 3
P[1] = TX_ID[0]
P[2] = TX_ID[1]
P[3] = TX_ID[2]
P[4] = RX_ID[0]
P[5] = RX_ID[1]
P[6] = RX_ID[2]
P[7] = 00
P[8] = sum P[0..7] + BF
Normal
------
TX sends
C=65,69,73,77 -> only one channel when telemetry is working
250K C=69 S=Y A= 66 4F 47 CC CC P(9)= 32 32 00 32 E0 00 01 5A 50
P[0] = A 00..32..64
P[1] = E 00..32..64
P[2] = T 00..64
P[3] = R 00..32..64
P[4] = alternates 20,60,A0,E0
trims
A 01..20..3F
E 41..60..7F
R 81..A0..BF
telemetry
E0 present when the telemetry works
6g/3d
C1 few times if P[6] flag 00->08
C0 few times if P[6] = flag 08->00
P[5] = flags
01=high rate
20=hover=long_press_left
40=light -> temporary
08=6g/3d=short_press_right sequece also switches for a few packets to C1 if 8 C0 if 0
P[6] = 00 telemetry nok
01 telemetry ok but sometimes switch to 1 also when telemetry is nok...
P[7] = 5A -> sum RX_ID[0..2]
P[8] = sum P[0..7] + TX_ID[0] - TX_ID[1] + TX_ID[2] + 21
Telemetry
RX on channel: 69, Time: 3408us P: 66 4F 47 00 00 00 00 00 C8
P[0] = TX_ID[0]
P[1] = TX_ID[1]
P[2] = TX_ID[2]
P[8] = sum P[0..7] + CC
Timing when plane is not detected:
RF
2469 110713 0
2473 114560 3847
2477 120291 5731
2465 135684 15393
2469 142138 6454
2473 145984 3846
2477 151753 5769
2465 155330 3577
*/
/* P10 Piper CUB
Bind
----
Phase 1
Plane sends these packets:
250K C=69 S=Y A= CC CC CC CC CC P(9)= 9C BB CC DD 84 24 20 00 97
P[0] = 9C bind phase 1
P[1] = Dummy TX_ID
P[2] = Dummy TX_ID
P[3] = Dummy TX_ID
P[4] = RX_ID[0]
P[5] = RX_ID[1]
P[6] = RX_ID[2]
P[7] = 00
P[8] = sum P[0..7] + BF + 10
Normal
------
TX sends
C=67 -> only one channel when telemetry is working
A= E8 25 3B CC CC P(9)= 32 32 00 32 A0 40 01 C8 6E
P[0] = A 00..32..64
P[1] = E 00..32..64
P[2] = T 00..64
P[3] = R 00..32..64
P[4] = alternates 20,60,A0,E0
trims
A 01..20..3F
E 41..60..7F
R 81..A0..BF
telemetry
E0 present when the telemetry works
6g/3d
C1 few times if P[6] flag 00->08
C0 few times if P[6] = flag 08->00
P[5] = flags
01=high rate
20=hover=long_press_left
40=light -> temporary
08=6g/3d=short_press_right sequece also switches for a few packets to C1 if 8 C0 if 0
P[6] = 00 telemetry nok
01 telemetry ok but sometimes switch to 1 also when telemetry is nok...
P[7] = C8 -> sum RX_ID[0..2]
P[8] = sum P[0..7] + TX_ID[0] - TX_ID[1] + TX_ID[2] + 21 +10
*/

View File

@@ -22,8 +22,10 @@
#include "iface_xn297.h"
// Parameters which can be modified
#define XN297DUMP_PERIOD_SCAN 50000 // 25000
#define XN297DUMP_MAX_RF_CHANNEL 127 // Default 84
#define XN297DUMP_PERIOD_FAST_SCAN 50000 // 50000
#define XN297DUMP_PERIOD_SLOW_SCAN 100000 // 75000
#define XN297DUMP_MAX_PACKET 50 // 20
#define XN297DUMP_MAX_RF_CHANNEL 84 // Default 84
// Do not touch from there
#define XN297DUMP_INITIAL_WAIT 500
@@ -37,6 +39,8 @@ boolean enhanced;
boolean ack;
uint8_t pid;
uint8_t bitrate;
uint8_t old_option;
uint32_t scan_counter;
static void __attribute__((unused)) XN297Dump_RF_init()
{
@@ -185,6 +189,7 @@ static void __attribute__((unused)) XN297Dump_overflow()
static uint16_t XN297Dump_callback()
{
static uint32_t time=0,*time_rf;
static uint8_t *nbr_rf,compare_channel;
//!!!Blocking mode protocol!!!
TX_MAIN_PAUSE_off;
@@ -193,10 +198,10 @@ static uint16_t XN297Dump_callback()
{
if(sub_protocol<XN297DUMP_AUTO)
{
if(option==0xFF && bind_counter>XN297DUMP_PERIOD_SCAN)
if(option==0xFF && scan_counter>XN297DUMP_PERIOD_SLOW_SCAN)
{ // Scan frequencies
hopping_frequency_no++;
bind_counter=0;
scan_counter=0;
}
if(hopping_frequency_no!=rf_ch_num)
{ // Channel has changed
@@ -292,10 +297,10 @@ static uint16_t XN297Dump_callback()
phase++;
break;
case 1:
if(bind_counter>XN297DUMP_PERIOD_SCAN)
if(scan_counter>XN297DUMP_PERIOD_FAST_SCAN)
{ // Scan frequencies
hopping_frequency_no++;
bind_counter=0;
scan_counter=0;
if(hopping_frequency_no>XN297DUMP_MAX_RF_CHANNEL)
{
hopping_frequency_no=0;
@@ -365,12 +370,18 @@ static uint16_t XN297Dump_callback()
debugln("\r\n--------------------------------");
phase=2;
debugln("Identifying all RF channels in use.");
bind_counter=0;
scan_counter=0;
hopping_frequency_no=0;
rf_ch_num=0;
packet_count=0;
nbr_rf=(uint8_t*)malloc(XN297DUMP_MAX_RF_CHANNEL*sizeof(uint8_t));
if(nbr_rf==NULL)
{
debugln("\r\nCan't allocate memory for next phase!!!");
phase=0;
break;
}
debug("Trying RF channel: 0");
XN297_SetTXAddr(rx_tx_addr,address_length);
XN297_SetRXAddr(rx_tx_addr,packet_length);
XN297_RFChannel(0);
@@ -381,18 +392,43 @@ static uint16_t XN297Dump_callback()
}
break;
case 2:
if(bind_counter>XN297DUMP_PERIOD_SCAN)
if(scan_counter>XN297DUMP_PERIOD_SLOW_SCAN)
{ // Scan frequencies
hopping_frequency_no++;
bind_counter=0;
if(packet_count && packet_count<=20)
scan_counter=0;
if(packet_count && packet_count<=XN297DUMP_MAX_PACKET)
debug("\r\nTrying RF channel: ");
packet_count=0;
if(hopping_frequency_no>XN297DUMP_MAX_RF_CHANNEL)
{
uint8_t nbr_max=0,j=0;
debug("\r\n\r\n%d RF channels identified:",rf_ch_num);
compare_channel=0;
for(uint8_t i=0;i<rf_ch_num;i++)
debug(" %d",hopping_frequency[i]);
{
debug(" %d[%d]",hopping_frequency[i],nbr_rf[i]);
if(nbr_rf[i]>nbr_max)
{
nbr_max=nbr_rf[i];
compare_channel=i;
}
}
nbr_max = (nbr_max*2)/3;
debug("\r\nKeeping only RF channels with more than %d packets:", nbr_max);
for(uint8_t i=0;i<rf_ch_num;i++)
if(nbr_rf[i]>=nbr_max)
{
hopping_frequency[j]=hopping_frequency[i];
debug(" %d",hopping_frequency[j]);
if(compare_channel==i)
{
compare_channel=j;
debug("*");
}
j++;
}
rf_ch_num = j;
free(nbr_rf);
time_rf=(uint32_t*)malloc(rf_ch_num*sizeof(time));
if(time_rf==NULL)
{
@@ -402,13 +438,13 @@ static uint16_t XN297Dump_callback()
}
debugln("\r\n--------------------------------");
debugln("Identifying RF channels order.");
hopping_frequency_no=1;
hopping_frequency_no=0;
phase=3;
packet_count=0;
bind_counter=0;
debugln("Time between CH:%d and CH:%d",hopping_frequency[0],hopping_frequency[hopping_frequency_no]);
scan_counter=0;
debugln("Time between CH:%d and CH:%d",hopping_frequency[compare_channel],hopping_frequency[hopping_frequency_no]);
time_rf[hopping_frequency_no]=0xFFFFFFFF;
XN297_RFChannel(hopping_frequency[0]);
XN297_RFChannel(hopping_frequency[compare_channel]);
uint16_t timeL=TCNT1;
if(TIMER2_BASE->SR & TIMER_SR_UIF)
{//timer just rolled over...
@@ -429,7 +465,7 @@ static uint16_t XN297Dump_callback()
}
if( XN297_IsRX() )
{ // RX fifo data ready
// if(NRF24L01_ReadReg(NRF24L01_09_CD))
if(NRF24L01_ReadReg(NRF24L01_09_CD))
{
uint8_t res;
if(enhanced)
@@ -461,9 +497,10 @@ static uint16_t XN297Dump_callback()
for(uint8_t i=0;i<packet_length;i++)
debug(" %02X",packet[i]);
packet_count++;
if(packet_count>20)
nbr_rf[rf_ch_num-1]=packet_count;
if(packet_count>XN297DUMP_MAX_PACKET)
{//change channel
bind_counter=XN297DUMP_PERIOD_SCAN+1;
scan_counter=XN297DUMP_PERIOD_SLOW_SCAN+1;
debug("\r\nTrying RF channel: ");
}
}
@@ -475,23 +512,23 @@ static uint16_t XN297Dump_callback()
XN297Dump_overflow();
break;
case 3:
if(bind_counter>XN297DUMP_PERIOD_SCAN)
if(scan_counter>XN297DUMP_PERIOD_SLOW_SCAN)
{ // Scan frequencies
hopping_frequency_no++;
bind_counter=0;
scan_counter=0;
if(hopping_frequency_no>=rf_ch_num)
{
uint8_t next=0;
debugln("\r\n\r\nChannel order:");
debugln("%d: 0us",hopping_frequency[0]);
uint8_t i=1;
debugln("%d: 0us",hopping_frequency[compare_channel]);
uint8_t i=0;
do
{
time=time_rf[i];
if(time!=0xFFFFFFFF)
{
next=i;
for(uint8_t j=2;j<rf_ch_num;j++)
for(uint8_t j=1;j<rf_ch_num;j++)
if(time>time_rf[j])
{
next=j;
@@ -511,9 +548,9 @@ static uint16_t XN297Dump_callback()
hopping_frequency_no=0;
break;
}
debugln("Time between CH:%d and CH:%d",hopping_frequency[0],hopping_frequency[hopping_frequency_no]);
debugln("Time between CH:%d and CH:%d",hopping_frequency[compare_channel],hopping_frequency[hopping_frequency_no]);
time_rf[hopping_frequency_no]=-1;
XN297_RFChannel(hopping_frequency[0]);
XN297_RFChannel(hopping_frequency[compare_channel]);
uint16_t timeL=TCNT1;
if(TIMER2_BASE->SR & TIMER_SR_UIF)
{//timer just rolled over...
@@ -527,7 +564,7 @@ static uint16_t XN297Dump_callback()
}
if( XN297_IsRX() )
{ // RX fifo data ready
//if(NRF24L01_ReadReg(NRF24L01_09_CD))
if(NRF24L01_ReadReg(NRF24L01_09_CD))
{
uint8_t res;
if(enhanced)
@@ -552,7 +589,7 @@ static uint16_t XN297Dump_callback()
if(time_rf[hopping_frequency_no] > (time>>1))
time_rf[hopping_frequency_no]=time>>1;
debugln("Time: %5luus", time>>1);
XN297_RFChannel(hopping_frequency[0]);
XN297_RFChannel(hopping_frequency[compare_channel]);
}
else
{
@@ -562,7 +599,7 @@ static uint16_t XN297Dump_callback()
packet_count++;
if(packet_count>24)
{
bind_counter=XN297DUMP_PERIOD_SCAN+1;
scan_counter=XN297DUMP_PERIOD_SLOW_SCAN+1;
packet_count=0;
}
}
@@ -609,12 +646,11 @@ static uint16_t XN297Dump_callback()
{
if(phase==0)
{
address_length=3;
memcpy(rx_tx_addr, (uint8_t *)"\xBD\x54\x78", address_length); //"\x62\xE6\xBD\x54\x78"
bitrate=XN297DUMP_1M;
packet_length=7;
hopping_frequency_no=40; //bind ?, normal 40
address_length=5;
memcpy(rx_tx_addr, (uint8_t *)"\xCC\xCC\xCC\xCC\xCC", address_length); // bind \x7E\xB8\x63\xA9
bitrate=XN297DUMP_250K;
packet_length=9;
hopping_frequency_no=71; //bind 71, normal ??
NRF24L01_Initialize();
NRF24L01_SetTxRxMode(TXRX_OFF);
@@ -623,8 +659,9 @@ static uint16_t XN297Dump_callback()
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, address_length); // set up RX address
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, packet_length); // Enable rx pipe 0
NRF24L01_WriteReg(NRF24L01_05_RF_CH, option); //hopping_frequency_no);
debug("NRF dump, len=%d, rf=%d, address length=%d, bitrate=",packet_length,hopping_frequency_no,address_length);
old_option = option;
debug("NRF dump, len=%d, rf=%d, address length=%d, bitrate=",packet_length,option,address_length); //hopping_frequency_no,address_length);
switch(bitrate)
{
case XN297DUMP_250K:
@@ -643,6 +680,7 @@ static uint16_t XN297Dump_callback()
}
NRF24L01_WriteReg(NRF24L01_00_CONFIG, _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX)); //_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) |
phase++;
time=0;
}
else
{
@@ -650,13 +688,23 @@ static uint16_t XN297Dump_callback()
{ // RX fifo data ready
if(NRF24L01_ReadReg(NRF24L01_09_CD))
{
XN297Dump_overflow();
uint16_t timeL=TCNT1;
if(TIMER2_BASE->SR & TIMER_SR_UIF)
{//timer just rolled over...
XN297Dump_overflow();
timeL=0;
}
time=(timeH<<16)+timeL-time;
debug("RX: %5luus ", time>>1);
time=(timeH<<16)+timeL;
NRF24L01_ReadPayload(packet, packet_length);
//bool ok=true;
uint8_t buffer[40];
memcpy(buffer,packet,packet_length);
//if(memcmp(&packet_in[0],&packet[0],packet_length))
{
debug("P:");
debug("C: %02X P:", option);
for(uint8_t i=0;i<packet_length;i++)
debug(" %02X",packet[i]);
debugln("");
@@ -718,7 +766,12 @@ static uint16_t XN297Dump_callback()
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_00_CONFIG, _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX)); // _BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) |
}
NRF24L01_WriteReg(NRF24L01_05_RF_CH, option); //hopping_frequency_no);
XN297Dump_overflow();
if(old_option != option)
{
NRF24L01_WriteReg(NRF24L01_05_RF_CH, option); //hopping_frequency_no);
old_option = option;
}
}
}
else if(sub_protocol == XN297DUMP_CC2500)
@@ -834,7 +887,64 @@ static uint16_t XN297Dump_callback()
}
#endif
}
bind_counter++;
else if(sub_protocol == XN297DUMP_XN297)
{
if(phase==0)
{
address_length=5;
memcpy(rx_tx_addr, (uint8_t *)"\x00\x00\x00\x00\x00", address_length); // bind \x7E\xB8\x63\xA9
packet_length=9;
hopping_frequency_no=0x30;
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTXAddr(rx_tx_addr, address_length);
XN297_SetRXAddr(rx_tx_addr, packet_length);
XN297_Hopping(option);
old_option = option;
XN297_SetTxRxMode(RX_EN);
debugln("XN297 dump, len=%d, rf=%d, address length=%d",packet_length,option,address_length); //hopping_frequency_no,address_length);
phase = 1;
}
else
{
bool rx = XN297_IsRX(); // Needed for the NRF24L01 since otherwise the bit gets cleared
if(rx)
{ // RX fifo data ready
XN297_SetTxRxMode(TXRX_OFF);
XN297Dump_overflow();
uint16_t timeL=TCNT1;
if(TIMER2_BASE->SR & TIMER_SR_UIF)
{//timer just rolled over...
XN297Dump_overflow();
timeL=0;
}
time=(timeH<<16)+timeL-time;
debug("RX: %5luus ", time>>1);
time=(timeH<<16)+timeL;
if(XN297_ReadPayload(packet_in, packet_length))
{
debug("OK:");
for(uint8_t i=0;i<packet_length;i++)
debug(" %02X",packet_in[i]);
}
else // Bad packet
debug(" NOK");
debugln("");
// restart RX mode
XN297_SetTxRxMode(RX_EN);
}
XN297Dump_overflow();
if(old_option != option)
{
debugln("C=%d(%02X)",option,option);
XN297_Hopping(option);
old_option = option;
}
}
}
scan_counter++;
if(IS_RX_FLAG_on) // Let the radio update the protocol
{
if(Update_All()) return 10000; // New protocol selected
@@ -860,7 +970,7 @@ void XN297Dump_init(void)
if(address_length<3||address_length>5)
address_length=5; //default
XN297Dump_RF_init();
bind_counter=0;
scan_counter=0;
rf_ch_num=0xFF;
prev_option=option^0x55;
phase=0; // init

View File

@@ -207,6 +207,27 @@ static void __attribute__((unused)) XN297_SetTxRxMode(enum TXRX_State mode)
#endif
}
#ifdef CC2500_INSTALLED
uint8_t XN297_Buffer[32];
uint8_t XN297_Buffer_Len = 0;
static void __attribute__((unused)) XN297_SendCC2500Payload()
{
// stop TX/RX
CC2500_Strobe(CC2500_SIDLE);
// flush tx FIFO
CC2500_Strobe(CC2500_SFTX);
// packet length
CC2500_WriteReg(CC2500_06_PKTLEN, XN297_Buffer_Len + 4); // Packet len, fix packet len
// xn297L preamble
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, (uint8_t*)"\x0C\x71\x0F\x55", 4);
// xn297 packet
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, XN297_Buffer, XN297_Buffer_Len);
// transmit
CC2500_Strobe(CC2500_STX);
}
#endif
static void __attribute__((unused)) XN297_SendPayload(uint8_t* msg, uint8_t len)
{
#ifdef NRF24L01_INSTALLED
@@ -220,22 +241,25 @@ static void __attribute__((unused)) XN297_SendPayload(uint8_t* msg, uint8_t len)
#ifdef CC2500_INSTALLED
if(xn297_rf == XN297_CC2500)
{
// stop TX/RX
CC2500_Strobe(CC2500_SIDLE);
// flush tx FIFO
CC2500_Strobe(CC2500_SFTX);
// packet length
CC2500_WriteReg(CC2500_06_PKTLEN, len + 4); // Packet len, fix packet len
// xn297L preamble
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, (uint8_t*)"\x0C\x71\x0F\x55", 4);
// xn297 packet
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, msg, len);
// transmit
CC2500_Strobe(CC2500_STX);
memcpy(XN297_Buffer, msg, len);
XN297_Buffer_Len = len;
XN297_SendCC2500Payload();
}
#endif
}
static void __attribute__((unused)) XN297_ReSendPayload()
{
#ifdef NRF24L01_INSTALLED
if(xn297_rf == XN297_NRF)
NRF24L01_Strobe(NRF24L01_E3_REUSE_TX_PL);
#endif
#ifdef CC2500_INSTALLED
if(xn297_rf == XN297_CC2500)
XN297_SendCC2500Payload();
#endif
}
static void __attribute__((unused)) XN297_WritePayload(uint8_t* msg, uint8_t len)
{
uint8_t buf[32];
@@ -329,9 +353,9 @@ static void __attribute__((unused)) XN297_WriteEnhancedPayload(uint8_t* msg, uin
last++;
buf[last] = bit_reverse(msg[len-1]) << 6; // last 2 bit of payload
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[scramble_index++] & 0xc0;
}
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[scramble_index++] & 0xc0;
// crc
if (xn297_crc)
@@ -351,8 +375,7 @@ static void __attribute__((unused)) XN297_WriteEnhancedPayload(uint8_t* msg, uin
buf[last++] = (crc & 0xff) << 6;
}
pid++;
if(pid>3)
pid=0;
pid &= 0x03;
// send packet
XN297_SendPayload(buf, last);
@@ -440,7 +463,6 @@ static uint8_t __attribute__((unused)) XN297_ReadEnhancedPayload(uint8_t* msg, u
// Read payload
XN297_ReceivePayload(buffer, len+2); // Read pcf + payload + CRC
// Decode payload
pcf_size = buffer[0];
if(xn297_scramble_enabled)

View File

@@ -0,0 +1,261 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
Thanks to Goebish ,Ported from his deviation firmware
*/
#if defined(YUXIANG_NRF24L01_INO)
#include "iface_xn297.h"
//#define YUXIANG_FORCE_ID
#define YUXIANG_PACKET_PERIOD 12422
#define YUXIANG_PACKET_SIZE 9
#define YUXIANG_BIND_COUNT 150
#define YUXIANG_BIND_FREQ 0x30 //48
#define YUXIANG_RF_NUM_CHANNELS 4
#define YUXIANG_WRITE_TIME 1000
//#define YUXIANG_TELEM_DEBUG
enum
{
YUXIANG_DATA = 0,
YUXIANG_RX
};
static void __attribute__((unused)) YUXIANG_send_packet()
{
if(bind_counter && packet_sent < 5 && (hopping_frequency_no & 0x07) == 0)
{
bind_counter--;
if(!bind_counter)
BIND_DONE;
#if 0
debug("B C:%d, ",YUXIANG_BIND_FREQ);
#endif
XN297_RFChannel(YUXIANG_BIND_FREQ);
XN297_SetTXAddr((uint8_t*)"\x00\x00\x00\x00\x00", 5);
bind_phase = 1;
packet_sent++;
}
else
{//Normal operation
XN297_Hopping(hopping_frequency_no & 0x03);
#if 0
debug("C:%d, ",hopping_frequency[hopping_frequency_no & 0x03]);
#endif
hopping_frequency_no++;
if(bind_phase)
{
XN297_SetTXAddr(rx_tx_addr, 5);
XN297_SetRXAddr(rx_tx_addr, YUXIANG_PACKET_SIZE);
bind_phase = 0;
packet_sent = 0;
}
}
packet[0] = GET_FLAG(!bind_phase, 0x80) // Bind packet
| GET_FLAG(telemetry_lost, 0x20) // No telem
| GET_FLAG(!CH5_SW, 0x10) // Lock
| GET_FLAG(CH6_SW, 0x08) // High
| GET_FLAG(CH11_SW, 0x01); // Screw pitch -> temporary
packet[1] = GET_FLAG(CH7_SW, 0x08) // Land only when unlock
| GET_FLAG(CH10_SW, 0x20); // Mode
packet[2] = GET_FLAG(CH5_SW, 0x02) // Altitude hold set when unlock
| GET_FLAG(CH8_SW, 0x01) // Manual
| GET_FLAG(CH9_SW, 0x40); // Flip
uint16_t value = convert_channel_16b_limit(AILERON,0,1000);
packet[3] = value;
packet[7] = value >> 8;
value = convert_channel_16b_limit(ELEVATOR,0,1000);
packet[4] = value;
packet[7] |= (value >> 6) & 0x0C;
value = convert_channel_16b_limit(THROTTLE,0,1000);
packet[5] = value;
packet[7] |= (value >> 4) & 0x30;
value = convert_channel_16b_limit(RUDDER,0,1000);
packet[6] = value;
packet[7] |= (value >> 2) & 0xC0;
if(bind_phase)
memcpy(&packet[3], rx_tx_addr, 4);
uint8_t checksum = 0;
for(uint8_t i=0; i<YUXIANG_PACKET_SIZE-1; i++)
checksum += packet[i];
packet[8] = checksum;
#if 0
debug("P:");
for(uint8_t i=0;i<YUXIANG_PACKET_SIZE;i++)
debug(" %02X",packet[i]);
debugln("");
#endif
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, YUXIANG_PACKET_SIZE);
}
static void __attribute__((unused)) YUXIANG_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
}
static void __attribute__((unused)) YUXIANG_initialize_txid()
{
//Modify address to influence hop
rx_tx_addr[0] += RX_num;
//Calc hop
uint8_t val;
for(uint8_t i=0; i<4; i++)
{
val = i*0x06;
if(i) val |= 0x01;
val += rx_tx_addr[0];
val &= 0x1F;
val += 47;
if(val < 50)
val = 50;
if(val > 62 && val < 66)
val = 62;
hopping_frequency[i] = val;
}
#ifdef YUXIANG_FORCE_ID
switch(RX_num)
{
case 0://TX1
memcpy(rx_tx_addr,(uint8_t *)"\xB3\x13\x36\xDD",4); //rx_tx_addr[4]=0xD9
memcpy(hopping_frequency,(uint8_t *)"\x42\x49\x32\x35",4); //66,73,50,53
break;
case 1://TX2
memcpy(rx_tx_addr,(uint8_t *)"\xEB\x13\x36\xAC",4); //rx_tx_addr[4]=0xE0
memcpy(hopping_frequency,(uint8_t *)"\x47\x4D\x3A\x3E",4); //58,62,71,77
break;
}
#endif
uint8_t sum=0;
for(uint8_t i=0; i<4; i++)
sum += rx_tx_addr[i];
rx_tx_addr[4] = sum;
debugln("ID: %02X %02X %02X %02X %02X , HOP: %2d %2d %2d %2d",rx_tx_addr[0],rx_tx_addr[1],rx_tx_addr[2],rx_tx_addr[3],rx_tx_addr[4],hopping_frequency[0],hopping_frequency[1],hopping_frequency[2],hopping_frequency[3]);
}
uint16_t YUXIANG_callback()
{
bool rx = false;
switch(phase)
{
case YUXIANG_DATA:
rx = XN297_IsRX(); // Needed for the NRF24L01 since otherwise the bit gets cleared
XN297_SetTxRxMode(TXRX_OFF);
#ifdef YUXIANG_HUB_TELEMETRY
if(packet_count > 240) // Around 3sec with no telemetry
telemetry_lost = 1;
else
packet_count++;
#endif
#ifdef MULTI_SYNC
telemetry_set_input_sync(YUXIANG_PACKET_PERIOD);
#endif
YUXIANG_send_packet();
if(rx)
{ // Check if a packet has been received
#ifdef YUXIANG_TELEM_DEBUG
debug("RX ");
#endif
if(XN297_ReadPayload(packet_in, YUXIANG_PACKET_SIZE))
{ // packet with good CRC and length
uint8_t checksum = 0;
for(uint8_t i=0; i<YUXIANG_PACKET_SIZE-1; i++)
checksum += packet_in[i];
if(packet_in[8] == checksum)
{
#ifdef YUXIANG_HUB_TELEMETRY
if(packet_in[0]==0x78)
{
#ifdef YUXIANG_TELEM_DEBUG
debug("OK:");
for(uint8_t i=0;i<YUXIANG_PACKET_SIZE;i++)
debug(" %02X",packet_in[i]);
#endif
v_lipo1 = packet_in[4];
v_lipo2 = packet_in[6];
}
telemetry_link = 1;
#endif
telemetry_lost = 0;
packet_count = 0;
bind_counter = 0; // Stop bind
BIND_DONE;
}
#ifdef YUXIANG_TELEM_DEBUG
else // Bad packet
debug(" NOK");
#endif
}
#ifdef YUXIANG_TELEM_DEBUG
else // Bad packet
debug("NOK");
debugln("");
#endif
rx = false;
}
phase++;
return YUXIANG_WRITE_TIME;
default:
// RX
{ // Wait for packet to be sent before switching to receive mode
uint16_t start=(uint16_t)micros();
while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 500)
if(XN297_IsPacketSent())
break;
}
//if(bind_phase)
// XN297_Hopping(3);
XN297_SetTxRxMode(RX_EN);
phase = YUXIANG_DATA;
return YUXIANG_PACKET_PERIOD - YUXIANG_WRITE_TIME;
}
return 0;
}
void YUXIANG_init(void)
{
YUXIANG_initialize_txid();
YUXIANG_RF_init();
if(IS_BIND_IN_PROGRESS)
bind_counter = YUXIANG_BIND_COUNT;
else
bind_counter = 0;
phase = YUXIANG_DATA;
hopping_frequency_no = 0;
bind_phase = 1;
packet_sent = 8;
#ifdef YUXIANG_HUB_TELEMETRY
packet_count = 0;
telemetry_lost = 1;
RX_RSSI = 100; // Dummy value
#endif
}
#endif

View File

@@ -165,9 +165,14 @@
/*** PROTOCOLS TO INCLUDE ***/
/****************************/
//In this section select the protocols you want to be accessible when using the module.
//All the protocols will not fit in the Atmega328p module so you need to pick and choose.
//All the protocols will not fit in the STM32 or Atmega328p modules so you need to pick and choose.
//Comment the protocols you are not using with "//" to save Flash space.
//Already defined protocols selection
//#define MULTI_AIR //Only Air protocols will be available, all the others are disabled
//#define MULTI_SURFACE //Only Surface protocols will be available, all the others are disabled
//#define MULTI_EU //Only LBT/EU protocols will be available, all the others are disabled
//Protocol for module configuration
#define MULTI_CONFIG_INO
@@ -191,6 +196,7 @@
#define E01X_CYRF6936_INO
#define E129_CYRF6936_INO
#define J6PRO_CYRF6936_INO
#define KYOSHO3_CYRF6936_INO
#define LOSI_CYRF6936_INO //Need DSM to be enabled
#define MLINK_CYRF6936_INO
#define SCORPIO_CYRF6936_INO
@@ -205,7 +211,7 @@
#define FRSKYL_CC2500_INO
#define FRSKYD_CC2500_INO
#define FRSKYV_CC2500_INO
#define FRSKYX_CC2500_INO
#define FRSKYX_CC2500_INO //Include FRSKYX2 protocol
#define FRSKY_RX_CC2500_INO
#define HITEC_CC2500_INO
#define HOTT_CC2500_INO
@@ -234,10 +240,13 @@
#define FX_NRF24L01_INO
#define FY326_NRF24L01_INO
#define GW008_NRF24L01_INO
#define H36_NRF24L01_INO
#define H8_3D_NRF24L01_INO
#define HISKY_NRF24L01_INO
#define HONTAI_NRF24L01_INO
#define H8_3D_NRF24L01_INO
#define JIABAILE_NRF24L01_INO
#define JJRC345_NRF24L01_INO
#define KAMTOM_NRF24L01_INO
#define KN_NRF24L01_INO
#define KYOSHO2_NRF24L01_INO
#define LOLI_NRF24L01_INO
@@ -248,11 +257,14 @@
#define REALACC_NRF24L01_INO
#define SGF22_NRF24L01_INO
#define SHENQI_NRF24L01_INO
#define SHENQI2_NRF24L01_INO
#define SYMAX_NRF24L01_INO
#define V2X2_NRF24L01_INO
#define V761_NRF24L01_INO
#define WPL_NRF24L01_INO
#define XERALL_NRF24L01_INO
#define YD717_NRF24L01_INO
#define YUXIANG_NRF24L01_INO
#define ZSX_NRF24L01_INO
//The protocols below need either a CC2500 or NRF24L01 to be installed
@@ -264,8 +276,11 @@
#define Q303_CCNRF_INO
#define Q90C_CCNRF_INO
#define SLT_CCNRF_INO
#define UDIRC_CCNRF_INO
#define V911S_CCNRF_INO
#define WL91X_CCNRF_INO
#define XK_CCNRF_INO
#define XK2_CCNRF_INO
//The protocols below need a SX1276 to be installed
#define FRSKYR9_SX1276_INO
@@ -288,11 +303,6 @@
//Enable DSM Forward Programming
#define DSM_FWD_PGM
//AFHDS2A specific settings
//-------------------------
//When enabled (remove the "//"), the below setting makes LQI (Link Quality Indicator) available on one of the RX ouput channel (5-14).
//#define AFHDS2A_LQI_CH 14
/**************************/
/*** FAILSAFE SETTINGS ***/
/**************************/
@@ -339,13 +349,18 @@
#define NCC1701_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define OMP_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define V761_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define KAMTOM_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define FX_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define XK2_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define SGF22_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define YUXIANG_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define PROPEL_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define CABELL_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define RLINK_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define WFLY2_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define LOLI_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define MT99XX_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
//#define MLINK_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
//#define MLINK_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
#define MLINK_FW_TELEMETRY // Forward received telemetry packet directly to TX to be decoded by erskyTX and OpenTX
//#define HITEC_HUB_TELEMETRY // Use FrSkyD Hub format to send basic telemetry to the radios which can decode it like er9x, erskyTX and OpenTX
#define HITEC_FW_TELEMETRY // Forward received telemetry packets to be decoded by erskyTX and OpenTX
@@ -551,10 +566,9 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
PPM_IBUS
PWM_SBUS
PPM_SBUS
PWM_IB16
PPM_IB16
PWM_SB16
PPM_SB16
AFHDS2A_GYRO_OFF
AFHDS2A_GYRO_ON
AFHDS2A_GYRO_ON_REV
PROTO_AFHDS2A_RX
NONE
PROTO_ASSAN
@@ -606,6 +620,7 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
DSMX_1F
DSMX_2F
DSMR
DSM2_SFC
PROTO_DSM_RX
DSM_RX
DSM_CLONE
@@ -639,7 +654,8 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
V912
CX20
PROTO_FQ777
NONE
FQ777
XBM37
PROTO_FRSKY_RX
FRSKY_RX
FRSKY_CLONE
@@ -682,6 +698,10 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
FX816
FX620
FX9630
FX_Q560
FX_QF012
FX_BM26
FX_A570
PROTO_FY326
FY326
FY319
@@ -690,6 +710,7 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
GD_V2
PROTO_GW008
NONE
PROTO_H36
PROTO_H8_3D
H8_3D
H20H
@@ -710,6 +731,7 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
JJRCX1
X5C1
FQ777_951
HONTAI_XKK170
PROTO_HOTT
HOTT_SYNC
HOTT_NO_SYNC
@@ -721,11 +743,16 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
NONE
PROTO_J6PRO
NONE
PROTO_JIABAILE
JIABAILE_STD
JIABAILE_GYRO
PROTO_JJRC345
JJRC345
SKYTMBLR
PROTO_JOYSWAY
NONE
PROTO_KAMTOM
NONE
PROTO_KF606
KF606_KF606
KF606_MIG320
@@ -738,6 +765,8 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
KYOSHO_HYPE
PROTO_KYOSHO2
NONE
PROTO_KYOSHO3
NONE
PROTO_LOLI
NONE
PROTO_LOSI
@@ -753,8 +782,9 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
PROTO_MLINK
NONE
PROTO_MOULDKG
MOULDKG_ANALOG
MOULDKG_DIGIT
MOULDKG_ANALOG4
MOULDKG_DIGIT4
MOULDKG_ANALOG6
PROTO_MT99XX
MT99
H7
@@ -766,6 +796,7 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
F949G
PROTO_MT99XX2
PA18
SU35
PROTO_NCC1701
NONE
PROTO_OMP
@@ -804,9 +835,14 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
PROTO_SCORPIO
NONE
PROTO_SGF22
NONE
SGF22
F22S
J20
CX10
PROTO_SHENQI
NONE
PROTO_SHENQI2
NONE
PROTO_SKYARTEC
NONE
PROTO_SLT
@@ -815,11 +851,15 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
Q100
Q200
MR100
V1_4CH
RF_SIM
PROTO_SYMAX
SYMAX
SYMAX5C
PROTO_TRAXXAS
NONE
PROTO_UDIRC
NONE
PROTO_V2X2
V2X2
JXD506
@@ -841,18 +881,25 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
W6_6_1
W6_HEL
W6_HEL_I
PROTO_WPL
NONE
PROTO_XERALL
NONE
PROTO_XK
X450
X420
XK_CARS
PROTO_XK2
XK2_X4
XK2_P10
PROTO_YD717
YD717
SKYWLKR
SYMAX4
XINXUN
NIHUI
PROTO_YUXIANG
NONE
PROTO_ZSX
NONE
*/

View File

@@ -71,7 +71,11 @@ enum CYRF_PWR {
CYRF_PWR_DEFAULT,
};
enum FIND_CHANNEL {
FIND_CHANNEL_ANY = 0,
FIND_CHANNEL_EVEN = 1,
FIND_CHANNEL_ODD = 2,
};
/* SPI CYRF6936 */
/*

View File

@@ -28,6 +28,7 @@ static void __attribute__((unused)) XN297_SetTXAddr(const uint8_t*, uint8_t);
static void __attribute__((unused)) XN297_SetRXAddr(const uint8_t*, uint8_t);
static void __attribute__((unused)) XN297_SetTxRxMode(enum TXRX_State);
static void __attribute__((unused)) XN297_SendPayload(uint8_t*, uint8_t);
static void __attribute__((unused)) XN297_ReSendPayload();
static void __attribute__((unused)) XN297_WritePayload(uint8_t*, uint8_t);
static void __attribute__((unused)) XN297_WriteEnhancedPayload(uint8_t*, uint8_t, uint8_t);
static bool __attribute__((unused)) XN297_IsRX();

File diff suppressed because it is too large Load Diff

View File

@@ -100,9 +100,9 @@ For example, if you have no interest in binding your Tx to an model with and FrS
## **Choice 3:** Which protocols to upload to the MULTI-Module
In the case of the ATmega328, the memory required by all the possible protocols exceeds the 32KB flash limit considerably. This means that you will need to make a choice of which protocols you will compile into your firmware. Fortunately, the process of selecting and compiling is not too difficult and it is fully documented on the [Compiling and Programming](docs/Compiling.md) page.
As the list of supported protocols grows even the STM32 ARM microcontroller cannot hold all of the protocols. You can select the protocols you need and complie your own firmware. Fortunately, the process of selecting and compiling is not too difficult and it is fully documented on the [Compiling and Programming](docs/Compiling.md) page. You can also download firmware from the [Multi-Module](https://downloads.multi-module.org) website. These firmware files have been split into two groups, surface "SFC" and air "AIR". You can check which protocols are included in each of these groups in the [Validate.h](Multiprotocol/Validate.h) source file.
An alternative is to use a STM32 ARM microcontroller based module which can hold all the protocols.
In the case of the ATmega328, the memory required by all the possible protocols exceeds the 32KB flash limit considerably. This means that you will need to make a choice of which protocols you will compile into your firmware.
## **Choice 4:** Choosing the type of interface between the MULTI-Module and your radio (PPM or Serial)

View File

@@ -5,8 +5,7 @@ exitcode=0;
# CC2500-only 64Kb FCC builds
printf "\e[33;1mBuilding mm-stm-cc2500-64-aetr-v$MULTI_VERSION.bin\e[0m\n";
#opt_enable $ALL_PROTOCOLS;
#opt_disable IKEAANSLUTA_CC2500_INO;
opt_disable SCANNER_CC2500_INO;
opt_disable ENABLE_PPM;
opt_disable A7105_INSTALLED;
opt_disable CYRF6936_INSTALLED;

View File

@@ -108,7 +108,7 @@ The images below indicate the pin layout and the location of the ground pin on t
|:---:|:---:|:---:|
<img src="images/V2b_ISP.jpeg" width="189" height="200" /> | <img src="images/MPTM_PCB_2.3d_ISP.png" width="486" height="201" /> | <img src="images/ProMini_ISP.png" width="195" height="200" /> |
You are now ready to plug in the USB programmer to the computer. If you are looking for a good working USBasp Windows driver, [use this one](http://www.protostack.com/download/USBasp-win-driver-x86-x64-v3.0.7.zip).
You are now ready to plug in the USB programmer to the computer. If you are looking for a good working USBasp Windows driver, [use this one](https://protostack.com.au/download/USBasp-win-driver-x86-x64-v3.0.7.zip).
### Burn bootloader and set fuses
The bootloader only needs to be burned once, unless you decide to switch from one option to the other (or it is accidentally erased). If you have already burned the bootloader / set the fuses you can skip this step.

670
docs/XBM-37_Analysis.md Normal file
View File

@@ -0,0 +1,670 @@
# 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](#1-capture-file-inventory)
2. [Key Protocol Parameters](#2-key-protocol-parameters)
3. [SPI Initialization Sequence](#3-spi-initialization-sequence)
4. [Bind Sequence Deep Analysis (02a + 02b)](#4-bind-sequence--deep-analysis-02a--02b)
5. [Bind vs No-RX Comparison (01b vs 02b)](#5-bind-vs-no-rx-comparison-01b-vs-02b)
6. [Normal Data Packet Format](#6-normal-data-packet-format)
7. [Per-Channel Control Analysis (03b 24b)](#7-per-channel-control-analysis-03b--24b)
8. [RF Timing Summary](#8-rf-timing-summary)
9. [MPM Implementation Notes](#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 (`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 -> 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
```c
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
```c
// 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
```c
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
```c
// 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
```c
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.*