diff --git a/Lua_scripts/MultiChan.txt b/Lua_scripts/MultiChan.txt index 51ee4dc9..2a146ce6 100644 --- a/Lua_scripts/MultiChan.txt +++ b/Lua_scripts/MultiChan.txt @@ -241,3 +241,4 @@ 105,0,Shenqi2,Std,1 106,0,WL91x,Std,0 107,0,WPL,Std,0,Light,TH_DR,ST_DR +108,0,Ares,6HPA_Tx,0,CH5,CH6 diff --git a/Multiprotocol/Ares_cc2500.ino b/Multiprotocol/Ares_cc2500.ino new file mode 100644 index 00000000..4b458201 --- /dev/null +++ b/Multiprotocol/Ares_cc2500.ino @@ -0,0 +1,254 @@ +/* + 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 . + */ +// Compatible with ARES 6HPA transmitter + +#if defined(ARES_CC2500_INO) + +#include "iface_cc2500.h" + +//#define ARES_FORCE_ID + +#define ARES_COARSE 0 + +#define ARES_PACKET_LEN 17 +#define ARES_NUM_FREQUENCIES 60 + +enum { + ARES_START = 0x00, + ARES_CALIB = 0x01, + ARES_PREP = 0x02, + ARES_DATA = 0x03, +}; + +// CC2500 register init values captured from the ARES 6HPA transmitter +const PROGMEM uint8_t ARES_init_values[] = { + /* 00 */ 0x06, 0x2E, 0x2E, 0x07, 0x5A, 0x60, 0x30, 0x04, + /* 08 */ 0x05, 0x00, 0x00, 0x06, 0x00, 0x5C, 0xB1, 0x3B + ARES_COARSE, + /* 10 */ 0x6A, 0xF8, 0x03, 0x23, 0x7A, 0x44, 0x07, 0x30, + /* 18 */ 0x18, 0x16, 0x6C, 0x43, 0x40, 0x91, 0x87, 0x6B, + /* 20 */ 0xF8, 0x56, 0x10, 0xA9, 0x0A, 0x00, 0x11 +}; + +// Fixed hopping sequence captured from the ARES 6HPA transmitter. +// This is a permutation of 60 channel values spread across the band. +static const PROGMEM uint8_t ARES_hop[] = { + 0xB0, 0x6F, 0x1D, 0xB4, 0x74, 0x20, 0xB8, 0xD8, + 0x24, 0xBC, 0xDC, 0x28, 0x48, 0xE0, 0x2C, 0x4C, + 0xE4, 0x90, 0x50, 0xE8, 0x94, 0x54, 0xEC, 0x00, + 0x98, 0x58, 0x04, 0x9B, 0x5C, 0x08, 0xA0, 0xC0, + 0x0C, 0xA4, 0xC3, 0x10, 0x30, 0xC6, 0x14, 0x34, + 0xCC, 0x78, 0x38, 0xD0, 0x7C, 0x3C, 0xD4, 0x80, + 0x40, 0x60, 0x84, 0x44, 0x64, 0x88, 0xA8, 0x68, + 0x8C, 0xAC, 0x6C, 0x18 +}; + +static void __attribute__((unused)) ARES_CC2500_init() +{ + CC2500_Strobe(CC2500_SRES); + delayMilliseconds(1); + CC2500_Strobe(CC2500_SIDLE); + + for (uint8_t i = 0; i < 39; ++i) + CC2500_WriteReg(i, pgm_read_byte_near(&ARES_init_values[i])); + + CC2500_WriteReg(CC2500_0C_FSCTRL0, option); + prev_option = option; + + // Write PATABLE to max power (0xFF for all 8 entries) as captured + for (uint8_t i = 0; i < 8; i++) + CC2500_WriteReg(CC2500_3E_PATABLE, 0xFF); + + CC2500_SetTxRxMode(TX_EN); + CC2500_SetPower(); +} + +// Load hopping table +static void __attribute__((unused)) ARES_RF_channels() +{ + for (uint8_t i = 0; i < ARES_NUM_FREQUENCIES; i++) + hopping_frequency[i] = pgm_read_byte_near(&ARES_hop[i]); +} + +static void __attribute__((unused)) ARES_tune_chan() +{ + CC2500_Strobe(CC2500_SIDLE); + CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[hopping_frequency_no]); + CC2500_Strobe(CC2500_SFTX); + CC2500_Strobe(CC2500_SCAL); +} + +static void __attribute__((unused)) ARES_change_chan_fast() +{ + CC2500_Strobe(CC2500_SIDLE); + CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[hopping_frequency_no]); + CC2500_WriteReg(CC2500_25_FSCAL1, calData[hopping_frequency_no]); +} + +// Advance the hop counter: cycles through 0-58 with step, inserting 59 when wrapping through 0 +static uint8_t __attribute__((unused)) ARES_next_counter(uint8_t current, uint8_t step) +{ + if (current == 59) + return 0; + uint8_t next = (current + step) % 59; + if (next == 0) + return 59; + return next; +} + +static void __attribute__((unused)) ARES_build_packet() +{ + // Length byte: 16 data bytes follow + packet[0] = 0x10; + + // TX ID + packet[1] = rx_tx_addr[1]; + packet[2] = rx_tx_addr[2]; + packet[3] = rx_tx_addr[3]; + + // 6 channels encoded as interleaved 12-bit values in bytes 4-12 + uint16_t ch[6]; + for (uint8_t i = 0; i < 6; i++) + ch[i] = convert_channel_16b_nolimit(i, 1820, 3300, false); + + packet[4] = ch[0] >> 4; + packet[5] = ((ch[0] & 0x0F) << 4) | (ch[1] & 0x0F); + packet[6] = ch[1] >> 4; + packet[7] = ch[2] >> 4; + packet[8] = ((ch[2] & 0x0F) << 4) | (ch[3] & 0x0F); + packet[9] = ch[3] >> 4; + packet[10] = ch[4] >> 4; + packet[11] = ((ch[4] & 0x0F) << 4) | (ch[5] & 0x0F); + packet[12] = ch[5] >> 4; + + // Byte 16: counter step size (stored in crc, set to 1-58 in ARES_init) + uint8_t step = crc; + + // Bytes 13-15: running counter with rotating bit 7 frame indicator + // The counter cycles 0-58 with a step, inserting 59 before wrapping to 0 + // Each group of 3 packets has 3 consecutive counter values + // packet_count holds the current counter value + uint8_t c0 = packet_count; + uint8_t c1 = ARES_next_counter(c0, step); + uint8_t c2 = ARES_next_counter(c1, step); + + // Frame indicator: each data frame is sent 3 times + // bind_phase tracks position 0/1/2 within the group of 3 + packet[13] = c0; + packet[14] = c1; + packet[15] = c2; + packet[16] = step; + + // Set the rotating frame bit (bit 7) on one of bytes 13-15 + switch (bind_phase) + { + case 0: + packet[13] |= 0x80; + break; + case 1: + packet[14] |= 0x80; + break; + case 2: + packet[15] |= 0x80; + break; + } +} + +static void __attribute__((unused)) ARES_send_packet() +{ + ARES_change_chan_fast(); + CC2500_SetPower(); + CC2500_WriteData(packet, ARES_PACKET_LEN); +} + +#define ARES_PACKET_PERIOD 6670 // 6.67ms between packets +#define ARES_PREP_TIMING 2000 + +uint16_t ARES_callback() +{ + switch(phase) + { + case ARES_START: + ARES_CC2500_init(); + hopping_frequency_no = 0; + bind_phase = 0; + ARES_tune_chan(); + phase = ARES_CALIB; + return ARES_PREP_TIMING; + case ARES_CALIB: + calData[hopping_frequency_no] = CC2500_ReadReg(CC2500_25_FSCAL1); + hopping_frequency_no++; + if (hopping_frequency_no < ARES_NUM_FREQUENCIES) + ARES_tune_chan(); + else + { + hopping_frequency_no = 0; + phase = ARES_PREP; + } + return ARES_PREP_TIMING; + case ARES_PREP: + if (prev_option != option) + { + phase = ARES_START; + return ARES_PREP_TIMING; + } + #ifdef MULTI_SYNC + telemetry_set_input_sync(ARES_PACKET_PERIOD); + #endif + ARES_build_packet(); + phase = ARES_DATA; + // Fall through + case ARES_DATA: + ARES_send_packet(); + hopping_frequency_no++; + if (hopping_frequency_no >= ARES_NUM_FREQUENCIES) + hopping_frequency_no = 0; + bind_phase++; + if (bind_phase >= 3) + { + bind_phase = 0; + // Advance counter to start of next group + uint8_t step = crc; + packet_count = ARES_next_counter(packet_count, step); + packet_count = ARES_next_counter(packet_count, step); + packet_count = ARES_next_counter(packet_count, step); + } + phase = ARES_PREP; + return ARES_PACKET_PERIOD; + } + return 0; +} + +void ARES_init() +{ + BIND_DONE; // Autobind protocol - no TX-initiated bind phase + ARES_RF_channels(); + + // rx_tx_addr[1] and [2] are already set from MProtocol_id by the framework + // RX_num (0-63) in byte 3 provides model match + rx_tx_addr[3] = RX_num; + + // Counter step and start from capture + crc = 23; + packet_count = 35; + + #ifdef ARES_FORCE_ID + rx_tx_addr[1] = 0xDC; + rx_tx_addr[2] = 0xCC; + rx_tx_addr[3] = 0x00; + #endif + phase = ARES_START; +} + +#endif diff --git a/Multiprotocol/Multi.txt b/Multiprotocol/Multi.txt index e7aff0ea..0474cfa7 100644 --- a/Multiprotocol/Multi.txt +++ b/Multiprotocol/Multi.txt @@ -103,3 +103,4 @@ 105,Shenqi2 106,WL91x 107,WPL +108,0,Ares \ No newline at end of file diff --git a/Multiprotocol/Multi_Protos.ino b/Multiprotocol/Multi_Protos.ino index b2d41fe1..f5153cc0 100644 --- a/Multiprotocol/Multi_Protos.ino +++ b/Multiprotocol/Multi_Protos.ino @@ -119,6 +119,7 @@ const char STR_JIABAILE[] ="JIABAILE"; const char STR_KAMTOM[] ="KAMTOM"; const char STR_WL91X[] ="WL91x"; const char STR_WPL[] ="WPL"; +const char STR_ARES[] ="ARES"; const char STR_SUBTYPE_FLYSKY[] = "\x04""Std\0""V9x9""V6x6""V912""CX20"; const char STR_SUBTYPE_HUBSAN[] = "\x04""H107""H301""H501"; @@ -231,6 +232,9 @@ const mm_protocol_definition multi_protocols[] = { #if defined(MULTI_CONFIG_INO) {PROTO_CONFIG, STR_CONFIG, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, 0, CONFIG_init, CONFIG_callback }, #endif + #if defined(ARES_CC2500_INO) + {PROTO_ARES, STR_ARES, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 0, SW_CC2500, ARES_init, ARES_callback }, + #endif #if defined(ASSAN_NRF24L01_INO) {PROTO_ASSAN, STR_ASSAN, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, ASSAN_init, ASSAN_callback }, #endif diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 44dfe356..450e2965 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 4 -#define VERSION_PATCH_LEVEL 62 +#define VERSION_PATCH_LEVEL 63 #define MODE_SERIAL 0 @@ -135,6 +135,7 @@ enum PROTOCOLS PROTO_SHENQI2 = 105, // =>NRF24L01 PROTO_WL91X = 106, // =>CC2500 & NRF24L01 PROTO_WPL = 107, // =>NRF24L01 + PROTO_ARES = 108, // =>CC2500 PROTO_NANORF = 126, // =>NRF24L01 PROTO_TEST = 127, // =>CC2500 diff --git a/Multiprotocol/Multiprotocol.ino b/Multiprotocol/Multiprotocol.ino index 61d0325f..31de31e4 100644 --- a/Multiprotocol/Multiprotocol.ino +++ b/Multiprotocol/Multiprotocol.ino @@ -104,7 +104,7 @@ uint16_t packet_period; uint8_t packet_count; uint8_t packet_sent; uint8_t packet_length; -#if defined(HOTT_CC2500_INO) || defined(ESKY150V2_CC2500_INO) || defined(MLINK_CYRF6936_INO) +#if defined(HOTT_CC2500_INO) || defined(ESKY150V2_CC2500_INO) || defined(MLINK_CYRF6936_INO) || defined(ARES_CC2500_INO) uint8_t hopping_frequency[78]; #else uint8_t hopping_frequency[50]; @@ -130,7 +130,7 @@ uint16_t pps_counter; #ifdef CC2500_INSTALLED #ifdef SCANNER_CC2500_INO uint8_t calData[255]; - #elif defined(HOTT_CC2500_INO) || defined(ESKY150V2_CC2500_INO) + #elif defined(HOTT_CC2500_INO) || defined(ESKY150V2_CC2500_INO) || defined(ARES_CC2500_INO) uint8_t calData[75]; #else uint8_t calData[50]; @@ -635,7 +635,11 @@ void setup() option = FORCE_HOTT_TUNING; // Use config-defined tuning value for HOTT else #endif - option = (uint8_t)PPM_prot_line->option; // Use radio-defined option value + #if defined(FORCE_ARES_TUNING) && defined(ARES_CC2500_INO) + if (protocol==PROTO_ARES) + option = FORCE_ARES_TUNING; // Use config-defined tuning value for ARES + else + #endif option = (uint8_t)PPM_prot_line->option; // Use radio-defined option value if(PPM_prot_line->power) POWER_FLAG_on; if(PPM_prot_line->autobind) @@ -1376,6 +1380,11 @@ void update_serial_data() if (protocol==PROTO_HOTT) option=FORCE_HOTT_TUNING; // Use config-defined tuning value for HOTT else + #endif + #if defined(FORCE_ARES_TUNING) && defined(ARES_CC2500_INO) + if (protocol==PROTO_ARES) + option=FORCE_ARES_TUNING; // Use config-defined tuning value for ARES + else #endif option=rx_ok_buff[3]; // Use radio-defined option value diff --git a/Multiprotocol/Validate.h b/Multiprotocol/Validate.h index 73ab47ec..d9d67756 100644 --- a/Multiprotocol/Validate.h +++ b/Multiprotocol/Validate.h @@ -69,6 +69,11 @@ // Check forced tuning values are valid //CC2500 +#ifdef FORCE_ARES_TUNING + #if ( FORCE_ARES_TUNING < -127 ) || ( FORCE_ARES_TUNING > 127 ) + #error "The ARES 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." @@ -276,6 +281,7 @@ #endif #if not defined(CC2500_INSTALLED) || defined MULTI_EU + #undef ARES_CC2500_INO #undef CORONA_CC2500_INO #undef E016HV2_CC2500_INO #undef ESKY150V2_CC2500_INO @@ -410,6 +416,7 @@ #endif #ifdef MULTI_SURFACE + #undef ARES_CC2500_INO #undef BUGS_A7105_INO #undef HEIGHT_A7105_INO #undef HUBSAN_A7105_INO diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index fd7f6508..f4f5d05e 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -107,6 +107,7 @@ //#define FORCE_REDPINE_TUNING 0 //#define FORCE_FUTABA_TUNING 0 //#define FORCE_SKYARTEC_TUNING 0 +//#define FORCE_ARES_TUNING 0 /** A7105 Fine Frequency Tuning **/ //This is required in rare cases where some A7105 modules and/or RXs have an inaccurate crystal oscillator. @@ -205,6 +206,7 @@ #define WK2x01_CYRF6936_INO //The protocols below need a CC2500 to be installed +#define ARES_CC2500_INO #define CORONA_CC2500_INO #define E016HV2_CC2500_INO #define ESKY150V2_CC2500_INO @@ -213,11 +215,11 @@ #define FRSKYV_CC2500_INO #define FRSKYX_CC2500_INO //Include FRSKYX2 protocol #define FRSKY_RX_CC2500_INO +#define FUTABA_CC2500_INO #define HITEC_CC2500_INO #define HOTT_CC2500_INO //#define IKEAANSLUTA_CC2500_INO // This is mostly a "for-fun" kind of a thing, not needed for most users #define SCANNER_CC2500_INO -#define FUTABA_CC2500_INO #define SKYARTEC_CC2500_INO #define REDPINE_CC2500_INO #define RLINK_CC2500_INO @@ -565,6 +567,8 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= { // - 0x0000ABCD will give to the protocol the channels in the order 1,2,3,4,10,11,12,13 which potentially enables acces to channels not available on your TX. Note A=10,B=11,C=12,D=13,E=14,F=15. /* Available protocols and associated sub protocols to pick and choose from (Listed in alphabetical order) + PROTO_ARES + NONE PROTO_AFHDS2A PWM_IBUS PPM_IBUS diff --git a/Protocols_Details.md b/Protocols_Details.md index 72f84423..47abdaa5 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -63,6 +63,7 @@ You've upgraded the module but the radio does not display the name of the protoc Protocol Name|Build|Protocol Number|Sub_Proto 0|Sub_Proto 1|Sub_Proto 2|Sub_Proto 3|Sub_Proto 4|Sub_Proto 5|Sub_Proto 6|Sub_Proto 7|RF Module|Emulation ---|---|---|---|---|---|---|---|---|---|---|---|--- +[Ares](Protocols_Details.md#Ares---108)|AIR|108|||||||||CC2500| [Assan](Protocols_Details.md#ASSAN---24)|AIR/SFC|24|||||||||NRF24L01| [Bayang](Protocols_Details.md#BAYANG---14)|AIR/SFC|14|Bayang|H8S3D|X16_AH|IRDRONE|DHD_D4|QX100|||NRF24L01|XN297 [Bayang RX](Protocols_Details.md#BAYANG-RX---59)|AIR/SFC|59|Multi|CPPM|||||||NRF24L01|XN297 @@ -761,6 +762,24 @@ CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9 *** # CC2500 RF Module +## Ares - *108* +Models: ARES Gamma 370, P-51D Mustang 350, RTF models with 6HPA-Tx and AZS12006-Rx (6 channel). + +Autobind protocol: +- to bind, power on the TX first +- then power on the receiver - LED slow flash +- press receiver bind button - LED faster flash +- receiver LED will flash quickly (15 seconds) when bound LED turns solid + +Receiver numbers (0-63) available for model match, MPM global ID used for unique module identifier. Changing the TX module or RX number will require re-binding the receiver. + +Option for this protocol corresponds to fine frequency tuning. This value is different for each Module and **must** be accurate otherwise the link will not be stable. +Check the [Frequency Tuning page](/docs/Frequency_Tuning.md) to determine it. + +CH1|CH2|CH3|CH4|CH5|CH6 +---|---|---|---|---|--- +CH1|CH2|CH3|CH4|CH5|CH6 + ## CORONA - *37* Models: Corona 2.4GHz FSS and DSSS receivers. diff --git a/buildroot/bin/build_release_avr_optiboot b/buildroot/bin/build_release_avr_optiboot index 1371b3af..11b53dfa 100644 --- a/buildroot/bin/build_release_avr_optiboot +++ b/buildroot/bin/build_release_avr_optiboot @@ -14,7 +14,7 @@ mv build/Multiprotocol.ino.bin ./binaries/mm-avr-txflash-aetr-A7105-inv-v$MULTI_ printf "\e[33;1mBuilding mm-avr-txflash-aetr-CC2500-inv-v$MULTI_VERSION.bin\e[0m\n"; opt_disable $ALL_PROTOCOLS; opt_enable $CC2500_PROTOCOLS; -opt_disable HITEC_CC2500_INO REDPINE_CC2500_INO OMP_CC2500_INO SKYARTEC_CC2500_INO SCANNER_CC2500_INO FRSKYL_CC2500_INO RLINK_CC2500_INO; +opt_disable HITEC_CC2500_INO REDPINE_CC2500_INO OMP_CC2500_INO SKYARTEC_CC2500_INO SCANNER_CC2500_INO FRSKYL_CC2500_INO RLINK_CC2500_INO ARES_CC2500_INO; buildMulti; exitcode=$((exitcode+$?)); mv build/Multiprotocol.ino.bin ./binaries/mm-avr-txflash-aetr-CC2500-inv-v$MULTI_VERSION.bin;