diff --git a/remote_multiprotocol/CC2500_SPI.ino b/remote_multiprotocol/CC2500_SPI.ino
new file mode 100644
index 0000000..82b56f8
--- /dev/null
+++ b/remote_multiprotocol/CC2500_SPI.ino
@@ -0,0 +1,160 @@
+
+/*
+ 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 .
+ */
+//-------------------------------
+//-------------------------------
+//CC2500 SPI routines
+//-------------------------------
+//-------------------------------
+#ifdef CC2500_INSTALLED
+#include "iface_cc2500.h"
+
+//----------------------------
+void CC2500_WriteReg(uint8_t address, uint8_t data)
+{
+ CC25_CSN_off;
+ SPI_Write(address);
+ SPI_Write(data);
+ CC25_CSN_on;
+}
+
+//----------------------
+static void CC2500_ReadRegisterMulti(uint8_t address, uint8_t data[], uint8_t length)
+{
+ CC25_CSN_off;
+ SPI_Write(CC2500_READ_BURST | address);
+ for(uint8_t i = 0; i < length; i++)
+ data[i] = SPI_Read();
+ CC25_CSN_on;
+}
+
+//--------------------------------------------
+static uint8_t CC2500_ReadReg(uint8_t address)
+{
+ uint8_t result;
+ CC25_CSN_off;
+ SPI_Write(CC2500_READ_SINGLE | address);
+ result = SPI_Read();
+ CC25_CSN_on;
+ return(result);
+}
+
+//------------------------
+void CC2500_ReadData(uint8_t *dpbuffer, uint8_t len)
+{
+ CC2500_ReadRegisterMulti(CC2500_3F_RXFIFO, dpbuffer, len);
+}
+
+//*********************************************
+void CC2500_Strobe(uint8_t state)
+{
+ CC25_CSN_off;
+ SPI_Write(state);
+ CC25_CSN_on;
+}
+
+static void CC2500_WriteRegisterMulti(uint8_t address, const uint8_t data[], uint8_t length)
+{
+ CC25_CSN_off;
+ SPI_Write(CC2500_WRITE_BURST | address);
+ for(uint8_t i = 0; i < length; i++)
+ SPI_Write(data[i]);
+ CC25_CSN_on;
+}
+
+void CC2500_WriteData(uint8_t *dpbuffer, uint8_t len)
+{
+ CC2500_Strobe(CC2500_SFTX);
+ CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, dpbuffer, len);
+ CC2500_Strobe(CC2500_STX);
+}
+
+void CC2500_SetTxRxMode(uint8_t mode)
+{
+ if(mode == TX_EN)
+ {//from deviation firmware
+ CC2500_WriteReg(CC2500_00_IOCFG2, 0x2F);
+ CC2500_WriteReg(CC2500_02_IOCFG0, 0x2F | 0x40);
+ }
+ else
+ if (mode == RX_EN)
+ {
+ CC2500_WriteReg(CC2500_02_IOCFG0, 0x2F);
+ CC2500_WriteReg(CC2500_00_IOCFG2, 0x2F | 0x40);
+ }
+ else
+ {
+ CC2500_WriteReg(CC2500_02_IOCFG0, 0x2F);
+ CC2500_WriteReg(CC2500_00_IOCFG2, 0x2F);
+ }
+}
+
+//------------------------
+/*static void cc2500_resetChip(void)
+{
+ // Toggle chip select signal
+ CC25_CSN_on;
+ delayMicroseconds(30);
+ CC25_CSN_off;
+ delayMicroseconds(30);
+ CC25_CSN_on;
+ delayMicroseconds(45);
+ CC2500_Strobe(CC2500_SRES);
+ _delay_ms(100);
+}
+*/
+uint8_t CC2500_Reset()
+{
+ CC2500_Strobe(CC2500_SRES);
+ delayMilliseconds(1);
+ CC2500_SetTxRxMode(TXRX_OFF);
+ return CC2500_ReadReg(CC2500_0E_FREQ1) == 0xC4;//check if reset
+}
+/*
+static void CC2500_SetPower_Value(uint8_t power)
+{
+ const unsigned char patable[8]= {
+ 0xC5, // -12dbm
+ 0x97, // -10dbm
+ 0x6E, // -8dbm
+ 0x7F, // -6dbm
+ 0xA9, // -4dbm
+ 0xBB, // -2dbm
+ 0xFE, // 0dbm
+ 0xFF // 1.5dbm
+ };
+ if (power > 7)
+ power = 7;
+ CC2500_WriteReg(CC2500_3E_PATABLE, patable[power]);
+}
+*/
+void CC2500_SetPower()
+{
+ uint8_t power=CC2500_BIND_POWER;
+ if(IS_BIND_DONE)
+ #ifdef CC2500_ENABLE_LOW_POWER
+ power=IS_POWER_FLAG_on?CC2500_HIGH_POWER:CC2500_LOW_POWER;
+ #else
+ power=CC2500_HIGH_POWER;
+ #endif
+ if(IS_RANGE_FLAG_on)
+ power=CC2500_RANGE_POWER;
+ if(prev_power != power)
+ {
+ CC2500_WriteReg(CC2500_3E_PATABLE, power);
+ prev_power=power;
+ }
+}
+#endif
diff --git a/remote_multiprotocol/Common.ino b/remote_multiprotocol/Common.ino
new file mode 100644
index 0000000..ccb94db
--- /dev/null
+++ b/remote_multiprotocol/Common.ino
@@ -0,0 +1,328 @@
+/*
+ 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 .
+*/
+
+void InitFailsafe()
+{
+ for (uint8_t i = 0; i < NUM_CHN; i++)
+ Failsafe_data[i] = 1024;
+ Failsafe_data[THROTTLE] = (uint16_t)204; //1=-125%, 204=-100%
+ FAILSAFE_VALUES_on;
+}
+
+void InitPPM()
+{
+ for(uint8_t i=0;i= 2048)
+ val = 2047;
+ Channel_data[num] = val;
+}
+// Channel value is converted to ppm 860<->2140 -125%<->+125% and 988<->2012 -100%<->+100%
+uint16_t convert_channel_ppm(uint8_t num)
+{
+ uint16_t val = Channel_data[num];
+ return (((val << 2) + val) >> 3) + 860; //value range 860<->2140 -125%<->+125%
+}
+// Channel value 100% is converted to 10bit values 0<->1023
+uint16_t convert_channel_10b(uint8_t num)
+{
+ uint16_t val = Channel_data[num];
+ val = ((val << 2) + val) >> 3;
+ if (val <= 128) return 0;
+ if (val >= 1152) return 1023;
+ return val - 128;
+}
+// Channel value 100% is converted to 8bit values 0<->255
+uint8_t convert_channel_8b(uint8_t num)
+{
+ uint16_t val = Channel_data[num];
+ val = ((val << 2) + val) >> 5;
+ if (val <= 32)
+ return 0;
+ if (val >= 288)
+ return 255;
+ return val - 32;
+}
+
+// Channel value 100% is converted to value scaled
+int16_t convert_channel_16b_limit(uint8_t num, int16_t min, int16_t max)
+{
+ int32_t val = limit_channel_100(num); // 204<->1844
+ val = (val - CHANNEL_MIN_100) * (max - min) / (CHANNEL_MAX_100 - CHANNEL_MIN_100) + min;
+ return (uint16_t)val;
+}
+
+// Channel value -125%<->125% is scaled to 16bit value with no limit
+int16_t convert_channel_16b_nolimit(uint8_t num, int16_t min, int16_t max)
+{
+ int32_t val = Channel_data[num]; // 0<->2047
+ val = (val - CHANNEL_MIN_100) * (max - min) / (CHANNEL_MAX_100 - CHANNEL_MIN_100) + min;
+ return (uint16_t)val;
+}
+
+// Channel value is converted sign + magnitude 8bit values
+uint8_t convert_channel_s8b(uint8_t num)
+{
+ uint8_t ch;
+ ch = convert_channel_8b(num);
+ return (ch < 128 ? 127 - ch : ch);
+}
+
+// Channel value is limited to 100%
+uint16_t limit_channel_100(uint8_t num)
+{
+ if (Channel_data[num] >= CHANNEL_MAX_100)
+ return CHANNEL_MAX_100;
+ if (Channel_data[num] <= CHANNEL_MIN_100)
+ return CHANNEL_MIN_100;
+ return Channel_data[num];
+}
+
+// Channel value is converted for HK310
+void convert_channel_HK310(uint8_t num, uint8_t *low, uint8_t *high)
+{
+ uint16_t temp = 0xFFFF - (3440 + ((Channel_data[num] * 5) >> 1)) / 3;
+ *low = (uint8_t)(temp & 0xFF);
+ *high = (uint8_t)(temp >> 8);
+}
+
+// Failsafe value is converted for HK310
+void convert_failsafe_HK310(uint8_t num, uint8_t *low, uint8_t *high)
+{
+ uint16_t temp = 0xFFFF - (3440 + ((Failsafe_data[num] * 5) >> 1)) / 3;
+ *low = (uint8_t)(temp & 0xFF);
+ *high = (uint8_t)(temp >> 8);
+}
+
+// Channel value for FrSky (PPM is multiplied by 1.5)
+uint16_t convert_channel_frsky(uint8_t num)
+{
+ uint16_t val = Channel_data[num];
+ return ((val * 15) >> 4) + 1290;
+}
+
+/******************************/
+/** FrSky D and X routines **/
+/******************************/
+#if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO)
+enum {
+ FRSKY_BIND = 0,
+ FRSKY_BIND_DONE = 1000,
+ FRSKY_DATA1,
+ FRSKY_DATA2,
+ FRSKY_DATA3,
+ FRSKY_DATA4,
+ FRSKY_DATA5
+};
+
+void Frsky_init_hop(void)
+{
+ uint8_t val;
+ uint8_t channel = rx_tx_addr[0] & 0x07;
+ uint8_t channel_spacing = rx_tx_addr[1];
+ //Filter bad tables
+ if (channel_spacing < 0x02) channel_spacing += 0x02;
+ if (channel_spacing > 0xE9) channel_spacing -= 0xE7;
+ if (channel_spacing % 0x2F == 0) channel_spacing++;
+
+ hopping_frequency[0] = channel;
+ for (uint8_t i = 1; i < 50; i++)
+ {
+ channel = (channel + channel_spacing) % 0xEB;
+ val = channel;
+ if ((val == 0x00) || (val == 0x5A) || (val == 0xDC))
+ val++;
+ hopping_frequency[i] = i > 46 ? 0 : val;
+ }
+}
+#endif
+/******************************/
+/** FrSky V, D and X routines **/
+/******************************/
+#if defined(FRSKYV_CC2500_INO) || defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO)
+const PROGMEM uint8_t FRSKY_common_startreg_cc2500_conf[] = {
+ CC2500_02_IOCFG0 ,
+ CC2500_00_IOCFG2 ,
+ CC2500_17_MCSM1 ,
+ CC2500_18_MCSM0 ,
+ CC2500_06_PKTLEN ,
+ CC2500_07_PKTCTRL1 ,
+ CC2500_08_PKTCTRL0 ,
+ CC2500_3E_PATABLE ,
+ CC2500_0B_FSCTRL1 ,
+ CC2500_0C_FSCTRL0 , // replaced by option value
+ CC2500_0D_FREQ2 ,
+ CC2500_0E_FREQ1 ,
+ CC2500_0F_FREQ0 ,
+ CC2500_10_MDMCFG4 ,
+ CC2500_11_MDMCFG3 ,
+ CC2500_12_MDMCFG2 ,
+ CC2500_13_MDMCFG1 ,
+ CC2500_14_MDMCFG0 ,
+ CC2500_15_DEVIATN
+};
+
+#if defined(FRSKYV_CC2500_INO)
+const PROGMEM uint8_t FRSKYV_cc2500_conf[] = {
+ /*02_IOCFG0*/ 0x06 ,
+ /*00_IOCFG2*/ 0x06 ,
+ /*17_MCSM1*/ 0x0c ,
+ /*18_MCSM0*/ 0x18 ,
+ /*06_PKTLEN*/ 0xff ,
+ /*07_PKTCTRL1*/ 0x04 ,
+ /*08_PKTCTRL0*/ 0x05 ,
+ /*3E_PATABLE*/ 0xfe ,
+ /*0B_FSCTRL1*/ 0x08 ,
+ /*0C_FSCTRL0*/ 0x00 ,
+ /*0D_FREQ2*/ 0x5c ,
+ /*0E_FREQ1*/ 0x58 ,
+ /*0F_FREQ0*/ 0x9d ,
+ /*10_MDMCFG4*/ 0xAA ,
+ /*11_MDMCFG3*/ 0x10 ,
+ /*12_MDMCFG2*/ 0x93 ,
+ /*13_MDMCFG1*/ 0x23 ,
+ /*14_MDMCFG0*/ 0x7a ,
+ /*15_DEVIATN*/ 0x41
+};
+#endif
+
+#if defined(FRSKYD_CC2500_INO)
+const PROGMEM uint8_t FRSKYD_cc2500_conf[] = {
+ /*02_IOCFG0*/ 0x06 ,
+ /*00_IOCFG2*/ 0x06 ,
+ /*17_MCSM1*/ 0x0c ,
+ /*18_MCSM0*/ 0x18 ,
+ /*06_PKTLEN*/ 0x19 ,
+ /*07_PKTCTRL1*/ 0x04 ,
+ /*08_PKTCTRL0*/ 0x05 ,
+ /*3E_PATABLE*/ 0xff ,
+ /*0B_FSCTRL1*/ 0x08 ,
+ /*0C_FSCTRL0*/ 0x00 ,
+ /*0D_FREQ2*/ 0x5c ,
+ /*0E_FREQ1*/ 0x76 ,
+ /*0F_FREQ0*/ 0x27 ,
+ /*10_MDMCFG4*/ 0xAA ,
+ /*11_MDMCFG3*/ 0x39 ,
+ /*12_MDMCFG2*/ 0x11 ,
+ /*13_MDMCFG1*/ 0x23 ,
+ /*14_MDMCFG0*/ 0x7a ,
+ /*15_DEVIATN*/ 0x42
+};
+#endif
+
+#if defined(FRSKYX_CC2500_INO)
+const PROGMEM uint8_t FRSKYX_cc2500_conf[] = {
+ //FRSKYX
+ /*02_IOCFG0*/ 0x06 ,
+ /*00_IOCFG2*/ 0x06 ,
+ /*17_MCSM1*/ 0x0c ,
+ /*18_MCSM0*/ 0x18 ,
+ /*06_PKTLEN*/ 0x1E ,
+ /*07_PKTCTRL1*/ 0x04 ,
+ /*08_PKTCTRL0*/ 0x01 ,
+ /*3E_PATABLE*/ 0xff ,
+ /*0B_FSCTRL1*/ 0x0A ,
+ /*0C_FSCTRL0*/ 0x00 ,
+ /*0D_FREQ2*/ 0x5c ,
+ /*0E_FREQ1*/ 0x76 ,
+ /*0F_FREQ0*/ 0x27 ,
+ /*10_MDMCFG4*/ 0x7B ,
+ /*11_MDMCFG3*/ 0x61 ,
+ /*12_MDMCFG2*/ 0x13 ,
+ /*13_MDMCFG1*/ 0x23 ,
+ /*14_MDMCFG0*/ 0x7a ,
+ /*15_DEVIATN*/ 0x51
+};
+const PROGMEM uint8_t FRSKYXEU_cc2500_conf[] = {
+ /*02_IOCFG0*/ 0x06 ,
+ /*00_IOCFG2*/ 0x06 ,
+ /*17_MCSM1*/ 0x0E ,
+ /*18_MCSM0*/ 0x18 ,
+ /*06_PKTLEN*/ 0x23 ,
+ /*07_PKTCTRL1*/ 0x04 ,
+ /*08_PKTCTRL0*/ 0x01 ,
+ /*3E_PATABLE*/ 0xff ,
+ /*0B_FSCTRL1*/ 0x08 ,
+ /*0C_FSCTRL0*/ 0x00 ,
+ /*0D_FREQ2*/ 0x5c ,
+ /*0E_FREQ1*/ 0x80 ,
+ /*0F_FREQ0*/ 0x00 ,
+ /*10_MDMCFG4*/ 0x7B ,
+ /*11_MDMCFG3*/ 0xF8 ,
+ /*12_MDMCFG2*/ 0x03 ,
+ /*13_MDMCFG1*/ 0x23 ,
+ /*14_MDMCFG0*/ 0x7a ,
+ /*15_DEVIATN*/ 0x53
+};
+#endif
+
+const PROGMEM uint8_t FRSKY_common_end_cc2500_conf[][2] = {
+ { CC2500_19_FOCCFG, 0x16 },
+ { CC2500_1A_BSCFG, 0x6c },
+ { CC2500_1B_AGCCTRL2, 0x43 },
+ { CC2500_1C_AGCCTRL1, 0x40 },
+ { CC2500_1D_AGCCTRL0, 0x91 },
+ { CC2500_21_FREND1, 0x56 },
+ { CC2500_22_FREND0, 0x10 },
+ { CC2500_23_FSCAL3, 0xa9 },
+ { CC2500_24_FSCAL2, 0x0A },
+ { CC2500_25_FSCAL1, 0x00 },
+ { CC2500_26_FSCAL0, 0x11 },
+ { CC2500_29_FSTEST, 0x59 },
+ { CC2500_2C_TEST2, 0x88 },
+ { CC2500_2D_TEST1, 0x31 },
+ { CC2500_2E_TEST0, 0x0B },
+ { CC2500_03_FIFOTHR, 0x07 },
+ { CC2500_09_ADDR, 0x00 }
+};
+
+void FRSKY_init_cc2500(const uint8_t *ptr)
+{
+ for (uint8_t i = 0; i < 19; i++)
+ {
+ uint8_t reg = pgm_read_byte_near(&FRSKY_common_startreg_cc2500_conf[i]);
+ uint8_t val = pgm_read_byte_near(&ptr[i]);
+ if (reg == CC2500_0C_FSCTRL0)
+ val = option;
+ CC2500_WriteReg(reg, val);
+ }
+ prev_option = option ; // Save option to monitor FSCTRL0 change
+ for (uint8_t i = 0; i < 17; i++)
+ {
+ uint8_t reg = pgm_read_byte_near(&FRSKY_common_end_cc2500_conf[i][0]);
+ uint8_t val = pgm_read_byte_near(&FRSKY_common_end_cc2500_conf[i][1]);
+ CC2500_WriteReg(reg, val);
+ }
+ CC2500_SetTxRxMode(TX_EN);
+ CC2500_SetPower();
+ CC2500_Strobe(CC2500_SIDLE); // Go to idle...
+}
+#endif
diff --git a/remote_multiprotocol/FrSkyD_cc2500.ino b/remote_multiprotocol/FrSkyD_cc2500.ino
new file mode 100644
index 0000000..bf15dbc
--- /dev/null
+++ b/remote_multiprotocol/FrSkyD_cc2500.ino
@@ -0,0 +1,210 @@
+/*
+ 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 .
+ */
+
+#if defined(FRSKYD_CC2500_INO)
+#include "iface_cc2500.h"
+
+static void __attribute__((unused)) frsky2way_init(uint8_t bind)
+{
+ //debugln("frsky2way_init");
+ FRSKY_init_cc2500(FRSKYD_cc2500_conf);
+
+ CC2500_WriteReg(CC2500_09_ADDR, bind ? 0x03 : rx_tx_addr[3]);
+ CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x05);
+ CC2500_Strobe(CC2500_SIDLE); // Go to idle...
+ //
+ CC2500_WriteReg(CC2500_0A_CHANNR, 0x00);
+ CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
+ CC2500_Strobe(CC2500_SFRX);
+ //#######END INIT########
+}
+
+static void __attribute__((unused)) frsky2way_build_bind_packet()
+{
+ //debugln("build bind");
+ //11 03 01 d7 2d 00 00 1e 3c 5b 78 00 00 00 00 00 00 01
+ //11 03 01 19 3e 00 02 8e 2f bb 5c 00 00 00 00 00 00 01
+ packet[0] = 0x11;
+ packet[1] = 0x03;
+ packet[2] = 0x01;
+ packet[3] = rx_tx_addr[3];
+ packet[4] = rx_tx_addr[2];
+ uint16_t idx = ((state -FRSKY_BIND) % 10) * 5;
+ packet[5] = idx;
+ packet[6] = hopping_frequency[idx++];
+ packet[7] = hopping_frequency[idx++];
+ packet[8] = hopping_frequency[idx++];
+ packet[9] = hopping_frequency[idx++];
+ packet[10] = hopping_frequency[idx++];
+ packet[11] = 0x00;
+ packet[12] = 0x00;
+ packet[13] = 0x00;
+ packet[14] = 0x00;
+ packet[15] = 0x00;
+ packet[16] = 0x00;
+ packet[17] = 0x01;
+}
+
+static void __attribute__((unused)) frsky2way_data_frame()
+{
+ //pachet[4] is telemetry user frame counter(hub)
+ //11 d7 2d 22 00 01 c9 c9 ca ca 88 88 ca ca c9 ca 88 88
+ //11 57 12 00 00 01 f2 f2 f2 f2 06 06 ca ca ca ca 18 18
+ packet[0] = 0x11; //Length
+ packet[1] = rx_tx_addr[3];
+ packet[2] = rx_tx_addr[2];
+ packet[3] = counter;//
+ #if defined TELEMETRY
+ packet[4] = telemetry_counter;
+ #else
+ packet[4] = 0x00;
+ #endif
+
+ packet[5] = 0x01;
+ //
+ packet[10] = 0;
+ packet[11] = 0;
+ packet[16] = 0;
+ packet[17] = 0;
+ for(uint8_t i = 0; i < 8; i++)
+ {
+ uint16_t value;
+ value = convert_channel_frsky(i);
+ if(i < 4)
+ {
+ packet[6+i] = value & 0xff;
+ packet[10+(i>>1)] |= ((value >> 8) & 0x0f) << (4 *(i & 0x01));
+ }
+ else
+ {
+ packet[8+i] = value & 0xff;
+ packet[16+((i-4)>>1)] |= ((value >> 8) & 0x0f) << (4 * ((i-4) & 0x01));
+ }
+ }
+}
+
+uint16_t initFrSky_2way()
+{
+ Frsky_init_hop();
+ packet_count=0;
+
+ if(IS_BIND_IN_PROGRESS)
+ {
+ frsky2way_init(1);
+ state = FRSKY_BIND;
+ debugln("initFrSky_2way bind");
+ }
+ else
+ {
+ state = FRSKY_BIND_DONE;
+ debugln("initFrSky_2way bind done");
+ }
+ return 10000;
+}
+
+uint16_t ReadFrSky_2way()
+{
+
+ if (state < FRSKY_BIND_DONE)
+ {
+ frsky2way_build_bind_packet();
+ CC2500_Strobe(CC2500_SIDLE);
+ CC2500_WriteReg(CC2500_0A_CHANNR, 0x00);
+ CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
+ CC2500_Strobe(CC2500_SFRX);//0x3A
+ CC2500_WriteData(packet, packet[0]+1);
+ if(IS_BIND_DONE) {
+ state = FRSKY_BIND;
+ debugln("%s bind done",__func__);
+ } else
+ state++;
+ return 9000;
+ }
+ if (state == FRSKY_BIND_DONE)
+ {
+ //debugln("%s bind done",__func__);
+
+ state = FRSKY_DATA2;
+ frsky2way_init(0);
+ counter = 0;
+ BIND_DONE;
+ }
+ else
+ if (state == FRSKY_DATA5)
+ {
+ CC2500_Strobe(CC2500_SRX);//0x34 RX enable
+ state = FRSKY_DATA1;
+ return 9200;
+ }
+ counter = (counter + 1) % 188;
+ if (state == FRSKY_DATA4)
+ { //telemetry receive
+ CC2500_SetTxRxMode(RX_EN);
+ CC2500_Strobe(CC2500_SIDLE);
+ CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[counter % 47]);
+ CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
+ state++;
+ return 1300;
+ }
+ else
+ {
+ if (state == FRSKY_DATA1)
+ {
+ len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
+
+ //debugln("%d len",len);
+ if (len && len<=(0x11+3))// 20bytes
+ {
+ CC2500_ReadData(pkt, len); //received telemetry packets
+ #if defined(TELEMETRY)
+ if(pkt[len-1] & 0x80)
+ {//with valid crc
+ packet_count=0;
+ frsky_check_telemetry(pkt,len); //check if valid telemetry packets and buffer them.
+ }
+ #endif
+ }
+ else
+ {
+ packet_count++;
+ // restart sequence on missed packet - might need count or timeout instead of one missed
+ if(packet_count>100)
+ {//~1sec
+ packet_count=0;
+ #if defined TELEMETRY
+ telemetry_link=0;//no link frames
+ pkt[6]=0;//no user frames.
+ #endif
+ }
+ }
+ CC2500_SetTxRxMode(TX_EN);
+ CC2500_SetPower(); // Set tx_power
+ }
+ CC2500_Strobe(CC2500_SIDLE);
+ CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[counter % 47]);
+ if ( prev_option != option )
+ {
+ CC2500_WriteReg(CC2500_0C_FSCTRL0,option); // Frequency offset hack
+ prev_option = option ;
+ }
+ CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
+ CC2500_Strobe(CC2500_SFRX);
+ frsky2way_data_frame();
+ CC2500_WriteData(packet, packet[0]+1);
+ state++;
+ }
+ return state == FRSKY_DATA4 ? 7500 : 9000;
+}
+#endif
diff --git a/remote_multiprotocol/FrSkyV_cc2500.ino b/remote_multiprotocol/FrSkyV_cc2500.ino
new file mode 100644
index 0000000..f277b2c
--- /dev/null
+++ b/remote_multiprotocol/FrSkyV_cc2500.ino
@@ -0,0 +1,165 @@
+/*
+ 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 .
+ */
+
+#if defined(FRSKYV_CC2500_INO)
+
+#define FRSKYV_BIND_COUNT 200
+
+enum {
+ FRSKYV_DATA1=0,
+ FRSKYV_DATA2,
+ FRSKYV_DATA3,
+ FRSKYV_DATA4,
+ FRSKYV_DATA5
+};
+
+
+#include "iface_cc2500.h"
+static uint8_t __attribute__((unused)) FRSKYV_crc8(uint8_t result, uint8_t *data, uint8_t len)
+{
+ for(uint8_t i = 0; i < len; i++)
+ {
+ result = result ^ data[i];
+ for(uint8_t j = 0; j < 8; j++)
+ if(result & 0x80)
+ result = (result << 1) ^ 0x07;
+ else
+ result = result << 1;
+ }
+ return result;
+}
+
+static uint8_t __attribute__((unused)) FRSKYV_crc8_le(uint8_t *data, uint8_t len)
+{
+ uint8_t result = 0xD6;
+
+ for(uint8_t i = 0; i < len; i++)
+ {
+ result = result ^ data[i];
+ for(uint8_t j = 0; j < 8; j++)
+ if(result & 0x01)
+ result = (result >> 1) ^ 0x83;
+ else
+ result = result >> 1;
+ }
+ return result;
+}
+
+static void __attribute__((unused)) FRSKYV_build_bind_packet()
+{
+ //0e 03 01 57 12 00 06 0b 10 15 1a 00 00 00 61
+ packet[0] = 0x0e; //Length
+ packet[1] = 0x03; //Packet type
+ packet[2] = 0x01; //Packet type
+ packet[3] = rx_tx_addr[3];
+ packet[4] = rx_tx_addr[2];
+ packet[5] = (binding_idx % 10) * 5;
+ packet[6] = packet[5] * 5 + 6;
+ packet[7] = packet[5] * 5 + 11;
+ packet[8] = packet[5] * 5 + 16;
+ packet[9] = packet[5] * 5 + 21;
+ packet[10] = packet[5] * 5 + 26;
+ packet[11] = 0x00;
+ packet[12] = 0x00;
+ packet[13] = 0x00;
+ packet[14] = FRSKYV_crc8(0x93, packet, 14);
+}
+
+static uint8_t __attribute__((unused)) FRSKYV_calc_channel()
+{
+ uint32_t temp=seed;
+ temp = (temp * 0xaa) % 0x7673;
+ seed = temp;
+ return (seed & 0xff) % 0x32;
+}
+
+static void __attribute__((unused)) FRSKYV_build_data_packet()
+{
+ uint8_t idx = 0; // transmit lower channels
+
+ packet[0] = 0x0e;
+ packet[1] = rx_tx_addr[3];
+ packet[2] = rx_tx_addr[2];
+ packet[3] = seed & 0xff;
+ packet[4] = seed >> 8;
+ if (phase == FRSKYV_DATA1 || phase == FRSKYV_DATA3)
+ packet[5] = 0x0f;
+ else
+ if(phase == FRSKYV_DATA2 || phase == FRSKYV_DATA4)
+ {
+ packet[5] = 0xf0;
+ idx=4; // transmit upper channels
+ }
+ else
+ packet[5] = 0x00;
+ for(uint8_t i = 0; i < 4; i++)
+ {
+ uint16_t value = convert_channel_frsky(i+idx);
+ packet[2*i + 6] = value & 0xff;
+ packet[2*i + 7] = value >> 8;
+ }
+ packet[14] = FRSKYV_crc8(crc8, packet, 14);
+}
+
+uint16_t ReadFRSKYV()
+{
+ if(IS_BIND_DONE)
+ { // Normal operation
+ uint8_t chan = FRSKYV_calc_channel();
+ CC2500_Strobe(CC2500_SIDLE);
+ if (option != prev_option)
+ {
+ CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
+ prev_option=option;
+ }
+ CC2500_WriteReg(CC2500_0A_CHANNR, chan * 5 + 6);
+ FRSKYV_build_data_packet();
+
+ if (phase == FRSKYV_DATA5)
+ {
+ CC2500_SetPower();
+ phase = FRSKYV_DATA1;
+ }
+ else
+ phase++;
+
+ CC2500_WriteData(packet, packet[0]+1);
+ return 9006;
+ }
+ // Bind mode
+ FRSKYV_build_bind_packet();
+ CC2500_Strobe(CC2500_SIDLE);
+ CC2500_WriteReg(CC2500_0A_CHANNR, 0x00);
+ CC2500_WriteData(packet, packet[0]+1);
+ binding_idx++;
+ if(binding_idx>=FRSKYV_BIND_COUNT)
+ BIND_DONE;
+ return 53460;
+}
+
+uint16_t initFRSKYV()
+{
+ //ID is 15 bits. Using rx_tx_addr[2] and rx_tx_addr[3] since we want to use RX_Num for model match
+ rx_tx_addr[2]&=0x7F;
+ crc8 = FRSKYV_crc8_le(rx_tx_addr+2, 2);
+
+ FRSKY_init_cc2500(FRSKYV_cc2500_conf);
+ seed = 1;
+ binding_idx=0;
+ phase = FRSKYV_DATA1;
+ return 10000;
+}
+
+#endif
diff --git a/remote_multiprotocol/FrSkyX_cc2500.ino b/remote_multiprotocol/FrSkyX_cc2500.ino
new file mode 100644
index 0000000..cb23485
--- /dev/null
+++ b/remote_multiprotocol/FrSkyX_cc2500.ino
@@ -0,0 +1,361 @@
+/* **************************
+ * By Midelic on RCGroups *
+ **************************
+ 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 .
+*/
+
+#if defined(FRSKYX_CC2500_INO)
+
+#include "iface_cc2500.h"
+
+uint8_t FrX_chanskip;
+uint8_t FrX_send_seq ;
+uint8_t FrX_receive_seq ;
+
+#define FRX_FAILSAFE_TIMEOUT 1032
+
+static void __attribute__((unused)) frskyX_set_start(uint8_t ch )
+{
+ CC2500_Strobe(CC2500_SIDLE);
+ CC2500_WriteReg(CC2500_25_FSCAL1, calData[ch]);
+ CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[ch]);
+}
+
+static void __attribute__((unused)) frskyX_init()
+{
+ FRSKY_init_cc2500((sub_protocol&2)?FRSKYXEU_cc2500_conf:FRSKYX_cc2500_conf); // LBT or FCC
+ //
+ for(uint8_t c=0;c < 48;c++)
+ {//calibrate hop channels
+ CC2500_Strobe(CC2500_SIDLE);
+ CC2500_WriteReg(CC2500_0A_CHANNR,hopping_frequency[c]);
+ CC2500_Strobe(CC2500_SCAL);
+ delayMicroseconds(900);//
+ calData[c] = CC2500_ReadReg(CC2500_25_FSCAL1);
+ }
+ //#######END INIT########
+}
+
+static void __attribute__((unused)) frskyX_initialize_data(uint8_t adr)
+{
+ CC2500_WriteReg(CC2500_0C_FSCTRL0,option); // Frequency offset hack
+ CC2500_WriteReg(CC2500_18_MCSM0, 0x8);
+ CC2500_WriteReg(CC2500_09_ADDR, adr ? 0x03 : rx_tx_addr[3]);
+ CC2500_WriteReg(CC2500_07_PKTCTRL1,0x05);
+}
+
+//**CRC**
+const uint16_t PROGMEM frskyX_CRC_Short[]={
+ 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
+ 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7 };
+static uint16_t __attribute__((unused)) frskyX_CRCTable(uint8_t val)
+{
+ uint16_t word ;
+ word = pgm_read_word(&frskyX_CRC_Short[val&0x0F]) ;
+ val /= 16 ;
+ return word ^ (0x1081 * val) ;
+}
+static uint16_t __attribute__((unused)) frskyX_crc_x(uint8_t *data, uint8_t len)
+{
+ uint16_t crc = 0;
+ for(uint8_t i=0; i < len; i++)
+ crc = (crc<<8) ^ frskyX_CRCTable((uint8_t)(crc>>8) ^ *data++);
+ return crc;
+}
+
+static void __attribute__((unused)) frskyX_build_bind_packet()
+{
+ // debugln("%s:%d build bind", __func__, __LINE__);
+ packet[0] = (sub_protocol & 2 ) ? 0x20 : 0x1D ; // LBT or FCC
+ packet[1] = 0x03;
+ packet[2] = 0x01;
+ //
+ packet[3] = rx_tx_addr[3];
+ packet[4] = rx_tx_addr[2];
+ int idx = ((state -FRSKY_BIND) % 10) * 5;
+ packet[5] = idx;
+ packet[6] = hopping_frequency[idx++];
+ packet[7] = hopping_frequency[idx++];
+ packet[8] = hopping_frequency[idx++];
+ packet[9] = hopping_frequency[idx++];
+ packet[10] = hopping_frequency[idx++];
+ packet[11] = 0x02;
+ packet[12] = RX_num;
+ //
+ uint8_t limit = (sub_protocol & 2 ) ? 31 : 28 ;
+ memset(&packet[13], 0, limit - 13);
+ uint16_t lcrc = frskyX_crc_x(&packet[3], limit-3);
+ //
+ packet[limit++] = lcrc >> 8;
+ packet[limit] = lcrc;
+ //
+}
+
+// 0-2047, 0 = 817, 1024 = 1500, 2047 = 2182
+//64=860,1024=1500,1984=2140//Taranis 125%
+static uint16_t __attribute__((unused)) frskyX_scaleForPXX( uint8_t i )
+{ //mapped 860,2140(125%) range to 64,1984(PXX values);
+ uint16_t chan_val=convert_channel_frsky(i)-1226;
+ if(i>7) chan_val|=2048; // upper channels offset
+ return chan_val;
+}
+#ifdef FAILSAFE_ENABLE
+static uint16_t __attribute__((unused)) frskyX_scaleForPXX_FS( uint8_t i )
+{ //mapped 1,2046(125%) range to 64,1984(PXX values);
+ uint16_t chan_val=((Failsafe_data[i]*15)>>4)+64;
+ if(Failsafe_data[i]==FAILSAFE_CHANNEL_NOPULSES)
+ chan_val=FAILSAFE_CHANNEL_NOPULSES;
+ else if(Failsafe_data[i]==FAILSAFE_CHANNEL_HOLD)
+ chan_val=FAILSAFE_CHANNEL_HOLD;
+ if(i>7) chan_val|=2048; // upper channels offset
+ return chan_val;
+}
+#endif
+
+#define FRX_FAILSAFE_TIME 1032
+static void __attribute__((unused)) frskyX_data_frame()
+{
+ //0x1D 0xB3 0xFD 0x02 0x56 0x07 0x15 0x00 0x00 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x96 0x12
+ //
+ static uint8_t chan_offset=0;
+ uint16_t chan_0 ;
+ uint16_t chan_1 ;
+ //
+ // data frames sent every 9ms; failsafe every 9 seconds
+ #ifdef FAILSAFE_ENABLE
+ static uint16_t failsafe_count=0;
+ static uint8_t FS_flag=0,failsafe_chan=0;
+ if (FS_flag == 0 && failsafe_count > FRX_FAILSAFE_TIME && chan_offset == 0 && IS_FAILSAFE_VALUES_on)
+ {
+ FS_flag = 0x10;
+ failsafe_chan = 0;
+ } else if (FS_flag & 0x10 && failsafe_chan < (sub_protocol & 0x01 ? 8-1:16-1))
+ {
+ FS_flag = 0x10 | ((FS_flag + 2) & 0x0F); //10, 12, 14, 16, 18, 1A, 1C, 1E - failsafe packet
+ failsafe_chan ++;
+ } else if (FS_flag & 0x10)
+ {
+ FS_flag = 0;
+ failsafe_count = 0;
+ }
+ failsafe_count++;
+ #endif
+
+ packet[0] = (sub_protocol & 0x02 ) ? 0x20 : 0x1D ; // LBT or FCC
+ packet[1] = rx_tx_addr[3];
+ packet[2] = rx_tx_addr[2];
+ packet[3] = 0x02;
+ //
+ packet[4] = (FrX_chanskip<<6)|hopping_frequency_no;
+ packet[5] = FrX_chanskip>>2;
+ packet[6] = RX_num;
+ //packet[7] = FLAGS 00 - standard packet
+ //10, 12, 14, 16, 18, 1A, 1C, 1E - failsafe packet
+ //20 - range check packet
+ #ifdef FAILSAFE_ENABLE
+ packet[7] = FS_flag;
+ #else
+ packet[7] = 0;
+ #endif
+ packet[8] = 0;
+ //
+ uint8_t startChan = chan_offset; for(uint8_t i = 0; i <12 ; i+=3)
+ {//12 bytes of channel data
+ #ifdef FAILSAFE_ENABLE
+ if( (FS_flag & 0x10) && ((failsafe_chan & 0x07) == (startChan & 0x07)) )
+ chan_0 = frskyX_scaleForPXX_FS(failsafe_chan);
+ else
+ #endif
+ chan_0 = frskyX_scaleForPXX(startChan);
+ startChan++;
+ //
+ #ifdef FAILSAFE_ENABLE
+ if( (FS_flag & 0x10) && ((failsafe_chan & 0x07) == (startChan & 0x07)) )
+ chan_1 = frskyX_scaleForPXX_FS(failsafe_chan);
+ else
+ #endif
+ chan_1 = frskyX_scaleForPXX(startChan);
+ startChan++;
+ //
+ packet[9+i] = lowByte(chan_0); //3 bytes*4
+ packet[9+i+1]=(((chan_0>>8) & 0x0F)|(chan_1 << 4));
+ packet[9+i+2]=chan_1>>4;
+ }
+ packet[21] = (FrX_receive_seq << 4) | FrX_send_seq ;//8 at start
+
+ if(sub_protocol & 0x01 ) // in X8 mode send only 8ch every 9ms
+ chan_offset = 0 ;
+ else
+ chan_offset^=0x08;
+
+ uint8_t limit = (sub_protocol & 2 ) ? 31 : 28 ;
+ for (uint8_t i=22;i>8;//high byte
+ packet[limit]=lcrc;//low byte
+}
+
+uint16_t ReadFrSkyX()
+{
+ switch(state)
+ {
+ default:
+ frskyX_set_start(47);
+ CC2500_SetPower();
+ CC2500_Strobe(CC2500_SFRX);
+ //
+ frskyX_build_bind_packet();
+ CC2500_Strobe(CC2500_SIDLE);
+ CC2500_WriteData(packet, packet[0]+1);
+ if(IS_BIND_DONE) {
+ state = FRSKY_BIND_DONE;
+ //debugln("%s:%d bind done", __func__, __LINE__);
+ }
+ else
+ state++;
+ return 9000;
+ case FRSKY_BIND_DONE:
+ frskyX_initialize_data(0);
+ hopping_frequency_no=0;
+ BIND_DONE;
+ state++;
+ break;
+ case FRSKY_DATA1:
+ if ( prev_option != option )
+ {
+ CC2500_WriteReg(CC2500_0C_FSCTRL0,option); // Frequency offset hack
+ prev_option = option ;
+ }
+ CC2500_SetTxRxMode(TX_EN);
+ frskyX_set_start(hopping_frequency_no);
+ CC2500_SetPower();
+ CC2500_Strobe(CC2500_SFRX);
+ hopping_frequency_no = (hopping_frequency_no+FrX_chanskip)%47;
+ CC2500_Strobe(CC2500_SIDLE);
+ CC2500_WriteData(packet, packet[0]+1);
+ //
+// frskyX_data_frame();
+ state++;
+ return 5200;
+ case FRSKY_DATA2:
+ CC2500_SetTxRxMode(RX_EN);
+ CC2500_Strobe(CC2500_SIDLE);
+ state++;
+ return 200;
+ case FRSKY_DATA3:
+ CC2500_Strobe(CC2500_SRX);
+ state++;
+ return 3100;
+ case FRSKY_DATA4:
+ len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
+ if (len && (len<=(0x0E + 3))) //Telemetry frame is 17
+ {
+ packet_count=0;
+ CC2500_ReadData(pkt, len);
+ #if defined TELEMETRY
+ frsky_check_telemetry(pkt,len); //check if valid telemetry packets
+ //parse telemetry packets here
+ //The same telemetry function used by FrSky(D8).
+ #endif
+ }
+ else
+ {
+ packet_count++;
+ // restart sequence on missed packet - might need count or timeout instead of one missed
+ if(packet_count>100)
+ {//~1sec
+// seq_last_sent = 0;
+// seq_last_rcvd = 8;
+ FrX_send_seq = 0x08 ;
+// FrX_receive_seq = 0 ;
+ packet_count=0;
+ #if defined TELEMETRY
+ telemetry_lost=1;
+ #endif
+ }
+ CC2500_Strobe(CC2500_SFRX); //flush the RXFIFO
+ }
+ frskyX_data_frame();
+ if ( FrX_send_seq != 0x08 )
+ {
+ FrX_send_seq = ( FrX_send_seq + 1 ) & 0x03 ;
+ }
+ state = FRSKY_DATA1;
+ return 500;
+ }
+ return 1;
+}
+
+uint16_t initFrSkyX()
+{
+ set_rx_tx_addr(MProtocol_id_master);
+ Frsky_init_hop();
+ packet_count=0;
+ while(!FrX_chanskip)
+ FrX_chanskip=random(0xfefefefe)%47;
+
+ //for test***************
+ //rx_tx_addr[3]=0xB3;
+ //rx_tx_addr[2]=0xFD;
+ //************************
+ frskyX_init();
+#if defined SPORT_POLLING
+#ifdef INVERT_SERIAL
+ start_timer4() ;
+#endif
+#endif
+ //
+ if(IS_BIND_IN_PROGRESS)
+ {
+ state = FRSKY_BIND;
+ frskyX_initialize_data(1);
+ }
+ else
+ {
+ state = FRSKY_DATA1;
+ frskyX_initialize_data(0);
+ }
+// seq_last_sent = 0;
+// seq_last_rcvd = 8;
+ FrX_send_seq = 0x08 ;
+ FrX_receive_seq = 0 ;
+ return 10000;
+}
+#endif
diff --git a/remote_multiprotocol/Multiprotocol.h b/remote_multiprotocol/Multiprotocol.h
new file mode 100644
index 0000000..d9cfb4c
--- /dev/null
+++ b/remote_multiprotocol/Multiprotocol.h
@@ -0,0 +1,808 @@
+/*
+ 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 .
+ */
+
+//******************
+// Version
+//******************
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 2
+#define VERSION_REVISION 1
+#define VERSION_PATCH_LEVEL 1
+
+//******************
+// Protocols
+//******************
+enum PROTOCOLS
+{
+ MODE_SERIAL = 0, // Serial commands
+ PROTO_FLYSKY = 1, // =>A7105
+ PROTO_HUBSAN = 2, // =>A7105
+ PROTO_FRSKYD = 3, // =>CC2500
+ PROTO_HISKY = 4, // =>NRF24L01
+ PROTO_V2X2 = 5, // =>NRF24L01
+ PROTO_DSM = 6, // =>CYRF6936
+ PROTO_DEVO = 7, // =>CYRF6936
+ PROTO_YD717 = 8, // =>NRF24L01
+ PROTO_KN = 9, // =>NRF24L01
+ PROTO_SYMAX = 10, // =>NRF24L01
+ PROTO_SLT = 11, // =>NRF24L01
+ PROTO_CX10 = 12, // =>NRF24L01
+ PROTO_CG023 = 13, // =>NRF24L01
+ PROTO_BAYANG = 14, // =>NRF24L01
+ PROTO_FRSKYX = 15, // =>CC2500
+ PROTO_ESKY = 16, // =>NRF24L01
+ PROTO_MT99XX = 17, // =>NRF24L01
+ PROTO_MJXQ = 18, // =>NRF24L01
+ PROTO_SHENQI = 19, // =>NRF24L01
+ PROTO_FY326 = 20, // =>NRF24L01
+ PROTO_SFHSS = 21, // =>CC2500
+ PROTO_J6PRO = 22, // =>CYRF6936
+ PROTO_FQ777 = 23, // =>NRF24L01
+ PROTO_ASSAN = 24, // =>NRF24L01
+ PROTO_FRSKYV = 25, // =>CC2500
+ PROTO_HONTAI = 26, // =>NRF24L01
+ PROTO_OPENLRS = 27, // =>OpenLRS hardware
+ PROTO_AFHDS2A = 28, // =>A7105
+ PROTO_Q2X2 = 29, // =>NRF24L01, extension of CX-10 protocol
+ PROTO_WK2x01 = 30, // =>CYRF6936
+ PROTO_Q303 = 31, // =>NRF24L01
+ PROTO_GW008 = 32, // =>NRF24L01
+ PROTO_DM002 = 33, // =>NRF24L01
+ PROTO_CABELL = 34, // =>NRF24L01
+ PROTO_ESKY150 = 35, // =>NRF24L01
+ PROTO_H8_3D = 36, // =>NRF24L01
+ PROTO_CORONA = 37, // =>CC2500
+ PROTO_CFLIE = 38, // =>NRF24L01
+ PROTO_HITEC = 39, // =>CC2500
+ PROTO_WFLY = 40, // =>CYRF6936
+ PROTO_BUGS = 41, // =>A7105
+};
+
+enum Flysky
+{
+ Flysky = 0,
+ V9X9 = 1,
+ V6X6 = 2,
+ V912 = 3,
+ CX20 = 4,
+};
+enum Hubsan
+{
+ H107 = 0,
+ H301 = 1,
+ H501 = 2,
+};
+enum AFHDS2A
+{
+ PWM_IBUS = 0,
+ PPM_IBUS = 1,
+ PWM_SBUS = 2,
+ PPM_SBUS = 3,
+};
+enum Hisky
+{
+ Hisky = 0,
+ HK310 = 1,
+};
+enum DSM
+{
+ DSM2_22 = 0,
+ DSM2_11 = 1,
+ DSMX_22 = 2,
+ DSMX_11 = 3,
+ DSM_AUTO = 4,
+};
+enum YD717
+{
+ YD717 = 0,
+ SKYWLKR = 1,
+ SYMAX4 = 2,
+ XINXUN = 3,
+ NIHUI = 4,
+};
+enum KN
+{
+ WLTOYS = 0,
+ FEILUN = 1,
+};
+enum SYMAX
+{
+ SYMAX = 0,
+ SYMAX5C = 1,
+};
+enum SLT
+{
+ SLT_V1 = 0,
+ SLT_V2 = 1,
+ Q100 = 2,
+ Q200 = 3,
+ MR100 = 4,
+};
+enum CX10
+{
+ CX10_GREEN = 0,
+ CX10_BLUE = 1, // also compatible with CX10-A, CX12
+ DM007 = 2,
+ JC3015_1 = 4,
+ JC3015_2 = 5,
+ MK33041 = 6,
+};
+enum Q2X2
+{
+ Q222 = 0,
+ Q242 = 1,
+ Q282 = 2,
+ F_Q222 = 8,
+ F_Q242 = 9,
+ F_Q282 = 10,
+};
+enum CG023
+{
+ CG023 = 0,
+ YD829 = 1,
+};
+enum BAYANG
+{
+ BAYANG = 0,
+ H8S3D = 1,
+ X16_AH = 2,
+ IRDRONE = 3,
+};
+enum MT99XX
+{
+ MT99 = 0,
+ H7 = 1,
+ YZ = 2,
+ LS = 3,
+ FY805 = 4,
+};
+enum MJXQ
+{
+ WLH08 = 0,
+ X600 = 1,
+ X800 = 2,
+ H26D = 3,
+ E010 = 4,
+ H26WH = 5,
+};
+enum FRSKYX
+{
+ CH_16 = 0,
+ CH_8 = 1,
+ EU_16 = 2,
+ EU_8 = 3,
+};
+enum HONTAI
+{
+ HONTAI = 0,
+ JJRCX1 = 1,
+ X5C1 = 2,
+ FQ777_951 =3,
+};
+enum V2X2
+{
+ V2X2 = 0,
+ JXD506 = 1,
+};
+enum FY326
+{
+ FY326 = 0,
+ FY319 = 1,
+};
+enum WK2x01
+{
+ WK2801 = 0,
+ WK2401 = 1,
+ W6_5_1 = 2,
+ W6_6_1 = 3,
+ W6_HEL = 4,
+ W6_HEL_I= 5,
+};
+enum Q303
+{
+ Q303 = 0,
+ CX35 = 1,
+ CX10D = 2,
+ CX10WD = 3,
+};
+enum CABELL
+{
+ CABELL_V3 = 0,
+ CABELL_V3_TELEMETRY = 1,
+ CABELL_SET_FAIL_SAFE= 6,
+ CABELL_UNBIND = 7,
+};
+enum H8_3D
+{
+ H8_3D = 0,
+ H20H = 1,
+ H20MINI = 2,
+ H30MINI = 3,
+};
+enum CORONA
+{
+ COR_V1 = 0,
+ COR_V2 = 1,
+ FD_V3 = 2,
+};
+enum HITEC
+{
+ OPT_FW = 0,
+ OPT_HUB = 1,
+ MINIMA = 2,
+};
+
+#define NONE 0
+#define P_HIGH 1
+#define P_LOW 0
+#define AUTOBIND 1
+#define NO_AUTOBIND 0
+
+struct PPM_Parameters
+{
+ uint8_t protocol : 6;
+ uint8_t sub_proto : 3;
+ uint8_t rx_num : 4;
+ uint8_t power : 1;
+ uint8_t autobind : 1;
+ uint8_t option;
+};
+
+// Telemetry
+
+enum MultiPacketTypes
+{
+ MULTI_TELEMETRY_STATUS = 1,
+ MULTI_TELEMETRY_SPORT = 2,
+ MULTI_TELEMETRY_HUB = 3,
+ MULTI_TELEMETRY_DSM = 4,
+ MULTI_TELEMETRY_DSMBIND = 5,
+ MULTI_TELEMETRY_AFHDS2A = 6,
+ MULTI_TELEMETRY_CONFIG = 7,
+ MULTI_TELEMETRY_SYNC = 8,
+ MULTI_TELEMETRY_SPORT_POLLING = 9,
+ MULTI_TELEMETRY_HITEC = 10,
+};
+
+// Macros
+#define NOP() __asm__ __volatile__("nop")
+
+//***************
+//*** Flags ***
+//***************
+#define RX_FLAG_on protocol_flags |= _BV(0)
+#define RX_FLAG_off protocol_flags &= ~_BV(0)
+#define IS_RX_FLAG_on ( ( protocol_flags & _BV(0) ) !=0 )
+//
+#define CHANGE_PROTOCOL_FLAG_on protocol_flags |= _BV(1)
+#define CHANGE_PROTOCOL_FLAG_off protocol_flags &= ~_BV(1)
+#define IS_CHANGE_PROTOCOL_FLAG_on ( ( protocol_flags & _BV(1) ) !=0 )
+//
+#define POWER_FLAG_on protocol_flags |= _BV(2)
+#define POWER_FLAG_off protocol_flags &= ~_BV(2)
+#define IS_POWER_FLAG_on ( ( protocol_flags & _BV(2) ) !=0 )
+//
+#define RANGE_FLAG_on protocol_flags |= _BV(3)
+#define RANGE_FLAG_off protocol_flags &= ~_BV(3)
+#define IS_RANGE_FLAG_on ( ( protocol_flags & _BV(3) ) !=0 )
+//
+#define AUTOBIND_FLAG_on protocol_flags |= _BV(4)
+#define AUTOBIND_FLAG_off protocol_flags &= ~_BV(4)
+#define IS_AUTOBIND_FLAG_on ( ( protocol_flags & _BV(4) ) !=0 )
+//
+#define BIND_BUTTON_FLAG_on protocol_flags |= _BV(5)
+#define BIND_BUTTON_FLAG_off protocol_flags &= ~_BV(5)
+#define IS_BIND_BUTTON_FLAG_on ( ( protocol_flags & _BV(5) ) !=0 )
+//PPM RX OK
+#define PPM_FLAG_off protocol_flags &= ~_BV(6)
+#define PPM_FLAG_on protocol_flags |= _BV(6)
+#define IS_PPM_FLAG_on ( ( protocol_flags & _BV(6) ) !=0 )
+//Bind flag
+#define BIND_IN_PROGRESS protocol_flags &= ~_BV(7)
+#define BIND_DONE protocol_flags |= _BV(7)
+#define IS_BIND_DONE ( ( protocol_flags & _BV(7) ) !=0 )
+#define IS_BIND_IN_PROGRESS ( ( protocol_flags & _BV(7) ) ==0 )
+//
+#define FAILSAFE_VALUES_off protocol_flags2 &= ~_BV(0)
+#define FAILSAFE_VALUES_on protocol_flags2 |= _BV(0)
+#define IS_FAILSAFE_VALUES_on ( ( protocol_flags2 & _BV(0) ) !=0 )
+//
+#define RX_DONOTUPDATE_off protocol_flags2 &= ~_BV(1)
+#define RX_DONOTUPDATE_on protocol_flags2 |= _BV(1)
+#define IS_RX_DONOTUPDATE_on ( ( protocol_flags2 & _BV(1) ) !=0 )
+//
+#define RX_MISSED_BUFF_off protocol_flags2 &= ~_BV(2)
+#define RX_MISSED_BUFF_on protocol_flags2 |= _BV(2)
+#define IS_RX_MISSED_BUFF_on ( ( protocol_flags2 & _BV(2) ) !=0 )
+//TX Pause
+#define TX_MAIN_PAUSE_off protocol_flags2 &= ~_BV(3)
+#define TX_MAIN_PAUSE_on protocol_flags2 |= _BV(3)
+#define IS_TX_MAIN_PAUSE_on ( ( protocol_flags2 & _BV(3) ) !=0 )
+#define TX_RX_PAUSE_off protocol_flags2 &= ~_BV(4)
+#define TX_RX_PAUSE_on protocol_flags2 |= _BV(4)
+#define IS_TX_RX_PAUSE_on ( ( protocol_flags2 & _BV(4) ) !=0 )
+#define IS_TX_PAUSE_on ( ( protocol_flags2 & (_BV(4)|_BV(3)) ) !=0 )
+//Signal OK
+#define INPUT_SIGNAL_off protocol_flags2 &= ~_BV(5)
+#define INPUT_SIGNAL_on protocol_flags2 |= _BV(5)
+#define IS_INPUT_SIGNAL_on ( ( protocol_flags2 & _BV(5) ) !=0 )
+#define IS_INPUT_SIGNAL_off ( ( protocol_flags2 & _BV(5) ) ==0 )
+//Bind from channel
+#define BIND_CH_PREV_off protocol_flags2 &= ~_BV(6)
+#define BIND_CH_PREV_on protocol_flags2 |= _BV(6)
+#define IS_BIND_CH_PREV_on ( ( protocol_flags2 & _BV(6) ) !=0 )
+#define IS_BIND_CH_PREV_off ( ( protocol_flags2 & _BV(6) ) ==0 )
+//Wait for bind
+#define WAIT_BIND_off protocol_flags2 &= ~_BV(7)
+#define WAIT_BIND_on protocol_flags2 |= _BV(7)
+#define IS_WAIT_BIND_on ( ( protocol_flags2 & _BV(7) ) !=0 )
+#define IS_WAIT_BIND_off ( ( protocol_flags2 & _BV(7) ) ==0 )
+
+// Failsafe
+#define FAILSAFE_CHANNEL_HOLD 2047
+#define FAILSAFE_CHANNEL_NOPULSES 0
+
+//********************
+//** Debug messages **
+//********************
+ uint16_t debug_time=0;
+ #define debug(msg, ...) {char buf[64]; sprintf(buf, msg, ##__VA_ARGS__); Serial.println(buf);}
+ #define debugln(msg, ...) {char buf[64]; sprintf(buf, msg "\r\n", ##__VA_ARGS__); Serial.println(buf);}
+ #define debug_time(msg) { uint16_t debug_time_TCNT1=TCNT1; debug_time=debug_time_TCNT1-debug_time; debugln(msg "%u", debug_time); debug_time=debug_time_TCNT1; }
+//********************
+//*** Blink timing ***
+//********************
+#define BLINK_BIND_TIME 100
+#define BLINK_SERIAL_TIME 500
+#define BLINK_PPM_TIME 1000
+#define BLINK_BAD_PROTO_TIME_HIGH 50
+#define BLINK_BAD_PROTO_TIME_LOW 1000
+#define BLINK_WAIT_BIND_TIME_HIGH 1000
+#define BLINK_WAIT_BIND_TIME_LOW 100
+#define BLINK_BANK_TIME_HIGH 50
+#define BLINK_BANK_TIME_LOW 500
+#define BLINK_BANK_REPEAT 1500
+
+//*******************
+//*** AUX flags ***
+//*******************
+#define GET_FLAG(ch, mask) ( ch ? mask : 0)
+#define CH5_SW (Channel_AUX & _BV(0))
+#define CH6_SW (Channel_AUX & _BV(1))
+#define CH7_SW (Channel_AUX & _BV(2))
+#define CH8_SW (Channel_AUX & _BV(3))
+#define CH9_SW (Channel_AUX & _BV(4))
+#define CH10_SW (Channel_AUX & _BV(5))
+#define CH11_SW (Channel_AUX & _BV(6))
+#define CH12_SW (Channel_AUX & _BV(7))
+#define CH13_SW (Channel_data[CH13]>CHANNEL_SWITCH)
+#define CH14_SW (Channel_data[CH14]>CHANNEL_SWITCH)
+#define CH15_SW (Channel_data[CH15]>CHANNEL_SWITCH)
+#define CH16_SW (Channel_data[CH16]>CHANNEL_SWITCH)
+
+//************************
+//*** Power settings ***
+//************************
+enum {
+ TXPOWER_100uW,
+ TXPOWER_300uW,
+ TXPOWER_1mW,
+ TXPOWER_3mW,
+ TXPOWER_10mW,
+ TXPOWER_30mW,
+ TXPOWER_100mW,
+ TXPOWER_150mW
+};
+
+// A7105 power
+// Power amp is ~+16dBm so:
+enum A7105_POWER
+{
+ A7105_POWER_0 = 0x00<<3 | 0x00, // TXPOWER_100uW = -23dBm == PAC=0 TBG=0
+ A7105_POWER_1 = 0x00<<3 | 0x01, // TXPOWER_300uW = -20dBm == PAC=0 TBG=1
+ A7105_POWER_2 = 0x00<<3 | 0x02, // TXPOWER_1mW = -16dBm == PAC=0 TBG=2
+ A7105_POWER_3 = 0x00<<3 | 0x04, // TXPOWER_3mW = -11dBm == PAC=0 TBG=4
+ A7105_POWER_4 = 0x01<<3 | 0x05, // TXPOWER_10mW = -6dBm == PAC=1 TBG=5
+ A7105_POWER_5 = 0x02<<3 | 0x07, // TXPOWER_30mW = 0dBm == PAC=2 TBG=7
+ A7105_POWER_6 = 0x03<<3 | 0x07, // TXPOWER_100mW = 1dBm == PAC=3 TBG=7
+ A7105_POWER_7 = 0x03<<3 | 0x07 // TXPOWER_150mW = 1dBm == PAC=3 TBG=7
+};
+#define A7105_HIGH_POWER A7105_POWER_7
+#define A7105_LOW_POWER A7105_POWER_3
+#define A7105_RANGE_POWER A7105_POWER_0
+#define A7105_BIND_POWER A7105_POWER_0
+
+// NRF Power
+// Power setting is 0..3 for nRF24L01
+// Claimed power amp for nRF24L01 from eBay is 20dBm.
+enum NRF_POWER
+{ // Raw w 20dBm PA
+ NRF_POWER_0 = 0x00, // 0 : -18dBm (16uW) 2dBm (1.6mW)
+ NRF_POWER_1 = 0x01, // 1 : -12dBm (60uW) 8dBm (6mW)
+ NRF_POWER_2 = 0x02, // 2 : -6dBm (250uW) 14dBm (25mW)
+ NRF_POWER_3 = 0x03 // 3 : 0dBm (1mW) 20dBm (100mW)
+};
+#define NRF_HIGH_POWER NRF_POWER_3
+#define NRF_LOW_POWER NRF_POWER_1
+#define NRF_RANGE_POWER NRF_POWER_0
+#define NRF_BIND_POWER NRF_POWER_0
+
+// CC2500 power output from the chip itself
+// The numbers do not take into account any outside amplifier
+enum CC2500_POWER
+{
+ CC2500_POWER_0 = 0x00, // -55dbm or less
+ CC2500_POWER_1 = 0x50, // -30dbm
+ CC2500_POWER_2 = 0x44, // -28dbm
+ CC2500_POWER_3 = 0xC0, // -26dbm
+ CC2500_POWER_4 = 0x84, // -24dbm
+ CC2500_POWER_5 = 0x81, // -22dbm
+ CC2500_POWER_6 = 0x46, // -20dbm
+ CC2500_POWER_7 = 0x93, // -18dbm
+ CC2500_POWER_8 = 0x55, // -16dbm
+ CC2500_POWER_9 = 0x8D, // -14dbm
+ CC2500_POWER_10 = 0xC6, // -12dbm
+ CC2500_POWER_11 = 0x97, // -10dbm
+ CC2500_POWER_12 = 0x6E, // -8dbm
+ CC2500_POWER_13 = 0x7F, // -6dbm
+ CC2500_POWER_14 = 0xA9, // -4dbm
+ CC2500_POWER_15 = 0xBB, // -2dbm
+ CC2500_POWER_16 = 0xFE, // 0dbm
+ CC2500_POWER_17 = 0xFF // +1dbm
+};
+#define CC2500_HIGH_POWER CC2500_POWER_17
+#define CC2500_LOW_POWER CC2500_POWER_13
+#define CC2500_RANGE_POWER CC2500_POWER_1
+#define CC2500_BIND_POWER CC2500_POWER_1
+
+// CYRF power
+enum CYRF_POWER
+{
+ CYRF_POWER_0 = 0x00, // -35dbm
+ CYRF_POWER_1 = 0x01, // -30dbm
+ CYRF_POWER_2 = 0x02, // -24dbm
+ CYRF_POWER_3 = 0x03, // -18dbm
+ CYRF_POWER_4 = 0x04, // -13dbm
+ CYRF_POWER_5 = 0x05, // -5dbm
+ CYRF_POWER_6 = 0x06, // 0dbm
+ CYRF_POWER_7 = 0x07 // +4dbm
+};
+#define CYRF_HIGH_POWER CYRF_POWER_7
+#define CYRF_LOW_POWER CYRF_POWER_3
+#define CYRF_RANGE_POWER CYRF_POWER_1 // 1/30 of the full power distance
+#define CYRF_BIND_POWER CYRF_POWER_0
+
+enum TXRX_State {
+ TXRX_OFF,
+ TX_EN,
+ RX_EN
+};
+
+// Packet ack status values
+enum {
+ PKT_PENDING = 0,
+ PKT_ACKED,
+ PKT_TIMEOUT
+};
+
+// baudrate defines for serial
+#define SPEED_100K 0
+#define SPEED_9600 1
+#define SPEED_57600 2
+#define SPEED_125K 3
+
+/** EEPROM Layout */
+#define EEPROM_ID_OFFSET 10 // Module ID (4 bytes)
+#define EEPROM_BANK_OFFSET 15 // Current bank number (1 byte)
+#define EEPROM_ID_VALID_OFFSET 20 // 1 byte flag that ID is valid
+#define MODELMODE_EEPROM_OFFSET 30 // Autobind mode, 1 byte per model, end is 30+16=46
+#define AFHDS2A_EEPROM_OFFSET 50 // RX ID, 4 byte per model id, end is 50+64=114
+#define BUGS_EEPROM_OFFSET 114 // RX ID, 4 byte per model id, end is 114+64=178
+//#define CONFIG_EEPROM_OFFSET 178 // Current configuration of the multimodule
+
+//****************************************
+//*** MULTI protocol serial definition ***
+//****************************************
+/*
+**************************
+16 channels serial protocol
+**************************
+Serial: 100000 Baud 8e2 _ xxxx xxxx p --
+ Total of 26 bytes
+ Stream[0] = 0x55 sub_protocol values are 0..31 Stream contains channels
+ Stream[0] = 0x54 sub_protocol values are 32..63 Stream contains channels
+ Stream[0] = 0x57 sub_protocol values are 0..31 Stream contains failsafe
+ Stream[0] = 0x56 sub_protocol values are 32..63 Stream contains failsafe
+ header
+ Stream[1] = sub_protocol|BindBit|RangeCheckBit|AutoBindBit;
+ sub_protocol is 0..31 (bits 0..4), value should be added with 32 if Stream[0] = 0x54
+ => Reserved 0
+ Flysky 1
+ Hubsan 2
+ FrskyD 3
+ Hisky 4
+ V2x2 5
+ DSM 6
+ Devo 7
+ YD717 8
+ KN 9
+ SymaX 10
+ SLT 11
+ CX10 12
+ CG023 13
+ Bayang 14
+ FrskyX 15
+ ESky 16
+ MT99XX 17
+ MJXQ 18
+ SHENQI 19
+ FY326 20
+ SFHSS 21
+ J6PRO 22
+ FQ777 23
+ ASSAN 24
+ FrskyV 25
+ HONTAI 26
+ OpenLRS 27
+ AFHDS2A 28
+ Q2X2 29
+ WK2x01 30
+ Q303 31
+ GW008 32
+ DM002 33
+ CABELL 34
+ ESKY150 35
+ H8_3D 36
+ CORONA 37
+ CFlie 38
+ Hitec 39
+ WFLY 40
+ BUGS 41
+ BindBit=> 0x80 1=Bind/0=No
+ AutoBindBit=> 0x40 1=Yes /0=No
+ RangeCheck=> 0x20 1=Yes /0=No
+ Stream[2] = RxNum | Power | Type;
+ RxNum value is 0..15 (bits 0..3)
+ Type is 0..7 <<4 (bit 4..6)
+ sub_protocol==Flysky
+ Flysky 0
+ V9x9 1
+ V6x6 2
+ V912 3
+ CX20 4
+ sub_protocol==Hubsan
+ H107 0
+ H301 1
+ H501 2
+ sub_protocol==Hisky
+ Hisky 0
+ HK310 1
+ sub_protocol==DSM
+ DSM2_22 0
+ DSM2_11 1
+ DSMX_22 2
+ DSMX_11 3
+ DSM_AUTO 4
+ sub_protocol==YD717
+ YD717 0
+ SKYWLKR 1
+ SYMAX4 2
+ XINXUN 3
+ NIHUI 4
+ sub_protocol==KN
+ WLTOYS 0
+ FEILUN 1
+ sub_protocol==SYMAX
+ SYMAX 0
+ SYMAX5C 1
+ sub_protocol==CX10
+ CX10_GREEN 0
+ CX10_BLUE 1 // also compatible with CX10-A, CX12
+ DM007 2
+ --- 3
+ JC3015_1 4
+ JC3015_2 5
+ MK33041 6
+ sub_protocol==Q2X2
+ Q222 0
+ Q242 1
+ Q282 2
+ sub_protocol==CG023
+ CG023 0
+ YD829 1
+ sub_protocol==BAYANG
+ BAYANG 0
+ H8S3D 1
+ X16_AH 2
+ IRDRONE 3
+ sub_protocol==MT99XX
+ MT99 0
+ H7 1
+ YZ 2
+ LS 3
+ FY805 4
+ sub_protocol==MJXQ
+ WLH08 0
+ X600 1
+ X800 2
+ H26D 3
+ E010 4
+ H26WH 5
+ sub_protocol==FRSKYX
+ CH_16 0
+ CH_8 1
+ EU_16 2
+ EU_8 3
+ sub_protocol==HONTAI
+ HONTAI 0
+ JJRCX1 1
+ X5C1 2
+ FQ777_951 3
+ sub_protocol==AFHDS2A
+ PWM_IBUS 0
+ PPM_IBUS 1
+ PWM_SBUS 2
+ PPM_SBUS 3
+ sub_protocol==V2X2
+ V2X2 0
+ JXD506 1
+ sub_protocol==FY326
+ FY326 0
+ FY319 1
+ sub_protocol==WK2x01
+ WK2801 0
+ WK2401 1
+ W6_5_1 2
+ W6_6_1 3
+ W6_HEL 4
+ W6_HEL_I 5
+ sub_protocol==Q303
+ Q303 0
+ CX35 1
+ CX10D 2
+ CX10WD 3
+ sub_protocol==CABELL
+ CABELL_V3 0
+ CABELL_V3_TELEMETRY 1
+ CABELL_SET_FAIL_SAFE 6
+ CABELL_UNBIND 7
+ sub_protocol==H8_3D
+ H8_3D 0
+ H20H 1
+ H20MINI 2
+ H30MINI 3
+ sub_protocol==CORONA
+ COR_V1 0
+ COR_V2 1
+ FD_V3 2
+ sub_protocol==HITEC
+ OPT_FW 0
+ OPT_HUB 1
+ MINIMA 2
+ sub_protocol==SLT
+ SLT_V1 0
+ SLT_V2 1
+ Q100 2
+ Q200 3
+ MR100 4
+
+ Power value => 0x80 0=High/1=Low
+ Stream[3] = option_protocol;
+ option_protocol value is -128..127
+ Stream[4] to [25] = Channels or failsafe depending on Steam[0]
+ 16 Channels on 11 bits (0..2047)
+ 0 -125%
+ 204 -100%
+ 1024 0%
+ 1843 +100%
+ 2047 +125%
+ Values are concatenated to fit in 22 bytes like in SBUS protocol.
+ Failsafe values have exactly the same range/values than normal channels except the extremes where
+ 0=hold, 2047=no pulse. If failsafe is not set or RX then failsafe packets should not be sent.
+*/
+/*
+ Multimodule Status
+ Based on #define MULTI_STATUS
+
+ Serial: 100000 Baud 8e2 (same as input)
+
+ Format: header (2 bytes) + data (variable)
+ [0] = 'M' (0x4d)
+ [1] Length (excluding the 2 header bytes)
+ [2-xx] data
+
+ Type = 0x01 Multimodule Status:
+ [2] Flags
+ 0x01 = Input signal detected
+ 0x02 = Serial mode enabled
+ 0x04 = Protocol is valid
+ 0x08 = Module is in binding mode
+ 0x10 = Module waits a bind event to load the protocol
+ 0x20 = Failsafe supported by currently running protocol
+ [3] major
+ [4] minor
+ [5] revision
+ [6] patchlevel,
+ version of multi code, should be displayed as major.minor.revision.patchlevel
+*/
+/*
+ Multiprotocol telemetry/command definition for OpenTX
+ Based on #define MULTI_TELEMETRY enables OpenTX to get the multimodule status and select the correct telemetry type automatically.
+
+ Serial: 100000 Baud 8e2 (same as input)
+
+ TLV Protocol (type, length, value), allows a TX to ignore unknown messages
+
+ Format: header (4 byte) + data (variable)
+ [0] = 'M' (0x4d)
+ [1] = 'P' (0x50)
+
+ The first byte is deliberatly chosen to be different from other telemetry protocols
+ (e.g. 0xAA for DSM/Multi, 0xAA for FlySky and 0x7e for Frsky) to allow a TX to detect
+ the telemetry format of older versions
+
+ [2] Type (see below)
+ [3] Length (excluding the 4 header bytes)
+
+ [4-xx] data
+
+ Commands from TX to multi cannot be longer than 22 bytes (RXLen -4byte header)
+
+ Type = 0x01 Multimodule Status:
+ [4] Flags
+ 0x01 = Input signal detected
+ 0x02 = Serial mode enabled
+ 0x04 = protocol is valid
+ 0x08 = module is in binding mode
+ 0x10 = module waits a bind event to load the protocol
+ [5] major
+ [6] minor
+ [7] revision
+ [8] patchlevel,
+ version of multi code, should be displayed as major.minor.revision.patchlevel
+
+ more information can be added by specifing a longer length of the type, the TX will just ignore these bytes
+
+
+ Type 0x02 Frksy S.port telemetry
+ Type 0x03 Frsky Hub telemetry
+
+ *No* usual frsky byte stuffing and without start/stop byte (0x7e)
+
+
+ Type 0x04 Spektrum telemetry data
+ data[0] TX RSSI
+ data[1-15] telemetry data
+
+ Type 0x05 DSM bind data
+ data[0-16] DSM bind data
+
+ technically DSM bind data is only 10 bytes but multi sends 16
+ like with telemtery, check length field)
+
+ Type 0x06 Flysky AFHDS2 telemetry data
+ length: 29
+ data[0] = RSSI value
+ data[1-28] telemetry data
+
+ Type 0x0A Hitec telemetry data
+ length: 8
+ data[0] = TX RSSI value
+ data[1] = TX LQI value
+ data[2] = frame number
+ data[3-7] telemetry data
+ Full description at the bottom of Hitec_cc2500.ino
+
+*/
diff --git a/remote_multiprotocol/Multiprotocol.ino b/remote_multiprotocol/Multiprotocol.ino
new file mode 100644
index 0000000..7bcd1c4
--- /dev/null
+++ b/remote_multiprotocol/Multiprotocol.ino
@@ -0,0 +1,1237 @@
+/*********************************************************
+ Multiprotocol Tx code
+ by Midelic and Pascal Langer(hpnuts)
+ http://www.rcgroups.com/forums/showthread.php?t=2165676
+ https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/edit/master/README.md
+
+ Thanks to PhracturedBlue, Hexfet, Goebish, Victzh and all protocol developers
+ Ported from deviation firmware
+
+ 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 .
+*/
+#include
+
+//#define DEBUG_PIN // Use pin TX for AVR and SPI_CS for STM32 => DEBUG_PIN_on, DEBUG_PIN_off, DEBUG_PIN_toggle
+#define DEBUG_SERIAL // Only for STM32_BOARD compiled with Upload method "Serial"->usart1, "STM32duino bootloader"->USB serial
+
+#if defined (ARDUINO_AVR_XMEGA32D4) || defined (ARDUINO_MULTI_ORANGERX)
+ #include "MultiOrange.h"
+#endif
+
+#include "Multiprotocol.h"
+
+//Multiprotocol module configuration file
+#include "_Config.h"
+
+#include "Pins.h"
+#include "TX_Def.h"
+#include "Validate.h"
+
+#include
+
+//Global constants/variables
+uint32_t MProtocol_id;//tx id,
+uint32_t MProtocol_id_master;
+uint32_t blink=0,last_signal=0;
+//
+uint16_t counter;
+uint8_t channel;
+uint8_t packet[40];
+
+#define NUM_CHN 16
+// Servo data
+uint16_t Channel_data[NUM_CHN];
+uint8_t Channel_AUX;
+#ifdef FAILSAFE_ENABLE
+ uint16_t Failsafe_data[NUM_CHN];
+#endif
+
+// Protocol variables
+uint8_t cyrfmfg_id[6];//for dsm2 and devo
+uint8_t rx_tx_addr[5];
+uint8_t rx_id[4];
+uint8_t phase;
+uint16_t bind_counter;
+uint8_t bind_phase;
+uint8_t binding_idx;
+uint16_t packet_period;
+uint8_t packet_count;
+uint8_t packet_sent;
+uint8_t packet_length;
+uint8_t hopping_frequency[50];
+uint8_t *hopping_frequency_ptr;
+uint8_t hopping_frequency_no=0;
+uint8_t rf_ch_num;
+uint8_t throttle, rudder, elevator, aileron;
+uint8_t flags;
+uint16_t crc;
+uint8_t crc8;
+uint16_t seed;
+uint16_t failsafe_count;
+uint16_t state;
+uint8_t len;
+
+#if defined(FRSKYX_CC2500_INO) || defined(SFHSS_CC2500_INO)
+ uint8_t calData[48];
+#endif
+
+//Channel mapping for protocols
+const uint8_t CH_AETR[]={AILERON, ELEVATOR, THROTTLE, RUDDER, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12, CH13, CH14, CH15, CH16};
+const uint8_t CH_TAER[]={THROTTLE, AILERON, ELEVATOR, RUDDER, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12, CH13, CH14, CH15, CH16};
+const uint8_t CH_RETA[]={RUDDER, ELEVATOR, THROTTLE, AILERON, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12, CH13, CH14, CH15, CH16};
+const uint8_t CH_EATR[]={ELEVATOR, AILERON, THROTTLE, RUDDER, CH5, CH6, CH7, CH8, CH9, CH10, CH11, CH12, CH13, CH14, CH15, CH16};
+
+// Mode_select variables
+uint8_t mode_select;
+uint8_t protocol_flags=0,protocol_flags2=0;
+
+#ifdef ENABLE_PPM
+// PPM variable
+volatile uint16_t PPM_data[NUM_CHN];
+volatile uint8_t PPM_chan_max=0;
+#endif
+
+#if not defined (ORANGE_TX) && not defined (STM32_BOARD)
+//Random variable
+volatile uint32_t gWDT_entropy=0;
+#endif
+
+//Serial protocol
+uint8_t sub_protocol;
+uint8_t protocol;
+uint8_t option;
+uint8_t cur_protocol[3];
+uint8_t prev_option;
+uint8_t prev_power=0xFD; // unused power value
+uint8_t RX_num;
+
+//Serial RX variables
+#define BAUD 100000
+#define RXBUFFER_SIZE 26
+volatile uint8_t rx_buff[RXBUFFER_SIZE];
+volatile uint8_t rx_ok_buff[RXBUFFER_SIZE];
+volatile uint8_t discard_frame = 0;
+
+// Telemetry
+#define MAX_PKT 29
+uint8_t pkt[MAX_PKT];//telemetry receiving packets
+
+// Callback
+typedef uint16_t (*void_function_t) (void);//pointer to a function with no parameters which return an uint16_t integer
+void_function_t remote_callback = 0;
+
+// Init
+void setup()
+{
+
+ eeprom_write_byte((EE_ADDR)EEPROM_BANK_OFFSET,0x00); // set bank to 0
+
+ // Setup diagnostic uart before anything else
+ #ifdef DEBUG_SERIAL
+ Serial.begin(115200,SERIAL_8N1);
+ while (!Serial); // Wait for ever for the serial port to connect...
+ debugln("Multiprotocol version: %d.%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH_LEVEL);
+ #endif
+
+ // General pinout
+ //ATMEGA328p
+ // all inputs
+ DDRB=0x00;DDRC=0x00;DDRD=0x00;
+ // outputs
+ SDI_output;
+ SCLK_output;
+ #ifdef A7105_CSN_pin
+ A7105_CSN_output;
+ #endif
+ #ifdef CC25_CSN_pin
+ CC25_CSN_output;
+ #endif
+ #ifdef CYRF_CSN_pin
+ CYRF_RST_output;
+ CYRF_CSN_output;
+ #endif
+ #ifdef NRF_CSN_pin
+ NRF_CSN_output;
+ #endif
+
+ // Timer1 config
+ TCCR1A = 0;
+ TCCR1B = (1 << CS11); //prescaler8, set timer1 to increment every 0.5us(16Mhz) and start timer
+
+ // Random
+ //random_init();
+
+ // Set Chip selects
+ #ifdef A7105_CSN_pin
+ A7105_CSN_on;
+ #endif
+ #ifdef CC25_CSN_pin
+ CC25_CSN_on;
+ #endif
+ #ifdef CYRF_CSN_pin
+ CYRF_CSN_on;
+ #endif
+ #ifdef NRF_CSN_pin
+ NRF_CSN_on;
+ #endif
+ // Set SPI lines
+ #ifdef STM32_BOARD
+ initSPI2();
+ #else
+ SDI_on;
+ SCLK_off;
+ #endif
+
+ //Wait for every component to start
+ delayMilliseconds(100);
+
+ // Read status of bind button
+ if( /*IS_BIND_BUTTON_on */ true)
+ {
+ BIND_BUTTON_FLAG_on; // If bind button pressed save the status
+ BIND_IN_PROGRESS; // Request bind
+ }
+ else
+ BIND_DONE;
+
+ // Read status of mode select binary switch
+ // after this mode_select will be one of {0000, 0001, ..., 1111}
+
+
+ String str;
+ debugln("select bank:");
+ str="";
+ while(true) {
+ if (Serial.available() > 0) {
+ char recieved = Serial.read();
+ if (recieved == '\n') {
+ int new_bank = atoi(str.c_str());
+ eeprom_write_byte((EE_ADDR)EEPROM_BANK_OFFSET,new_bank);
+ debugln("Bank selection %d", new_bank);
+ break;
+ }else {
+ str += recieved;
+ }
+ }
+ }
+
+ debugln("select mode:");
+ str="";
+ while(true) {
+ if (Serial.available() > 0) {
+ char recieved = Serial.read();
+ if (recieved == '\n') {
+ int new_mode = atoi(str.c_str());
+ debugln("Protocol selection switch reads as %d with \'%s\'", new_mode,str.c_str());
+ mode_select = new_mode;
+ break;
+ }else {
+ str += recieved;
+ }
+ }
+ }
+ debugln("Protocol selection switch reads as %d", mode_select);
+
+ #ifdef ENABLE_PPM
+ uint8_t bank=bank_switch();
+ #endif
+
+ // Set default channels' value
+ InitChannel();
+ InitPPM();
+
+ //Init RF modules
+ modules_reset();
+
+ randomSeed(42);
+
+ // Read or create protocol id
+ MProtocol_id_master=random_id(10,false);
+
+ debugln("Module Id: %lx", MProtocol_id_master);
+
+#ifdef ENABLE_PPM
+ //Protocol and interrupts initialization
+ if(mode_select != MODE_SERIAL)
+ { // PPM
+ uint8_t line=bank*14+mode_select-1;
+ protocol = PPM_prot[line].protocol;
+ cur_protocol[1] = protocol;
+ sub_protocol = PPM_prot[line].sub_proto;
+ RX_num = PPM_prot[line].rx_num;
+
+ debugln("protocol: %d", protocol);
+ switch(protocol) {
+ case PROTO_FRSKYD:
+ debugln("PROTO_FRSKYD");
+ break;
+ case PROTO_FRSKYX:
+ debugln("PROTO_FRSKYX");
+ break;
+ case PROTO_FRSKYV:
+ debugln("PROTO_FRSKYV");
+ break;
+ }
+ debugln("sub_protocol: %d", sub_protocol);
+
+ //Forced frequency tuning values for CC2500 protocols
+ #if defined(FORCE_FRSKYD_TUNING) && defined(FRSKYD_CC2500_INO)
+ if(protocol==PROTO_FRSKYD)
+ option = FORCE_FRSKYD_TUNING; // Use config-defined tuning value for FrSkyD
+ else
+ #endif
+ #if defined(FORCE_FRSKYV_TUNING) && defined(FRSKYV_CC2500_INO)
+ if(protocol==PROTO_FRSKYV)
+ option = FORCE_FRSKYV_TUNING; // Use config-defined tuning value for FrSkyV
+ else
+ #endif
+ #if defined(FORCE_FRSKYX_TUNING) && defined(FRSKYX_CC2500_INO)
+ if(protocol==PROTO_FRSKYX)
+ option = FORCE_FRSKYX_TUNING; // Use config-defined tuning value for FrSkyX
+ else
+ #endif
+ #if defined(FORCE_SFHSS_TUNING) && defined(SFHSS_CC2500_INO)
+ if (protocol==PROTO_SFHSS)
+ option = FORCE_SFHSS_TUNING; // Use config-defined tuning value for SFHSS
+ else
+ #endif
+ #if defined(FORCE_CORONA_TUNING) && defined(CORONA_CC2500_INO)
+ if (protocol==PROTO_CORONA)
+ option = FORCE_CORONA_TUNING; // Use config-defined tuning value for CORONA
+ else
+ #endif
+ #if defined(FORCE_HITEC_TUNING) && defined(HITEC_CC2500_INO)
+ if (protocol==PROTO_HITEC)
+ option = FORCE_HITEC_TUNING; // Use config-defined tuning value for HITEC
+ else
+ #endif
+ option = PPM_prot[line].option; // Use radio-defined option value
+
+ if(PPM_prot[line].power) POWER_FLAG_on;
+ if(PPM_prot[line].autobind)
+ {
+ AUTOBIND_FLAG_on;
+ BIND_IN_PROGRESS; // Force a bind at protocol startup
+ }
+ line++;
+
+ protocol_init();
+
+ #ifndef STM32_BOARD
+ //Configure PPM interrupt
+ #else
+ attachInterrupt(PPM_pin,PPM_decode,FALLING);
+ #endif
+
+ #if defined(TELEMETRY)
+ PPM_Telemetry_serial_init();// Configure serial for telemetry
+ #endif
+ }
+ else
+#endif //ENABLE_PPM
+ { // Serial
+ #ifdef ENABLE_SERIAL
+ for(uint8_t i=0;i<3;i++)
+ cur_protocol[i]=0;
+ protocol=0;
+ #ifdef CHECK_FOR_BOOTLOADER
+ Mprotocol_serial_init(1); // Configure serial and enable RX interrupt
+ #else
+ Mprotocol_serial_init(); // Configure serial and enable RX interrupt
+ #endif
+ #endif //ENABLE_SERIAL
+ }
+ debugln("Init complete");
+}
+
+// Main
+// Protocol scheduler
+void loop()
+{
+ uint16_t next_callback,diff=0xFFFF;
+ debugln("%s:%d",__func__, __LINE__);
+ while(1)
+ {
+ if(remote_callback==0 || IS_WAIT_BIND_on || diff>2*200)
+ {
+ do
+ {
+ Update_All();
+ }
+ while(remote_callback==0 || IS_WAIT_BIND_on);
+ }
+ #ifndef STM32_BOARD
+ if( (TIFR1 & OCF1A_bm) != 0)
+ {
+ cli(); // Disable global int due to RW of 16 bits registers
+ OCR1A=TCNT1; // Callback should already have been called... Use "now" as new sync point.
+ sei(); // Enable global int
+ }
+ else
+ while((TIFR1 & OCF1A_bm) == 0); // Wait before callback
+ #else
+
+ #endif
+
+ do
+ {
+ TX_MAIN_PAUSE_on;
+ tx_pause();
+ if(IS_INPUT_SIGNAL_on && remote_callback!=0)
+ next_callback=remote_callback();
+ else
+ next_callback=2000; // No PPM/serial signal check again in 2ms...
+ #if 0
+ delayMilliseconds(next_callback);
+ #else
+ TX_MAIN_PAUSE_off;
+ tx_resume();
+ while(next_callback>4000)
+ { // start to wait here as much as we can...
+ next_callback-=2000; // We will wait below for 2ms
+ cli(); // Disable global int due to RW of 16 bits registers
+ OCR1A += 2000*2 ; // set compare A for callback
+ #ifndef STM32_BOARD
+ TIFR1=OCF1A_bm; // clear compare A=callback flag
+ #else
+ TIMER2_BASE->SR = 0x1E5F & ~TIMER_SR_CC1IF; // Clear Timer2/Comp1 interrupt flag
+ #endif
+ sei(); // enable global int
+ if(Update_All()) // Protocol changed?
+ {
+ next_callback=0; // Launch new protocol ASAP
+ debugln("%s:%d Launch new protocol ASAP ",__func__, __LINE__);
+ break;
+ }
+ #ifndef STM32_BOARD
+ while((TIFR1 & OCF1A_bm) == 0); // wait 2ms...
+ #else
+ while((TIMER2_BASE->SR & TIMER_SR_CC1IF)==0);//2ms wait
+ #endif
+ }
+ // at this point we have a maximum of 4ms in next_callback
+ next_callback *= 2 ;
+ cli(); // Disable global int due to RW of 16 bits registers
+ OCR1A+= next_callback ; // set compare A for callback
+ #ifndef STM32_BOARD
+ TIFR1=OCF1A_bm; // clear compare A=callback flag
+ #else
+ TIMER2_BASE->SR = 0x1E5F & ~TIMER_SR_CC1IF; // Clear Timer2/Comp1 interrupt flag
+ #endif
+ diff=OCR1A-TCNT1; // compare timer and comparator
+
+ sei(); // enable global int
+ #endif
+ }
+ while(diff&0x8000); // Callback did not took more than requested time for next callback
+ // so we can launch Update_All before next callback
+ }
+}
+
+uint8_t Update_All()
+{
+ #ifdef ENABLE_PPM
+ INPUT_SIGNAL_on;
+ if(mode_select!=MODE_SERIAL && IS_PPM_FLAG_on) // PPM mode and a full frame has been received
+ {
+ debugln("%s:%d",__func__, __LINE__);
+ for(uint8_t i=0;iCHANNEL_MAX_125) val=CHANNEL_MAX_125;
+ Channel_data[i]=val;
+ }
+ PPM_FLAG_off; // wait for next frame before update
+ update_channels_aux();
+ INPUT_SIGNAL_on; // valid signal received
+ last_signal=millis();
+ }
+ #endif //ENABLE_PPM
+
+
+ #ifdef ENABLE_BIND_CH
+ if(IS_AUTOBIND_FLAG_on && IS_BIND_CH_PREV_off && Channel_data[BIND_CH-1]>CHANNEL_MAX_COMMAND && Channel_data[THROTTLE]<(CHANNEL_MIN_100+50))
+ { // Autobind is on and BIND_CH went up and Throttle is low
+ CHANGE_PROTOCOL_FLAG_on; //reload protocol
+ BIND_IN_PROGRESS; //enable bind
+
+ debugln("%s:%d set bind prog",__func__, __LINE__);
+ BIND_CH_PREV_on;
+ }
+ if(IS_AUTOBIND_FLAG_on && IS_BIND_CH_PREV_on && Channel_data[BIND_CH-1]2)
+ bind_counter=2;
+ }
+ #endif //ENABLE_BIND_CH
+
+ if(IS_CHANGE_PROTOCOL_FLAG_on)
+ {
+ debugln("%s:%d set bind prog",__func__, __LINE__);
+ // Protocol needs to be changed or relaunched for bind
+ protocol_init(); //init new protocol
+ return 1;
+ }
+ return 0;
+}
+
+// Update channels direction and Channel_AUX flags based on servo AUX positions
+static void update_channels_aux(void)
+{
+ //Reverse channels direction
+ #ifdef REVERSE_AILERON
+ reverse_channel(AILREON);
+ #endif
+ #ifdef REVERSE_ELEVATOR
+ reverse_channel(ELEVATOR);
+ #endif
+ #ifdef REVERSE_THROTTLE
+ reverse_channel(THROTTLE);
+ #endif
+ #ifdef REVERSE_RUDDER
+ reverse_channel(RUDDER);
+ #endif
+
+ //Calc AUX flags
+ Channel_AUX=0;
+ for(uint8_t i=0;i<8;i++)
+ if(Channel_data[CH5+i]>CHANNEL_SWITCH)
+ Channel_AUX|=1<=NBR_BANKS)
+ { // Wrong number of bank
+ eeprom_write_byte((EE_ADDR)EEPROM_BANK_OFFSET,0x00); // set bank to 0
+ bank=0;
+ }
+ debugln("Using bank %d", bank);
+
+ phase=3;
+ uint32_t check=millis();
+ blink=millis();
+ while(mode_select==15)
+ { //loop here if the dial is on position 15 for user to select the bank
+ if(blink>2) >= bank)
+ {
+ phase=0;
+ blink+=BLINK_BANK_REPEAT;
+ }
+ else
+ phase+=2;
+ break;
+ case 3:
+ blink+=BLINK_BANK_TIME_LOW;
+ phase=0;
+ break;
+ }
+ }
+ if(check=NBR_BANKS)
+ bank=0;
+ eeprom_write_byte((EE_ADDR)EEPROM_BANK_OFFSET,bank);
+ debugln("Using bank %d", bank);
+ phase=3;
+ blink+=BLINK_BANK_REPEAT;
+ check+=2*BLINK_BANK_REPEAT;
+ }
+ check+=1;
+ }
+ }
+ return bank;
+}
+#endif
+
+inline void tx_pause()
+{
+ #ifdef TELEMETRY
+ // Pause telemetry by disabling transmitter interrupt
+ #ifdef ORANGE_TX
+ USARTC0.CTRLA &= ~0x03 ;
+ #else
+ #ifndef BASH_SERIAL
+ #ifdef STM32_BOARD
+ USART3_BASE->CR1 &= ~ USART_CR1_TXEIE;
+ #else
+ UCSR0B &= ~_BV(UDRIE0);
+ #endif
+ #endif
+ #endif
+ #endif
+}
+
+inline void tx_resume()
+{
+ #ifdef TELEMETRY
+ // Resume telemetry by enabling transmitter interrupt
+ #ifndef SPORT_POLLING
+ if(!IS_TX_PAUSE_on)
+ #endif
+ {
+ #ifdef ORANGE_TX
+ cli() ;
+ USARTC0.CTRLA = (USARTC0.CTRLA & 0xFC) | 0x01 ;
+ sei() ;
+ #else
+ #ifndef BASH_SERIAL
+ #ifdef STM32_BOARD
+ USART3_BASE->CR1 |= USART_CR1_TXEIE;
+ #else
+ UCSR0B |= _BV(UDRIE0);
+ #endif
+ #else
+ resumeBashSerial();
+ #endif
+ #endif
+ }
+ #endif
+}
+
+// Protocol start
+static void protocol_init()
+{
+ static uint16_t next_callback;
+ if(IS_WAIT_BIND_off)
+ {
+ remote_callback = 0; // No protocol
+ next_callback=0; // Default is immediate call back
+ modules_reset(); // Reset all modules
+
+ //Set global ID and rx_tx_addr
+ MProtocol_id = RX_num + MProtocol_id_master;
+ set_rx_tx_addr(MProtocol_id);
+
+ #ifdef FAILSAFE_ENABLE
+ InitFailsafe();
+ #endif
+
+ blink=millis();
+
+ debugln("Protocol selected: %d, sub proto %d, rxnum %d, option %d", protocol, sub_protocol, RX_num, option);
+
+ switch(protocol) // Init the requested protocol
+ {
+ #ifdef CC2500_INSTALLED
+ #if defined(FRSKYD_CC2500_INO)
+ case PROTO_FRSKYD:
+ next_callback = initFrSky_2way();
+ remote_callback = ReadFrSky_2way;
+ break;
+ #endif
+ #if defined(FRSKYV_CC2500_INO)
+ case PROTO_FRSKYV:
+ next_callback = initFRSKYV();
+ remote_callback = ReadFRSKYV;
+ break;
+ #endif
+ #if defined(FRSKYX_CC2500_INO)
+ case PROTO_FRSKYX:
+ next_callback = initFrSkyX();
+ remote_callback = ReadFrSkyX;
+ break;
+ #endif
+ #endif
+ }
+ }
+
+ #if defined(WAIT_FOR_BIND) && defined(ENABLE_BIND_CH)
+ if( IS_AUTOBIND_FLAG_on && IS_BIND_CH_PREV_off && (cur_protocol[1]&0x80)==0 && mode_select == MODE_SERIAL)
+ { // Autobind is active but no bind requested by either BIND_CH or BIND. But do not wait if in PPM mode...
+ WAIT_BIND_on;
+ return;
+ }
+ #endif
+ WAIT_BIND_off;
+ CHANGE_PROTOCOL_FLAG_off;
+
+ if(next_callback>32000)
+ { // next_callback should not be more than 32767 so we will wait here...
+ uint16_t temp=(next_callback>>10)-2;
+ delayMilliseconds(temp);
+ next_callback-=temp<<10; // between 2-3ms left at this stage
+ }
+ cli(); // disable global int
+ OCR1A = TCNT1 + next_callback*2; // set compare A for callback
+ #ifndef STM32_BOARD
+ TIFR1 = OCF1A_bm ; // clear compare A flag
+ #else
+ TIMER2_BASE->SR = 0x1E5F & ~TIMER_SR_CC1IF; // Clear Timer2/Comp1 interrupt flag
+ #endif
+ sei(); // enable global int
+ BIND_BUTTON_FLAG_off; // do not bind/reset id anymore even if protocol change
+
+ debugln("%s BIND_BUTTON_FLAG_off",__func__);
+}
+
+void update_serial_data()
+{
+ RX_DONOTUPDATE_on;
+ RX_FLAG_off; //data is being processed
+ #ifdef SAMSON // Extremely dangerous, do not enable this unless you know what you are doing...
+ if( rx_ok_buff[0]==0x55 && (rx_ok_buff[1]&0x1F)==PROTO_FRSKYD && rx_ok_buff[2]==0x7F && rx_ok_buff[24]==217 && rx_ok_buff[25]==202 )
+ {//proto==FRSKYD+sub==7+rx_num==7+CH15==73%+CH16==73%
+ rx_ok_buff[1]=(rx_ok_buff[1]&0xE0) | PROTO_FLYSKY; // change the protocol to Flysky
+ memcpy((void*)(rx_ok_buff+4),(void*)(rx_ok_buff+4+11),11); // reassign channels 9-16 to 1-8
+ }
+ #endif
+ if(rx_ok_buff[1]&0x20) //check range
+ RANGE_FLAG_on;
+ else
+ RANGE_FLAG_off;
+ if(rx_ok_buff[1]&0x40) //check autobind
+ AUTOBIND_FLAG_on;
+ else
+ AUTOBIND_FLAG_off;
+ if(rx_ok_buff[2]&0x80) //if rx_ok_buff[2] ==1,power is low ,0-power high
+ POWER_FLAG_off; //power low
+ else
+ POWER_FLAG_on; //power high
+
+ //Forced frequency tuning values for CC2500 protocols
+ #if defined(FORCE_FRSKYD_TUNING) && defined(FRSKYD_CC2500_INO)
+ if(protocol==PROTO_FRSKYD)
+ option=FORCE_FRSKYD_TUNING; // Use config-defined tuning value for FrSkyD
+ else
+ #endif
+ #if defined(FORCE_FRSKYV_TUNING) && defined(FRSKYV_CC2500_INO)
+ if(protocol==PROTO_FRSKYV)
+ option=FORCE_FRSKYV_TUNING; // Use config-defined tuning value for FrSkyV
+ else
+ #endif
+ #if defined(FORCE_FRSKYX_TUNING) && defined(FRSKYX_CC2500_INO)
+ if(protocol==PROTO_FRSKYX)
+ option=FORCE_FRSKYX_TUNING; // Use config-defined tuning value for FrSkyX
+ else
+ #endif
+ #if defined(FORCE_SFHSS_TUNING) && defined(SFHSS_CC2500_INO)
+ if (protocol==PROTO_SFHSS)
+ option=FORCE_SFHSS_TUNING; // Use config-defined tuning value for SFHSS
+ else
+ #endif
+ #if defined(FORCE_CORONA_TUNING) && defined(CORONA_CC2500_INO)
+ if (protocol==PROTO_CORONA)
+ option=FORCE_CORONA_TUNING; // Use config-defined tuning value for CORONA
+ else
+ #endif
+ #if defined(FORCE_HITEC_TUNING) && defined(HITEC_CC2500_INO)
+ if (protocol==PROTO_HITEC)
+ option=FORCE_HITEC_TUNING; // Use config-defined tuning value for HITEC
+ else
+ #endif
+ option=rx_ok_buff[3]; // Use radio-defined option value
+
+ #ifdef FAILSAFE_ENABLE
+ bool failsafe=false;
+ if(rx_ok_buff[0]&0x02)
+ { // Packet contains failsafe instead of channels
+ failsafe=true;
+ rx_ok_buff[0]&=0xFD; //remove the failsafe flag
+ FAILSAFE_VALUES_on; //failsafe data has been received
+ }
+ #endif
+ #ifdef BONI
+ if(CH14_SW)
+ rx_ok_buff[2]=(rx_ok_buff[2]&0xF0)|((rx_ok_buff[2]+1)&0x0F); // Extremely dangerous, do not enable this!!! This is really for a special case...
+ #endif
+ if( (rx_ok_buff[0] != cur_protocol[0]) || ((rx_ok_buff[1]&0x5F) != (cur_protocol[1]&0x5F)) || ( (rx_ok_buff[2]&0x7F) != (cur_protocol[2]&0x7F) ) )
+ { // New model has been selected
+ CHANGE_PROTOCOL_FLAG_on; //change protocol
+ WAIT_BIND_off;
+ if((rx_ok_buff[1]&0x80)!=0 || IS_AUTOBIND_FLAG_on)
+ BIND_IN_PROGRESS; //launch bind right away if in autobind mode or bind is set
+ else
+ BIND_DONE;
+ protocol=(rx_ok_buff[0]==0x55?0:32) + (rx_ok_buff[1]&0x1F); //protocol no (0-63) bits 4-6 of buff[1] and bit 0 of buf[0]
+ sub_protocol=(rx_ok_buff[2]>>4)& 0x07; //subprotocol no (0-7) bits 4-6
+ RX_num=rx_ok_buff[2]& 0x0F; // rx_num bits 0---3
+ }
+ else
+ if( ((rx_ok_buff[1]&0x80)!=0) && ((cur_protocol[1]&0x80)==0) ) // Bind flag has been set
+ { // Restart protocol with bind
+ CHANGE_PROTOCOL_FLAG_on;
+ BIND_IN_PROGRESS;
+ }
+ else
+ if( ((rx_ok_buff[1]&0x80)==0) && ((cur_protocol[1]&0x80)!=0) ) // Bind flag has been reset
+ { // Request protocol to end bind
+ #if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYV_CC2500_INO)
+ if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYV)
+ BIND_DONE;
+ else
+ #endif
+ if(bind_counter>2)
+ bind_counter=2;
+ }
+
+ //store current protocol values
+ for(uint8_t i=0;i<3;i++)
+ cur_protocol[i] = rx_ok_buff[i];
+
+ // decode channel/failsafe values
+ volatile uint8_t *p=rx_ok_buff+3;
+ uint8_t dec=-3;
+ for(uint8_t i=0;i=8)
+ {
+ dec-=8;
+ p++;
+ }
+ p++;
+ uint16_t temp=((*((uint32_t *)p))>>dec)&0x7FF;
+ #ifdef FAILSAFE_ENABLE
+ if(failsafe)
+ Failsafe_data[i]=temp; //value range 0..2047, 0=no pulses, 2047=hold
+ else
+ #endif
+ Channel_data[i]=temp; //value range 0..2047, 0=-125%, 2047=+125%
+ }
+ RX_DONOTUPDATE_off;
+ cli();
+ if(IS_RX_MISSED_BUFF_on) // If the buffer is still valid
+ { memcpy((void*)rx_ok_buff,(const void*)rx_buff,RXBUFFER_SIZE);// Duplicate the buffer
+ RX_FLAG_on; // data to be processed next time...
+ RX_MISSED_BUFF_off;
+ }
+ sei();
+ #ifdef FAILSAFE_ENABLE
+ if(failsafe)
+ debugln("RX_FS:%d,%d,%d,%d",Failsafe_data[0],Failsafe_data[1],Failsafe_data[2],Failsafe_data[3]);
+ #endif
+}
+
+void modules_reset()
+{
+ #ifdef CC2500_INSTALLED
+ CC2500_Reset();
+ #endif
+ #ifdef A7105_INSTALLED
+ A7105_Reset();
+ #endif
+ #ifdef CYRF6936_INSTALLED
+ CYRF_Reset();
+ #endif
+ #ifdef NRF24L01_INSTALLED
+ NRF24L01_Reset();
+ #endif
+
+ //Wait for every component to reset
+ delayMilliseconds(100);
+ prev_power=0xFD; // unused power value
+}
+
+#ifdef STM32_BOARD
+ void usart2_begin(uint32_t baud,uint32_t config )
+ {
+ usart_init(USART2);
+ usart_config_gpios_async(USART2,GPIOA,PIN_MAP[PA3].gpio_bit,GPIOA,PIN_MAP[PA2].gpio_bit,config);
+ LED2_output;
+ usart_set_baud_rate(USART2, STM32_PCLK1, baud);
+ usart_enable(USART2);
+ }
+ void usart3_begin(uint32_t baud,uint32_t config )
+ {
+ usart_init(USART3);
+ usart_config_gpios_async(USART3,GPIOB,PIN_MAP[PB11].gpio_bit,GPIOB,PIN_MAP[PB10].gpio_bit,config);
+ usart_set_baud_rate(USART3, STM32_PCLK1, baud);
+ usart_enable(USART3);
+ }
+ void init_HWTimer()
+ {
+ HWTimer2.pause(); // Pause the timer2 while we're configuring it
+
+ TIMER2_BASE->PSC = 35; // 36-1;for 72 MHZ /0.5sec/(35+1)
+ TIMER2_BASE->ARR = 0xFFFF; // Count until 0xFFFF
+
+ HWTimer2.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); // Main scheduler
+ HWTimer2.setMode(TIMER_CH2, TIMER_OUTPUT_COMPARE); // Serial check
+
+ TIMER2_BASE->SR = 0x1E5F & ~TIMER_SR_CC2IF; // Clear Timer2/Comp2 interrupt flag
+ HWTimer2.attachInterrupt(TIMER_CH2,ISR_COMPB); // Assign function to Timer2/Comp2 interrupt
+ TIMER2_BASE->DIER &= ~TIMER_DIER_CC2IE; // Disable Timer2/Comp2 interrupt
+
+ HWTimer2.refresh(); // Refresh the timer's count, prescale, and overflow
+ HWTimer2.resume();
+ }
+#endif
+
+#ifdef CHECK_FOR_BOOTLOADER
+void pollBoot()
+{
+ uint8_t rxchar ;
+ uint8_t lState = BootState ;
+ uint8_t millisTime = millis(); // Call this once only
+
+ #ifdef ORANGE_TX
+ if ( USARTC0.STATUS & USART_RXCIF_bm )
+ #elif defined STM32_BOARD
+ if ( USART2_BASE->SR & USART_SR_RXNE )
+ #else
+ if ( UCSR0A & ( 1 << RXC0 ) )
+ #endif
+ {
+ rxchar = UDR0 ;
+ BootCount += 1 ;
+ if ( ( lState == BOOT_WAIT_30_IDLE ) || ( lState == BOOT_WAIT_30_DATA ) )
+ {
+ if ( lState == BOOT_WAIT_30_IDLE ) // Waiting for 0x30
+ BootTimer = millisTime ; // Start timeout
+ if ( rxchar == 0x30 )
+ lState = BOOT_WAIT_20 ;
+ else
+ lState = BOOT_WAIT_30_DATA ;
+ }
+ else
+ if ( lState == BOOT_WAIT_20 && rxchar == 0x20 ) // Waiting for 0x20
+ lState = BOOT_READY ;
+ }
+ else // No byte received
+ {
+ if ( lState != BOOT_WAIT_30_IDLE ) // Something received
+ {
+ uint8_t time = millisTime - BootTimer ;
+ if ( time > 5 )
+ {
+ #ifdef STM32_BOARD
+ if ( BootCount > 4 )
+ #else
+ if ( BootCount > 2 )
+ #endif
+ { // Run normally
+ NotBootChecking = 0xFF ;
+ Mprotocol_serial_init( 0 ) ;
+ }
+ else if ( lState == BOOT_READY )
+ {
+ #ifdef STM32_BOARD
+ nvic_sys_reset();
+ while(1); /* wait until reset */
+ #else
+ cli(); // Disable global int due to RW of 16 bits registers
+ void (*p)();
+ #ifndef ORANGE_TX
+ p = (void (*)())0x3F00 ; // Word address (0x7E00 byte)
+ #else
+ p = (void (*)())0x4000 ; // Word address (0x8000 byte)
+ #endif
+ (*p)() ; // go to boot
+ #endif
+ }
+ else
+ {
+ lState = BOOT_WAIT_30_IDLE ;
+ BootCount = 0 ;
+ }
+ }
+ }
+ }
+ BootState = lState ;
+}
+#endif //CHECK_FOR_BOOTLOADER
+
+#if defined(TELEMETRY)
+void PPM_Telemetry_serial_init()
+{
+ if( (protocol==PROTO_FRSKYD) || (protocol==PROTO_HUBSAN) || (protocol==PROTO_AFHDS2A) || (protocol==PROTO_BAYANG) || (protocol==PROTO_CABELL) || (protocol==PROTO_HITEC) || (protocol==PROTO_BUGS))
+ initTXSerial( SPEED_9600 ) ;
+ if(protocol==PROTO_FRSKYX)
+ initTXSerial( SPEED_57600 ) ;
+ if(protocol==PROTO_DSM)
+ initTXSerial( SPEED_125K ) ;
+}
+#endif
+
+// Convert 32b id to rx_tx_addr
+static void set_rx_tx_addr(uint32_t id)
+{ // Used by almost all protocols
+ rx_tx_addr[0] = (id >> 24) & 0xFF;
+ rx_tx_addr[1] = (id >> 16) & 0xFF;
+ rx_tx_addr[2] = (id >> 8) & 0xFF;
+ rx_tx_addr[3] = (id >> 0) & 0xFF;
+ rx_tx_addr[4] = (rx_tx_addr[2]&0xF0)|(rx_tx_addr[3]&0x0F);
+}
+
+static uint32_t random_id(uint16_t address, uint8_t create_new)
+{
+ #ifndef FORCE_GLOBAL_ID
+ uint32_t id=0;
+
+ if(eeprom_read_byte((EE_ADDR)(address+10))==0xf0 && !create_new)
+ { // TXID exists in EEPROM
+ for(uint8_t i=4;i>0;i--)
+ {
+ id<<=8;
+ id|=eeprom_read_byte((EE_ADDR)address+i-1);
+ }
+ if(id!=0x2AD141A7) //ID with seed=0
+ {
+ debugln("Read ID from EEPROM");
+ return id;
+ }
+ }
+ // Generate a random ID
+ #if defined STM32_BOARD
+ #define STM32_UUID ((uint32_t *)0x1FFFF7E8)
+ if (!create_new)
+ {
+ id = STM32_UUID[0] ^ STM32_UUID[1] ^ STM32_UUID[2];
+ debugln("Generated ID from STM32 UUID");
+ }
+ else
+ #endif
+ id = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
+
+ for(uint8_t i=0;i<4;i++)
+ eeprom_write_byte((EE_ADDR)address+i,id >> (i*8));
+ eeprom_write_byte((EE_ADDR)(address+10),0xf0);//write bind flag in eeprom.
+ return id;
+ #else
+ (void)address;
+ (void)create_new;
+ return FORCE_GLOBAL_ID;
+ #endif
+}
+
+/**************************/
+/**************************/
+/** Interrupt routines **/
+/**************************/
+/**************************/
+
+//PPM
+#ifdef ENABLE_PPM
+ #ifdef ORANGE_TX
+ #if PPM_pin == 2
+ ISR(PORTD_INT0_vect)
+ #else
+ ISR(PORTD_INT1_vect)
+ #endif
+ #elif defined STM32_BOARD
+ void PPM_decode()
+ #else
+ #if PPM_pin == 2
+ ISR(INT0_vect, ISR_NOBLOCK)
+ #else
+ ISR(INT1_vect, ISR_NOBLOCK)
+ #endif
+ #endif
+ { // Interrupt on PPM pin
+ static int8_t chan=0,bad_frame=1;
+ static uint16_t Prev_TCNT1=0;
+ uint16_t Cur_TCNT1;
+
+ Cur_TCNT1 = TCNT1 - Prev_TCNT1 ; // Capture current Timer1 value
+ if(Cur_TCNT1<1600)
+ bad_frame=1; // bad frame
+ else
+ if(Cur_TCNT1>4400)
+ { //start of frame
+ if(chan>=MIN_PPM_CHANNELS)
+ {
+ PPM_FLAG_on; // good frame received if at least 4 channels have been seen
+ if(chan>PPM_chan_max) PPM_chan_max=chan; // Saving the number of channels received
+ }
+ chan=0; // reset channel counter
+ bad_frame=0;
+ }
+ else
+ if(bad_frame==0) // need to wait for start of frame
+ { //servo values between 800us and 2200us will end up here
+ PPM_data[chan]=Cur_TCNT1;
+ if(chan++>=MAX_PPM_CHANNELS)
+ bad_frame=1; // don't accept any new channels
+ }
+ Prev_TCNT1+=Cur_TCNT1;
+ }
+#endif //ENABLE_PPM
+
+//Serial RX
+#ifdef ENABLE_SERIAL
+ #ifdef ORANGE_TX
+ ISR(USARTC0_RXC_vect)
+ #elif defined STM32_BOARD
+ void __irq_usart2()
+ #else
+ ISR(USART_RX_vect)
+ #endif
+ { // RX interrupt
+ static uint8_t idx=0;
+ #ifdef ORANGE_TX
+ if((USARTC0.STATUS & 0x1C)==0) // Check frame error, data overrun and parity error
+ #elif defined STM32_BOARD
+ if((USART2_BASE->SR & USART_SR_RXNE) && (USART2_BASE->SR &0x0F)==0)
+ #else
+ UCSR0B &= ~_BV(RXCIE0) ; // RX interrupt disable
+ sei() ;
+ if((UCSR0A&0x1C)==0) // Check frame error, data overrun and parity error
+ #endif
+ { // received byte is ok to process
+ if(idx==0||discard_frame==1)
+ { // Let's try to sync at this point
+ idx=0;discard_frame=0;
+ RX_MISSED_BUFF_off; // If rx_buff was good it's not anymore...
+ rx_buff[0]=UDR0;
+ #ifdef FAILSAFE_ENABLE
+ if((rx_buff[0]&0xFC)==0x54) // If 1st byte is 0x54, 0x55, 0x56 or 0x57 it looks ok
+ #else
+ if((rx_buff[0]&0xFE)==0x54) // If 1st byte is 0x54 or 0x55 it looks ok
+ #endif
+ {
+ TX_RX_PAUSE_on;
+ tx_pause();
+ #if defined STM32_BOARD
+ TIMER2_BASE->CCR2=TIMER2_BASE->CNT+(6500L); // Full message should be received within timer of 3250us
+ TIMER2_BASE->SR = 0x1E5F & ~TIMER_SR_CC2IF; // Clear Timer2/Comp2 interrupt flag
+ TIMER2_BASE->DIER |= TIMER_DIER_CC2IE; // Enable Timer2/Comp2 interrupt
+ #else
+ OCR1B = TCNT1+(6500L) ; // Full message should be received within timer of 3250us
+ TIFR1 = OCF1B_bm ; // clear OCR1B match flag
+ SET_TIMSK1_OCIE1B ; // enable interrupt on compare B match
+ #endif
+ idx++;
+ }
+ }
+ else
+ {
+ rx_buff[idx++]=UDR0; // Store received byte
+ if(idx>=RXBUFFER_SIZE)
+ { // A full frame has been received
+ if(!IS_RX_DONOTUPDATE_on)
+ { //Good frame received and main is not working on the buffer
+ memcpy((void*)rx_ok_buff,(const void*)rx_buff,RXBUFFER_SIZE);// Duplicate the buffer
+ RX_FLAG_on; // flag for main to process servo data
+ }
+ else
+ RX_MISSED_BUFF_on; // notify that rx_buff is good
+ discard_frame=1; // start again
+ }
+ }
+ }
+ else
+ {
+ idx=UDR0; // Dummy read
+ discard_frame=1; // Error encountered discard full frame...
+ debugln("Bad frame RX");
+ }
+ if(discard_frame==1)
+ {
+ #ifdef STM32_BOARD
+ TIMER2_BASE->DIER &= ~TIMER_DIER_CC2IE; // Disable Timer2/Comp2 interrupt
+ #else
+ CLR_TIMSK1_OCIE1B; // Disable interrupt on compare B match
+ #endif
+ TX_RX_PAUSE_off;
+ tx_resume();
+ }
+ #if not defined (ORANGE_TX) && not defined (STM32_BOARD)
+ cli() ;
+ UCSR0B |= _BV(RXCIE0) ; // RX interrupt enable
+ #endif
+ }
+
+ //Serial timer
+ #ifdef ORANGE_TX
+ ISR(TCC1_CCB_vect)
+ #elif defined STM32_BOARD
+ void ISR_COMPB()
+ #else
+ ISR(TIMER1_COMPB_vect, ISR_NOBLOCK )
+ #endif
+ { // Timer1 compare B interrupt
+ discard_frame=1;
+ #ifdef STM32_BOARD
+ TIMER2_BASE->DIER &= ~TIMER_DIER_CC2IE; // Disable Timer2/Comp2 interrupt
+ debugln("Bad frame timer");
+ #else
+ CLR_TIMSK1_OCIE1B; // Disable interrupt on compare B match
+ #endif
+ tx_resume();
+ }
+#endif //ENABLE_SERIAL
+
+#if not defined (ORANGE_TX) && not defined (STM32_BOARD)
+ static void random_init(void)
+ {
+ cli(); // Temporarily turn off interrupts, until WDT configured
+ MCUSR = 0; // Use the MCU status register to reset flags for WDR, BOR, EXTR, and POWR
+ WDTCSR |= _BV(WDCE); // WDT control register, This sets the Watchdog Change Enable (WDCE) flag, which is needed to set the prescaler
+ WDTCSR = _BV(WDIE); // Watchdog interrupt enable (WDIE)
+ sei(); // Turn interupts on
+ }
+
+ static uint32_t random_value(void)
+ {
+ while (!gWDT_entropy);
+ return gWDT_entropy;
+ }
+
+ // Random interrupt service routine called every time the WDT interrupt is triggered.
+ // It is only enabled at startup to generate a seed.
+ ISR(WDT_vect)
+ {
+ static uint8_t gWDT_buffer_position=0;
+ #define gWDT_buffer_SIZE 32
+ static uint8_t gWDT_buffer[gWDT_buffer_SIZE];
+ gWDT_buffer[gWDT_buffer_position] = TCNT1L; // Record the Timer 1 low byte (only one needed)
+ gWDT_buffer_position++; // every time the WDT interrupt is triggered
+ if (gWDT_buffer_position >= gWDT_buffer_SIZE)
+ {
+ // The following code is an implementation of Jenkin's one at a time hash
+ for(uint8_t gWDT_loop_counter = 0; gWDT_loop_counter < gWDT_buffer_SIZE; ++gWDT_loop_counter)
+ {
+ gWDT_entropy += gWDT_buffer[gWDT_loop_counter];
+ gWDT_entropy += (gWDT_entropy << 10);
+ gWDT_entropy ^= (gWDT_entropy >> 6);
+ }
+ gWDT_entropy += (gWDT_entropy << 3);
+ gWDT_entropy ^= (gWDT_entropy >> 11);
+ gWDT_entropy += (gWDT_entropy << 15);
+ WDTCSR = 0; // Disable Watchdog interrupt
+ }
+ }
+#endif
diff --git a/remote_multiprotocol/Pins.h b/remote_multiprotocol/Pins.h
new file mode 100644
index 0000000..88ba3ae
--- /dev/null
+++ b/remote_multiprotocol/Pins.h
@@ -0,0 +1,63 @@
+/*
+ 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 .
+ */
+//*******************
+//*** Pinouts ***
+//*******************
+
+ // SDIO MOSI
+ #define SDI_pin 2 //D5 = PD5
+ #define SDI_port PORTB
+ #define SDI_ipr PINB
+ #define SDI_ddr DDRB
+ #define SDI_on SDI_port |= _BV(SDI_pin)
+ #define SDI_off SDI_port &= ~_BV(SDI_pin)
+ #define SDI_1 (SDI_ipr & _BV(SDI_pin))
+ #define SDI_0 (SDI_ipr & _BV(SDI_pin)) == 0x00
+ #define SDI_input SDI_ddr &= ~_BV(SDI_pin)
+ #define SDI_output SDI_ddr |= _BV(SDI_pin)
+
+ //SDO / MISO
+ #define SDO_pin 3 //D6 = PD6
+ #define SDO_port PORTB
+ #define SDO_ipr PINB
+ #define SDO_1 (SDO_ipr & _BV(SDO_pin))
+ #define SDO_0 (SDO_ipr & _BV(SDO_pin)) == 0x00
+
+ // SCLK
+ #define SCLK_port PORTB
+ #define SCLK_ddr DDRB
+ #define SCLK_pin 1 //D4 = PD4
+ #define SCLK_output SCLK_ddr |= _BV(SCLK_pin)
+ #define SCLK_on SCLK_port |= _BV(SCLK_pin)
+ #define SCLK_off SCLK_port &= ~_BV(SCLK_pin)
+
+ // Chip select CC2500
+ #define CC25_CSN_pin 7 //D7 = PD7
+ #define CC25_CSN_port PORTE
+ #define CC25_CSN_ddr DDRE
+ #define CC25_CSN_output CC25_CSN_ddr |= _BV(CC25_CSN_pin)
+ #define CC25_CSN_on CC25_CSN_port |= _BV(CC25_CSN_pin)
+ #define CC25_CSN_off CC25_CSN_port &= ~_BV(CC25_CSN_pin)
+
+//*******************
+//*** Timer ***
+//*******************
+#define OCF1A_bm _BV(OCF1A)
+#define OCF1B_bm _BV(OCF1B)
+#define SET_TIMSK1_OCIE1B TIMSK1 |= _BV(OCIE1B)
+#define CLR_TIMSK1_OCIE1B TIMSK1 &=~_BV(OCIE1B)
+
+//*******************
+//*** EEPROM ***
+//*******************
+#define EE_ADDR uint8_t*
diff --git a/remote_multiprotocol/SPI.ino b/remote_multiprotocol/SPI.ino
new file mode 100644
index 0000000..3896f5d
--- /dev/null
+++ b/remote_multiprotocol/SPI.ino
@@ -0,0 +1,53 @@
+/*
+ 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 .
+ */
+/********************/
+/** SPI routines **/
+/********************/
+void SPI_Write(uint8_t command)
+{
+ uint8_t n=8;
+
+ SCLK_off;//SCK start low
+ SDI_off;
+ do
+ {
+ if(command&0x80)
+ SDI_on;
+ else
+ SDI_off;
+ SCLK_on;
+ //delay(1);
+ command = command << 1;
+ SCLK_off;
+ }
+ while(--n) ;
+ SDI_on;
+}
+
+uint8_t SPI_Read(void)
+{
+ uint8_t result=0,i;
+ for(i=0;i<8;i++)
+ {
+ result=result<<1;
+ if(SDO_1)
+ result |= 0x01;
+ SCLK_on;
+ NOP();
+ SCLK_off;
+ }
+ return result;
+}
+
diff --git a/remote_multiprotocol/TX_Def.h b/remote_multiprotocol/TX_Def.h
new file mode 100644
index 0000000..e122af4
--- /dev/null
+++ b/remote_multiprotocol/TX_Def.h
@@ -0,0 +1,211 @@
+// Turnigy PPM and channels
+#if defined(TX_ER9X)
+ #define PPM_MAX_100 2012 // 100%
+ #define PPM_MIN_100 988 // 100%
+#endif
+
+// Devo PPM and channels
+#if defined(TX_DEVO7)
+ #define PPM_MAX_100 1920 // 100%
+ #define PPM_MIN_100 1120 // 100%
+#endif
+
+// SPEKTRUM PPM and channels
+#if defined(TX_SPEKTRUM)
+ #define PPM_MAX_100 1900 // 100%
+ #define PPM_MIN_100 1100 // 100%
+#endif
+
+// HISKY
+#if defined(TX_HISKY)
+ #define PPM_MAX_100 1920 // 100%
+ #define PPM_MIN_100 1120 // 100%
+#endif
+
+// Multiplex MC2020
+#if defined(TX_MPX)
+ #define PPM_MAX_100 1950 // 100%
+ #define PPM_MIN_100 1250 // 100%
+#endif
+
+// Walkera PL0811-01H
+#if defined(TX_WALKERA)
+ #define PPM_MAX_100 1800 // 100%
+ #define PPM_MIN_100 1000 // 100%
+#endif
+
+//Channel MIN MAX values
+#define CHANNEL_MAX_100 1844 // 100%
+#define CHANNEL_MIN_100 204 // 100%
+#define CHANNEL_MAX_125 2047 // 125%
+#define CHANNEL_MIN_125 0 // 125%
+
+#define CHANNEL_MIN_COMMAND 784 // 1350us
+#define CHANNEL_SWITCH 1104 // 1550us
+#define CHANNEL_MAX_COMMAND 1424 // 1750us
+
+//Channel definitions
+#ifdef AETR
+ #define AILERON 0
+ #define ELEVATOR 1
+ #define THROTTLE 2
+ #define RUDDER 3
+#endif
+#ifdef AERT
+ #define AILERON 0
+ #define ELEVATOR 1
+ #define THROTTLE 3
+ #define RUDDER 2
+#endif
+#ifdef ARET
+ #define AILERON 0
+ #define ELEVATOR 2
+ #define THROTTLE 3
+ #define RUDDER 1
+#endif
+#ifdef ARTE
+ #define AILERON 0
+ #define ELEVATOR 3
+ #define THROTTLE 2
+ #define RUDDER 1
+#endif
+#ifdef ATRE
+ #define AILERON 0
+ #define ELEVATOR 3
+ #define THROTTLE 1
+ #define RUDDER 2
+#endif
+#ifdef ATER
+ #define AILERON 0
+ #define ELEVATOR 2
+ #define THROTTLE 1
+ #define RUDDER 3
+#endif
+
+#ifdef EATR
+ #define AILERON 1
+ #define ELEVATOR 0
+ #define THROTTLE 2
+ #define RUDDER 3
+#endif
+#ifdef EART
+ #define AILERON 1
+ #define ELEVATOR 0
+ #define THROTTLE 3
+ #define RUDDER 2
+#endif
+#ifdef ERAT
+ #define AILERON 2
+ #define ELEVATOR 0
+ #define THROTTLE 3
+ #define RUDDER 1
+#endif
+#ifdef ERTA
+ #define AILERON 3
+ #define ELEVATOR 0
+ #define THROTTLE 2
+ #define RUDDER 1
+#endif
+#ifdef ETRA
+ #define AILERON 3
+ #define ELEVATOR 0
+ #define THROTTLE 1
+ #define RUDDER 2
+#endif
+#ifdef ETAR
+ #define AILERON 2
+ #define ELEVATOR 0
+ #define THROTTLE 1
+ #define RUDDER 3
+#endif
+
+#ifdef TEAR
+ #define AILERON 2
+ #define ELEVATOR 1
+ #define THROTTLE 0
+ #define RUDDER 3
+#endif
+#ifdef TERA
+ #define AILERON 3
+ #define ELEVATOR 1
+ #define THROTTLE 0
+ #define RUDDER 2
+#endif
+#ifdef TREA
+ #define AILERON 3
+ #define ELEVATOR 2
+ #define THROTTLE 0
+ #define RUDDER 1
+#endif
+#ifdef TRAE
+ #define AILERON 2
+ #define ELEVATOR 3
+ #define THROTTLE 0
+ #define RUDDER 1
+#endif
+#ifdef TARE
+ #define AILERON 1
+ #define ELEVATOR 3
+ #define THROTTLE 0
+ #define RUDDER 2
+#endif
+#ifdef TAER
+ #define AILERON 1
+ #define ELEVATOR 2
+ #define THROTTLE 0
+ #define RUDDER 3
+#endif
+
+#ifdef RETA
+ #define AILERON 3
+ #define ELEVATOR 1
+ #define THROTTLE 2
+ #define RUDDER 0
+#endif
+#ifdef REAT
+ #define AILERON 2
+ #define ELEVATOR 1
+ #define THROTTLE 3
+ #define RUDDER 0
+#endif
+#ifdef RAET
+ #define AILERON 1
+ #define ELEVATOR 2
+ #define THROTTLE 3
+ #define RUDDER 0
+#endif
+#ifdef RATE
+ #define AILERON 1
+ #define ELEVATOR 3
+ #define THROTTLE 2
+ #define RUDDER 0
+#endif
+#ifdef RTAE
+ #define AILERON 2
+ #define ELEVATOR 3
+ #define THROTTLE 1
+ #define RUDDER 0
+#endif
+#ifdef RTEA
+ #define AILERON 3
+ #define ELEVATOR 2
+ #define THROTTLE 1
+ #define RUDDER 0
+#endif
+
+#define CH1 0
+#define CH2 1
+#define CH3 2
+#define CH4 3
+#define CH5 4
+#define CH6 5
+#define CH7 6
+#define CH8 7
+#define CH9 8
+#define CH10 9
+#define CH11 10
+#define CH12 11
+#define CH13 12
+#define CH14 13
+#define CH15 14
+#define CH16 15
diff --git a/remote_multiprotocol/Validate.h b/remote_multiprotocol/Validate.h
new file mode 100644
index 0000000..5e56df6
--- /dev/null
+++ b/remote_multiprotocol/Validate.h
@@ -0,0 +1,274 @@
+// Check selected board type
+#if defined (STM32_BOARD) && defined (ORANGE_TX)
+ #error You must comment the board type STM32_BOARD in _Config.h to compile ORANGE_TX
+#endif
+#if not defined (ORANGE_TX) && not defined (STM32_BOARD)
+ //Atmega328p
+ #if (not defined(ARDUINO_AVR_PRO) && not defined(ARDUINO_AVR_LEONARDO) )&& not defined(ARDUINO_MULTI_NO_BOOT) && not defined(ARDUINO_MULTI_FLASH_FROM_TX) && not defined(ARDUINO_AVR_MINI) && not defined(ARDUINO_AVR_NANO)
+ #error You must select one of these boards: "Multi 4-in-1", "Arduino Pro or Pro Mini" or "Arduino Mini"
+ #endif
+ #if F_CPU != 16000000L || not defined(__AVR_ATmega328P__) || not defined(__AVR_ATmega32U4__)
+ #warning You must select the processor type "ATmega328(5V, 16MHz)"
+ #endif
+#endif
+#if defined (STM32_BOARD) && not defined (ORANGE_TX)
+ //STM32
+ #if not defined(ARDUINO_GENERIC_STM32F103C) && not defined(ARDUINO_MULTI_STM32_FLASH_FROM_TX) && not defined(ARDUINO_MULTI_STM32_NO_BOOT) && not defined(ARDUINO_MULTI_STM32_WITH_BOOT)
+ #error You must select one of these boards: "Multi 4-in-1 (STM32F103CB)" or "Generic STM32F103C series"
+ #endif
+#endif
+
+// Check for minimum version of multi-module boards
+#define MIN_AVR_BOARD 103
+#define MIN_ORX_BOARD 103
+#define MIN_STM32_BOARD 104
+//AVR
+#if (defined(ARDUINO_MULTI_NO_BOOT) && ARDUINO_MULTI_NO_BOOT < MIN_AVR_BOARD) || (defined(ARDUINO_MULTI_FLASH_FROM_TX) && ARDUINO_MULTI_FLASH_FROM_TX < MIN_AVR_BOARD)
+ #error You need to update your Multi 4-in-1 board definition. Open Boards Manager and update to the latest version of the Multi 4-in-1 AVR Boards.
+#endif
+//OrangeRX
+#if (defined(ARDUINO_MULTI_ORANGERX) && ARDUINO_MULTI_ORANGERX < MIN_ORX_BOARD)
+ #error You need to update your Multi 4-in-1 board definition. Open Boards Manager and update to the latest version of the Multi 4-in-1 AVR Boards.
+#endif
+//STM32
+#if (defined(ARDUINO_MULTI_STM32_NO_BOOT) && ARDUINO_MULTI_STM32_NO_BOOT < MIN_STM32_BOARD) || (defined(ARDUINO_MULTI_STM32_FLASH_FROM_TX) && ARDUINO_MULTI_STM32_FLASH_FROM_TX < MIN_STM32_BOARD) || (defined(ARDUINO_MULTI_STM32_WITH_BOOT) && ARDUINO_MULTI_STM32_WITH_BOOT < MIN_STM32_BOARD)
+ #error You need to update your Multi 4-in-1 board definition. Open Boards Manager and update to the latest version of the Multi 4-in-1 STM32 Board.
+#endif
+
+// Error if CHECK_FOR_BOOTLOADER is not enabled but a FLASH_FROM_TX board is selected
+#if (defined(ARDUINO_MULTI_FLASH_FROM_TX) || defined(ARDUINO_MULTI_STM32_FLASH_FROM_TX)) &! defined(CHECK_FOR_BOOTLOADER)
+ #if defined(STM32_BOARD)
+ #error "You have selected the 'Flash from TX' upload method but not enabled CHECK_FOR_BOOTLOADER."
+ #else
+ #error "You have selected the 'Flash from TX' bootloader but not enabled CHECK_FOR_BOOTLOADER."
+ #endif
+#endif
+
+// Error if CHECK_FOR_BOOTLOADER is enabled but the 'Flash from TX' bootloader
+#if defined(ARDUINO_MULTI_NO_BOOT) && defined(CHECK_FOR_BOOTLOADER)
+ #error "You have enabled CHECK_FOR_BOOTLOADER but not selected the 'Flash from TX' bootloader."
+#endif
+
+//Check number of banks
+#if NBR_BANKS < 1 || NBR_BANKS > 5
+ #error "You need to select a number of banks between 1 and 5."
+#endif
+
+//Check failsafe throttle value
+#ifdef FAILSAFE_ENABLE
+ #if ( FAILSAFE_THROTTLE_LOW < -125 ) || ( FAILSAFE_THROTTLE_LOW > 125 )
+ #error "The failsafe value for throttle is outside of the range -125..125."
+ #endif
+#endif
+
+// Check forced tuning values are valid
+#ifdef FORCE_FRSKYD_TUNING
+ #if ( FORCE_FRSKYD_TUNING < -127 ) || ( FORCE_FRSKYD_TUNING > 127 )
+ #error "The FrSkyD forced frequency tuning value is outside of the range -127..127."
+ #endif
+#endif
+#ifdef FORCE_FRSKYV_TUNING
+ #if ( FORCE_FRSKYV_TUNING < -127 ) || ( FORCE_FRSKYV_TUNING > 127 )
+ #error "The FrSkyV forced frequency tuning value is outside of the range -127..127."
+ #endif
+#endif
+#ifdef FORCE_FRSKYX_TUNING
+ #if ( FORCE_FRSKYX_TUNING < -127 ) || ( FORCE_FRSKYX_TUNING > 127 )
+ #error "The FrSkyX forced frequency tuning value is outside of the range -127..127."
+ #endif
+#endif
+#ifdef FORCE_SFHSS_TUNING
+ #if ( FORCE_SFHSS_TUNING < -127 ) || ( FORCE_SFHSS_TUNING > 127 )
+ #error "The SFHSS forced frequency tuning value is outside of the range -127..127."
+ #endif
+#endif
+#ifdef FORCE_CORONA_TUNING
+ #if ( FORCE_CORONA_TUNING < -127 ) || ( FORCE_CORONA_TUNING > 127 )
+ #error "The CORONA forced frequency tuning value is outside of the range -127..127."
+ #endif
+#endif
+#ifdef FORCE_HITEC_TUNING
+ #if ( FORCE_HITEC_TUNING < -127 ) || ( FORCE_HITEC_TUNING > 127 )
+ #error "The HITEC forced frequency tuning value is outside of the range -127..127."
+ #endif
+#endif
+#ifdef FORCE_FLYSKY_TUNING
+ #if ( FORCE_FLYSKY_TUNING < -300 ) || ( FORCE_FLYSKY_TUNING > 300 )
+ #error "The Flysky forced frequency tuning value is outside of the range -300..300."
+ #endif
+#endif
+#ifdef FORCE_HUBSAN_TUNING
+ #if ( FORCE_HUBSAN_TUNING < -300 ) || ( FORCE_HUBSAN_TUNING > 300 )
+ #error "The Hubsan forced frequency tuning value is outside of the range -300..300."
+ #endif
+#endif
+#ifdef FORCE_AFHDS2A_TUNING
+ #if ( FORCE_AFHDS2A_TUNING < -300 ) || ( FORCE_AFHDS2A_TUNING > 300 )
+ #error "The AFHDS2A forced frequency tuning value is outside of the range -300..300."
+ #endif
+#endif
+#ifndef USE_A7105_CH15_TUNING
+ #ifndef FORCE_FLYSKY_TUNING
+ #define FORCE_FLYSKY_TUNING 0
+ #endif
+ #ifndef FORCE_HUBSAN_TUNING
+ #define FORCE_HUBSAN_TUNING 0
+ #endif
+ #ifndef FORCE_AFHDS2A_TUNING
+ #define FORCE_AFHDS2A_TUNING 0
+ #endif
+#endif
+
+//Change/Force configuration if OrangeTX
+#ifdef ORANGE_TX
+ #undef ENABLE_PPM // Disable PPM for OrangeTX module
+ #undef A7105_INSTALLED // Disable A7105 for OrangeTX module
+ #undef A7105_CSN_pin
+ #undef CC2500_INSTALLED // Disable CC2500 for OrangeTX module
+ #undef CC25_CSN_pin
+ #undef NRF24L01_INSTALLED // Disable NRF for OrangeTX module
+ #undef NRF_CSN_pin
+ #define TELEMETRY // Enable telemetry
+ #define INVERT_TELEMETRY // Enable invert telemetry
+ #define DSM_TELEMETRY // Enable DSM telemetry
+#endif
+
+//Make sure protocols are selected correctly
+#ifndef A7105_INSTALLED
+ #undef FLYSKY_A7105_INO
+ #undef HUBSAN_A7105_INO
+ #undef AFHDS2A_A7105_INO
+ #undef BUGS_A7105_INO
+#endif
+#ifndef CYRF6936_INSTALLED
+ #undef DEVO_CYRF6936_INO
+ #undef DSM_CYRF6936_INO
+ #undef J6PRO_CYRF6936_INO
+ #undef WFLY_CYRF6936_INO
+ #undef WK2x01_CYRF6936_INO
+#endif
+#ifndef CC2500_INSTALLED
+ #undef FRSKYD_CC2500_INO
+ #undef FRSKYV_CC2500_INO
+ #undef FRSKYX_CC2500_INO
+ #undef SFHSS_CC2500_INO
+ #undef CORONA_CC2500_INO
+ #undef HITEC_CC2500_INO
+#endif
+#ifndef NRF24L01_INSTALLED
+ #undef BAYANG_NRF24L01_INO
+ #undef CG023_NRF24L01_INO
+ #undef CX10_NRF24L01_INO
+ #undef ESKY_NRF24L01_INO
+ #undef HISKY_NRF24L01_INO
+ #undef KN_NRF24L01_INO
+ #undef SLT_NRF24L01_INO
+ #undef SYMAX_NRF24L01_INO
+ #undef V2X2_NRF24L01_INO
+ #undef YD717_NRF24L01_INO
+ #undef MT99XX_NRF24L01_INO
+ #undef MJXQ_NRF24L01_INO
+ #undef SHENQI_NRF24L01_INO
+ #undef FY326_NRF24L01_INO
+ #undef FQ777_NRF24L01_INO
+ #undef ASSAN_NRF24L01_INO
+ #undef HONTAI_NRF24L01_INO
+ #undef Q303_NRF24L01_INO
+ #undef GW008_NRF24L01_INO
+ #undef DM002_NRF24L01_INO
+ #undef CABELL_NRF24L01_INO
+ #undef ESKY150_NRF24L01_INO
+ #undef H8_3D_NRF24L01_INO
+ #undef CFLIE_NRF24L01_INO
+#endif
+
+//Make sure telemetry is selected correctly
+#ifndef TELEMETRY
+ #undef INVERT_TELEMETRY
+ #undef AFHDS2A_FW_TELEMETRY
+ #undef AFHDS2A_HUB_TELEMETRY
+ #undef HITEC_FW_TELEMETRY
+ #undef HITEC_HUB_TELEMETRY
+ #undef BAYANG_HUB_TELEMETRY
+ #undef CABELL_HUB_TELEMETRY
+ #undef HUBSAN_HUB_TELEMETRY
+ #undef BUGS_HUB_TELEMETRY
+ #undef HUB_TELEMETRY
+ #undef SPORT_TELEMETRY
+ #undef SPORT_POLLING
+ #undef DSM_TELEMETRY
+ #undef MULTI_STATUS
+ #undef MULTI_TELEMETRY
+#else
+ #if defined MULTI_TELEMETRY && not defined INVERT_TELEMETRY
+ #warning MULTI_TELEMETRY has been defined but not INVERT_TELEMETRY. They should be both enabled for OpenTX telemetry and status to work.
+ #endif
+ #if not defined(BAYANG_NRF24L01_INO)
+ #undef BAYANG_HUB_TELEMETRY
+ #endif
+ #if not defined(CABELL_NRF24L01_INO)
+ #undef CABELL_HUB_TELEMETRY
+ #endif
+ #if not defined(HUBSAN_A7105_INO)
+ #undef HUBSAN_HUB_TELEMETRY
+ #endif
+ #if not defined(AFHDS2A_A7105_INO)
+ #undef AFHDS2A_HUB_TELEMETRY
+ #undef AFHDS2A_FW_TELEMETRY
+ #endif
+ #if not defined(HITEC_CC2500_INO)
+ #undef HITEC_HUB_TELEMETRY
+ #undef HITEC_FW_TELEMETRY
+ #endif
+ #if not defined(FRSKYD_CC2500_INO)
+ #undef HUB_TELEMETRY
+ #endif
+ #if not defined(FRSKYX_CC2500_INO)
+ #undef SPORT_TELEMETRY
+ #undef SPORT_POLLING
+ #endif
+ #if not defined (SPORT_TELEMETRY) || not defined (STM32_BOARD)
+ #undef SPORT_POLLING
+ #endif
+ #if defined SPORT_POLLING && not defined INVERT_TELEMETRY
+ #error SPORT_POLLING has been defined but not INVERT_TELEMETRY. They should be both enabled to work.
+ #endif
+ #if not defined(DSM_CYRF6936_INO)
+ #undef DSM_TELEMETRY
+ #endif
+ #if not defined(DSM_TELEMETRY) && not defined(SPORT_TELEMETRY) && not defined(HUB_TELEMETRY) && not defined(HUBSAN_HUB_TELEMETRY) && not defined(BUGS_HUB_TELEMETRY) && not defined(BAYANG_HUB_TELEMETRY) && not defined(CABELL_HUB_TELEMETRY) && not defined(AFHDS2A_HUB_TELEMETRY) && not defined(AFHDS2A_FW_TELEMETRY) && not defined(MULTI_TELEMETRY) && not defined(MULTI_STATUS) && not defined(HITEC_HUB_TELEMETRY) && not defined(HITEC_FW_TELEMETRY)
+ #undef TELEMETRY
+ #undef INVERT_TELEMETRY
+ #undef SPORT_POLLING
+ #endif
+#endif
+
+//Make sure TX is defined correctly
+#ifndef AILERON
+ #error You must select a correct channel order.
+#endif
+#if not defined(PPM_MAX_100) || not defined(PPM_MIN_100)
+ #error You must set correct PPM end points for your TX.
+#endif
+
+#if defined(ENABLE_BIND_CH)
+ #if BIND_CH<4
+ #error BIND_CH must be above 4.
+ #endif
+ #if BIND_CH>16
+ #error BIND_CH must be below or equal to 16.
+ #endif
+#endif
+
+#if MIN_PPM_CHANNELS>16
+ #error MIN_PPM_CHANNELS must be below or equal to 16. The default for this value is 4.
+#endif
+#if MIN_PPM_CHANNELS<2
+ #error MIN_PPM_CHANNELS must be larger than 1. The default for this value is 4.
+#endif
+#if MAX_PPM_CHANNELS16
+ #error MAX_PPM_CHANNELS must be below or equal to 16. The default for this value is 16.
+#endif
diff --git a/remote_multiprotocol/_Config.h b/remote_multiprotocol/_Config.h
new file mode 100644
index 0000000..5657f5c
--- /dev/null
+++ b/remote_multiprotocol/_Config.h
@@ -0,0 +1,454 @@
+/*
+ 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 .
+ */
+
+/**********************************************/
+/** Multiprotocol module configuration file ***/
+/**********************************************/
+
+/*******************/
+/*** TX SETTINGS ***/
+/*******************/
+//Modify the channel order based on your TX: AETR, TAER, RETA...
+//Examples: Flysky & DEVO is AETR, JR/Spektrum radio is TAER, Multiplex is AERT...
+//Default is AETR.
+#define AETR
+
+/*****************/
+/*** AUTO BIND ***/ // Also referred as "Bind on powerup"
+/*****************/
+//Bind from channel enables you to bind when a specified channel is going from low to high. This feature is only active
+// if you specify AUTOBIND in PPM mode or set AutoBind to YES for serial mode. It also requires that the throttle channel is low.
+//Comment to globaly disable the bind feature from a channel.
+#define ENABLE_BIND_CH
+//Set the channel number used for bind. Default is 16.
+#define BIND_CH 16
+
+//Comment to disable the wait for bind feature. If Autobind is enabled in the model config, this feature will not activate
+// the selected protocol unless a bind is requested using bind from channel or the GUI "Bind" button.
+//The goal is to prevent binding other people's model when powering up the TX, changing model or scanning through protocols.
+#define WAIT_FOR_BIND
+
+
+/****************/
+/*** RF CHIPS ***/
+/****************/
+//There are 4 RF components supported. If one of them is not installed you must comment it using "//".
+//If a chip is not installed all associated protocols are automatically disabled.
+//4-in-1 modules have all RF chips installed
+//!!!If a RF chip is present it MUST be marked as installed!!! or weird things will happen you have been warned.
+//#define A7105_INSTALLED
+//#define CYRF6936_INSTALLED
+#define CC2500_INSTALLED
+//#define NRF24L01_INSTALLED
+
+/** CC2500 Fine Frequency Tuning **/
+//For optimal performance the CC2500 RF module used by the FrSkyD, FrSkyV, FrSkyX, SFHSS, CORONA and Hitec protocols needs to be tuned for each protocol.
+//Initial tuning should be done via the radio menu with a genuine FrSky/Futaba/CORONA/Hitec receiver.
+//Once a good tuning value is found it can be set here and will override the radio's 'option' setting for all existing and new models which use that protocol.
+//For more information: https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/tree/master/docs/Frequency_Tuning.md
+//Uncomment the lines below (remove the "//") and set an appropriate value (replace the "0") to enable. Valid range is -127 to +127.
+#define FORCE_FRSKYD_TUNING 0
+#define FORCE_FRSKYV_TUNING 0
+#define FORCE_FRSKYX_TUNING 0
+
+/** Low Power **/
+//Low power is reducing the transmit power of the multi module. This setting is configurable per model in PPM (table below) or Serial mode (radio GUI).
+//It can be activated when flying indoor or small models since the distance is short or if a model is causing issues when flying closed to the TX.
+//By default low power is completly disabled on all rf chips to prevent mistakes, but you can enable it by uncommenting the lines below:
+//#define CC2500_ENABLE_LOW_POWER
+
+
+/*****************/
+/*** GLOBAL ID ***/
+/*****************/
+//A global ID is used by most protocols to bind and retain the bind to models. To prevent duplicate IDs, it is automatically
+// generated using a random 32 bits number the first time the eeprom is initialized.
+//If you have 2 Multi modules which you want to share the same ID so you can use either to control the same RC model
+// then you can force the ID to a certain known value using the lines below.
+//Default is commented, you should uncoment only for test purpose or if you know exactly what you are doing!!!
+#define FORCE_GLOBAL_ID 0x12345678
+
+//Protocols using the CYRF6936 (DSM, Devo, Walkera...) are using the CYRF ID instead which should prevent duplicated IDs.
+//If you have 2 Multi modules which you want to share the same ID so you can use either to control the same RC model
+// then you can force the ID to a certain known value using the lines below.
+//Default is commented, you should uncoment only for test purpose or if you know exactly what you are doing!!!
+//#define FORCE_CYRF_ID "\x12\x34\x56\x78\x9A\xBC"
+
+
+/****************************/
+/*** PROTOCOLS TO INCLUDE ***/
+/****************************/
+//In this section select the protocols you want to be accessible when using the module.
+//All the protocols will not fit in the Atmega328p module so you need to pick and choose.
+//Comment the protocols you are not using with "//" to save Flash space.
+
+//The protocols below need a CC2500 to be installed
+#define FRSKYD_CC2500_INO
+#define FRSKYV_CC2500_INO
+#define FRSKYX_CC2500_INO
+
+
+/**************************/
+/*** FAILSAFE SETTINGS ***/
+/**************************/
+//The module is using the same default failsafe values for all protocols which currently supports it:
+// Devo, WK2x01, SFHSS, HISKY/HK310 and AFHDS2A
+//All channels are centered except throttle which is forced low.
+//If you want to diasble failsafe globally comment the line below using "//".
+#define FAILSAFE_ENABLE
+
+//Failsafe throttle low value in percentage.
+//Value between -125% and +125%. Default -100.
+#define FAILSAFE_THROTTLE_LOW -100
+
+//The radio using serial protocol can set failsafe data.
+// Two options are available:
+// a. replace the default failsafe data with serial failsafe data when they are received.
+// b. wait for the radio to provide failsafe before sending it. Enable advanced settings like "FAILSAFE NOT SET" or "FAILSAFE RX".
+// Option a. is the default since you have a protection even if no failsafe has been set on the radio.
+// You can force option b. by uncommenting the line below (remove the "//").
+//#define FAILSAFE_SERIAL_ONLY
+
+
+/**************************/
+/*** TELEMETRY SETTINGS ***/
+/**************************/
+//In this section you can configure the telemetry.
+
+//If you do not plan using the telemetry comment this global setting using "//" and skip to the next section.
+//#define TELEMETRY
+
+//Comment to invert the polarity of the output telemetry serial signal.
+//This function takes quite some flash space and processor power on an atmega.
+//For OpenTX it must be uncommented.
+//On a 9XR_PRO running ersky9x both commented and uncommented will work depending on the radio setting Invert COM1 under the Telemetry menu.
+//On other addon/replacement boards like the 9xtreme board or the Ar9x board running ersky9x, you need to uncomment the line below.
+//For er9x it depends if you have an inveter mod or not on the telemetry pin. If you don't have an inverter comment this line.
+//#define INVERT_TELEMETRY
+
+//Comment if you don't want to send Multi status telemetry frames (Protocol available, Bind in progress, version...)
+//Use with er9x/erksy9x, for OpenTX MULTI_TELEMETRY below is preferred instead
+//#define MULTI_STATUS
+
+//Uncomment to send Multi status and allow OpenTX to autodetect the telemetry format
+//Supported by OpenTX version 2.2 RC9 and newer. NOT supported by er9x/ersky9x use MULTI_STATUS instead.
+//#define MULTI_TELEMETRY
+
+//Comment a line to disable a specific protocol telemetry
+//#define DSM_TELEMETRY // Forward received telemetry packet directly to TX to be decoded by er9x, ersky9x and OpenTX
+//#define SPORT_TELEMETRY // Use FrSkyX SPORT format to send telemetry to TX
+//#define AFHDS2A_FW_TELEMETRY // Forward received telemetry packet directly to TX to be decoded by ersky9x and OpenTX
+//#define AFHDS2A_HUB_TELEMETRY // Use FrSkyD Hub format to send basic telemetry to TX like er9x
+//#define HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
+//#define BAYANG_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
+//#define BUGS_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
+//#define HUBSAN_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
+//#define CABELL_HUB_TELEMETRY // Use FrSkyD Hub format to send telemetry to TX
+//#define HITEC_HUB_TELEMETRY // Use FrSkyD Hub format to send basic telemetry to the radios which can decode it like er9x, ersky9x and OpenTX
+//#define HITEC_FW_TELEMETRY // Under development: Forward received telemetry packets to be decoded by ersky9x and OpenTX
+
+//SPORT_POLLING is an implementation of the same polling routine as XJT module for sport telemetry bidirectional communication.
+//This is useful for passing sport control frames from TX to RX(ex: changing Betaflight PID or VTX channels on the fly using LUA scripts with OpentX).
+//Using this feature requires to uncomment INVERT_TELEMETRY as this TX output on telemetry pin only inverted signal.
+//!!!! This is a work in progress!!! Do not enable unless you want to test and report
+//#define SPORT_POLLING
+
+
+/****************************/
+/*** SERIAL MODE SETTINGS ***/
+/****************************/
+//In this section you can configure the serial mode.
+//The serial mode enables full editing of all the parameters in the GUI of the radio. It is enabled by placing the rotary switch on position 0.
+//This is available natively for ER9X, ERSKY9X and OpenTX.
+
+//If you do not plan to use the Serial mode comment this line using "//" to save Flash space
+//#define ENABLE_SERIAL
+
+
+/*************************/
+/*** PPM MODE SETTINGS ***/
+/*************************/
+//In this section you can configure all details about PPM.
+//If you do not plan to use the PPM mode comment this line using "//" to save Flash space, you don't need to configure anything below in this case
+#define ENABLE_PPM
+
+/** TX END POINTS **/
+//It is important for the module to know the endpoints of your radio.
+//Below are some standard transmitters already preconfigured.
+//Uncomment only the one which matches your transmitter.
+#define TX_ER9X //ER9X/ERSKY9X/OpenTX ( 988<->2012 microseconds)
+//#define TX_DEVO7 //DEVO (1120<->1920 microseconds)
+//#define TX_SPEKTRUM //Spektrum (1100<->1900 microseconds)
+//#define TX_HISKY //HISKY (1120<->1920 microseconds)
+//#define TX_MPX //Multiplex MC2020 (1250<->1950 microseconds)
+//#define TX_WALKERA //Walkera PL0811-01H (1000<->1800 microseconds)
+//#define TX_CUSTOM //Custom
+
+// The lines below are used to set the end points in microseconds if you have selected TX_CUSTOM.
+// A few things to consider:
+// - If you put too big values compared to your TX you won't be able to reach the extremes which is bad for throttle as an example
+// - If you put too low values you won't be able to use your full stick range, it will be maxed out before reaching the ends
+// - Centered stick value is usually 1500. It should match the middle between MIN and MAX, ie Center=(MAX+MIN)/2. If your TX is not centered you can adjust the value MIN or MAX.
+// - 100% is referred as the value when the TX is set to default with no trims
+#if defined(TX_CUSTOM)
+ #define PPM_MAX_100 1900 // 100%
+ #define PPM_MIN_100 1100 // 100%
+#endif
+
+/** Number of PPM Channels **/
+// The line below is used to set the minimum number of channels which the module should receive to consider a PPM frame valid.
+// The default value is 4 to receive at least AETR for flying models but you could also connect the PPM from a car radio which has only 3 channels by changing this number to 3.
+#define MIN_PPM_CHANNELS 4
+// The line below is used to set the maximum number of channels which the module should work with. Any channels received above this number are discarded.
+// The default value is 16 to receive all possible channels but you might want to filter some "bad" channels from the PPM frame like the ones above 6 on the Walkera PL0811.
+#define MAX_PPM_CHANNELS 16
+
+/** Rotary Switch Protocol Selector Settings **/
+//The table below indicates which protocol to run when a specific position on the rotary switch has been selected.
+//All fields and values are explained below. Everything is configurable from here like in the Serial mode.
+//Tip: You can associate multiple times the same protocol to different rotary switch positions to take advantage of the model match based on RX_Num
+
+//A system of banks enable the access to more protocols than positions on the rotary switch. Banks can be selected by placing the rotary switch on position 15, power up the module and
+// short press the bind button multiple times until you reach the desired one. The bank number currently selected is indicated by the number of LED flash.
+// Full procedure is located here: https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/blob/master/Protocols_Details.md#protocol-selection-in-ppm-mode
+
+//The parameter below indicates the number of desired banks between 1 and 5. Default is 5.
+#define NBR_BANKS 3
+
+const PPM_Parameters PPM_prot[14*NBR_BANKS]= {
+#if NBR_BANKS > 0
+//****************************** BANK 1 ******************************
+// Switch Protocol Sub protocol RX_Num Power Auto Bind Option
+/* 1 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , -120 },
+/* 2 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , -100 },
+/* 3 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , -80 },
+/* 4 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , -60 },
+/* 5 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , -40 },
+/* 6 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , -20 },
+/* 7 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , 0 },
+/* 8 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , 10 },
+/* 9 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , 20 },
+/* 10 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , 40 },
+/* 11 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , 60 },
+/* 12 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , 80 }, // option=fine freq tuning
+/* 13 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , 100 },
+/* 14 */ {PROTO_FRSKYD, 0 , 0 , P_LOW , AUTOBIND , 120 },
+#endif
+#if NBR_BANKS > 1
+//****************************** BANK 2 ******************************
+// Switch Protocol Sub protocol RX_Num Power Auto Bind Option
+/* 1 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , -120 },
+/* 2 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , -100 },
+/* 3 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , -80 },
+/* 4 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , -60 },
+/* 5 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , -40 },
+/* 6 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , -20 },
+/* 7 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , 0 },
+/* 8 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , 10 },
+/* 9 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , 20 },
+/* 10 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , 40 },
+/* 11 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , 60 },
+/* 12 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , 80 }, // option=fine freq tuning
+/* 13 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , 100 },
+/* 14 */ {PROTO_FRSKYX, EU_16 , 0 , P_LOW , AUTOBIND , 120 },
+#endif
+#if NBR_BANKS > 2
+//****************************** BANK 3 ******************************
+// Switch Protocol Sub protocol RX_Num Power Auto Bind Option
+/* 1 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , -120 },
+/* 2 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , -100 },
+/* 3 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , -80 },
+/* 4 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , -60 },
+/* 5 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , -40 },
+/* 6 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , -20 },
+/* 7 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , 0 },
+/* 8 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , 10 },
+/* 9 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , 20 },
+/* 10 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , 40 },
+/* 11 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , 60 },
+/* 12 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , 80 }, // option=fine freq tuning
+/* 13 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , 100 },
+/* 14 */ {PROTO_FRSKYX, CH_16 , 0 , P_LOW , AUTOBIND , 120 },
+#endif
+};
+/* Available protocols and associated sub protocols to pick and choose from
+ PROTO_FLYSKY
+ Flysky
+ V9X9
+ V6X6
+ V912
+ CX20
+ PROTO_HUBSAN
+ H107
+ H301
+ H501
+ PROTO_FRSKYV
+ NONE
+ PROTO_FRSKYD
+ NONE
+ PROTO_FRSKYX
+ CH_16
+ CH_8
+ EU_16
+ EU_8
+ PROTO_HISKY
+ Hisky
+ HK310
+ PROTO_V2X2
+ V2X2
+ JXD506
+ PROTO_DSM
+ DSM2_22
+ DSM2_11
+ DSMX_22
+ DSMX_11
+ PROTO_DEVO
+ NONE
+ PROTO_YD717
+ YD717
+ SKYWLKRP_LOW
+ SYMAX4
+ XINXUN
+ NIHUI
+ PROTO_KN
+ WLTOYS
+ FEILUN
+ PROTO_SYMAX
+ SYMAX
+ SYMAX5C
+ PROTO_SLT
+ NONE
+ PROTO_CX10
+ CX10_GREEN
+ CX10_BLUE
+ DM007
+ JC3015_1
+ JC3015_2
+ MK33041
+ PROTO_Q2X2
+ Q222
+ Q242
+ Q282
+ PROTO_SLT
+ SLT
+ VISTA
+ PROTO_CG023
+ CG023
+ YD829
+ PROTO_BAYANG
+ BAYANG
+ H8S3D
+ X16_AH
+ IRDRONE
+ PROTO_ESKY
+ NONE
+ PROTO_MT99XX
+ MT99
+ H7
+ YZ
+ LS
+ FY805
+ PROTO_MJXQ
+ WLH08
+ X600
+ X800
+ H26D
+ E010
+ H26WH
+ PROTO_SHENQI
+ NONE
+ PROTO_FY326
+ FY326
+ FY319
+ PROTO_SFHSS
+ NONE
+ PROTO_J6PRO
+ NONE
+ PROTO_FQ777
+ NONE
+ PROTO_ASSAN
+ NONE
+ PROTO_HONTAI
+ HONTAI
+ JJRCX1
+ X5C1
+ FQ777_951
+ PROTO_AFHDS2A
+ PWM_IBUS
+ PPM_IBUS
+ PWM_SBUS
+ PPM_SBUS
+ PROTO_WK2x01
+ WK2801
+ WK2401
+ W6_5_1
+ W6_6_1
+ W6_HEL
+ W6_HEL_I
+ PROTO_Q303
+ Q303
+ CX35
+ CX10D
+ CX10WD
+ PROTO_GW008
+ NONE
+ PROTO_DM002
+ NONE
+ PROTO_CABELL
+ CABELL_V3
+ CABELL_V3_TELEMETRY
+ CABELL_SET_FAIL_SAFE
+ CABELL_UNBIND
+ PROTO_ESKY150
+ PROTO_H8_3D
+ H8_3D
+ H20H
+ H20MINI
+ H30MINI
+ PROTO_CORONA
+ COR_V1
+ COR_V2
+ FD_V3
+ PROTO_CFLIE
+ NONE
+ PROTO_HITEC
+ OPT_FW
+ OPT_HUB
+ MINIMA
+ PROTO_WFLY
+ NONE
+ PROTO_BUGS
+ NONE
+ PROTO_SLT
+ SLT_V1
+ SLT_V2
+ Q100
+ Q200
+ MR100
+*/
+
+// RX_Num is used for TX & RX match. Using different RX_Num values for each receiver will prevent starting a model with the false config loaded...
+// RX_Num value is between 0 and 15.
+
+// Power P_HIGH or P_LOW: High or low power setting for the transmission.
+// For indoor P_LOW is more than enough.
+
+// Auto Bind AUTOBIND or NO_AUTOBIND
+// For protocols which does not require binding at each power up (like Flysky, FrSky...), you might still want a bind to be initiated each time you power up the TX.
+// As an example, it's usefull for the WLTOYS F929/F939/F949/F959 (all using the Flysky protocol) which requires a bind at each power up.
+// It also enables the Bind from channel feature, allowing to execute a bind by toggling a designated channel.
+
+// Option: the value is between -128 and +127.
+// The option value is only valid for some protocols, read this page for more information: https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/blob/master/Protocols_Details.md
diff --git a/remote_multiprotocol/iface_cc2500.h b/remote_multiprotocol/iface_cc2500.h
new file mode 100644
index 0000000..81b101b
--- /dev/null
+++ b/remote_multiprotocol/iface_cc2500.h
@@ -0,0 +1,150 @@
+/*
+ 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 .
+ */
+
+#ifndef _IFACE_CC2500_H_
+#define _IFACE_CC2500_H_
+
+enum {
+ CC2500_00_IOCFG2 = 0x00, // GDO2 output pin configuration
+ CC2500_01_IOCFG1 = 0x01, // GDO1 output pin configuration
+ CC2500_02_IOCFG0 = 0x02, // GDO0 output pin configuration
+ CC2500_03_FIFOTHR = 0x03, // RX FIFO and TX FIFO thresholds
+ CC2500_04_SYNC1 = 0x04, // Sync word, high byte
+ CC2500_05_SYNC0 = 0x05, // Sync word, low byte
+ CC2500_06_PKTLEN = 0x06, // Packet length
+ CC2500_07_PKTCTRL1 = 0x07, // Packet automation control
+ CC2500_08_PKTCTRL0 = 0x08, // Packet automation control
+ CC2500_09_ADDR = 0x09, // Device address
+ CC2500_0A_CHANNR = 0x0A, // Channel number
+ CC2500_0B_FSCTRL1 = 0x0B, // Frequency synthesizer control
+ CC2500_0C_FSCTRL0 = 0x0C, // Frequency synthesizer control
+ CC2500_0D_FREQ2 = 0x0D, // Frequency control word, high byte
+ CC2500_0E_FREQ1 = 0x0E, // Frequency control word, middle byte
+ CC2500_0F_FREQ0 = 0x0F, // Frequency control word, low byte
+ CC2500_10_MDMCFG4 = 0x10, // Modem configuration
+ CC2500_11_MDMCFG3 = 0x11, // Modem configuration
+ CC2500_12_MDMCFG2 = 0x12, // Modem configuration
+ CC2500_13_MDMCFG1 = 0x13, // Modem configuration
+ CC2500_14_MDMCFG0 = 0x14, // Modem configuration
+ CC2500_15_DEVIATN = 0x15, // Modem deviation setting
+ CC2500_16_MCSM2 = 0x16, // Main Radio Cntrl State Machine config
+ CC2500_17_MCSM1 = 0x17, // Main Radio Cntrl State Machine config
+ CC2500_18_MCSM0 = 0x18, // Main Radio Cntrl State Machine config
+ CC2500_19_FOCCFG = 0x19, // Frequency Offset Compensation config
+ CC2500_1A_BSCFG = 0x1A, // Bit Synchronization configuration
+ CC2500_1B_AGCCTRL2 = 0x1B, // AGC control
+ CC2500_1C_AGCCTRL1 = 0x1C, // AGC control
+ CC2500_1D_AGCCTRL0 = 0x1D, // AGC control
+ CC2500_1E_WOREVT1 = 0x1E, // High byte Event 0 timeout
+ CC2500_1F_WOREVT0 = 0x1F, // Low byte Event 0 timeout
+ CC2500_20_WORCTRL = 0x20, // Wake On Radio control
+ CC2500_21_FREND1 = 0x21, // Front end RX configuration
+ CC2500_22_FREND0 = 0x22, // Front end TX configuration
+ CC2500_23_FSCAL3 = 0x23, // Frequency synthesizer calibration
+ CC2500_24_FSCAL2 = 0x24, // Frequency synthesizer calibration
+ CC2500_25_FSCAL1 = 0x25, // Frequency synthesizer calibration
+ CC2500_26_FSCAL0 = 0x26, // Frequency synthesizer calibration
+ CC2500_27_RCCTRL1 = 0x27, // RC oscillator configuration
+ CC2500_28_RCCTRL0 = 0x28, // RC oscillator configuration
+ CC2500_29_FSTEST = 0x29, // Frequency synthesizer cal control
+ CC2500_2A_PTEST = 0x2A, // Production test
+ CC2500_2B_AGCTEST = 0x2B, // AGC test
+ CC2500_2C_TEST2 = 0x2C, // Various test settings
+ CC2500_2D_TEST1 = 0x2D, // Various test settings
+ CC2500_2E_TEST0 = 0x2E, // Various test settings
+
+// Status registers
+ CC2500_30_PARTNUM = 0x30, // Part number
+ CC2500_31_VERSION = 0x31, // Current version number
+ CC2500_32_FREQEST = 0x32, // Frequency offset estimate
+ CC2500_33_LQI = 0x33, // Demodulator estimate for link quality
+ CC2500_34_RSSI = 0x34, // Received signal strength indication
+ CC2500_35_MARCSTATE = 0x35, // Control state machine state
+ CC2500_36_WORTIME1 = 0x36, // High byte of WOR timer
+ CC2500_37_WORTIME0 = 0x37, // Low byte of WOR timer
+ CC2500_38_PKTSTATUS = 0x38, // Current GDOx status and packet status
+ CC2500_39_VCO_VC_DAC = 0x39, // Current setting from PLL cal module
+ CC2500_3A_TXBYTES = 0x3A, // Underflow and # of bytes in TXFIFO
+ CC2500_3B_RXBYTES = 0x3B, // Overflow and # of bytes in RXFIFO
+
+// Multi byte memory locations
+ CC2500_3E_PATABLE = 0x3E,
+ CC2500_3F_TXFIFO = 0x3F,
+ CC2500_3F_RXFIFO = 0x3F,
+};
+
+// Definitions for burst/single access to registers
+#define CC2500_WRITE_SINGLE 0x00
+#define CC2500_WRITE_BURST 0x40
+#define CC2500_READ_SINGLE 0x80
+#define CC2500_READ_BURST 0xC0
+
+// Strobe commands
+#define CC2500_SRES 0x30 // Reset chip.
+#define CC2500_SFSTXON 0x31 // Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1).
+ // If in RX/TX: Go to a wait state where only the synthesizer is
+ // running (for quick RX / TX turnaround).
+#define CC2500_SXOFF 0x32 // Turn off crystal oscillator.
+#define CC2500_SCAL 0x33 // Calibrate frequency synthesizer and turn it off
+ // (enables quick start).
+#define CC2500_SRX 0x34 // Enable RX. Perform calibration first if coming from IDLE and
+ // MCSM0.FS_AUTOCAL=1.
+#define CC2500_STX 0x35 // In IDLE state: Enable TX. Perform calibration first if
+ // MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled:
+ // Only go to TX if channel is clear.
+#define CC2500_SIDLE 0x36 // Exit RX / TX, turn off frequency synthesizer and exit
+ // Wake-On-Radio mode if applicable.
+#define CC2500_SAFC 0x37 // Perform AFC adjustment of the frequency synthesizer
+#define CC2500_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio)
+#define CC2500_SPWD 0x39 // Enter power down mode when CSn goes high.
+#define CC2500_SFRX 0x3A // Flush the RX FIFO buffer.
+#define CC2500_SFTX 0x3B // Flush the TX FIFO buffer.
+#define CC2500_SWORRST 0x3C // Reset real time clock.
+#define CC2500_SNOP 0x3D // No operation. May be used to pad strobe commands to two
+ // bytes for simpler software.
+//----------------------------------------------------------------------------------
+// Chip Status Byte
+//----------------------------------------------------------------------------------
+
+// Bit fields in the chip status byte
+#define CC2500_STATUS_CHIP_RDYn_BM 0x80
+#define CC2500_STATUS_STATE_BM 0x70
+#define CC2500_STATUS_FIFO_BYTES_AVAILABLE_BM 0x0F
+
+// Chip states
+#define CC2500_STATE_IDLE 0x00
+#define CC2500_STATE_RX 0x10
+#define CC2500_STATE_TX 0x20
+#define CC2500_STATE_FSTXON 0x30
+#define CC2500_STATE_CALIBRATE 0x40
+#define CC2500_STATE_SETTLING 0x50
+#define CC2500_STATE_RX_OVERFLOW 0x60
+#define CC2500_STATE_TX_UNDERFLOW 0x70
+
+//----------------------------------------------------------------------------------
+// Other register bit fields
+//----------------------------------------------------------------------------------
+#define CC2500_LQI_CRC_OK_BM 0x80
+#define CC2500_LQI_EST_BM 0x7F
+
+//void CC2500_WriteReg(u8 addr, u8 data);
+//u8 CC2500_ReadReg(u8 addr);
+//void CC2500_Reset();
+//void CC2500_Strobe(u8 cmd);
+//void CC2500_WriteData(u8 *packet, u8 length);
+//void CC2500_ReadData(u8 *dpbuffer, int len);
+//void CC2500_SetTxRxMode(enum TXRX_State);
+
+#endif
diff --git a/remote_multiprotocol/mi.ino b/remote_multiprotocol/mi.ino
new file mode 100644
index 0000000..d0df521
--- /dev/null
+++ b/remote_multiprotocol/mi.ino
@@ -0,0 +1,69 @@
+/*
+ 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 .
+ */
+/************************************/
+/************************************/
+/** Arduino replacement routines **/
+/************************************/
+// replacement map()
+int16_t map16b( int16_t x, int16_t in_min, int16_t in_max, int16_t out_min, int16_t out_max)
+{
+// return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+ long y ;
+ x -= in_min ;
+ y = out_max - out_min ;
+ y *= x ;
+ x = y / (in_max - in_min) ;
+ return x + out_min ;
+}
+
+int16_t map( int16_t x, int16_t in_min, int16_t in_max, int16_t out_min, int16_t out_max)
+{
+// return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+ long y ;
+ x -= in_min ;
+ y = out_max - out_min ;
+ y *= x ;
+ x = y / (in_max - in_min) ;
+ return x + out_min ;
+}
+
+// replacement millis() and micros()
+// These work polled, no interrupts
+// micros() MUST be called at least once every 32 milliseconds
+uint16_t MillisPrecount ;
+uint16_t lastTimerValue ;
+uint32_t TotalMicros ;
+uint32_t TotalMillis ;
+uint8_t Correction ;
+
+void delayMilliseconds(unsigned long ms)
+{
+ #if 0
+ uint16_t start = (uint16_t)micros();
+ uint16_t lms = ms ;
+
+ while (lms > 0) {
+ if (((uint16_t)micros() - start) >= 1000) {
+ lms--;
+ start += 1000;
+ }
+ }
+ #else
+
+
+delay(ms);
+#endif
+
+}