2016-09-09 18:34:20 +02: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/>.
*/
#if defined(HONTAI_NRF24L01_INO)
2021-03-17 17:05:42 +01:00
#include "iface_xn297.h" // mix of nrf and xn297 at 1Mb...
2016-09-09 18:34:20 +02:00
2025-01-30 23:14:15 +01:00
#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
2016-09-09 18:34:20 +02:00
enum {
2025-01-30 23:14:15 +01:00
HONTAI_FLAG_FLIP = 0x01 ,
HONTAI_FLAG_PICTURE = 0x02 ,
HONTAI_FLAG_VIDEO = 0x04 ,
HONTAI_FLAG_HEADLESS = 0x08 ,
HONTAI_FLAG_RTH = 0x10 ,
HONTAI_FLAG_CALIBRATE = 0x20 ,
2016-09-09 18:34:20 +02:00
};
2021-02-11 10:54:55 +01:00
static void __attribute__ (( unused )) HONTAI_send_packet ()
2016-09-09 18:34:20 +02:00
{
2021-02-11 10:54:55 +01:00
if ( IS_BIND_IN_PROGRESS )
2016-09-09 18:34:20 +02:00
{
memcpy ( packet , rx_tx_addr , 5 );
memset ( & packet [ 5 ], 0 , 3 );
2025-01-30 23:14:15 +01:00
//if(sub_protocol == HONTAI_XKK170)
// packet[6] = 0xD2;
2021-02-11 10:54:55 +01:00
packet_length = HONTAI_BIND_PACKET_SIZE ;
2016-09-09 18:34:20 +02:00
}
else
{
2021-03-17 17:05:42 +01:00
/*if(sub_protocol == JJRCX1)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
else*/
XN297_Hopping ( hopping_frequency_no ++ );
hopping_frequency_no %= 3 ;
2016-12-12 18:07:38 +01:00
memset ( packet , 0 , HONTAI_PACKET_SIZE );
2025-01-30 23:14:15 +01:00
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
}
2016-09-10 11:28:53 +08:00
else
2025-01-30 23:14:15 +01:00
{ //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
2026-07-14 19:26:55 +02:00
packet [ 1 ] = GET_FLAG ( CH8_SW , 0x04 ) //Gyro calibration (momentary)
| GET_FLAG ( CH11_SW , 0x10 ); //XK K270 optical flow switch
2025-01-30 23:14:15 +01:00
// |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
}
2016-12-12 18:07:38 +01:00
switch ( sub_protocol )
{
2018-01-30 12:02:45 +01:00
case HONTAI :
2016-12-12 18:07:38 +01:00
packet [ 0 ] = 0x0B ;
2018-01-08 19:37:14 +01:00
packet [ 3 ] |= GET_FLAG ( CH7_SW , 0x01 ); // Picture
2025-01-30 23:14:15 +01:00
packet [ 4 ] |= GET_FLAG ( CH10_SW , 0x80 ) // RTH
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH9_SW , 0x40 ); // Headless
2025-01-30 23:14:15 +01:00
packet [ 5 ] |= GET_FLAG ( CH11_SW , 0x80 ) // Calibrate
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH5_SW , 0x40 ); // Flip
packet [ 6 ] |= GET_FLAG ( CH8_SW , 0x80 ); // Video
2016-12-12 18:07:38 +01:00
break ;
2018-01-30 12:02:45 +01:00
case JJRCX1 :
2018-01-08 19:37:14 +01:00
packet [ 0 ] = GET_FLAG ( CH6_SW , 0x02 ); // Arm
packet [ 3 ] |= GET_FLAG ( CH7_SW , 0x01 ); // Picture
2025-01-30 23:14:15 +01:00
packet [ 4 ] |= 0x80 ; // unknown
packet [ 5 ] |= GET_FLAG ( CH11_SW , 0x80 ) // Calibrate
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH5_SW , 0x40 ); // Flip
packet [ 6 ] |= GET_FLAG ( CH8_SW , 0x80 ); // Video
2025-01-30 23:14:15 +01:00
packet [ 8 ] = 0xC0 // high rate, no rudder trim
| GET_FLAG ( CH10_SW , 0x02 ) // RTH
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH9_SW , 0x01 ); // Headless
2016-12-12 18:07:38 +01:00
break ;
2018-01-30 12:02:45 +01:00
case X5C1 :
2016-12-12 18:07:38 +01:00
packet [ 0 ] = 0x0B ;
2018-01-08 19:37:14 +01:00
packet [ 3 ] |= GET_FLAG ( CH7_SW , 0x01 ); // Picture
2025-01-30 23:14:15 +01:00
packet [ 4 ] = 0x80 // unknown
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH6_SW , 0x40 ); // Lights
2025-01-30 23:14:15 +01:00
packet [ 5 ] |= GET_FLAG ( CH11_SW , 0x80 ) // Calibrate
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH5_SW , 0x40 ); // Flip
packet [ 6 ] |= GET_FLAG ( CH8_SW , 0x80 ); // Video
2025-01-30 23:14:15 +01:00
packet [ 7 ] <<= 1 ; // Aileron trim
packet [ 8 ] = 0xC0 // high rate, no rudder trim
| GET_FLAG ( CH10_SW , 0x02 ) // RTH
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH9_SW , 0x01 ); // Headless
2025-01-30 23:14:15 +01:00
packet [ 9 ] <<= 1 ; // Elevator trim
2016-12-12 18:07:38 +01:00
break ;
2018-01-30 12:02:45 +01:00
case FQ777_951 :
2018-01-08 19:37:14 +01:00
packet [ 0 ] = GET_FLAG ( CH7_SW , 0x01 ) // Picture
| GET_FLAG ( CH8_SW , 0x02 ); // Video
packet [ 3 ] |= GET_FLAG ( CH5_SW , 0x01 ); // Flip
2025-01-30 23:14:15 +01:00
packet [ 4 ] |= 0xC0 ; // High rate (mid=0xa0, low=0x60)
2018-01-08 19:37:14 +01:00
packet [ 5 ] |= GET_FLAG ( CH11_SW , 0x80 ); // Calibrate
packet [ 6 ] |= GET_FLAG ( CH9_SW , 0x40 ); // Headless
2016-12-12 18:07:38 +01:00
break ;
}
2021-02-11 10:54:55 +01:00
packet_length = HONTAI_PACKET_SIZE ;
2016-09-09 18:34:20 +02:00
}
2021-02-11 10:54:55 +01:00
2025-01-30 23:14:15 +01:00
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 );
2016-09-09 18:34:20 +02:00
// Power on, TX mode, 2byte CRC
2021-03-17 17:05:42 +01:00
/*if(sub_protocol == JJRCX1)
{
NRF24L01_SetPower();
2016-09-09 18:34:20 +02:00
NRF24L01_SetTxRxMode(TX_EN);
2021-03-17 17:05:42 +01:00
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
}
else*/
{
XN297_SetPower ();
XN297_SetTxRxMode ( TX_EN );
}
2016-09-09 18:34:20 +02:00
2018-01-30 12:02:45 +01:00
if ( sub_protocol == JJRCX1 )
2021-02-11 10:54:55 +01:00
NRF24L01_WritePayload ( packet , packet_length );
2016-09-09 18:34:20 +02:00
else
2021-02-11 10:54:55 +01:00
XN297_WritePayload ( packet , packet_length );
2025-01-30 23:14:15 +01:00
#ifdef DEBUG_SERIAL
for ( uint8_t i = 0 ; i < packet_length ; i ++ )
debug ( "%02X " , packet [ i ]);
debugln ();
#endif
2016-09-09 18:34:20 +02:00
}
2021-02-09 18:23:33 +01:00
static void __attribute__ (( unused )) HONTAI_RF_init ()
2016-09-09 18:34:20 +02:00
{
2021-03-17 17:05:42 +01:00
XN297_Configure ( XN297_CRCEN , XN297_SCRAMBLED , XN297_1M ); // this will select the nrf and initialize it, therefore both sub protocols can use common instructions
2018-01-30 12:02:45 +01:00
if ( sub_protocol == JJRCX1 )
2016-09-09 18:34:20 +02:00
{
2021-03-17 17:05:42 +01:00
//NRF24L01_Initialize();
NRF24L01_WriteRegisterMulti ( NRF24L01_10_TX_ADDR , ( uint8_t * ) " \xd2\xb5\x99\xb3\x4a " , 5 );
2016-09-09 18:34:20 +02:00
NRF24L01_WriteReg ( NRF24L01_04_SETUP_RETR , 0xff ); // JJRC uses dynamic payload length
NRF24L01_WriteReg ( NRF24L01_1C_DYNPD , 0x3f ); // match other stock settings even though AA disabled...
NRF24L01_WriteReg ( NRF24L01_1D_FEATURE , 0x07 );
2021-03-17 17:05:42 +01:00
//NRF24L01_WriteReg(NRF24L01_05_RF_CH, HONTAI_RF_BIND_CHANNEL);
2016-09-09 18:34:20 +02:00
}
2021-03-17 17:05:42 +01:00
else
{
2025-01-30 23:14:15 +01:00
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 );
2021-03-17 17:05:42 +01:00
//XN297_HoppingCalib(3);
}
2025-01-30 23:14:15 +01:00
XN297_RFChannel ( sub_protocol == HONTAI_XKK170 ? HONTAI_XKK170_RF_BIND_CHANNEL : HONTAI_RF_BIND_CHANNEL );
2016-09-09 18:34:20 +02:00
}
2025-01-30 23:14:15 +01:00
const uint8_t PROGMEM HONTAI_hopping_frequency [ 3 ] = { 0x05 , 0x19 , 0x28 };
2016-09-09 18:34:20 +02:00
2016-12-12 18:07:38 +01:00
const uint8_t PROGMEM HONTAI_addr_vals [ 4 ][ 16 ] = {
2016-09-09 18:34:20 +02:00
{ 0x24 , 0x26 , 0x2a , 0x2c , 0x32 , 0x34 , 0x36 , 0x4a , 0x4c , 0x4e , 0x54 , 0x56 , 0x5a , 0x64 , 0x66 , 0x6a },
{ 0x92 , 0x94 , 0x96 , 0x9a , 0xa4 , 0xa6 , 0xac , 0xb2 , 0xb4 , 0xb6 , 0xca , 0xcc , 0xd2 , 0xd4 , 0xd6 , 0xda },
{ 0x93 , 0x95 , 0x99 , 0x9b , 0xa5 , 0xa9 , 0xab , 0xad , 0xb3 , 0xb5 , 0xc9 , 0xcb , 0xcd , 0xd3 , 0xd5 , 0xd9 },
{ 0x25 , 0x29 , 0x2b , 0x2d , 0x33 , 0x35 , 0x49 , 0x4b , 0x4d , 0x59 , 0x5b , 0x65 , 0x69 , 0x6b , 0x6d , 0x6e }};
static void __attribute__ (( unused )) HONTAI_init2 ()
{
uint8_t data_tx_addr [ 5 ];
//TX address
2016-12-12 18:07:38 +01:00
data_tx_addr [ 0 ] = pgm_read_byte_near ( & HONTAI_addr_vals [ 0 ][ rx_tx_addr [ 3 ] & 0x0f ]);
data_tx_addr [ 1 ] = pgm_read_byte_near ( & HONTAI_addr_vals [ 1 ][( rx_tx_addr [ 3 ] >> 4 ) & 0x0f ]);
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 ]);
2016-09-09 18:34:20 +02:00
data_tx_addr [ 4 ] = 0x24 ;
2025-01-30 23:14:15 +01:00
#ifdef DEBUG_SERIAL
debug ( "A N" );
for ( uint8_t i = 0 ; i < 5 ; i ++ )
debug ( " %02X" , data_tx_addr [ i ]);
debugln ();
#endif
2018-01-30 12:02:45 +01:00
if ( sub_protocol == JJRCX1 )
2021-03-17 17:05:42 +01:00
NRF24L01_WriteRegisterMulti ( NRF24L01_10_TX_ADDR , data_tx_addr , 5 );
2016-09-09 18:34:20 +02:00
else
2021-03-17 17:05:42 +01:00
XN297_SetTXAddr ( data_tx_addr , 5 );
2016-09-09 18:34:20 +02:00
//Hopping frequency table
2025-01-30 23:14:15 +01:00
uint8_t val ;
2016-09-09 18:34:20 +02:00
for ( uint8_t i = 0 ; i < 3 ; i ++ )
2025-01-30 23:14:15 +01:00
{
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 ;
}
2016-09-09 18:34:20 +02:00
hopping_frequency_no = 0 ;
2025-01-30 23:14:15 +01:00
#ifdef DEBUG_SERIAL
debug ( "H" );
for ( uint8_t i = 0 ; i < 3 ; i ++ )
debug ( " %d(%02X)" , hopping_frequency [ i ], hopping_frequency [ i ]);
debugln ();
#endif
2016-09-09 18:34:20 +02:00
}
static void __attribute__ (( unused )) HONTAI_initialize_txid ()
{
rx_tx_addr [ 4 ] = rx_tx_addr [ 2 ];
2025-01-30 23:14:15 +01:00
// 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 ;
2016-09-09 18:34:20 +02:00
}
else
2025-01-30 23:14:15 +01:00
{ //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
2016-09-09 18:34:20 +02:00
}
2025-01-30 23:14:15 +01:00
#ifdef DEBUG_SERIAL
debug ( "A B" );
for ( uint8_t i = 0 ; i < 5 ; i ++ )
debug ( " %02X" , rx_tx_addr [ i ]);
debugln ();
#endif
2016-09-09 18:34:20 +02:00
}
uint16_t HONTAI_callback ()
{
2021-02-12 11:21:42 +01:00
#ifdef MULTI_SYNC
telemetry_set_input_sync ( packet_period );
#endif
2021-02-11 10:54:55 +01:00
if ( bind_counter )
2016-09-09 18:34:20 +02:00
{
bind_counter -- ;
if ( bind_counter == 0 )
{
HONTAI_init2 ();
BIND_DONE ;
}
}
2021-02-12 11:21:42 +01:00
HONTAI_send_packet ();
2019-10-10 23:12:09 +02:00
return packet_period ;
2016-09-09 18:34:20 +02:00
}
2021-02-09 18:23:33 +01:00
void HONTAI_init ()
2016-09-09 18:34:20 +02:00
{
2016-09-10 09:42:39 +02:00
BIND_IN_PROGRESS ; // autobind protocol
2016-09-09 18:34:20 +02:00
bind_counter = HONTAI_BIND_COUNT ;
2021-02-09 18:23:33 +01:00
HONTAI_RF_init ();
2025-01-30 23:14:15 +01:00
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 ;
2016-09-09 18:34:20 +02:00
}
#endif