2024-02-15 15:51:20 +01:00
/*
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/>.
*/
2024-09-26 01:11:19 -07:00
// Compatible with SGF22, ParkTen F22S, KF700 J20
2024-02-15 15:51:20 +01:00
#if defined(SGF22_NRF24L01_INO)
#include "iface_xn297.h"
2024-02-19 18:34:27 +01:00
//#define FORCE_SGF22_ORIGINAL_ID
2025-07-28 11:20:57 +02:00
#define FORCE_SGF22_CX10_ORIGINAL_ID
2024-02-15 15:51:20 +01:00
2025-04-01 23:01:18 +02:00
#define SGF22_PACKET_PERIOD 11950 //10240
#define SGF22_PAYLOAD_SIZE 12
#define SGF22_BIND_COUNT 50
#define SGF22_RF_NUM_CHANNELS 4
2026-08-14 14:19:30 +02:00
2025-07-28 11:20:57 +02:00
#define SGF22_BIND_RF_CHANNEL 78
2025-04-01 23:01:18 +02:00
#define SGF22_F22S_BIND_RF_CHANNEL 10
#define SGF22_J20_BIND_RF_CHANNEL 28
2025-07-28 11:20:57 +02:00
#define SGF22_CX10_BIND_RF_CHANNEL 48
2026-08-14 14:19:30 +02:00
#define SGF22_T28_BIND_RF_CHANNEL 28
2024-02-15 15:51:20 +01:00
2024-02-16 10:24:44 +01:00
//packet[8]
2025-04-01 23:01:18 +02:00
#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
2024-09-26 01:11:19 -07:00
2025-01-25 17:49:22 +01:00
#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
2026-08-16 11:43:27 +02:00
#define SGF22_T28_RTH_SET 0x20
#define SGF22_T28_FLAG_OPTIMIZED 0x80
2024-09-26 01:11:19 -07:00
2024-02-16 10:24:44 +01:00
//packet[9]
2025-04-01 23:01:18 +02:00
#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 ,
};
2024-02-15 18:24:58 +01:00
2024-02-15 15:51:20 +01:00
static void __attribute__ (( unused )) SGF22_send_packet ()
{
if ( IS_BIND_IN_PROGRESS )
{
2024-02-15 18:24:58 +01:00
packet [ 0 ] = 0x5B ;
packet [ 8 ] = 0x00 ; // ??? do they have to be 0 for bind to succeed ?
packet [ 9 ] = 0x00 ; // ??? do they have to be 0 for bind to succeed ?
2024-02-15 15:51:20 +01:00
packet [ 10 ] = 0xAA ;
packet [ 11 ] = 0x55 ;
}
else
{
//hop
2024-02-15 18:24:58 +01:00
XN297_Hopping ( packet_sent & 0x03 ); // ??? from the dumps I can't really say how hop and seq are sync, there could be an offset (0,1,2,3)...
2024-02-21 11:41:23 +01:00
//sequence from 02 to 7A by increments of 4, sometimes with a flag 0x80 from 82 to FA, I can't tell from the dumps when the switch happens
2024-02-15 15:51:20 +01:00
if ( ( packet_sent & 0x03 ) == 0x02 )
packet_count = packet_sent ;
packet_sent ++ ;
if ( packet_sent > 0x7B )
packet_sent = 0 ;
//packet
packet [ 0 ] = 0x1B ;
2025-07-28 11:20:57 +02:00
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 )
2026-08-16 11:30:22 +02:00
| GET_FLAG ( CH12_SW , SGF22_FX922_FLAG_BALANCEHIGH )
2026-08-16 11:43:27 +02:00
| GET_FLAG ( CH13_SW , SGF22_T28_RTH_SET );
2025-07-28 11:20:57 +02:00
if ( Channel_data [ CH5 ] > CHANNEL_MAX_COMMAND )
2026-08-16 11:30:22 +02:00
packet [ 8 ] |= ( sub_protocol == SGF22_T28 ? SGF22_T28_FLAG_OPTIMIZED : SGF22_FLAG_VERTICAL ); // CH5 100%, vertical mode (torque)
2025-07-28 11:20:57 +02:00
else if ( Channel_data [ CH5 ] > CHANNEL_MIN_COMMAND )
2026-08-16 11:30:22 +02:00
packet [ 8 ] |= ( sub_protocol == SGF22_J20 ? SGF22_J20_FLAG_HORIZONTAL : SGF22_FLAG_6G ); // CH5 0%, F22 & F22S - 6G mode, J20 - Horizontal mode
2025-07-28 11:20:57 +02:00
}
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
2025-01-25 11:31:21 +01:00
| 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
2025-07-28 11:20:57 +02:00
packet [ 10 ] = 0x42 ; // no fine tune
packet [ 11 ] = 0x10 ; // no fine tune
2024-02-15 15:51:20 +01:00
}
2026-08-14 14:19:30 +02:00
if ( sub_protocol == SGF22_F22S || sub_protocol == SGF22_T28 )
2024-09-02 18:14:25 +02:00
packet [ 0 ] += 6 ;
2024-09-26 01:11:19 -07:00
else if ( sub_protocol == SGF22_J20 )
packet [ 0 ] += 3 ;
2025-07-28 11:20:57 +02:00
else if ( sub_protocol == SGF22_CX10 )
packet [ 0 ] += 0x6A ;
packet [ 1 ] = packet_count ; // sequence
2024-02-15 15:51:20 +01:00
packet [ 2 ] = rx_tx_addr [ 2 ];
packet [ 3 ] = rx_tx_addr [ 3 ];
packet [ 4 ] = convert_channel_8b ( THROTTLE );
packet [ 5 ] = convert_channel_8b ( RUDDER );
packet [ 6 ] = convert_channel_8b ( ELEVATOR );
packet [ 7 ] = convert_channel_8b ( AILERON );
XN297_SetPower ();
XN297_SetTxRxMode ( TX_EN );
2025-07-28 11:20:57 +02:00
if ( sub_protocol != SGF22_CX10 )
XN297_WriteEnhancedPayload ( packet , SGF22_PAYLOAD_SIZE , 0 );
else
XN297_WritePayload ( packet , SGF22_PAYLOAD_SIZE );
2024-02-15 15:51:20 +01:00
#if 0
debug_time("");
for(uint8_t i=0; i<SGF22_PAYLOAD_SIZE; i++)
debug(" %02X",packet[i]);
debugln("");
#endif
}
static void __attribute__ (( unused )) SGF22_initialize_txid ()
{
2024-04-24 07:00:44 +02:00
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 ;
2024-02-19 18:34:27 +01:00
const uint8_t hop [ 5 ][ 4 ] =
{ { 0x0C , 0x2A , 0x1B , 0x39 },
{ 0x0F , 0x2D , 0x1E , 0x3D },
{ 0x12 , 0x31 , 0x21 , 0x41 },
{ 0x15 , 0x34 , 0x24 , 0x44 },
{ 0x18 , 0x37 , 0x27 , 0x47 } };
memcpy ( hopping_frequency , & hop [ val ], SGF22_RF_NUM_CHANNELS );
2024-03-21 16:33:16 +01:00
2025-07-28 11:20:57 +02:00
/*//Same code size...
2024-02-19 18:34:27 +01:00
hopping_frequency[0] = 0x0C + 3 * val;
hopping_frequency[1] = hopping_frequency[0] + 0x1E;
if(val > 1) hopping_frequency[1]++;
hopping_frequency[2] = hopping_frequency[0] + 0x0F;
hopping_frequency[3] = hopping_frequency[1] + 0x0F;
if(val ) hopping_frequency[3]++;*/
2024-02-15 15:51:20 +01:00
#ifdef FORCE_SGF22_ORIGINAL_ID
2024-03-29 16:19:20 +01:00
rx_tx_addr [ 2 ] = 0x1F ; // TX2:27 TX3:2B
rx_tx_addr [ 3 ] = 0x61 ; // TX2:51 TX3:0C
2024-02-15 15:51:20 +01:00
memcpy ( hopping_frequency , " \x15\x34\x24\x44 " , SGF22_RF_NUM_CHANNELS ); //Original dump=>21=0x15,52=0x34,36=0x24,68=0x44
#endif
2025-07-28 11:20:57 +02:00
#ifdef FORCE_SGF22_CX10_ORIGINAL_ID
2025-09-26 17:41:06 +02:00
if ( sub_protocol == SGF22_CX10 )
2025-07-28 11:20:57 +02:00
{
2025-09-26 17:41:06 +02:00
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 );
}
2025-07-28 11:20:57 +02:00
}
#endif
2024-02-19 18:34:27 +01:00
#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++)
debug(" %02X",hopping_frequency[i]);
debugln("");
#endif
2024-02-15 15:51:20 +01:00
}
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 );
2025-04-01 23:01:18 +02:00
#ifdef SGF22_HUB_TELEMETRY
XN297_SetRXAddr (( uint8_t * ) " \xC7\x95\x3C\xBB\xA5 " , SGF22_PAYLOAD_SIZE );
#endif
2026-08-14 14:19:30 +02:00
const uint8_t bind_chan [] = { SGF22_BIND_RF_CHANNEL , SGF22_F22S_BIND_RF_CHANNEL , SGF22_J20_BIND_RF_CHANNEL , SGF22_CX10_BIND_RF_CHANNEL , SGF22_T28_BIND_RF_CHANNEL };
2024-09-26 01:11:19 -07:00
XN297_RFChannel ( bind_chan [ sub_protocol ]); // Set bind channel
2024-02-15 15:51:20 +01:00
}
uint16_t SGF22_callback ()
{
2025-04-01 23:01:18 +02:00
#ifdef SGF22_HUB_TELEMETRY
bool rx = false ;
2025-04-16 07:53:47 +02:00
static uint8_t telem_count = 0 ;
2025-04-01 23:01:18 +02:00
#endif
switch ( phase )
2024-02-15 15:51:20 +01:00
{
2025-04-01 23:01:18 +02:00
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 ])
2025-04-02 14:24:18 +02:00
{ //packets: 00 0B 00 -> 00 0B 01
2025-04-01 23:01:18 +02:00
telemetry_link = 1 ;
2025-04-02 14:24:18 +02:00
v_lipo1 = packet_in [ 2 ] ? 0 : 255 ; //2.9V for 1S, 7.0V for 2S
2025-04-16 07:53:47 +02:00
telemetry_lost = 0 ;
telem_count = 0 ;
2025-04-01 23:01:18 +02:00
}
#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
}
2025-04-16 07:53:47 +02:00
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
}
}
2025-04-01 23:01:18 +02:00
#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 ;
2024-02-15 15:51:20 +01:00
}
return 1550 ;
}
void SGF22_init ()
{
BIND_IN_PROGRESS ; // autobind protocol
SGF22_initialize_txid ();
SGF22_RF_init ();
bind_counter = SGF22_BIND_COUNT ;
2024-03-29 16:19:20 +01:00
packet_sent = packet_count = 0x26 ; // TX2:26 TX3:26
2025-04-01 23:01:18 +02:00
phase = SGF22_DATA1 ;
#ifdef SGF22_HUB_TELEMETRY
RX_RSSI = 100 ; // Dummy value
2025-04-16 07:53:47 +02:00
telemetry_lost = 1 ;
2025-04-01 23:01:18 +02:00
#endif
2024-02-15 15:51:20 +01:00
}
#endif