From 0a7f74dc064b4519567739dd6784d2e5a0fa5cfe Mon Sep 17 00:00:00 2001 From: pascallanger Date: Mon, 3 Aug 2026 23:51:35 +0200 Subject: [PATCH] SLT/SLT6X new subprotocol --- Multiprotocol/Multi.txt | 2 +- Multiprotocol/Multi_Protos.ino | 4 +- Multiprotocol/Multiprotocol.h | 4 +- Multiprotocol/NRF250K_EMU.ino | 37 ++++++-- Multiprotocol/SLT_ccnrf.ino | 151 +++++++++++++++++++++++++++++++-- Multiprotocol/_Config.h | 1 + Multiprotocol/iface_nrf250k.h | 2 +- Protocols_Details.md | 12 ++- 8 files changed, 193 insertions(+), 20 deletions(-) diff --git a/Multiprotocol/Multi.txt b/Multiprotocol/Multi.txt index c9998485..e7aff0ea 100644 --- a/Multiprotocol/Multi.txt +++ b/Multiprotocol/Multi.txt @@ -8,7 +8,7 @@ 8,YD717,YD717,SKYWLKR,SYMAX4,XINXUN,NIHUI 9,KN,WLTOYS,FEILUN 10,SymaX,SYMAX,SYMAX5C -11,SLT,SLT_V1,SLT_V2,Q100,Q200,MR100,V1_4CH,RF_SIM +11,SLT,SLT_V1,SLT_V2,Q100,Q200,MR100,V1_4CH,RF_SIM,SLT6TX 12,CX10,GREEN,BLUE,DM007,---,J3015_1,J3015_2,MK33041 13,CG023,CG023,YD829 14,Bayang,Bayang,H8S3D,X16_AH,IRDRONE,DHD_D4,QX100 diff --git a/Multiprotocol/Multi_Protos.ino b/Multiprotocol/Multi_Protos.ino index 5a67422b..b2d41fe1 100644 --- a/Multiprotocol/Multi_Protos.ino +++ b/Multiprotocol/Multi_Protos.ino @@ -139,7 +139,7 @@ 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""V1_4ch""RF_SIM"; +const char STR_SUBTYPE_SLT[] = "\x06""V1_6ch""V2_8ch""Q100\0 ""Q200\0 ""MR100\0""V1_4ch""RF_SIM""SLT6TX"; 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 "; @@ -488,7 +488,7 @@ const mm_protocol_definition multi_protocols[] = { {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, 7, OPTION_RFTUNE, 0, 1, SW_NRF, SLT_init, SLT_callback }, + {PROTO_SLT, STR_SLT, STR_SUBTYPE_SLT, 8, 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 }, diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 455e7cf6..44dfe356 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 4 -#define VERSION_PATCH_LEVEL 61 +#define VERSION_PATCH_LEVEL 62 #define MODE_SERIAL 0 @@ -216,6 +216,7 @@ enum SLT MR100 = 4, SLT_V1_4 = 5, RF_SIM = 6, + SLT6TX = 7, }; enum CX10 { @@ -1149,6 +1150,7 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p -- MR100 4 SLT_V1_4CH 5 RF_SIM 6 + SLT6TX 7 sub_protocol==E01X E012 0 E015 1 diff --git a/Multiprotocol/NRF250K_EMU.ino b/Multiprotocol/NRF250K_EMU.ino index 44da9b8c..ea31dd2e 100644 --- a/Multiprotocol/NRF250K_EMU.ino +++ b/Multiprotocol/NRF250K_EMU.ino @@ -16,13 +16,25 @@ #include "iface_nrf250k.h" +#if defined(CC2500_INSTALLED) && defined(NRF24L01_INSTALLED) + extern bool xn297_rf; +#endif + uint8_t cc2500_nrf_tx_addr[5], cc2500_nrf_addr_len; static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len) { if (len > 5) len = 5; if (len < 3) len = 3; - #if defined(CC2500_INSTALLED) + #if defined(CC2500_INSTALLED) && defined(NRF24L01_INSTALLED) + cc2500_nrf_addr_len = len; + memcpy(cc2500_nrf_tx_addr, addr, len); + if(xn297_rf == XN297_NRF) + { + NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2); + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, addr, len); + } + #elif defined(CC2500_INSTALLED) cc2500_nrf_addr_len = len; memcpy(cc2500_nrf_tx_addr, addr, len); #elif defined(NRF24L01_INSTALLED) @@ -33,6 +45,22 @@ static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t len) { + #if defined(NRF24L01_INSTALLED) + #if defined(CC2500_INSTALLED) + if(xn297_rf == XN297_NRF) + #endif + { + if(len<=32) + { + NRF24L01_FlushTx(); + NRF24L01_WriteReg(NRF24L01_07_STATUS, _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_MAX_RT)); + NRF24L01_WritePayload(msg, len); + } + #if defined(CC2500_INSTALLED) + return; + #endif + } + #endif #if defined(CC2500_INSTALLED) #if defined(ESKY150V2_CC2500_INO) uint8_t buf[158]; @@ -106,13 +134,6 @@ static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t l CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, last); CC2500_Strobe(CC2500_STX); } - #elif defined(NRF24L01_INSTALLED) - if(len<=32) - { - NRF24L01_FlushTx(); - NRF24L01_WriteReg(NRF24L01_07_STATUS, _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_MAX_RT)); - NRF24L01_WritePayload(msg, len); - } #endif } diff --git a/Multiprotocol/SLT_ccnrf.ino b/Multiprotocol/SLT_ccnrf.ino index 3e1d37ee..19cb5f23 100644 --- a/Multiprotocol/SLT_ccnrf.ino +++ b/Multiprotocol/SLT_ccnrf.ino @@ -28,6 +28,9 @@ #define SLT_NFREQCHANNELS 15 #define SLT_TXID_SIZE 4 #define SLT_BIND_CHANNEL 0x50 +#define SLT6_CH_MIN 182 // 10-bit AETR minimum (captures: 180-185, symmetric around 512) +#define SLT6_CH_MAX 842 // 10-bit AETR maximum (captures: 829-843, symmetric around 512) +#define SLT6_SW_THRESHOLD 273 // ~33% of half-range (820/3) for 3-position switch zones enum{ // flags going to packet[6] (Q200) @@ -54,9 +57,32 @@ enum { SLT_BIND2, }; +// SLT6 sub-cycle states: 3 sub-cycles per triple, each with 2 copies +enum { + SLT6_BUILD_A=0, // Build packet, configure for 7B sub-cycle + SLT6_DATA_A1, // Send 7B copy 1 + SLT6_DATA_A2, // Send 7B copy 2, configure for 6B sub-cycle + SLT6_DATA_B1, // Send 6B copy 1 + SLT6_DATA_B2, // Send 6B copy 2, configure for 5B sub-cycle + SLT6_DATA_C1, // Send 5B copy 1 + SLT6_DATA_C2, // Send 5B copy 2 + SLT6_BIND, // Send bind packet +}; + +// SLT6 address XOR values for the 3 sub-cycles +#define SLT6_ADDR_XOR_A 0x00 // 7B sub-cycle: base address +#define SLT6_ADDR_XOR_B 0x06 // 6B sub-cycle: byte[0] XOR 0x06 +#define SLT6_ADDR_XOR_C 0x09 // 5B sub-cycle: byte[0] XOR 0x09 + +// SLT6 timing (from capture 12b, in microseconds) +#define SLT6_TIMING_SUBCYCLE 5994 // ~6000us between sub-cycle starts +#define SLT6_TIMING_PAIR 1633 // ~1633us between the two copies within a sub-cycle +#define SLT6_TIMING_BUILD 1000 // Build+config time at start of each triple +#define SLT6_TIMING_TRIPLE (3 * SLT6_TIMING_SUBCYCLE) // ~18ms triple period + static void __attribute__((unused)) SLT_RF_init() { - NRF250K_Init(); + NRF250K_Init(option == 0); // SLT: option==0 uses NRF24L01, option!=0 uses CC2500 with freq tuning NRF250K_SetTXAddr(rx_tx_addr, SLT_TXID_SIZE); } @@ -152,8 +178,6 @@ static void __attribute__((unused)) SLT_build_packet() packet[5] = convert_channel_8b(CH5); packet[6] = convert_channel_8b(CH6); - //->V1 stops here - if(sub_protocol == Q200) packet[6] = GET_FLAG(CH9_SW , FLAG_Q200_FMODE) |GET_FLAG(CH10_SW, FLAG_Q200_FLIP) @@ -188,6 +212,47 @@ static void __attribute__((unused)) SLT_build_packet() calib_counter = 0; } +// SLT6: build 7-byte data packet from current channel values +static void __attribute__((unused)) SLT6_build_packet() +{ + // aileron, elevator, throttle, rudder (10-bit, limited range) + uint8_t e = 0; + for (uint8_t i = 0; i < 4; ++i) + { + uint16_t v = convert_channel_16b_limit(CH_AETR[i], SLT6_CH_MIN, SLT6_CH_MAX); + packet[i] = v; + e = (e >> 2) | (uint8_t) ((v >> 2) & 0xC0); + } + packet[4] = e; + + // Flight mode: 3 positions at ~33% each + if(Channel_data[CH5] > CHANNEL_MID + SLT6_SW_THRESHOLD) + packet[5] = 0xD0; + else if(Channel_data[CH5] < CHANNEL_MID - SLT6_SW_THRESHOLD) + packet[5] = 0x30; + else + packet[5] = 0x80; + + // Panic: only active when CH6 is below -33%, center and up = no panic + if(Channel_data[CH6] < CHANNEL_MID - SLT6_SW_THRESHOLD) + packet[6] = 0x30; + else + packet[6] = 0xD0; +} + +// SLT6: configure radio for a sub-cycle (set address and channel) +static void __attribute__((unused)) SLT6_configure_radio(uint8_t addr_xor, uint8_t hop_offset) +{ + SLT_wait_radio(); + // Set TX address with XOR on byte[0] + uint8_t addr[SLT_TXID_SIZE]; + memcpy(addr, rx_tx_addr, SLT_TXID_SIZE); + addr[0] ^= addr_xor; + NRF250K_SetTXAddr(addr, SLT_TXID_SIZE); + // Set RF channel + NRF250K_Hopping((hopping_frequency_no + hop_offset) % SLT_NFREQCHANNELS); +} + static void __attribute__((unused)) SLT_send_bind_packet() { SLT_wait_radio(); @@ -197,7 +262,7 @@ static void __attribute__((unused)) SLT_send_bind_packet() 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) + if(phase == SLT_BIND2 || phase == SLT6_BIND) SLT_send_packet(SLT_TXID_SIZE); else // SLT_BIND1 SLT_send_packet(SLT_PAYLOADSIZE_V2); @@ -210,8 +275,71 @@ static void __attribute__((unused)) SLT_send_bind_packet() #define SLT_V1_TIMING_BIND2 1000 #define SLT_V2_TIMING_BIND1 6507 #define SLT_V2_TIMING_BIND2 2112 + +// SLT6 callback: triple-address state machine +// Each triple: 3 sub-cycles (7B, 6B, 5B), each sent twice, with different addresses and channels +static uint16_t __attribute__((unused)) SLT6_callback() +{ + switch (phase) + { + case SLT6_BUILD_A: + #ifdef MULTI_SYNC + telemetry_set_input_sync(SLT6_TIMING_TRIPLE); + #endif + SLT6_build_packet(); + NRF250K_SetPower(); + SLT6_configure_radio(SLT6_ADDR_XOR_A, 0); // 7B sub-cycle: base address, hop+0 + phase = SLT6_DATA_A1; + return SLT6_TIMING_BUILD; + case SLT6_DATA_A1: + SLT_send_packet(7); + phase = SLT6_DATA_A2; + return SLT6_TIMING_PAIR; // 1633us between copies + case SLT6_DATA_A2: + SLT_send_packet(7); + SLT6_configure_radio(SLT6_ADDR_XOR_B, 3); // 6B sub-cycle: XOR 0x06 address, hop+3 + phase = SLT6_DATA_B1; + return SLT6_TIMING_SUBCYCLE - SLT6_TIMING_PAIR; // 4361us to next sub-cycle TX + case SLT6_DATA_B1: + SLT_send_packet(6); + phase = SLT6_DATA_B2; + return SLT6_TIMING_PAIR; + case SLT6_DATA_B2: + SLT_send_packet(6); + SLT6_configure_radio(SLT6_ADDR_XOR_C, 6); // 5B sub-cycle: XOR 0x09 address, hop+6 + phase = SLT6_DATA_C1; + return SLT6_TIMING_SUBCYCLE - SLT6_TIMING_PAIR; // 4361us + case SLT6_DATA_C1: + SLT_send_packet(5); + phase = SLT6_DATA_C2; + return SLT6_TIMING_PAIR; + case SLT6_DATA_C2: + SLT_send_packet(5); + // Advance hopping for next triple + if (++hopping_frequency_no >= SLT_NFREQCHANNELS) + hopping_frequency_no = 0; + if (++packet_count >= 100) + {// Send bind packet periodically + packet_count = 0; + phase = SLT6_BIND; + return SLT_V1_TIMING_BIND2; + } + phase = SLT6_BUILD_A; + return SLT6_TIMING_SUBCYCLE - SLT6_TIMING_PAIR - SLT6_TIMING_BUILD; // Gap before next build + case SLT6_BIND: + SLT_send_bind_packet(); + phase = SLT6_BUILD_A; + return SLT6_TIMING_SUBCYCLE - SLT6_TIMING_PAIR - SLT6_TIMING_BUILD; + } + return SLT6_TIMING_TRIPLE; +} + uint16_t SLT_callback() { + // SLT6 has its own state machine + if(sub_protocol == SLT6TX) + return SLT6_callback(); + switch (phase) { case SLT_BUILD: @@ -285,7 +413,15 @@ void SLT_init() packet_sent = 0; hopping_frequency_no = 0; - if(sub_protocol == SLT_V1) + if(sub_protocol == SLT6TX) + { + hopping_frequency_no = 1; // SLT6 starts hopping at index 1 (verified from captures) + // packet_length not used for SLT6 (lengths vary per sub-cycle) + #ifdef MULTI_SYNC + packet_period = SLT6_TIMING_TRIPLE; + #endif + } + else if(sub_protocol == SLT_V1) { packet_length = SLT_PAYLOADSIZE_V1; #ifdef MULTI_SYNC @@ -326,7 +462,10 @@ void SLT_init() SLT_RF_init(); SLT_set_freq(); - phase = SLT_BUILD; + if(sub_protocol == SLT6TX) + phase = SLT6_BUILD_A; + else + phase = SLT_BUILD; } #endif diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index d8da1f1d..fd7f6508 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -858,6 +858,7 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= { MR100 V1_4CH RF_SIM + SLT6TX PROTO_SYMAX SYMAX SYMAX5C diff --git a/Multiprotocol/iface_nrf250k.h b/Multiprotocol/iface_nrf250k.h index 15d0f8bf..71ec966b 100644 --- a/Multiprotocol/iface_nrf250k.h +++ b/Multiprotocol/iface_nrf250k.h @@ -13,7 +13,7 @@ ////////////// // Functions -#define NRF250K_Init() XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K) +#define NRF250K_Init(X) XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K, X) #define NRF250K_HoppingCalib(X) XN297_HoppingCalib(X) #define NRF250K_Hopping(X) XN297_Hopping(X) #define NRF250K_RFChannel(X) XN297_RFChannel(X) diff --git a/Protocols_Details.md b/Protocols_Details.md index 0faa8510..72f84423 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -146,7 +146,7 @@ CFlie|AIR|38|CFlie||||||||NRF24L01| [Shenqi](Protocols_Details.md#Shenqi---19)||19|Shenqi||||||||NRF24L01|LT8900 [Shenqi2](Protocols_Details.md#Shenqi2---105)||105|Shenqi2||||||||NRF24L01|XN297 [Skyartec](Protocols_Details.md#Skyartec---68)||68|||||||||CC2500|CC2500 -[SLT](Protocols_Details.md#SLT---11)||11|SLT_V1|SLT_V2|Q100|Q200|MR100|V1_4CH|RF_SIM||NRF24L01|CC2500 +[SLT](Protocols_Details.md#SLT---11)||11|SLT_V1|SLT_V2|Q100|Q200|MR100|V1_4CH|RF_SIM|SLT6TX|NRF24L01|CC2500 [SymaX](Protocols_Details.md#Symax---10)||10|SYMAX|SYMAX5C|||||||NRF24L01| [Traxxas](Protocols_Details.md#Traxxas---43)||43|TQ2|TQ1|||||||CYRF6936| [V2x2](Protocols_Details.md#V2X2---5)||5|V2x2|JXD506|MR101||||||NRF24L01| @@ -1532,6 +1532,16 @@ Please save radio-profile with a new name without setting reset-button in RF8. T Find the [Reset21] section and change Input=INT:-1 to Input=INT:9 +### Sub_protocol SLT6TX - *7* +Models: Blade Revolution 90 FP helicopter (SLT6 transmitter) + +CH1|CH2|CH3|CH4|CH5|CH6 +---|---|---|---|---|--- +A|E|T|R|FMODE|PANIC + +FMODE: flight mode switch (3-position) + +PANIC: panic/recovery button ## V911S - *46*