Skip to content

Commit 1b09a7e

Browse files
CopilotMRC3742
andcommitted
Add SLT6_Tx subprotocol with NRF24L01 direct transmission support (#10)
* Initial plan * Add SLT6_Tx subprotocol: rename SLT6Tx to 7-char SLT6_Tx, bump VERSION_PATCH_LEVEL to 55 Co-authored-by: MRC3742 <26642502+MRC3742@users.noreply.github.com> * Final verification complete Co-authored-by: MRC3742 <26642502+MRC3742@users.noreply.github.com> * Remove codeql artifact and add to gitignore Co-authored-by: MRC3742 <26642502+MRC3742@users.noreply.github.com> * Apply Protocols_Details.md review suggestions: add NRF24L01 asterisks, update SLT6_Tx docs Co-authored-by: MRC3742 <26642502+MRC3742@users.noreply.github.com> * Update SLT protocol details and frequency tuning notes Clarified details about SLT subprotocols and their interaction with NRF24L01 and CC2500 modules. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: MRC3742 <26642502+MRC3742@users.noreply.github.com> Co-authored-by: MRC3742 <MRC3742@users.noreply.github.com>
1 parent fc653e6 commit 1b09a7e

File tree

8 files changed

+201
-19
lines changed

8 files changed

+201
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
Captures_TTX610_Tx/extracted/
2+
_codeql_detected_source_root

Multiprotocol/Multi.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
8,YD717,YD717,SKYWLKR,SYMAX4,XINXUN,NIHUI
99
9,KN,WLTOYS,FEILUN
1010
10,SymaX,SYMAX,SYMAX5C
11-
11,SLT,SLT_V1,SLT_V2,Q100,Q200,MR100,V1_4CH,RF_SIM
11+
11,SLT,SLT_V1,SLT_V2,Q100,Q200,MR100,V1_4CH,RF_SIM,SLT6_Tx
1212
12,CX10,GREEN,BLUE,DM007,---,J3015_1,J3015_2,MK33041
1313
13,CG023,CG023,YD829
1414
14,Bayang,Bayang,H8S3D,X16_AH,IRDRONE,DHD_D4,QX100

Multiprotocol/Multi_Protos.ino

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ const char STR_SUBTYPE_DEVO[] = "\x04""8ch\0""10ch""12ch""6ch\0""7ch\0";
139139
const char STR_SUBTYPE_YD717[] = "\x07""Std\0 ""SkyWlkr""Syma X4""XINXUN\0""NIHUI\0 ";
140140
const char STR_SUBTYPE_KN[] = "\x06""WLtoys""FeiLun";
141141
const char STR_SUBTYPE_SYMAX[] = "\x03""Std""X5C";
142-
const char STR_SUBTYPE_SLT[] = "\x06""V1_6ch""V2_8ch""Q100\0 ""Q200\0 ""MR100\0""V1_4ch""RF_SIM";
142+
const char STR_SUBTYPE_SLT[] = "\x07""V1_6ch\0""V2_8ch\0""Q100\0 ""Q200\0 ""MR100\0 ""V1_4ch\0""RF_SIM\0""SLT6_Tx";
143143
const char STR_SUBTYPE_CX10[] = "\x07""Green\0 ""Blue\0 ""DM007\0 ""-\0 ""JC3015a""JC3015b""MK33041";
144144
const char STR_SUBTYPE_CG023[] = "\x05""Std\0 ""YD829";
145145
const char STR_SUBTYPE_BAYANG[] = "\x07""Std\0 ""H8S3D\0 ""X16 AH\0""IRDrone""DHD D4\0""QX100\0 ";
@@ -486,7 +486,7 @@ const mm_protocol_definition multi_protocols[] = {
486486
{PROTO_SKYARTEC, STR_SKYARTEC, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 1, SW_CC2500, SKYARTEC_init, SKYARTEC_callback },
487487
#endif
488488
#if defined(SLT_CCNRF_INO)
489-
{PROTO_SLT, STR_SLT, STR_SUBTYPE_SLT, 7, OPTION_RFTUNE, 0, 1, SW_NRF, SLT_init, SLT_callback },
489+
{PROTO_SLT, STR_SLT, STR_SUBTYPE_SLT, 8, OPTION_RFTUNE, 0, 1, SW_NRF, SLT_init, SLT_callback },
490490
#endif
491491
#if defined(SYMAX_NRF24L01_INO)
492492
{PROTO_SYMAX, STR_SYMAX, STR_SUBTYPE_SYMAX, 2, OPTION_NONE, 0, 0, SW_NRF, SYMAX_init, SYMAX_callback },

Multiprotocol/Multiprotocol.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#define VERSION_MAJOR 1
2020
#define VERSION_MINOR 9
2121
#define VERSION_REVISION 4
22-
#define VERSION_PATCH_LEVEL 54
22+
#define VERSION_PATCH_LEVEL 55
2323

2424
#define MODE_SERIAL 0
2525

@@ -216,6 +216,7 @@ enum SLT
216216
MR100 = 4,
217217
SLT_V1_4 = 5,
218218
RF_SIM = 6,
219+
SLT6_Tx = 7,
219220
};
220221
enum CX10
221222
{
@@ -1135,6 +1136,7 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p --
11351136
MR100 4
11361137
SLT_V1_4CH 5
11371138
RF_SIM 6
1139+
SLT6_Tx 7
11381140
sub_protocol==E01X
11391141
E012 0
11401142
E015 1

Multiprotocol/NRF250K_EMU.ino

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,25 @@
1616

1717
#include "iface_nrf250k.h"
1818

19+
#if defined(CC2500_INSTALLED) && defined(NRF24L01_INSTALLED)
20+
extern bool xn297_rf;
21+
#endif
22+
1923
uint8_t cc2500_nrf_tx_addr[5], cc2500_nrf_addr_len;
2024

2125
static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len)
2226
{
2327
if (len > 5) len = 5;
2428
if (len < 3) len = 3;
25-
#if defined(CC2500_INSTALLED)
29+
#if defined(CC2500_INSTALLED) && defined(NRF24L01_INSTALLED)
30+
cc2500_nrf_addr_len = len;
31+
memcpy(cc2500_nrf_tx_addr, addr, len);
32+
if(xn297_rf == XN297_NRF)
33+
{
34+
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2);
35+
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, addr, len);
36+
}
37+
#elif defined(CC2500_INSTALLED)
2638
cc2500_nrf_addr_len = len;
2739
memcpy(cc2500_nrf_tx_addr, addr, len);
2840
#elif defined(NRF24L01_INSTALLED)
@@ -33,6 +45,22 @@ static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len
3345

3446
static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t len)
3547
{
48+
#if defined(NRF24L01_INSTALLED)
49+
#if defined(CC2500_INSTALLED)
50+
if(xn297_rf == XN297_NRF)
51+
#endif
52+
{
53+
if(len<=32)
54+
{
55+
NRF24L01_FlushTx();
56+
NRF24L01_WriteReg(NRF24L01_07_STATUS, _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_MAX_RT));
57+
NRF24L01_WritePayload(msg, len);
58+
}
59+
#if defined(CC2500_INSTALLED)
60+
return;
61+
#endif
62+
}
63+
#endif
3664
#if defined(CC2500_INSTALLED)
3765
#if defined(ESKY150V2_CC2500_INO)
3866
uint8_t buf[158];
@@ -106,13 +134,6 @@ static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t l
106134
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, last);
107135
CC2500_Strobe(CC2500_STX);
108136
}
109-
#elif defined(NRF24L01_INSTALLED)
110-
if(len<=32)
111-
{
112-
NRF24L01_FlushTx();
113-
NRF24L01_WriteReg(NRF24L01_07_STATUS, _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_MAX_RT));
114-
NRF24L01_WritePayload(msg, len);
115-
}
116137
#endif
117138
}
118139

Multiprotocol/SLT_ccnrf.ino

Lines changed: 147 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#define SLT_NFREQCHANNELS 15
3030
#define SLT_TXID_SIZE 4
3131
#define SLT_BIND_CHANNEL 0x50
32+
#define SLT6_CH_MIN 182 // 10-bit AETR minimum (captures: 180-185, symmetric around 512)
33+
#define SLT6_CH_MAX 842 // 10-bit AETR maximum (captures: 829-843, symmetric around 512)
34+
#define SLT6_SW_THRESHOLD 273 // ~33% of half-range (820/3) for 3-position switch zones
3235

3336
enum{
3437
// flags going to packet[6] (Q200)
@@ -54,9 +57,32 @@ enum {
5457
SLT_BIND2,
5558
};
5659

60+
// SLT6 sub-cycle states: 3 sub-cycles per triple, each with 2 copies
61+
enum {
62+
SLT6_BUILD_A=0, // Build packet, configure for 7B sub-cycle
63+
SLT6_DATA_A1, // Send 7B copy 1
64+
SLT6_DATA_A2, // Send 7B copy 2, configure for 6B sub-cycle
65+
SLT6_DATA_B1, // Send 6B copy 1
66+
SLT6_DATA_B2, // Send 6B copy 2, configure for 5B sub-cycle
67+
SLT6_DATA_C1, // Send 5B copy 1
68+
SLT6_DATA_C2, // Send 5B copy 2
69+
SLT6_BIND, // Send bind packet
70+
};
71+
72+
// SLT6 address XOR values for the 3 sub-cycles
73+
#define SLT6_ADDR_XOR_A 0x00 // 7B sub-cycle: base address
74+
#define SLT6_ADDR_XOR_B 0x06 // 6B sub-cycle: byte[0] XOR 0x06
75+
#define SLT6_ADDR_XOR_C 0x09 // 5B sub-cycle: byte[0] XOR 0x09
76+
77+
// SLT6 timing (from capture 12b, in microseconds)
78+
#define SLT6_TIMING_SUBCYCLE 5994 // ~6000us between sub-cycle starts
79+
#define SLT6_TIMING_PAIR 1633 // ~1633us between the two copies within a sub-cycle
80+
#define SLT6_TIMING_BUILD 1000 // Build+config time at start of each triple
81+
#define SLT6_TIMING_TRIPLE (3 * SLT6_TIMING_SUBCYCLE) // ~18ms triple period
82+
5783
static void __attribute__((unused)) SLT_RF_init()
5884
{
59-
NRF250K_Init();
85+
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K, option == 0); // SLT: option==0 uses NRF24L01, option!=0 uses CC2500 with freq tuning
6086
NRF250K_SetTXAddr(rx_tx_addr, SLT_TXID_SIZE);
6187
}
6288

@@ -193,17 +219,58 @@ static void __attribute__((unused)) SLT_build_packet()
193219
packet[i] = 0x00;
194220
}
195221

222+
// SLT6: build 7-byte data packet from current channel values
223+
static void __attribute__((unused)) SLT6_build_packet()
224+
{
225+
// aileron, elevator, throttle, rudder (10-bit, limited range)
226+
uint8_t e = 0;
227+
for (uint8_t i = 0; i < 4; ++i)
228+
{
229+
uint16_t v = convert_channel_16b_limit(CH_AETR[i], SLT6_CH_MIN, SLT6_CH_MAX);
230+
packet[i] = v;
231+
e = (e >> 2) | (uint8_t) ((v >> 2) & 0xC0);
232+
}
233+
packet[4] = e;
234+
235+
// Flight mode: 3 positions at ~33% each
236+
if(Channel_data[CH5] > CHANNEL_MID + SLT6_SW_THRESHOLD)
237+
packet[5] = 0xD0;
238+
else if(Channel_data[CH5] < CHANNEL_MID - SLT6_SW_THRESHOLD)
239+
packet[5] = 0x30;
240+
else
241+
packet[5] = 0x80;
242+
243+
// Panic: only active when CH6 is below -33%, center and up = no panic
244+
if(Channel_data[CH6] < CHANNEL_MID - SLT6_SW_THRESHOLD)
245+
packet[6] = 0x30;
246+
else
247+
packet[6] = 0xD0;
248+
}
249+
250+
// SLT6: configure radio for a sub-cycle (set address and channel)
251+
static void __attribute__((unused)) SLT6_configure_radio(uint8_t addr_xor, uint8_t hop_offset)
252+
{
253+
SLT_wait_radio();
254+
// Set TX address with XOR on byte[0]
255+
uint8_t addr[SLT_TXID_SIZE];
256+
memcpy(addr, rx_tx_addr, SLT_TXID_SIZE);
257+
addr[0] ^= addr_xor;
258+
NRF250K_SetTXAddr(addr, SLT_TXID_SIZE);
259+
// Set RF channel
260+
NRF250K_Hopping((hopping_frequency_no + hop_offset) % SLT_NFREQCHANNELS);
261+
}
262+
196263
static void __attribute__((unused)) SLT_send_bind_packet()
197264
{
198265
SLT_wait_radio();
199-
if(phase == SLT_BIND2)
200-
NRF250K_Hopping(SLT_NFREQCHANNELS); //Bind channel for BIND2 only
266+
if(phase == SLT_BIND2 || phase == SLT6_BIND)
267+
NRF250K_Hopping(SLT_NFREQCHANNELS); //Bind channel for BIND2 and SLT6 BIND
201268
BIND_IN_PROGRESS; //Limit TX power to bind level
202269
NRF250K_SetPower();
203270
BIND_DONE;
204271
NRF250K_SetTXAddr((uint8_t *)"\x7E\xB8\x63\xA9", SLT_TXID_SIZE);
205272
memcpy((void*)packet, (void*)rx_tx_addr, SLT_TXID_SIZE);
206-
if(phase == SLT_BIND2)
273+
if(phase == SLT_BIND2 || phase == SLT6_BIND)
207274
SLT_send_packet(SLT_TXID_SIZE);
208275
else // SLT_BIND1
209276
SLT_send_packet(packet_length);
@@ -222,8 +289,71 @@ static void __attribute__((unused)) SLT_send_bind_packet()
222289
#define SLT_Q100_TIMING_BIND1 3652
223290
#define SLT_Q100_TIMING_BIND2 1217
224291
#define SLT_MR100_TIMING_BIND2 1008
292+
293+
// SLT6 callback: triple-address state machine
294+
// Each triple: 3 sub-cycles (7B, 6B, 5B), each sent twice, with different addresses and channels
295+
static uint16_t __attribute__((unused)) SLT6_callback()
296+
{
297+
switch (phase)
298+
{
299+
case SLT6_BUILD_A:
300+
#ifdef MULTI_SYNC
301+
telemetry_set_input_sync(SLT6_TIMING_TRIPLE);
302+
#endif
303+
SLT6_build_packet();
304+
NRF250K_SetPower();
305+
SLT6_configure_radio(SLT6_ADDR_XOR_A, 0); // 7B sub-cycle: base address, hop+0
306+
phase = SLT6_DATA_A1;
307+
return SLT6_TIMING_BUILD;
308+
case SLT6_DATA_A1:
309+
SLT_send_packet(7);
310+
phase = SLT6_DATA_A2;
311+
return SLT6_TIMING_PAIR; // 1633us between copies
312+
case SLT6_DATA_A2:
313+
SLT_send_packet(7);
314+
SLT6_configure_radio(SLT6_ADDR_XOR_B, 3); // 6B sub-cycle: XOR 0x06 address, hop+3
315+
phase = SLT6_DATA_B1;
316+
return SLT6_TIMING_SUBCYCLE - SLT6_TIMING_PAIR; // 4361us to next sub-cycle TX
317+
case SLT6_DATA_B1:
318+
SLT_send_packet(6);
319+
phase = SLT6_DATA_B2;
320+
return SLT6_TIMING_PAIR;
321+
case SLT6_DATA_B2:
322+
SLT_send_packet(6);
323+
SLT6_configure_radio(SLT6_ADDR_XOR_C, 6); // 5B sub-cycle: XOR 0x09 address, hop+6
324+
phase = SLT6_DATA_C1;
325+
return SLT6_TIMING_SUBCYCLE - SLT6_TIMING_PAIR; // 4361us
326+
case SLT6_DATA_C1:
327+
SLT_send_packet(5);
328+
phase = SLT6_DATA_C2;
329+
return SLT6_TIMING_PAIR;
330+
case SLT6_DATA_C2:
331+
SLT_send_packet(5);
332+
// Advance hopping for next triple
333+
if (++hopping_frequency_no >= SLT_NFREQCHANNELS)
334+
hopping_frequency_no = 0;
335+
if (++packet_count >= 100)
336+
{// Send bind packet periodically
337+
packet_count = 0;
338+
phase = SLT6_BIND;
339+
return SLT_V1_TIMING_BIND2;
340+
}
341+
phase = SLT6_BUILD_A;
342+
return SLT6_TIMING_SUBCYCLE - SLT6_TIMING_PAIR - SLT6_TIMING_BUILD; // Gap before next build
343+
case SLT6_BIND:
344+
SLT_send_bind_packet();
345+
phase = SLT6_BUILD_A;
346+
return SLT6_TIMING_SUBCYCLE - SLT6_TIMING_PAIR - SLT6_TIMING_BUILD;
347+
}
348+
return SLT6_TIMING_TRIPLE;
349+
}
350+
225351
uint16_t SLT_callback()
226352
{
353+
// SLT6 has its own state machine
354+
if(sub_protocol == SLT6_Tx)
355+
return SLT6_callback();
356+
227357
switch (phase)
228358
{
229359
case SLT_BUILD:
@@ -323,7 +453,15 @@ void SLT_init()
323453
packet_sent = 0;
324454
hopping_frequency_no = 0;
325455

326-
if(sub_protocol == SLT_V1)
456+
if(sub_protocol == SLT6_Tx)
457+
{
458+
hopping_frequency_no = 1; // SLT6 starts hopping at index 1 (verified from captures)
459+
// packet_length not used for SLT6 (lengths vary per sub-cycle)
460+
#ifdef MULTI_SYNC
461+
packet_period = SLT6_TIMING_TRIPLE;
462+
#endif
463+
}
464+
else if(sub_protocol == SLT_V1)
327465
{
328466
packet_length = SLT_PAYLOADSIZE_V1;
329467
rf_ch_num = 1; //2 packets per frame
@@ -397,7 +535,10 @@ void SLT_init()
397535
SLT_RF_init();
398536
SLT_set_freq();
399537

400-
phase = SLT_BUILD;
538+
if(sub_protocol == SLT6_Tx)
539+
phase = SLT6_BUILD_A;
540+
else
541+
phase = SLT_BUILD;
401542
}
402543

403544
#endif

Multiprotocol/_Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,7 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
850850
MR100
851851
V1_4CH
852852
RF_SIM
853+
SLT6_Tx
853854
PROTO_SYMAX
854855
SYMAX
855856
SYMAX5C

Protocols_Details.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ CFlie|AIR|38|CFlie||||||||NRF24L01|
146146
[Shenqi](Protocols_Details.md#Shenqi---19)||19|Shenqi||||||||NRF24L01|LT8900
147147
[Shenqi2](Protocols_Details.md#Shenqi2---105)||105|Shenqi2||||||||NRF24L01|XN297
148148
[Skyartec](Protocols_Details.md#Skyartec---68)||68|||||||||CC2500|CC2500
149-
[SLT](Protocols_Details.md#SLT---11)||11|SLT_V1|SLT_V2|Q100|Q200|MR100|V1_4CH|RF_SIM||NRF24L01|CC2500
149+
[SLT](Protocols_Details.md#SLT---11)||11|SLT_V1*|SLT_V2*|Q100*|Q200*|MR100*|V1_4CH*|RF_SIM*|SLT6_Tx*|NRF24L01|CC2500
150150
[SymaX](Protocols_Details.md#Symax---10)||10|SYMAX|SYMAX5C|||||||NRF24L01|
151151
[Traxxas](Protocols_Details.md#Traxxas---43)||43|TQ2|TQ1|||||||CYRF6936|
152152
[V2x2](Protocols_Details.md#V2X2---5)||5|V2x2|JXD506|MR101||||||NRF24L01|
@@ -1407,6 +1407,8 @@ VTX+: -100%->+100% channel+
14071407

14081408
## SLT - *11*
14091409
Autobind protocol
1410+
* All SLT subprotococols are operating at a transmission of 250kbps which will originally default to the NRF24L01 module. This sometimes doesn't work well with every NRF24L01, as there are a few reports of hardware issues with the authenticity and accuracy of components.
1411+
* If option (freq tune) is changed from the default "0", then upon module restart the CC2500 module (if installed) will be used instead. Any value other than "0" will also [freq fine tune](/docs/Frequency_Tuning.md) the CC2500 chip.
14101412

14111413
### Sub_protocol V1 - *0*
14121414

@@ -1502,6 +1504,20 @@ Please save radio-profile with a new name without setting reset-button in RF8. T
15021504

15031505
Find the [Reset21] section and change Input=INT:-1 to Input=INT:9
15041506

1507+
### Sub_protocol SLT6_Tx - *7*
1508+
Models using SLT6 transmitter: Revolution 90 FP heli, and others.
1509+
* The complex triple layer output of this sub protocol requires the use of the NRF24L01 chip. Changing the freq tune to any value other than the default "0", upon module restart, will change to the CC2500 module (if installed) and cause intermittent, jerky servo movements.
1510+
1511+
CH1|CH2|CH3|CH4|CH5|CH6
1512+
---|---|---|---|---|---
1513+
A|E|T|R|FMODE|PANIC
1514+
1515+
FMODE: flight mode switch (3-positions available)
1516+
* Revolution 90 FP: (only 2-positions needed) -100% or 0%=stab with alt hold, +100%=stabilized mode
1517+
1518+
PANIC: panic/recovery button, -100%=down (on), +100%=up (off)
1519+
* Revolution 90 FP: not used for this model
1520+
15051521

15061522
## V911S - *46*
15071523

0 commit comments

Comments
 (0)