2016-02-04 13:35:16 +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/>.
*/
2016-08-28 14:03:22 +02:00
// compatible with MJX WLH08, X600, X800, H26D, Eachine E010
2016-02-04 13:35:16 +01:00
// Last sync with hexfet new_protocols/mjxq_nrf24l01.c dated 2016-01-17
2021-03-17 17:05:42 +01:00
# if defined(MJXQ_CCNRF_INO)
2016-02-04 13:35:16 +01:00
2021-03-17 17:05:42 +01:00
# include "iface_xn297.h"
2016-02-04 13:35:16 +01:00
# define MJXQ_BIND_COUNT 150
# define MJXQ_PACKET_PERIOD 4000 // Timeout for callback in uSec
# define MJXQ_INITIAL_WAIT 500
# define MJXQ_PACKET_SIZE 16
# define MJXQ_RF_NUM_CHANNELS 4
# define MJXQ_ADDRESS_LENGTH 5
2016-08-24 14:52:47 +02:00
// haven't figured out txid<-->rf channel mapping for MJX models
const uint8_t PROGMEM MJXQ_map_txid [ ] [ 3 ] = {
{ 0xF8 , 0x4F , 0x1C } ,
{ 0xC8 , 0x6E , 0x02 } ,
{ 0x48 , 0x6A , 0x40 } } ;
2016-10-19 23:22:12 +02:00
const uint8_t PROGMEM MJXQ_map_rfchan [ ] [ 4 ] = {
{ 0x0A , 0x46 , 0x3A , 0x42 } ,
{ 0x0A , 0x3C , 0x36 , 0x3F } ,
{ 0x0A , 0x43 , 0x36 , 0x3F } } ;
2016-08-24 14:52:47 +02:00
2016-12-09 16:54:24 +01:00
const uint8_t PROGMEM E010_map_txid [ ] [ 2 ] = {
{ 0x4F , 0x1C } ,
{ 0x90 , 0x1C } ,
{ 0x24 , 0x36 } ,
{ 0x7A , 0x40 } ,
{ 0x61 , 0x31 } ,
{ 0x5D , 0x37 } ,
{ 0xFD , 0x4F } ,
{ 0x86 , 0x3C } ,
{ 0x41 , 0x22 } ,
{ 0xEE , 0xB3 } ,
2016-12-12 11:20:25 +01:00
{ 0x9A , 0xB2 } ,
{ 0xC0 , 0x44 } ,
2016-12-13 14:58:02 +01:00
{ 0x2A , 0xFE } ,
{ 0xD7 , 0x6E } ,
2016-12-19 15:43:18 +01:00
{ 0x3C , 0xCD } , // for this ID rx_tx_addr[2]=0x01
{ 0xF5 , 0x2B } // for this ID rx_tx_addr[2]=0x02
2016-12-13 14:58:02 +01:00
} ;
const uint8_t PROGMEM E010_map_rfchan [ ] [ 2 ] = {
{ 0x3A , 0x35 } ,
{ 0x2E , 0x36 } ,
{ 0x32 , 0x3E } ,
{ 0x2E , 0x3C } ,
{ 0x2F , 0x3B } ,
{ 0x33 , 0x3B } ,
{ 0x33 , 0x3B } ,
{ 0x34 , 0x3E } ,
{ 0x34 , 0x2F } ,
{ 0x39 , 0x3E } ,
2016-12-19 15:43:18 +01:00
{ 0x2E , 0x38 } ,
2016-12-13 14:58:02 +01:00
{ 0x2E , 0x36 } ,
2016-12-19 15:43:18 +01:00
{ 0x2E , 0x38 } ,
2016-12-13 14:58:02 +01:00
{ 0x3A , 0x41 } ,
2016-12-19 15:43:18 +01:00
{ 0x32 , 0x3E } ,
{ 0x33 , 0x3F }
} ;
2016-08-24 14:52:47 +02:00
2016-02-04 13:35:16 +01:00
# define MJXQ_PAN_TILT_COUNT 16 // for H26D - match stock tx timing
# define MJXQ_PAN_DOWN 0x08
# define MJXQ_PAN_UP 0x04
# define MJXQ_TILT_DOWN 0x20
# define MJXQ_TILT_UP 0x10
2019-05-16 13:17:39 +02:00
2016-02-04 13:35:16 +01:00
static uint8_t __attribute__ ( ( unused ) ) MJXQ_pan_tilt_value ( )
{
2018-01-08 19:37:14 +01:00
// CH12_SW PAN // H26D
// CH13_SW TILT
2016-02-04 13:35:16 +01:00
uint8_t pan = 0 ;
packet_count + + ;
if ( packet_count & MJXQ_PAN_TILT_COUNT )
{
2018-01-08 19:37:14 +01:00
if ( CH12_SW )
2016-02-04 13:35:16 +01:00
pan = MJXQ_PAN_UP ;
2018-01-08 19:37:14 +01:00
if ( Channel_data [ CH12 ] < CHANNEL_MIN_COMMAND )
2016-02-04 13:35:16 +01:00
pan = MJXQ_PAN_DOWN ;
2018-01-08 19:37:14 +01:00
if ( CH13_SW )
2016-08-24 14:52:47 +02:00
pan + = MJXQ_TILT_UP ;
2018-01-08 19:37:14 +01:00
if ( Channel_data [ CH13 ] < CHANNEL_MIN_COMMAND )
2016-08-24 14:52:47 +02:00
pan + = MJXQ_TILT_DOWN ;
2016-02-04 13:35:16 +01:00
}
return pan ;
}
# define MJXQ_CHAN2TRIM(X) (((X) & 0x80 ? (X) : 0x7f - (X)) >> 1)
static void __attribute__ ( ( unused ) ) MJXQ_send_packet ( uint8_t bind )
{
2021-03-17 17:05:42 +01:00
//RF freq
hopping_frequency_no + + ;
XN297_Hopping ( hopping_frequency_no / 2 ) ;
hopping_frequency_no % = 2 * MJXQ_RF_NUM_CHANNELS ; // channels repeated
//Build packet
2016-02-04 13:35:16 +01:00
packet [ 0 ] = convert_channel_8b ( THROTTLE ) ;
packet [ 1 ] = convert_channel_s8b ( RUDDER ) ;
packet [ 4 ] = 0x40 ; // rudder does not work well with dyntrim
2016-09-13 16:38:04 +02:00
packet [ 2 ] = 0x80 ^ convert_channel_s8b ( ELEVATOR ) ;
2018-01-08 19:37:14 +01:00
packet [ 5 ] = ( CH9_SW | | CH14_SW ) ? 0x40 : MJXQ_CHAN2TRIM ( packet [ 2 ] ) ; // trim elevator
2016-02-04 13:35:16 +01:00
packet [ 3 ] = convert_channel_s8b ( AILERON ) ;
2018-01-08 19:37:14 +01:00
packet [ 6 ] = ( CH9_SW | | CH14_SW ) ? 0x40 : MJXQ_CHAN2TRIM ( packet [ 3 ] ) ; // trim aileron
2016-02-04 13:35:16 +01:00
packet [ 7 ] = rx_tx_addr [ 0 ] ;
packet [ 8 ] = rx_tx_addr [ 1 ] ;
packet [ 9 ] = rx_tx_addr [ 2 ] ;
packet [ 10 ] = 0x00 ; // overwritten below for feature bits
packet [ 11 ] = 0x00 ; // overwritten below for X600
packet [ 12 ] = 0x00 ;
packet [ 13 ] = 0x00 ;
packet [ 14 ] = 0xC0 ; // bind value
2018-01-08 19:37:14 +01:00
// CH5_SW FLIP
2019-04-15 10:48:20 +02:00
// CH6_SW LED / ARM // H26WH - TDR Phoenix mini
2018-01-08 19:37:14 +01:00
// CH7_SW PICTURE
// CH8_SW VIDEO
// CH9_SW HEADLESS
// CH10_SW RTH
// CH11_SW AUTOFLIP // X800, X600
// CH12_SW PAN
// CH13_SW TILT
// CH14_SW XTRM // Dyntrim, don't use if high.
2016-02-04 13:35:16 +01:00
switch ( sub_protocol )
{
2016-12-13 14:58:02 +01:00
case H26WH :
2016-02-04 13:35:16 +01:00
case H26D :
packet [ 10 ] = MJXQ_pan_tilt_value ( ) ;
// fall through on purpose - no break
case WLH08 :
2016-08-29 08:23:00 +02:00
case E010 :
2019-04-15 10:48:20 +02:00
case PHOENIX :
2018-01-08 19:37:14 +01:00
packet [ 10 ] + = GET_FLAG ( CH10_SW , 0x02 ) //RTH
| GET_FLAG ( CH9_SW , 0x01 ) ; //HEADLESS
2016-02-04 13:35:16 +01:00
if ( ! bind )
{
packet [ 14 ] = 0x04
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH5_SW , 0x01 ) //FLIP
| GET_FLAG ( CH7_SW , 0x08 ) //PICTURE
| GET_FLAG ( CH8_SW , 0x10 ) //VIDEO
| GET_FLAG ( ! CH6_SW , 0x20 ) ; // LED or air/ground mode
2019-04-15 10:48:20 +02:00
if ( sub_protocol = = PHOENIX )
{
packet [ 10 ] | = 0x20 //High rate
| GET_FLAG ( CH6_SW , 0x80 ) ; // arm
packet [ 14 ] & = ~ 0x24 ; // unset air/ground & arm flags
}
2016-12-13 14:58:02 +01:00
if ( sub_protocol = = H26WH )
{
packet [ 10 ] | = 0x40 ; //High rate
packet [ 14 ] & = ~ 0x24 ; // unset air/ground & arm flags
2018-01-08 19:37:14 +01:00
packet [ 14 ] | = GET_FLAG ( CH6_SW , 0x02 ) ; // arm
2016-12-13 14:58:02 +01:00
}
2016-02-04 13:35:16 +01:00
}
break ;
case X600 :
2018-01-08 19:37:14 +01:00
packet [ 10 ] = GET_FLAG ( ! CH6_SW , 0x02 ) ; //LED
packet [ 11 ] = GET_FLAG ( CH10_SW , 0x01 ) ; //RTH
2016-02-04 13:35:16 +01:00
if ( ! bind )
{
packet [ 14 ] = 0x02 // always high rates by bit2 = 1
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH5_SW , 0x04 ) //FLIP
| GET_FLAG ( CH11_SW , 0x10 ) //AUTOFLIP
| GET_FLAG ( CH9_SW , 0x20 ) ; //HEADLESS
2016-02-04 13:35:16 +01:00
}
break ;
case X800 :
default :
packet [ 10 ] = 0x10
2018-01-08 19:37:14 +01:00
| GET_FLAG ( ! CH6_SW , 0x02 ) //LED
| GET_FLAG ( CH11_SW , 0x01 ) ; //AUTOFLIP
2016-02-04 13:35:16 +01:00
if ( ! bind )
{
packet [ 14 ] = 0x02 // always high rates by bit2 = 1
2018-01-08 19:37:14 +01:00
| GET_FLAG ( CH5_SW , 0x04 ) //FLIP
| GET_FLAG ( CH7_SW , 0x08 ) //PICTURE
| GET_FLAG ( CH8_SW , 0x10 ) ; //VIDEO
2016-02-04 13:35:16 +01:00
}
break ;
}
uint8_t sum = packet [ 0 ] ;
for ( uint8_t i = 1 ; i < MJXQ_PACKET_SIZE - 1 ; i + + ) sum + = packet [ i ] ;
packet [ 15 ] = sum ;
2021-03-17 17:05:42 +01:00
// Send
XN297_SetTxRxMode ( TX_EN ) ;
XN297_SetPower ( ) ;
# ifdef NRF24L01_INSTALLED
if ( sub_protocol = = H26D | | sub_protocol = = H26WH )
2019-06-05 21:54:47 +02:00
{
2021-03-17 17:05:42 +01:00
//NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no / 2]);
//NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
//NRF24L01_FlushTx();
//NRF24L01_SetTxRxMode(TX_EN);
//NRF24L01_SetPower();
NRF24L01_WritePayload ( packet , MJXQ_PACKET_SIZE ) ;
2016-12-13 14:58:02 +01:00
}
2016-02-04 13:35:16 +01:00
else
2021-03-17 17:05:42 +01:00
# endif
{ //E010, PHOENIX, WLH08, X600, X800
XN297_SetFreqOffset ( ) ;
XN297_WritePayload ( packet , MJXQ_PACKET_SIZE ) ;
2016-12-13 14:58:02 +01:00
}
2016-02-04 13:35:16 +01:00
}
2021-02-09 18:23:33 +01:00
static void __attribute__ ( ( unused ) ) MJXQ_RF_init ( )
2016-02-04 13:35:16 +01:00
{
uint8_t addr [ MJXQ_ADDRESS_LENGTH ] ;
memcpy ( addr , " \x6d \x6a \x77 \x77 \x77 " , MJXQ_ADDRESS_LENGTH ) ;
if ( sub_protocol = = WLH08 )
memcpy ( hopping_frequency , " \x12 \x22 \x32 \x42 " , MJXQ_RF_NUM_CHANNELS ) ;
else
2019-04-15 10:48:20 +02:00
if ( sub_protocol = = H26D | | sub_protocol = = H26WH | | sub_protocol = = E010 | | sub_protocol = = PHOENIX )
2016-12-13 14:58:02 +01:00
memcpy ( hopping_frequency , " \x2e \x36 \x3e \x46 " , MJXQ_RF_NUM_CHANNELS ) ;
2016-02-04 13:35:16 +01:00
else
2016-12-13 14:58:02 +01:00
{
memcpy ( hopping_frequency , " \x0a \x35 \x42 \x3d " , MJXQ_RF_NUM_CHANNELS ) ;
memcpy ( addr , " \x6d \x6a \x73 \x73 \x73 " , MJXQ_ADDRESS_LENGTH ) ;
}
2019-06-05 21:54:47 +02:00
if ( sub_protocol = = E010 | | sub_protocol = = PHOENIX )
{
2021-03-17 17:05:42 +01:00
XN297_Configure ( XN297_CRCEN , XN297_SCRAMBLED , XN297_250K ) ;
XN297_SetTXAddr ( addr , MJXQ_ADDRESS_LENGTH ) ;
XN297_HoppingCalib ( MJXQ_RF_NUM_CHANNELS ) ;
2016-10-19 23:22:12 +02:00
}
2016-02-04 13:35:16 +01:00
else
2019-05-16 13:17:39 +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 H26 sub protocols can use common instructions
# ifdef NRF24L01_INSTALLED
2019-05-16 13:17:39 +02:00
if ( sub_protocol = = H26D | | sub_protocol = = H26WH )
NRF24L01_WriteRegisterMulti ( NRF24L01_10_TX_ADDR , addr , MJXQ_ADDRESS_LENGTH ) ;
else
2021-03-17 17:05:42 +01:00
# endif
2019-05-16 13:17:39 +02:00
XN297_SetTXAddr ( addr , MJXQ_ADDRESS_LENGTH ) ;
2021-03-17 17:05:42 +01:00
//NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, MJXQ_PACKET_SIZE); // no RX???
2019-05-16 13:17:39 +02:00
}
2016-02-04 13:35:16 +01:00
}
static void __attribute__ ( ( unused ) ) MJXQ_init2 ( )
{
2016-12-13 14:58:02 +01:00
switch ( sub_protocol )
{
case H26D :
memcpy ( hopping_frequency , " \x32 \x3e \x42 \x4e " , MJXQ_RF_NUM_CHANNELS ) ;
break ;
case H26WH :
memcpy ( hopping_frequency , " \x37 \x32 \x47 \x42 " , MJXQ_RF_NUM_CHANNELS ) ;
break ;
case E010 :
2019-04-15 10:48:20 +02:00
case PHOENIX :
2016-12-13 14:58:02 +01:00
for ( uint8_t i = 0 ; i < 2 ; i + + )
{
2016-12-19 15:43:18 +01:00
hopping_frequency [ i ] = pgm_read_byte_near ( & E010_map_rfchan [ rx_tx_addr [ 3 ] & 0x0F ] [ i ] ) ;
2016-12-13 14:58:02 +01:00
hopping_frequency [ i + 2 ] = hopping_frequency [ i ] + 0x10 ;
}
2021-03-17 17:05:42 +01:00
XN297_HoppingCalib ( MJXQ_RF_NUM_CHANNELS ) ;
2016-12-13 14:58:02 +01:00
break ;
case WLH08 :
// do nothing
break ;
default :
2016-08-24 14:52:47 +02:00
for ( uint8_t i = 0 ; i < MJXQ_RF_NUM_CHANNELS ; i + + )
2016-10-19 23:22:12 +02:00
hopping_frequency [ i ] = pgm_read_byte_near ( & MJXQ_map_rfchan [ rx_tx_addr [ 3 ] % 3 ] [ i ] ) ;
2016-12-13 14:58:02 +01:00
break ;
}
2016-02-04 13:35:16 +01:00
}
static void __attribute__ ( ( unused ) ) MJXQ_initialize_txid ( )
{
2016-12-13 14:58:02 +01:00
switch ( sub_protocol )
2016-08-28 14:03:22 +02:00
{
2016-12-13 14:58:02 +01:00
case H26WH :
memcpy ( rx_tx_addr , " \xa4 \x03 \x00 " , 3 ) ;
break ;
case E010 :
2019-04-15 10:48:20 +02:00
case PHOENIX :
2016-12-13 14:58:02 +01:00
for ( uint8_t i = 0 ; i < 2 ; i + + )
2016-12-19 15:43:18 +01:00
rx_tx_addr [ i ] = pgm_read_byte_near ( & E010_map_txid [ rx_tx_addr [ 3 ] & 0x0F ] [ i ] ) ;
if ( ( rx_tx_addr [ 3 ] & 0x0E ) = = 0x0E )
rx_tx_addr [ 2 ] = ( rx_tx_addr [ 3 ] & 0x01 ) + 1 ;
else
rx_tx_addr [ 2 ] = 0 ;
2016-12-13 14:58:02 +01:00
break ;
case WLH08 :
rx_tx_addr [ 0 ] & = 0xF8 ;
rx_tx_addr [ 2 ] = rx_tx_addr [ 3 ] ; // Make use of RX_Num
break ;
default :
for ( uint8_t i = 0 ; i < 3 ; i + + )
rx_tx_addr [ i ] = pgm_read_byte_near ( & MJXQ_map_txid [ rx_tx_addr [ 3 ] % 3 ] [ i ] ) ;
break ;
2016-08-28 14:03:22 +02:00
}
2016-02-04 13:35:16 +01:00
}
uint16_t MJXQ_callback ( )
{
2018-01-03 13:04:58 +01:00
if ( IS_BIND_DONE )
2019-10-10 23:12:09 +02:00
{
2019-11-11 19:15:39 +01:00
# ifdef MULTI_SYNC
telemetry_set_input_sync ( MJXQ_PACKET_PERIOD ) ;
# endif
2016-02-04 13:35:16 +01:00
MJXQ_send_packet ( 0 ) ;
2019-10-10 23:12:09 +02:00
}
2016-02-04 13:35:16 +01:00
else
{
if ( bind_counter = = 0 )
{
MJXQ_init2 ( ) ;
BIND_DONE ;
}
else
{
bind_counter - - ;
MJXQ_send_packet ( 1 ) ;
}
}
return MJXQ_PACKET_PERIOD ;
}
2021-02-09 18:23:33 +01:00
void MJXQ_init ( void )
2016-02-04 13:35:16 +01:00
{
BIND_IN_PROGRESS ; // autobind protocol
bind_counter = MJXQ_BIND_COUNT ;
MJXQ_initialize_txid ( ) ;
2021-02-09 18:23:33 +01:00
MJXQ_RF_init ( ) ;
2016-02-04 13:35:16 +01:00
packet_count = 0 ;
}
# endif