diff --git a/AVR8_Burn-O-Mat_2_1_2_setup.exe b/AVR8_Burn-O-Mat_2_1_2_setup.exe new file mode 100644 index 0000000..5f9e431 Binary files /dev/null and b/AVR8_Burn-O-Mat_2_1_2_setup.exe differ diff --git a/Multiprotocol/A7105_joysway.ino b/Multiprotocol/A7105_joysway.ino new file mode 100644 index 0000000..b153c79 --- /dev/null +++ b/Multiprotocol/A7105_joysway.ino @@ -0,0 +1,164 @@ +/* + 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. + + Deviation 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 Deviation. If not, see . + */ + +#if defined JOYSWAY_A7105_INO +#include "iface_a7105.h" + + +#define EVEN_ODD 0x00 +//#define EVEN_ODD 0x01 +static const uint8_t A7105_regs[] = { + 0x00, 0x62, -1, 0x0f, 0x00, -1 , -1 , 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0xf5, 0x00, 0x15, + 0x9e, 0x4b, 0x00, 0x03, 0x56, 0x2b, 0x12, 0x4a, 0x02, 0x80, 0x80, 0x00, 0x0e, 0x91, 0x03, 0x0f, + 0x16, 0x2a, 0x00, -1, -1, -1, 0x3a, 0x06, 0x1f, 0x47, 0x80, 0x01, 0x05, 0x45, 0x18, 0x00, + 0x01, 0x0f, 0x00 +}; + +static uint8_t next_ch; + +static int joysway_init() +{ + int i; + uint8_t if_calibration1; + //uint8_t vco_calibration0; + //uint8_t vco_calibration1; + + counter = 0; + next_ch = 0x30; + + for (i = 0; i < 0x33; i++) + if((uint8_t)A7105_regs[i] != -1) + A7105_WriteReg(i, A7105_regs[i]); + A7105_WriteID(0x5475c52a); + + A7105_Strobe(A7105_PLL); + + //IF Filter Bank Calibration + A7105_WriteReg(0x02, 1); + A7105_ReadReg(0x02); + uint32_t ms = micros(); + while(micros() - ms < 500) { + if(! A7105_ReadReg(0x02)) + break; + } + if (micros() - ms >= 500) + return 0; + A7105_Strobe(A7105_STANDBY); + if_calibration1 = A7105_ReadReg(0x22); + if(if_calibration1 & A7105_MASK_FBCF) { + //Calibration failed...what do we do? + return 0; + } + + //VCO Current Calibration + A7105_WriteReg(0x24, 0x13); //Recomended calibration from A7105 Datasheet + A7105_WriteReg(0x25, 0x09); //Recomended calibration from A7105 Datasheet + + A7105_WriteID(MProtocol_id_master); + A7105_Strobe(A7105_PLL); + A7105_WriteReg(0x02, 1); + ms = micros(); + while(micros() - ms < 500) { + if(! A7105_ReadReg(0x02)) + break; + } + if (micros() - ms >= 500) + return 0; + A7105_Strobe(A7105_STANDBY); + if_calibration1 = A7105_ReadReg(0x22); + if(if_calibration1 & A7105_MASK_FBCF) { + //Calibration failed...what do we do? + return 0; + } + A7105_WriteReg(0x24, 0x13); //Recomended calibration from A7105 Datasheet + A7105_WriteReg(0x25, 0x09); //Recomended calibration from A7105 Datasheet + + A7105_SetTxRxMode(TX_EN); + A7105_SetPower(); + + A7105_Strobe(A7105_STANDBY); + return 1; +} + +static void joysway_build_packet() +{ + int i; + //-100% =~ 0x03e8 + //+100% =~ 0x07ca + //Calculate: + //Center = 0x5d9 + //1 % = 5 + packet[0] = counter == 0 ? 0xdd : 0xff; + packet[1] = (MProtocol_id_master >> 24) & 0xff; + packet[2] = (MProtocol_id_master >> 16) & 0xff; + packet[3] = (MProtocol_id_master >> 8) & 0xff; + packet[4] = (MProtocol_id_master >> 0) & 0xff; + packet[5] = 0x00; + static const int chmap[4] = {6, 7, 10, 11}; + for (i = 0; i < 4; i++) { +// if (i >= Model.num_channels) { packet[chmap[i]] = 0x64; continue; } + uint32_t value = (uint32_t)Servo_data[i] * 0x66 / PPM_MAX + 0x66; + if (value < 0) { value = 0; } + if (value > 0xff) { value = 0xff; } + packet[chmap[i]] = value; + } + packet[8] = 0x64; + packet[9] = 0x64; + packet[12] = 0x64; + packet[13] = 0x64; + packet[14] = counter == 0 ? 0x30 : 0xaa; + uint8_t value = 0; + for (int i = 0; i < 15; i++) { value += packet[i]; } + packet[15] = value; +} + +static uint16_t joysway_cb() +{ + uint8_t ch; + if (counter == 254) { + counter = 0; + A7105_WriteID(0x5475c52a); + ch = 0x0a; + } else if (counter == 2) { + A7105_WriteID(MProtocol_id_master); + ch = 0x30; + } else { + if ((counter & 0x01) ^ EVEN_ODD) { + ch = 0x30; + } else { + ch = next_ch; + } + } + if (! ((counter & 0x01) ^ EVEN_ODD)) { + next_ch++; + if (next_ch == 0x45) + next_ch = 0x30; + } + joysway_build_packet(); + A7105_Strobe(A7105_STANDBY); + A7105_WriteData(16, ch); + counter++; + return 6000; +} + +static uint16_t JOYSWAY_Setup() { + while(1) { + A7105_Reset(); + if (joysway_init()) + break; + } + return 2400; +} +#endif diff --git a/Multiprotocol/CYRF6936_SPI.ino b/Multiprotocol/CYRF6936_SPI.ino index 9880233..3985acb 100644 --- a/Multiprotocol/CYRF6936_SPI.ino +++ b/Multiprotocol/CYRF6936_SPI.ino @@ -215,11 +215,10 @@ static void CYRF_StartReceive() CYRF_ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, 0x10); } */ -/*static void CYRF_ReadDataPacketLen(uint8_t dpbuffer[], uint8_t length) +static void CYRF_ReadDataPacketLen(uint8_t dpbuffer[], uint8_t length) { ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, length); } -*/ static void CYRF_WriteDataPacketLen(const uint8_t dpbuffer[], uint8_t len) { CYRF_WriteRegister(CYRF_01_TX_LENGTH, len); diff --git a/Multiprotocol/CYRF6936_j6pro.ino b/Multiprotocol/CYRF6936_j6pro.ino new file mode 100644 index 0000000..623ae5f --- /dev/null +++ b/Multiprotocol/CYRF6936_j6pro.ino @@ -0,0 +1,277 @@ +/* + 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. + + Deviation 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 Deviation. If not, see . + */ + +#if defined(J6PRO_CYRF6936_INO) +#include "iface_cyrf6936.h" + +#define NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us + +//For Debug +//#define NO_SCRAMBLE + +enum J6ProState { + J6PRO_BIND, + J6PRO_BIND_01, + J6PRO_BIND_03_START, + J6PRO_BIND_03_CHECK, + J6PRO_BIND_05_1, + J6PRO_BIND_05_2, + J6PRO_BIND_05_3, + J6PRO_BIND_05_4, + J6PRO_BIND_05_5, + J6PRO_BIND_05_6, + J6PRO_CHANSEL, + J6PRO_CHAN_1, + J6PRO_CHAN_2, + J6PRO_CHAN_3, + J6PRO_CHAN_4, +}; + +static const uint8_t J6Pro_sopcodes[][8] = { + /* Note these are in order transmitted (LSB 1st) */ + {0x3C, 0x37, 0xCC, 0x91, 0xE2, 0xF8, 0xCC, 0x91}, + {0x9B, 0xC5, 0xA1, 0x0F, 0xAD, 0x39, 0xA2, 0x0F}, + {0xEF, 0x64, 0xB0, 0x2A, 0xD2, 0x8F, 0xB1, 0x2A}, + {0x66, 0xCD, 0x7C, 0x50, 0xDD, 0x26, 0x7C, 0x50}, + {0x5C, 0xE1, 0xF6, 0x44, 0xAD, 0x16, 0xF6, 0x44}, + {0x5A, 0xCC, 0xAE, 0x46, 0xB6, 0x31, 0xAE, 0x46}, + {0xA1, 0x78, 0xDC, 0x3C, 0x9E, 0x82, 0xDC, 0x3C}, + {0xB9, 0x8E, 0x19, 0x74, 0x6F, 0x65, 0x18, 0x74}, + {0xDF, 0xB1, 0xC0, 0x49, 0x62, 0xDF, 0xC1, 0x49}, + {0x97, 0xE5, 0x14, 0x72, 0x7F, 0x1A, 0x14, 0x72}, + {0x82, 0xC7, 0x90, 0x36, 0x21, 0x03, 0xFF, 0x17}, + {0xE2, 0xF8, 0xCC, 0x91, 0x3C, 0x37, 0xCC, 0x91}, //Note: the '03' was '9E' in the Cypress recommended table + {0xAD, 0x39, 0xA2, 0x0F, 0x9B, 0xC5, 0xA1, 0x0F}, //The following are the same as the 1st 8 above, + {0xD2, 0x8F, 0xB1, 0x2A, 0xEF, 0x64, 0xB0, 0x2A}, //but with the upper and lower word swapped + {0xDD, 0x26, 0x7C, 0x50, 0x66, 0xCD, 0x7C, 0x50}, + {0xAD, 0x16, 0xF6, 0x44, 0x5C, 0xE1, 0xF6, 0x44}, + {0xB6, 0x31, 0xAE, 0x46, 0x5A, 0xCC, 0xAE, 0x46}, + {0x9E, 0x82, 0xDC, 0x3C, 0xA1, 0x78, 0xDC, 0x3C}, + {0x6F, 0x65, 0x18, 0x74, 0xB9, 0x8E, 0x19, 0x74}, +}; +const uint8_t bind_sop_code[] = {0x62, 0xdf, 0xc1, 0x49, 0xdf, 0xb1, 0xc0, 0x49}; +const uint8_t data_code[] = {0x02, 0xf9, 0x93, 0x97, 0x02, 0xfa, 0x5c, 0xe3, 0x01, 0x2b, 0xf1, 0xdb, 0x01, 0x32, 0xbe, 0x6f}; + +static uint8_t stateJ6P; +static uint8_t radio_ch[4]; +static uint8_t num_channels; + +void J6Pro_build_bind_packet() +{ + packet[0] = 0x01; //Packet type + packet[1] = 0x01; //FIXME: What is this? Model number maybe? + packet[2] = 0x56; //FIXME: What is this? + packet[3] = cyrfmfg_id[0]; + packet[4] = cyrfmfg_id[1]; + packet[5] = cyrfmfg_id[2]; + packet[6] = cyrfmfg_id[3]; + packet[7] = cyrfmfg_id[4]; + packet[8] = cyrfmfg_id[5]; +} +void J6Pro_build_data_packet() +{ + uint8_t i; + uint32_t upperbits = 0; + packet[0] = 0xaa; //FIXME what is this? + for (i = 0; i < 12; i++) { + if (i >= num_channels) { + packet[i+1] = 0xff; + continue; + } + uint32_t value = (uint32_t)Servo_data[i] * 0x200 / PPM_MAX + 0x200; + if (value < 0) + value = 0; + if (value > 0x3ff) + value = 0x3ff; + packet[i+1] = value & 0xff; + upperbits |= (value >> 8) << (i * 2); + } + packet[13] = upperbits & 0xff; + packet[14] = (upperbits >> 8) & 0xff; + packet[15] = (upperbits >> 16) & 0xff; +} + +static void J6Pro_cyrf_init() +{ + /* Initialise CYRF chip */ + CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02); + CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3c); + CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14); + CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05); + CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55); + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25); + CYRF_WriteRegister(CYRF_03_TX_CFG, 0x05 | CYRF_BIND_POWER); + CYRF_WriteRegister(CYRF_06_RX_CFG, 0x8a); + CYRF_WriteRegister(CYRF_03_TX_CFG, 0x28 | CYRF_BIND_POWER); + CYRF_WriteRegister(CYRF_12_DATA64_THOLD, 0x0e); + CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0xee); + CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x00); + CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x00); + CYRF_ConfigDataCode(data_code, 16); + CYRF_WritePreamble(0x023333); +} +static void J6Pro_cyrf_bindinit() +{ +/* Use when binding */ + //0.060470# 03 2f + CYRF_WriteRegister(CYRF_03_TX_CFG, 0x28 | 0x07); //Use max power for binding in case there is no telem module + + CYRF_ConfigRFChannel(0x52); + CYRF_ConfigSOPCode(bind_sop_code); + CYRF_ConfigCRCSeed(0x0000); + CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4a); + CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x83); + //0.061511# 13 20 + + CYRF_ConfigRFChannel(0x52); + //0.062684# 0f 05 + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25); + //0.062792# 0f 05 + CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x40); + J6Pro_build_bind_packet(); //01 01 e9 49 ec a9 c4 c1 ff + //CYRF_WriteDataPacketLen(packet, 0x09); +} +static void J6Pro_cyrf_datainit() +{ +/* Use when already bound */ + //0.094007# 0f 05 + uint8_t sop_idx = (0xff & (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + cyrfmfg_id[3] - cyrfmfg_id[5])) % 19; + uint16_t crc = (0xff & (cyrfmfg_id[1] - cyrfmfg_id[4] + cyrfmfg_id[5])) | + ((0xff & (cyrfmfg_id[2] + cyrfmfg_id[3] - cyrfmfg_id[4] + cyrfmfg_id[5])) << 8); + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25); + CYRF_ConfigSOPCode(J6Pro_sopcodes[sop_idx]); + CYRF_ConfigCRCSeed(crc); +} + +static void J6Pro_set_radio_channels() +{ + //FIXME: Query free channels + //lowest channel is 0x08, upper channel is 0x4d? + CYRF_FindBestChannels(radio_ch, 3, 5, 8, 77); + radio_ch[3] = radio_ch[0]; +} + +static uint16_t j6pro_cb() +{ + switch(stateJ6P) { + case J6PRO_BIND: + J6Pro_cyrf_bindinit(); + stateJ6P = J6PRO_BIND_01; + //no break because we want to send the 1st bind packet now + case J6PRO_BIND_01: + CYRF_ConfigRFChannel(0x52); + CYRF_SetTxRxMode(TX_EN); + //0.062684# 0f 05 + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25); + //0.062684# 0f 05 + CYRF_WriteDataPacketLen(packet, 0x09); + stateJ6P = J6PRO_BIND_03_START; + return 3000; //3msec + case J6PRO_BIND_03_START: + { + int i = 0; + while (! (CYRF_ReadRegister(0x04) & 0x06)) + if(++i > NUM_WAIT_LOOPS) + break; + } + CYRF_ConfigRFChannel(0x53); + CYRF_SetTxRxMode(RX_EN); + CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4a); + CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x83); + stateJ6P = J6PRO_BIND_03_CHECK; + return 30000; //30msec + case J6PRO_BIND_03_CHECK: + { + uint8_t rx = CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS); + if((rx & 0x1a) == 0x1a) { + rx = CYRF_ReadRegister(CYRF_0A_RX_LENGTH); + if(rx == 0x0f) { + rx = CYRF_ReadRegister(CYRF_09_RX_COUNT); + if(rx == 0x0f) { + //Expected and actual length are both 15 + CYRF_ReadDataPacketLen(packet, rx); + if (packet[0] == 0x03 && + packet[3] == cyrfmfg_id[0] && + packet[4] == cyrfmfg_id[1] && + packet[5] == cyrfmfg_id[2] && + packet[6] == cyrfmfg_id[3] && + packet[7] == cyrfmfg_id[4] && + packet[8] == cyrfmfg_id[5]) + { + //Send back Ack + packet[0] = 0x05; + CYRF_ConfigRFChannel(0x54); + CYRF_SetTxRxMode(TX_EN); + stateJ6P = J6PRO_BIND_05_1; + return 2000; //2msec + } + } + } + } + stateJ6P = J6PRO_BIND_01; + return 500; + } + case J6PRO_BIND_05_1: + case J6PRO_BIND_05_2: + case J6PRO_BIND_05_3: + case J6PRO_BIND_05_4: + case J6PRO_BIND_05_5: + case J6PRO_BIND_05_6: + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x25); + CYRF_WriteDataPacketLen(packet, 0x0f); + stateJ6P = stateJ6P + 1; + return 4600; //4.6msec + case J6PRO_CHANSEL: + BIND_DONE; + J6Pro_set_radio_channels(); + J6Pro_cyrf_datainit(); + stateJ6P = J6PRO_CHAN_1; + case J6PRO_CHAN_1: + //Keep transmit power updated + CYRF_SetPower(CYRF_HIGH_POWER); + J6Pro_build_data_packet(); + //return 3400; + case J6PRO_CHAN_2: + //return 3500; + case J6PRO_CHAN_3: + //return 3750 + case J6PRO_CHAN_4: + CYRF_ConfigRFChannel(radio_ch[stateJ6P - J6PRO_CHAN_1]); + CYRF_SetTxRxMode(TX_EN); + CYRF_WriteDataPacket(packet); + if (stateJ6P == J6PRO_CHAN_4) { + stateJ6P = J6PRO_CHAN_1; + return 13900; + } + stateJ6P = stateJ6P + 1; + return 3550; + } + return 0; +} + +static uint16_t j6pro_setup() +{ + CYRF_Reset(); + J6Pro_cyrf_init(); + num_channels = 8; + if (IS_AUTOBIND_FLAG_on) { + stateJ6P = J6PRO_BIND; + BIND_IN_PROGRESS; + } else { + stateJ6P = J6PRO_CHANSEL; + } + return 2400; +} +#endif diff --git a/Multiprotocol/Cyrf6936_wk2x01.ino b/Multiprotocol/Cyrf6936_wk2x01.ino new file mode 100644 index 0000000..363dd61 --- /dev/null +++ b/Multiprotocol/Cyrf6936_wk2x01.ino @@ -0,0 +1,449 @@ +/* + 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. + Deviation 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 Deviation. If not, see . + */ +#if defined(WK2x01_CYRF6936_INO) +#include "iface_cyrf6936.h" + +#define PKTS_PER_CHANNEL 4 + +//Fewer bind packets in the emulator so we can get right to the important bits +#define WK_BIND_COUNT 2980 + +#define NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us + + +#define WK_BIND 0 +#define WK_BOUND_1 1 +#define WK_BOUND_2 2 +#define WK_BOUND_3 3 +#define WK_BOUND_4 4 +#define WK_BOUND_5 5 +#define WK_BOUND_6 6 +#define WK_BOUND_7 7 +#define WK_BOUND_8 8 + +static const uint8_t sopcode[8] = { + /* Note these are in order transmitted (LSB 1st) */ + 0xDF,0xB1,0xC0,0x49,0x62,0xDF,0xC1,0x49 //0x49C1DF6249C0B1DF +}; +static const uint8_t fail_map[8] = {2, 1, 0, 3, 4, 5, 6, 7}; + +static uint8_t wk_pkt_num; +static u8 *radio_ch_ptr; +static uint16_t WK_BIND_COUNTer; +static uint8_t last_beacon; +/* +static const char * const wk2601_opts[] = { + _tr_noop("Chan mode"), _tr_noop("5+1"), _tr_noop("Heli"), _tr_noop("6+1"), NULL, + _tr_noop("COL Inv"), _tr_noop("Normal"), _tr_noop("Inverted"), NULL, + _tr_noop("COL Limit"), "-100", "100", NULL, + NULL +}; +*/ +#define WK2601_OPT_CHANMODE 0 +#define WK2601_OPT_PIT_INV 1 +#define WK2601_OPT_PIT_LIMIT 2 +#define LAST_PROTO_OPT 3 + +static void add_pkt_crc(uint8_t init) { + uint8_t add = init; + uint8_t xou = init; + int i; + for (i = 0; i < 14; i++) { add += packet[i]; xou ^= packet[i]; } + packet[14] = xou; + packet[15] = add & 0xff; +} +static const char init_2801[] = {0xc5, 0x34, 0x60, 0x00, 0x25}; +static const char init_2601[] = {0xb9, 0x45, 0xb0, 0xf1, 0x3a}; +static const char init_2401[] = {0xa5, 0x23, 0xd0, 0xf0, 0x00}; +static void build_bind_pkt(const char *init) { + packet[0] = init[0]; + packet[1] = init[1]; + packet[2] = rx_tx_addr[0]; + packet[3] = rx_tx_addr[1]; + packet[4] = init[2]; + packet[5] = rx_tx_addr[2]; + packet[6] = 0xff; + packet[7] = 0x00; + packet[8] = 0x00; + packet[9] = 0x32; + if (sub_protocol == WK2401) { packet[10] = 0x10 | ((fixed_id >> 0) & 0x0e); } + else { packet[10] = (fixed_id >> 0) & 0xff; } + packet[11] = (fixed_id >> 8) & 0xff; + packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num; + packet[13] = init[3]; + add_pkt_crc(init[4]); +} + +static uint16_t get_channel(uint8_t ch, uint32_t scale, uint32_t center, uint32_t range) { + uint32_t value = (uint32_t)Servo_data[ch] * scale / PPM_MAX + center; + if (value < center - range) { value = center - range; } + if (value > center + range) { value = center + range; } + return value; +} + +static void build_data_pkt_2401() { + uint8_t i; + uint16_t msb = 0; + uint8_t offset = 0; + for (i = 0; i < 4; i++) { + if (i == 2) { offset = 1; } + uint16_t value = get_channel(i, 0x800, 0, 0xA00); //12 bits, allow value to go to 125% + uint16_t base = abs(value) >> 2; //10 bits is the base value + uint16_t trim = abs(value) & 0x03; //lowest 2 bits represent trim + if (base >= 0x200) { //if value is > 100%, remainder goes to trim + trim = 4 *(base - 0x200); + base = 0x1ff; + } + base = (value >= 0) ? 0x200 + base : 0x200 - base; + trim = (value >= 0) ? 0x200 + trim : 0x200 - trim; + + packet[2*i+offset] = base & 0xff; + packet[2*i+offset+1] = trim & 0xff; + msb = (msb << 4) | ((base >> 6) & 0x0c) | ((trim >> 8) & 0x03); + } + packet[4] = msb >> 8; //Ele/Ail MSB + packet[9] = msb & 0xff; //Thr/Rud MSB + packet[10] = 0xe0 | ((fixed_id >> 0) & 0x0e); + packet[11] = (fixed_id >> 8) & 0xff; + packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num; + packet[13] = 0xf0; //FIXME - What is this? + add_pkt_crc(0x00); +} + +#define PCT(pct, max) (((max) * (pct) + 1L) / 1000) +#define MAXTHR 426 //Measured to provide equal value at +/-0 +static void channels_6plus1_2601(int frame, int *_v1, int *_v2) { + uint16_t thr = get_channel(2, 1000, 0, 1000); + int v1; + int thr_rev = 0, pitch_rev = 0; + if(thr > 0) { + if(thr >= 780) { //78% + v1 = 0; //thr = 60% * (x - 78%) / 22% + 40% + thr = PCT(1000-MAXTHR,512) * (thr-PCT(780,1000)) / PCT(220,1000) + PCT(MAXTHR,512); + } else { + v1 = 1023 - 1023 * thr / 780; + thr = PCT(MAXTHR, 512); //40% + } + } + else { + thr = -thr; + thr_rev = 1; + if(thr >= 780) { //78% + v1 = 1023; //thr = 60% * (x - 78%) / 22% + 40% + thr = PCT(1000-MAXTHR,512) * (thr-PCT(780,1000)) / PCT(220,1000) + PCT(MAXTHR,512); + if (thr >= 512) { thr = 511; } + } + else { + v1 = 1023 * thr / 780; + thr = PCT(MAXTHR, 512); //40% + } + } + if (thr >= 512) { thr = 511; } + packet[2] = thr & 0xff; + packet[4] = (packet[4] & 0xF3) | ((thr >> 6) & 0x04); + + uint16_t pitch= get_channel(5, 0x400, 0, 0x400); + if (pitch < 0) { + pitch_rev = 1; + pitch = -pitch; + } + if (frame == 1) { + //Pitch curve and range + if (thr > PCT(MAXTHR, 512)) { *_v2 = pitch - pitch * 16 * (thr - PCT(MAXTHR, 512)) / PCT(1000 - MAXTHR, 512) / 100; } + else { *_v2 = pitch; } + *_v1 = 0; + } + else if (frame == 2) { + //Throttle curve & Expo + *_v1 = v1; + *_v2 = 512; + } + packet[7] = (thr_rev << 5) | (pitch_rev << 2); //reverse bits + packet[8] = 0; +} + +static void channels_5plus1_2601(int frame, int *v1, int *v2) { + (void)v1; + //Zero out pitch, provide ail, ele, thr, rud, gyr + gear + if (frame == 1) { *v2 = 0; } //Pitch curve and range + packet[7] = 0; + packet[8] = 0; +} +static void channels_heli_2601(int frame, int *v1, int *v2) { + (void)frame; + //pitch is controlled by rx + //we can only control fmode, pit-reverse and pit/thr rate + int pit_rev = 0; + if (Model.proto_opts[WK2601_OPT_PIT_INV]) { pit_rev = 1; } + uint16_t pit_rate = get_channel(5, 0x400, 0, 0x400); + int fmode = 1; + if (pit_rate < 0) { pit_rate = -pit_rate; fmode = 0; } + if (frame == 1) { + //Pitch curve and range + *v1 = pit_rate; + *v2 = Model.proto_opts[WK2601_OPT_PIT_LIMIT] * 0x400 / 100 + 0x400; + } + packet[7] = (pit_rev << 2); //reverse bits + packet[8] = fmode ? 0x02 : 0x00; +} + +static void build_data_pkt_2601() { + uint8_t i; + uint8_t msb = 0; + uint8_t frame = (wk_pkt_num % 3); + for (i = 0; i < 4; i++) { + uint16_t value = get_channel(i, 0x190, 0, 0x1FF); + uint16_t mag = value < 0 ? -value : value; + packet[i] = mag & 0xff; + msb = (msb << 2) | ((mag >> 8) & 0x01) | (value < 0 ? 0x02 : 0x00); + } + packet[4] = msb; + int v1 = 0x200, v2 = 0x200; + if (frame == 0) { + //Gyro & Rudder mix + v1 = get_channel(6, 0x200, 0x200, 0x200); + v2 = 0; + } + if (Model.proto_opts[WK2601_OPT_CHANMODE] == 1) { channels_heli_2601(frame, &v1, &v2); } + else if (Model.proto_opts[WK2601_OPT_CHANMODE] == 2) { channels_6plus1_2601(frame, &v1, &v2); } + else { channels_5plus1_2601(frame, &v1, &v2); } + if (v1 > 1023) { v1 = 1023; } + if (v2 > 1023) { v2 = 1023; } + packet[5] = v2 & 0xff; + packet[6] = v1 & 0xff; + //packet[7] handled by channel code + packet[8] |= (get_channel(4, 0x190, 0, 0x1FF) > 0 ? 1 : 0); + packet[9] = ((v1 >> 4) & 0x30) | ((v2 >> 2) & 0xc0) | 0x04 | frame; + packet[10] = (fixed_id >> 0) & 0xff; + packet[11] = (fixed_id >> 8) & 0xff; + packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num; + packet[13] = 0xff; + + add_pkt_crc(0x3A); +} + +static void build_data_pkt_2801() { + uint8_t i; + uint16_t msb = 0; + uint8_t offset = 0; + uint8_t sign = 0; + for (i = 0; i < 8; i++) { + if (i == 4) { offset = 1; } + uint16_t value = get_channel(i, 0x190, 0, 0x3FF); + uint16_t mag = value < 0 ? -value : value; + packet[i+offset] = mag & 0xff; + msb = (msb << 2) | ((mag >> 8) & 0x03); + if (value < 0) { sign |= 1 << i; } + } + packet[4] = msb >> 8; + packet[9] = msb & 0xff; + packet[10] = (fixed_id >> 0) & 0xff; + packet[11] = (fixed_id >> 8) & 0xff; + packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num; + packet[13] = sign; + add_pkt_crc(0x25); +} + +static void build_beacon_pkt_2801() { + last_beacon ^= 1; + uint8_t i; + uint8_t en = 0; + uint8_t bind_state; + if (WK_BIND_COUNTer) { bind_state = 0xe4; } + else { bind_state = 0x1b; } + for (i = 0; i < 4; i++) { +/* if (Model.limits[fail_map[i + last_beacon * 4]].flags & CH_FAILSAFE_EN) { + uint32_t value = Model.limits[fail_map[i + last_beacon * 4]].failsafe + 128; + if (value > 255) { value = 255; } + if (value < 0) { value = 0; } + packet[i+1] = value; + en |= 1 << i; + } else +*/ { packet[i+1] = 0; } + } + packet[0] = en; + packet[5] = packet[4]; + packet[4] = last_beacon << 6; + packet[6] = rx_tx_addr[0]; + packet[7] = rx_tx_addr[1]; + packet[8] = rx_tx_addr[2]; + packet[9] = bind_state; + packet[10] = (fixed_id >> 0) & 0xff; + packet[11] = (fixed_id >> 8) & 0xff; + packet[12] = ((fixed_id >> 12) & 0xf0) | wk_pkt_num; + packet[13] = 0x00; //Does this matter? in the docs it is the same as the data packet + add_pkt_crc(0x1C); +} + +static void wk2x01_cyrf_init() { + /* Initialise CYRF chip */ + CYRF_WriteRegister(CYRF_03_TX_CFG, 0x28 | CYRF_HIGH_POWER); + CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4A); + CYRF_WriteRegister(CYRF_0B_PWR_CTRL, 0x00); + CYRF_WriteRegister(CYRF_0C_XTAL_CTRL, 0xC0); + CYRF_WriteRegister(CYRF_0D_IO_CFG, 0x04); + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x2C); + CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0xEE); + CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55); + CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05); + CYRF_WriteRegister(CYRF_1D_MODE_OVERRIDE, 0x18); + CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3C); + CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14); + CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x90); + CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x00); + CYRF_WriteRegister(CYRF_01_TX_LENGTH, 0x10); + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x2C); + CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02); + CYRF_WriteRegister(CYRF_27_CLK_OVERRIDE, 0x02); + CYRF_ConfigSOPCode(sopcode); + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x28); + CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x10); + CYRF_WriteRegister(CYRF_0E_GPIO_CTRL, 0x20); + CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x2C); +} + +void WK_BuildPacket_2801() { + switch(phase) { + case WK_BIND: + build_bind_pkt(init_2801); +// if ((--WK_BIND_COUNTer == 0) || PROTOCOL_SticksMoved(0)) { + if ((--WK_BIND_COUNTer == 0)) { + WK_BIND_COUNTer = 0; + BIND_DONE; + phase = WK_BOUND_1; + } + break; + case WK_BOUND_1: + case WK_BOUND_2: + case WK_BOUND_3: + case WK_BOUND_4: + case WK_BOUND_5: + case WK_BOUND_6: + case WK_BOUND_7: + build_data_pkt_2801(); + phase++; + break; + case WK_BOUND_8: + build_beacon_pkt_2801(); + phase = WK_BOUND_1; + if (WK_BIND_COUNTer) { + WK_BIND_COUNTer--; + if (WK_BIND_COUNTer == 0) { BIND_DONE; } + } + break; + } + wk_pkt_num = (wk_pkt_num + 1) % 12; +} + +void WK_BuildPacket_2601() { + if (WK_BIND_COUNTer) { + WK_BIND_COUNTer--; + build_bind_pkt(init_2601); + if ((WK_BIND_COUNTer == 0)) { + WK_BIND_COUNTer = 0; + BIND_DONE; + } + } + else { build_data_pkt_2601(); } + wk_pkt_num = (wk_pkt_num + 1) % 12; +} + +void WK_BuildPacket_2401() { + if (WK_BIND_COUNTer) { + WK_BIND_COUNTer--; + build_bind_pkt(init_2401); + if ((WK_BIND_COUNTer == 0)) { + WK_BIND_COUNTer = 0; + BIND_DONE; + } + } + else { build_data_pkt_2401(); } + wk_pkt_num = (wk_pkt_num + 1) % 12; +} + +static uint16_t wk_cb() { + if (packet_sent == 0) { + packet_sent = 1; + if(sub_protocol == WK2801) { WK_BuildPacket_2801(); } + else if(sub_protocol == WK2601) { WK_BuildPacket_2601(); } + else if(sub_protocol == WK2401) { WK_BuildPacket_2401(); } + CYRF_WriteDataPacket(packet); + return 1600; + } + packet_sent = 0; + int i = 0; + while (! (CYRF_ReadRegister(0x04) & 0x02)) { if(++i > NUM_WAIT_LOOPS) { break; } } + if((wk_pkt_num & 0x03) == 0) { + radio_ch_ptr = radio_ch_ptr == &rx_tx_addr[2] ? rx_tx_addr : radio_ch_ptr + 1; + CYRF_ConfigRFChannel(*radio_ch_ptr); + //Keep transmit power updated + CYRF_WriteRegister(CYRF_03_TX_CFG, 0x28 | CYRF_HIGH_POWER); + } + return 1200; +} + +static void wk_bind() { + if((sub_protocol != WK2801)) { return; } + fixed_id = ((MProtocol_id_master << 2) & 0x0ffc00) | ((MProtocol_id_master >> 10) & 0x000300) | ((MProtocol_id_master) & 0x0000ff); + WK_BIND_COUNTer = WK_BIND_COUNT / 8 + 1; + BIND_IN_PROGRESS; +} + +static uint16_t wk_setup() { + CYRF_Reset(); + wk2x01_cyrf_init(); + CYRF_SetTxRxMode(TX_EN); + CYRF_FindBestChannels(rx_tx_addr, 3, 4, 4, 80); + + radio_ch_ptr = rx_tx_addr; + CYRF_ConfigRFChannel(*radio_ch_ptr); + + wk_pkt_num = 0; + packet_sent = 0; + last_beacon = 0; + fixed_id = ((MProtocol_id_master << 2) & 0x0ffc00) | ((MProtocol_id_master >> 10) & 0x000300) | ((MProtocol_id_master) & 0x0000ff); + if (sub_protocol == WK2401) { fixed_id |= 0x01; } //Fixed ID must be odd for 2401 + if(sub_protocol != WK2801) { + WK_BIND_COUNTer = WK_BIND_COUNT; + phase = WK_BIND; + BIND_IN_PROGRESS; + } + else { + phase = WK_BOUND_1; + WK_BIND_COUNTer = 0; + } + CYRF_ConfigRFChannel(*radio_ch_ptr); + return 2800; +} +/* +const void *WK2x01_Cmds(enum ProtoCmds cmd) { + switch(cmd) { + case PROTOCMD_INIT: initialize(); return 0; + case PROTOCMD_DEINIT: return 0; + case PROTOCMD_CHECK_AUTOBIND: + return (Model.protocol == WK2801 && Model.fixed_id) ? 0 : (void *)1L; + case PROTOCMD_BIND: wk_bind(); return 0; + case PROTOCMD_DEFAULT_NUMCHAN: return (Model.protocol == WK2801) + ? (void *)8L + : (Model.protocol == WK2601) + ? (void *)6L + : (void *)4L; + case PROTOCMD_NUMCHAN: return (Model.protocol == WK2801) + ? (void *)8L + : (Model.protocol == WK2601) + ? (void *)7L + : (void *)4L; + case PROTOCMD_GETOPTIONS: + if(Model.protocol == WK2601) + return wk2601_opts; + break; + case PROTOCMD_TELEMETRYSTATE: return (void *)(long)PROTO_TELEM_UNSUPPORTED; + default: break; + } + return 0; +} +*/ +#endif diff --git a/Multiprotocol/DSM2_cyrf6936.ino b/Multiprotocol/DSM2_cyrf6936.ino index e6769a6..928d24e 100644 --- a/Multiprotocol/DSM2_cyrf6936.ino +++ b/Multiprotocol/DSM2_cyrf6936.ino @@ -42,8 +42,7 @@ enum { DSM2_CH2_READ_B = BIND_COUNT1 + 10, }; - -const uint8_t pncodes[5][9][8] = { +const uint8_t PROGMEM pncodes[5][9][8] = { /* Note these are in order transmitted (LSB 1st) */ { /* Row 0 */ /* Col 0 */ {0x03, 0xBC, 0x6E, 0x8A, 0xEF, 0xBD, 0xFE, 0xF8}, @@ -102,6 +101,12 @@ const uint8_t pncodes[5][9][8] = { }, }; +static void __attribute__((unused)) read_code(uint8_t *buf, uint8_t row, uint8_t col, uint8_t len) +{ + for(uint8_t i=0;i. - */ +/* ************************** + * 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 chanskip; + uint8_t calData[48][3]; + uint8_t channr; + uint8_t pass_ = 1 ; + uint8_t counter_rst; + uint8_t ctr; + uint8_t FS_flag=0; + // uint8_t ptr[4]={0x01,0x12,0x23,0x30}; + //uint8_t ptr[4]={0x00,0x11,0x22,0x33}; + + const PROGMEM uint8_t hop_data[]={ + 0x02, 0xD4, 0xBB, 0xA2, 0x89, + 0x70, 0x57, 0x3E, 0x25, 0x0C, + 0xDE, 0xC5, 0xAC, 0x93, 0x7A, + 0x61, 0x48, 0x2F, 0x16, 0xE8, + 0xCF, 0xB6, 0x9D, 0x84, 0x6B, + 0x52, 0x39, 0x20, 0x07, 0xD9, + 0xC0, 0xA7, 0x8E, 0x75, 0x5C, + 0x43, 0x2A, 0x11, 0xE3, 0xCA, + 0xB1, 0x98, 0x7F, 0x66, 0x4D, + 0x34, 0x1B, 0x00, 0x1D, 0x03 + }; -#include "iface_cc2500.h" + uint8_t hop(uint8_t byte) + { + return pgm_read_byte_near(&hop_data[byte]); + } + uint16_t initFrSkyX() + { + while(!chanskip) + { + randomSeed((uint32_t)analogRead(A6) << 10 | analogRead(A7)); + chanskip=random(0xfefefefe)%47; + } + while((chanskip-ctr)%4) + ctr=(ctr+1)%4; + + counter_rst=(chanskip-ctr)>>2; + //for test*************** + //rx_tx_addr[3]=0xB3; + //rx_tx_addr[2]=0xFD; + //************************ + frskyX_init(); + // + if(IS_AUTOBIND_FLAG_on) + { + state = FRSKY_BIND; + initialize_data(1); + } + else + { + state = FRSKY_DATA1; + initialize_data(0); + } + return 10000; + } + + uint16_t ReadFrSkyX() + { + switch(state) + { + default: + set_start(47); + CC2500_SetPower(); + cc2500_strobe(CC2500_SFRX); + // + frskyX_build_bind_packet(); + cc2500_strobe(CC2500_SIDLE); + cc2500_writeFifo(packet, packet[0]+1); + state++; + return 9000; + case FRSKY_BIND_DONE: + initialize_data(0); + channr=0; + BIND_DONE; + state++; + break; + case FRSKY_DATA1: + LED_ON; + CC2500_SetTxRxMode(TX_EN); + set_start(channr); + CC2500_SetPower(); + cc2500_strobe(CC2500_SFRX); + channr = (channr+chanskip)%47; + cc2500_strobe(CC2500_SIDLE); + cc2500_writeFifo(packet, packet[0]+1); + // + frskyX_data_frame(); + state++; + return 5500; + case FRSKY_DATA2: + CC2500_SetTxRxMode(RX_EN); + cc2500_strobe(CC2500_SIDLE); + state++; + return 200; + case FRSKY_DATA3: + cc2500_strobe(CC2500_SRX); + state++; + return 3000; + case FRSKY_DATA4: + len = cc2500_readReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F; + if (len &&(len>8) & 0x0F)|(chan_1 << 4))); + packet[9+i+2]=crc_Byte(chan_1>>4); + } + //packet[21]=crc_Byte(0x08);//first + packet[21]=crc_Byte(0x80);//??? when received first telemetry frame is changed to 0x80 + //packet[21]=crc_Byte(ptr[p]);//??? + //p=(p+1)%4;//repeating 4 bytes sequence pattern every 4th frame. + + pass_=lpass+1; + + for (uint8_t i=22;i<28;i++) + packet[i]=crc_Byte(0); + + packet[28]=highByte(crc); + packet[29]=lowByte(crc); + } -#endif + uint16_t scaleForPXX( uint8_t i ) + { //mapped 860,2140(125%) range to 64,1984(PXX values); + return (uint16_t)(((Servo_data[i]-PPM_MIN)*3)>>1)+64; + } + + uint8_t crc_Byte( uint8_t byte ) + { + crc = (crc<<8) ^ pgm_read_word(&CRCTable[((uint8_t)(crc>>8) ^ byte) & 0xFF]); + return byte; + } +#endif \ No newline at end of file diff --git a/Multiprotocol/FrSky_cc2500.ino b/Multiprotocol/FrSky_cc2500.ino index dae03c7..f3c6d3c 100644 --- a/Multiprotocol/FrSky_cc2500.ino +++ b/Multiprotocol/FrSky_cc2500.ino @@ -20,7 +20,6 @@ //##########Variables######## //uint32_t state; //uint8_t len; -uint8_t telemetry_counter=0; /* enum { @@ -128,8 +127,6 @@ static void __attribute__((unused)) frsky2way_build_bind_packet() 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 @@ -138,7 +135,11 @@ static void __attribute__((unused)) frsky2way_data_frame() packet[1] = rx_tx_addr[3]; packet[2] = rx_tx_addr[2]; packet[3] = counter;// - packet[4]=telemetry_counter; + #if defined TELEMETRY + packet[4] = telemetry_counter; + #else + packet[4] = 0x00; + #endif packet[5] = 0x01; // diff --git a/Multiprotocol/MT99xx_nrf24l01.ino b/Multiprotocol/MT99xx_nrf24l01.ino index ab4c825..5e719ed 100644 --- a/Multiprotocol/MT99xx_nrf24l01.ino +++ b/Multiprotocol/MT99xx_nrf24l01.ino @@ -37,29 +37,16 @@ enum{ FLAG_MT_FLIP = 0x80, }; -enum{ - // flags going to ?????? (Yi Zhan i6S)ROLL - BLABLA, -}; - enum { MT99XX_INIT = 0, MT99XX_BIND, MT99XX_DATA }; -static uint8_t __attribute__((unused)) MT99XX_calcChecksum() -{ - uint8_t result=checksum_offset; - for(uint8_t i=0; i<8; i++) - result += packet[i]; - return result; -} - static void __attribute__((unused)) MT99XX_send_packet() { - static const uint8_t yz_p4_seq[] = {0xa0, 0x20, 0x60}; - static const uint8_t mys_byte[] = { + const uint8_t yz_p4_seq[] = {0xa0, 0x20, 0x60}; + const uint8_t mys_byte[] = { 0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14, 0x05, 0x15, 0x06, 0x16, 0x07, 0x17, 0x00, 0x10 }; @@ -71,8 +58,8 @@ static void __attribute__((unused)) MT99XX_send_packet() packet[1] = convert_channel_8b_scale(RUDDER ,0x00,0xE1); // rudder packet[2] = convert_channel_8b_scale(AILERON ,0x00,0xE1); // aileron packet[3] = convert_channel_8b_scale(ELEVATOR,0x00,0xE1); // elevator - packet[4] = convert_channel_8b_scale(AUX5,0x00,0x3F); // pitch trim (0x3f-0x20-0x00) - packet[5] = convert_channel_8b_scale(AUX6,0x00,0x3F); // roll trim (0x00-0x20-0x3f) + packet[4] = 0x20; // pitch trim (0x3f-0x20-0x00) + packet[5] = 0x20; // roll trim (0x00-0x20-0x3f) packet[6] = GET_FLAG( Servo_AUX1, FLAG_MT_FLIP ) | GET_FLAG( Servo_AUX3, FLAG_MT_SNAPSHOT ) | GET_FLAG( Servo_AUX4, FLAG_MT_VIDEO ); @@ -84,7 +71,10 @@ static void __attribute__((unused)) MT99XX_send_packet() // low nibble: index in chan list ? // high nibble: 0->start from start of list, 1->start from end of list ? packet[7] = mys_byte[hopping_frequency_no]; - packet[8] = MT99XX_calcChecksum(); + uint8_t result=checksum_offset; + for(uint8_t i=0; i<8; i++) + result += packet[i]; + packet[8] = result; } else { // YZ @@ -100,8 +90,12 @@ static void __attribute__((unused)) MT99XX_send_packet() packet_count=0; } packet[4] = yz_p4_seq[yz_seq_num]; - packet[5] = 0x02; // expert ? (0=unarmed, 1=normal) - packet[6] = 0x80; + packet[5] = 0x02 // expert ? (0=unarmed, 1=normal) + | GET_FLAG(Servo_AUX4, 0x10) //VIDEO + | GET_FLAG(Servo_AUX1, 0x80) //FLIP + | GET_FLAG(Servo_AUX5, 0x04) //HEADLESS + | GET_FLAG(Servo_AUX3, 0x20); //SNAPSHOT + packet[6] = GET_FLAG(Servo_AUX2, 0x80); //LED packet[7] = packet[0]; for(uint8_t idx = 1; idx < MT99XX_PACKET_SIZE-2; idx++) packet[7] += packet[idx]; @@ -138,7 +132,10 @@ static void __attribute__((unused)) MT99XX_init() else NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps NRF24L01_SetPower(); - XN297_SetTXAddr((uint8_t *)"\0xCC\0xCC\0xCC\0xCC\0xCC", 5); + + XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP) | (sub_protocol == YZ ? BV(XN297_UNSCRAMBLED):0) ); + + XN297_SetTXAddr((uint8_t *)"\xCC\xCC\xCC\xCC\xCC", 5); } static void __attribute__((unused)) MT99XX_initialize_txid() @@ -190,7 +187,7 @@ uint16_t initMT99XX(void) BIND_IN_PROGRESS; // autobind protocol bind_counter = MT99XX_BIND_COUNT; - memcpy(hopping_frequency,"\0x02\0x48\0x0C\0x3e\0x16\0x34\0x20\0x2A,\0x2A\0x20\0x34\0x16\0x3e\0x0c\0x48\0x02",16); + memcpy(hopping_frequency,"\x02\x48\x0C\x3e\x16\x34\x20\x2A\x2A\x20\x34\x16\x3e\x0c\x48\x02",16); MT99XX_initialize_txid(); MT99XX_init(); @@ -210,11 +207,11 @@ uint16_t initMT99XX(void) packet[2] = 0x05; packet[3] = 0x06; } - packet[4] = rx_tx_addr[0]; // 1th byte for data state tx address - packet[5] = rx_tx_addr[1]; // 2th byte for data state tx address (always 0x00 on Yi Zhan ?) - packet[6] = 0x00; // 3th byte for data state tx address (always 0x00 ?) + packet[4] = rx_tx_addr[0]; // 1st byte for data state tx address + packet[5] = rx_tx_addr[1]; // 2nd byte for data state tx address (always 0x00 on Yi Zhan ?) + packet[6] = 0x00; // 3rd byte for data state tx address (always 0x00 ?) packet[7] = checksum_offset; // checksum offset - packet[8] = 0xAA; // fixed + packet[8] = 0xAA; // fixed packet_count=0; return MT99XX_INITIAL_WAIT+MT99XX_PACKET_PERIOD_MT; } diff --git a/Multiprotocol/Multiprotocol.ino b/Multiprotocol/Multiprotocol.ino index e9fa9e6..46f6697 100644 --- a/Multiprotocol/Multiprotocol.ino +++ b/Multiprotocol/Multiprotocol.ino @@ -1,6 +1,7 @@ /********************************************************* Multiprotocol Tx code by Midelic and Pascal Langer(hpnuts) + fork by Tipouic http://www.rcgroups.com/forums/showthread.php?t=2165676 https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/edit/master/README.md @@ -29,7 +30,6 @@ #include "_Config.h" //Global constants/variables - uint32_t MProtocol_id;//tx id, uint32_t MProtocol_id_master; uint32_t Model_fixed_id=0; @@ -48,7 +48,7 @@ uint8_t Servo_AUX; // PPM variable volatile uint16_t PPM_data[NUM_CHN]; -// NRF variables +// Protocol variables uint8_t rx_tx_addr[5]; uint8_t phase; uint16_t bind_counter; @@ -65,6 +65,7 @@ uint8_t hopping_frequency_no=0; uint8_t rf_ch_num; uint8_t throttle, rudder, elevator, aileron; uint8_t flags; +uint16_t crc; // uint32_t state; uint8_t len; @@ -100,6 +101,7 @@ uint8_t pktt[MAX_PKT];//telemetry receiving packets int16_t RSSI_dBm; //const uint8_t RSSI_offset=72;//69 71.72 values db uint8_t telemetry_link=0; + uint8_t telemetry_counter=0; #endif // Callback @@ -313,6 +315,49 @@ static void protocol_init() switch(cur_protocol[0]&0x1F) // Init the requested protocol { +#if defined(HM830_NRF24L01_INO) + case MODE_HM830: + next_callback=HM830_setup(); + remote_callback = HM830_callback; + break; +#endif +#if defined(CFlie_NRF24L01_INO) + case MODE_CFLIE: + next_callback=Cflie_setup(); + remote_callback = cflie_callback; + break; +#endif +#if defined(JOYSWAY_A7105_INO) + case MODE_JOYSWAY: + next_callback=JOYSWAY_Setup(); + remote_callback = joysway_cb; + break; +#endif +#if defined(H377_NRF24L01_INO) + case MODE_H377: + next_callback=h377_setup(); + remote_callback = h377_cb; + break; +#endif +#if defined(J6PRO_CYRF6936_INO) + case MODE_J6PRO: + next_callback=j6pro_setup(); + remote_callback = j6pro_cb; + break; +#endif +#if defined(WK2x01_CYRF6936_INO) + case MODE_WK2x01: + next_callback=wk_setup(); + remote_callback = wk_cb; + break; +#endif +#if defined(FY326_NRF24L01_INO) + case MODE_FY326: + next_callback=FY326_setup(); + remote_callback = fy326_callback; + break; +#endif + #if defined(FLYSKY_A7105_INO) case MODE_FLYSKY: CTRL1_off; //antenna RF1 diff --git a/Multiprotocol/NRF24l01_SPI.ino b/Multiprotocol/NRF24l01_SPI.ino index c14f5d5..cec1439 100644 --- a/Multiprotocol/NRF24l01_SPI.ino +++ b/Multiprotocol/NRF24l01_SPI.ino @@ -257,6 +257,7 @@ uint8_t NRF24L01_packet_ack() /////////////// // XN297 emulation layer +uint8_t xn297_scramble_enabled; uint8_t xn297_addr_len; uint8_t xn297_tx_addr[5]; uint8_t xn297_rx_addr[5]; @@ -269,9 +270,16 @@ static const uint8_t xn297_scramble[] = { 0x1b, 0x5d, 0x19, 0x10, 0x24, 0xd3, 0xdc, 0x3f, 0x8e, 0xc5, 0x2f}; -static const uint16_t xn297_crc_xorout[] = { - 0x0000, 0x3448, 0x9BA7, 0x8BBB, 0x85E1, 0x3E8C, // 1st entry is missing, probably never needed - 0x451E, 0x18E6, 0x6B24, 0xE7AB, 0x3828, 0x814B, // it's used for 3-byte address w/ 0 byte payload only +const uint16_t PROGMEM xn297_crc_xorout[] = { + 0x0000, 0x3d5f, 0xa6f1, 0x3a23, 0xaa16, 0x1caf, + 0x62b2, 0xe0eb, 0x0821, 0xbe07, 0x5f1a, 0xaf15, + 0x4f0a, 0xad24, 0x5e48, 0xed34, 0x068c, 0xf2c9, + 0x1852, 0xdf36, 0x129d, 0xb17c, 0xd5f5, 0x70d7, + 0xb798, 0x5133, 0x67db, 0xd94e}; + +const uint16_t PROGMEM xn297_crc_xorout_scrambled[] = { + 0x0000, 0x3448, 0x9BA7, 0x8BBB, 0x85E1, 0x3E8C, + 0x451E, 0x18E6, 0x6B24, 0xE7AB, 0x3828, 0x814B, 0xD461, 0xF494, 0x2503, 0x691D, 0xFE8B, 0x9BA7, 0x8B17, 0x2920, 0x8B5F, 0x61B1, 0xD391, 0x7401, 0x2138, 0x129F, 0xB3A0, 0x2988}; @@ -327,16 +335,21 @@ void XN297_SetRXAddr(const uint8_t* addr, uint8_t len) memcpy(buf, addr, len); memcpy(xn297_rx_addr, addr, len); for (uint8_t i = 0; i < xn297_addr_len; ++i) - buf[i] = xn297_rx_addr[i] ^ xn297_scramble[xn297_addr_len-i-1]; + { + buf[i] = xn297_rx_addr[i]; + if(xn297_scramble_enabled) + buf[i] ^= xn297_scramble[xn297_addr_len-i-1]; + } NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2); NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, buf, 5); } -void XN297_Configure(uint8_t flags) +void XN297_Configure(uint16_t flags) { + xn297_scramble_enabled = !(flags & BV(XN297_UNSCRAMBLED)); xn297_crc = !!(flags & BV(NRF24L01_00_EN_CRC)); flags &= ~(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO)); - NRF24L01_WriteReg(NRF24L01_00_CONFIG, flags); + NRF24L01_WriteReg(NRF24L01_00_CONFIG, flags & 0xFF); } void XN297_WritePayload(uint8_t* msg, uint8_t len) @@ -352,12 +365,20 @@ void XN297_WritePayload(uint8_t* msg, uint8_t len) buf[last++] = 0x55; } for (uint8_t i = 0; i < xn297_addr_len; ++i) - buf[last++] = xn297_tx_addr[xn297_addr_len-i-1] ^ xn297_scramble[i]; - - for (uint8_t i = 0; i < len; ++i) { + { + buf[last] = xn297_tx_addr[xn297_addr_len-i-1]; + if(xn297_scramble_enabled) + buf[last] ^= xn297_scramble[i]; + last++; + } + for (uint8_t i = 0; i < len; ++i) + { // bit-reverse bytes in packet uint8_t b_out = bit_reverse(msg[i]); - buf[last++] = b_out ^ xn297_scramble[xn297_addr_len+i]; + buf[last] = b_out; + if(xn297_scramble_enabled) + buf[last] ^= xn297_scramble[xn297_addr_len+i]; + last++; } if (xn297_crc) { @@ -365,7 +386,10 @@ void XN297_WritePayload(uint8_t* msg, uint8_t len) uint16_t crc = 0xb5d2; for (uint8_t i = offset; i < last; ++i) crc = crc16_update(crc, buf[i]); - crc ^= xn297_crc_xorout[xn297_addr_len - 3 + len]; + if(xn297_scramble_enabled) + crc ^= pgm_read_word(&xn297_crc_xorout_scrambled[xn297_addr_len - 3 + len]); + else + crc ^= pgm_read_word(&xn297_crc_xorout[xn297_addr_len - 3 + len]); buf[last++] = crc >> 8; buf[last++] = crc & 0xff; } @@ -374,9 +398,14 @@ void XN297_WritePayload(uint8_t* msg, uint8_t len) void XN297_ReadPayload(uint8_t* msg, uint8_t len) { + // TODO: if xn297_crc==1, check CRC before filling *msg NRF24L01_ReadPayload(msg, len); for(uint8_t i=0; i. + */ + +/* NB: Not implemented + Uncomment define below to enable telemetry. Also add CFlie protocol to TELEMETRY_SetTypeByProtocol to set type to DSM. +#define CFLIE_TELEMETRY + */ + + +#if defined(CFlie_NRF24L01_INO) +#include "iface_nrf24l01.h" + +#define BIND_COUNT 60 + +// Address size +#define TX_ADDR_SIZE 5 + +// Timeout for callback in uSec, 10ms=10000us for Crazyflie +#define PACKET_PERIOD 10000 + + +// For code readability +enum { + CHANNEL1 = 0, + CHANNEL2, + CHANNEL3, + CHANNEL4, + CHANNEL5, + CHANNEL6, + CHANNEL7, + CHANNEL8, + CHANNEL9, + CHANNEL10 +}; + +#define PAYLOADSIZE 8 // receive data pipes set to this size, but unused +#define MAX_PACKET_SIZE 9 // YD717 packets have 8-byte payload, Syma X4 is 9 + +//static uint8_t packet[MAX_PACKET_SIZE]; + +static uint8_t data_rate, rf_channel; + +enum { + CFLIE_INIT_SEARCH = 0, + CFLIE_INIT_DATA, + CFLIE_SEARCH, + CFLIE_DATA +}; + +#ifdef CFLIE_TELEMETRY +static const char * const cflie_opts[] = { + _tr_noop("Telemetry"), _tr_noop("Off"), _tr_noop("On"), NULL, + NULL +}; +enum { + PROTOOPTS_TELEMETRY = 0, + LAST_PROTO_OPT, +}; +ctassert(LAST_PROTO_OPT <= NUM_PROTO_OPTS, too_many_protocol_opts); + +#define TELEM_OFF 0 +#define TELEM_ON 1 +#endif + +#define PACKET_CHKTIME 500 // time to wait if packet not yet acknowledged or timed out + +static uint16_t dbg_cnt = 0; +static uint8_t packet_ack() { + if (++dbg_cnt > 50) { dbg_cnt = 0; } + switch (NRF24L01_ReadReg(NRF24L01_07_STATUS) & (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT))) { + case BV(NRF24L01_07_TX_DS): + return PKT_ACKED; + case BV(NRF24L01_07_MAX_RT): + return PKT_TIMEOUT; + } + return PKT_PENDING; +} + +static void set_rate_channel(uint8_t rate, uint8_t channel) { + NRF24L01_WriteReg(NRF24L01_05_RF_CH, channel); // Defined by model id + NRF24L01_SetBitrate(rate); // Defined by model id +} + +static void send_search_packet() { + uint8_t buf[1]; + buf[0] = 0xff; + // clear packet status bits and TX FIFO + NRF24L01_WriteReg(NRF24L01_07_STATUS, (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT))); + NRF24L01_FlushTx(); + + if (rf_channel++ > 125) { + rf_channel = 0; + switch(data_rate) { + case NRF24L01_BR_250K: + data_rate = NRF24L01_BR_1M; + break; + case NRF24L01_BR_1M: + data_rate = NRF24L01_BR_2M; + break; + case NRF24L01_BR_2M: + data_rate = NRF24L01_BR_250K; + break; + } + } + + set_rate_channel(data_rate, rf_channel); + + NRF24L01_WritePayload(buf, sizeof(buf)); + + ++packet_counter; +} + +// Frac 16.16 +#define FRAC_MANTISSA 16 +#define FRAC_SCALE (1 << FRAC_MANTISSA) + +// Convert fractional 16.16 to float32 +static void frac2float(uint32_t n, float* res) { + if (n == 0) { + *res = 0.0; + return; + } + uint32_t m = n < 0 ? -n : n; + int i; + for (i = (31-FRAC_MANTISSA); (m & 0x80000000) == 0; i--, m <<= 1) ; + m <<= 1; // Clear implicit leftmost 1 + m >>= 9; + uint32_t e = 127 + i; + if (n < 0) m |= 0x80000000; + m |= e << 23; + *((uint32_t *) res) = m; +} + +static void send_cmd_packet() { + // Commander packet, 15 bytes + uint8_t buf[15]; + float x_roll, x_pitch, yaw; + + // Channels in AETR order + + // Roll, aka aileron, float +- 50.0 in degrees + // float roll = -(float) Servo_data[AILERON]*50.0/10000; + uint32_t f_roll = -Servo_data[AILERON] * FRAC_SCALE / (10000 / 50); + + // Pitch, aka elevator, float +- 50.0 degrees + //float pitch = -(float) Servo_data[ELEVATOR]*50.0/10000; + uint32_t f_pitch = -Servo_data[ELEVATOR] * FRAC_SCALE / (10000 / 50); + + // Thrust, aka throttle 0..65535, working range 5535..65535 + // No space for overshoot here, hard limit Channel3 by -10000..10000 + uint32_t ch = Servo_data[THROTTLE]; + if (ch < PPM_MIN) { + ch = PPM_MIN; + } else if (ch > PPM_MAX) { + ch = PPM_MAX; + } + uint16_t thrust = ch*3L + 35535L; + + // Yaw, aka rudder, float +- 400.0 deg/s + // float yaw = -(float) Servo_data[RUDDER]*400.0/10000; + uint32_t f_yaw = - Servo_data[RUDDER] * FRAC_SCALE / (10000 / 400); + frac2float(f_yaw, &yaw); + + // Convert + to X. 181 / 256 = 0.70703125 ~= sqrt(2) / 2 + uint32_t f_x_roll = (f_roll + f_pitch) * 181 / 256; + frac2float(f_x_roll, &x_roll); + uint32_t f_x_pitch = (f_pitch - f_roll) * 181 / 256; + frac2float(f_x_pitch, &x_pitch); + + int bufptr = 0; + buf[bufptr++] = 0x30; // Commander packet to channel 0 + memcpy(&buf[bufptr], (char*) &x_roll, 4); bufptr += 4; + memcpy(&buf[bufptr], (char*) &x_pitch, 4); bufptr += 4; + memcpy(&buf[bufptr], (char*) &yaw, 4); bufptr += 4; + memcpy(&buf[bufptr], (char*) &thrust, 2); bufptr += 2; + + + // clear packet status bits and TX FIFO + NRF24L01_WriteReg(NRF24L01_07_STATUS, (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT))); + NRF24L01_FlushTx(); + + NRF24L01_WritePayload(buf, sizeof(buf)); + + ++packet_counter; + + NRF24L01_SetPower(); +} + +static int cflie_init() { + NRF24L01_Initialize(); + + // CRC, radio on + NRF24L01_SetTxRxMode(TX_EN); + NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP)); + // NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowledgement + NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x01); // Auto Acknowledgement for data pipe 0 + NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 + NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, TX_ADDR_SIZE-2); // 5-byte RX/TX address + NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x13); // 3 retransmits, 500us delay + + NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_channel); // Defined by model id + NRF24L01_SetBitrate(data_rate); // Defined by model id + + NRF24L01_SetPower(); + NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit + + NRF24L01_WriteReg(NRF24L01_17_FIFO_STATUS, 0x00); // Just in case, no real bits to write here + + // this sequence necessary for module from stock tx + NRF24L01_ReadReg(NRF24L01_1D_FEATURE); + NRF24L01_Activate(0x73); // Activate feature register + NRF24L01_ReadReg(NRF24L01_1D_FEATURE); + + NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x01); // Enable Dynamic Payload Length on pipe 0 + NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x06); // Enable Dynamic Payload Length, enable Payload with ACK + + + // NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, TX_ADDR_SIZE); + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, TX_ADDR_SIZE); + + NRF24L01_Activate(0x53); // switch bank back + + // 50ms delay in callback + return 50000; +} + + +#ifdef CFLIE_TELEMETRY +static void update_telemetry() { + static uint8_t frameloss = 0; + + frameloss += NRF24L01_ReadReg(NRF24L01_08_OBSERVE_TX) >> 4; + NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_channel); // reset packet loss counter + + Telemetry.p.dsm.flog.frameloss = frameloss; +// Telemetry.p.dsm.flog.volt[0] = read battery voltage from ack payload + TELEMETRY_SetUpdated(TELEM_DSM_FLOG_FRAMELOSS); +} +#endif + + +static uint16_t cflie_callback() { + switch (phase) { + case CFLIE_INIT_SEARCH: + send_search_packet(); + phase = CFLIE_SEARCH; + break; + case CFLIE_INIT_DATA: + send_cmd_packet(); + phase = CFLIE_DATA; + break; + case CFLIE_SEARCH: + switch (packet_ack()) { + case PKT_PENDING: + return PACKET_CHKTIME; // packet send not yet complete + case PKT_ACKED: + phase = CFLIE_DATA; + BIND_DONE; + break; + case PKT_TIMEOUT: + send_search_packet(); + counter = BIND_COUNT; + } + break; + case CFLIE_DATA: + #ifdef CFLIE_TELEMETRY + update_telemetry(); + #endif + if (packet_ack() == PKT_PENDING) + return PACKET_CHKTIME; // packet send not yet complete + send_cmd_packet(); + break; + } + return PACKET_PERIOD; // Packet at standard protocol interval +} + + +// Generate address to use from TX id and manufacturer id (STM32 unique id) +static uint8_t initialize_rx_tx_addr() { + rx_tx_addr[0] = + rx_tx_addr[1] = + rx_tx_addr[2] = + rx_tx_addr[3] = + rx_tx_addr[4] = 0xE7; // CFlie uses fixed address + + data_rate = NRF24L01_BR_250K; + rf_channel = 0; + return CFLIE_INIT_SEARCH; + // return CFLIE_INIT_DATA; +} + +static uint16_t Cflie_setup() { + phase = initialize_rx_tx_addr(); + packet_counter = 0; + + int delay = cflie_init(); + + #ifdef CFLIE_TELEMETRY + memset(&Telemetry, 0, sizeof(Telemetry)); + TELEMETRY_SetType(TELEM_DSM); + #endif + if (phase == CFLIE_INIT_SEARCH) { BIND_IN_PROGRESS; } + return delay; +} + +#endif diff --git a/Multiprotocol/Nrf24l01_fy326.ino b/Multiprotocol/Nrf24l01_fy326.ino new file mode 100644 index 0000000..5fe0a92 --- /dev/null +++ b/Multiprotocol/Nrf24l01_fy326.ino @@ -0,0 +1,234 @@ +/* + 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. + + Deviation 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 Deviation. If not, see . + */ + + +#if defined(FY326_NRF24L01_INO) +#include "iface_nrf24l01.h" + +#define INITIAL_WAIT 500 +#define FY326_PERIOD 1500 // Timeout for callback in uSec +#define FY326_CHKTIME 300 // Time to wait if packet not yet received or sent +#define FY326_SIZE 15 +#define FY326_BIND_COUNT 16 + + +static const char * const fy326_opts[] = { + _tr_noop("Expert"), _tr_noop("On"), _tr_noop("Off"), NULL, + NULL +}; +#define EXPERT_ON 0 +#define EXPERT_OFF 1 + +#define CHANNEL_FLIP AUX1 +#define CHANNEL_HEADLESS AUX2 +#define CHANNEL_RTH AUX3 +#define CHANNEL_CALIBRATE AUX4 + +static uint8_t tx_power; +static uint8_t packet[FY326_SIZE]; + +// frequency channel management +#define RF_BIND_CHANNEL 0x17 +#define NUM_RF_CHANNELS 5 +static uint8_t current_chan; +static uint8_t rf_chans[NUM_RF_CHANNELS]; +static uint8_t txid[5]; +static uint8_t rxid; + +enum { + FY326_INIT1 = 0, + FY326_BIND1, + FY326_BIND2, + FY326_DATA +}; + +// Bit vector from bit position +#define BV(bit) (1 << bit) + +#define CHAN_RANGE (CHAN_MAX_VALUE - CHAN_MIN_VALUE) +static uint8_t scale_channel(uint8_t ch, uint8_t destMin, uint8_t destMax) +{ + uint32_t chanval = Channels[ch]; + uint32_t range = destMax - destMin; + + if (chanval < CHAN_MIN_VALUE) chanval = CHAN_MIN_VALUE; + else if (chanval > CHAN_MAX_VALUE) chanval = CHAN_MAX_VALUE; + return (range * (chanval - CHAN_MIN_VALUE)) / CHAN_RANGE + destMin; +} + +#define GET_FLAG(ch, mask) (Channels[ch] > 0 ? mask : 0) +#define CHAN_TO_TRIM(chanval) ((uint8_t)(((uint16_t)chanval/10)-10)) // scale to [-10,10]. [-20,20] caused problems. +static void send_packet(uint8_t bind) +{ + packet[0] = txid[3]; + if (bind) { + packet[1] = 0x55; + } else { + packet[1] = GET_FLAG(CHANNEL_HEADLESS, 0x80) + | GET_FLAG(CHANNEL_RTH, 0x40) + | GET_FLAG(CHANNEL_FLIP, 0x02) + | GET_FLAG(CHANNEL_CALIBRATE, 0x01) + | (Model.proto_opts[PROTOOPTS_EXPERT] == EXPERT_ON ? 4 : 0); + } + packet[2] = 200 - scale_channel(AILERON, 0, 200); // aileron + packet[3] = scale_channel(ELEVATOR, 0, 200); // elevator + packet[4] = 200 - scale_channel(RUDDER, 0, 200); // rudder + packet[5] = scale_channel(THROTTLE, 0, 200); // throttle + packet[6] = txid[0]; + packet[7] = txid[1]; + packet[8] = txid[2]; + packet[9] = CHAN_TO_TRIM(packet[2]); // aileron_trim; + packet[10] = CHAN_TO_TRIM(packet[3]); // elevator_trim; + packet[11] = CHAN_TO_TRIM(packet[4]); // rudder_trim; + packet[12] = 0; // throttle_trim; + packet[13] = rxid; + packet[14] = txid[4]; + + if (bind) { + NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_BIND_CHANNEL); + } else { + NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_chans[current_chan++]); + current_chan %= NUM_RF_CHANNELS; + } + + // clear packet status bits and TX FIFO + NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); + NRF24L01_FlushTx(); + + NRF24L01_WritePayload(packet, FY326_SIZE); + + // Check and adjust transmission power. We do this after + // transmission to not bother with timeout after power + // settings change - we have plenty of time until next + // packet. + if (tx_power != Model.tx_power) { + //Keep transmit power updated + tx_power = Model.tx_power; + NRF24L01_SetPower(tx_power); + } +} + +static void fy326_init() +{ + const uint8_t rx_tx_addr[] = {0x15, 0x59, 0x23, 0xc6, 0x29}; + + NRF24L01_Initialize(); + NRF24L01_SetTxRxMode(TX_EN); + NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x01); // Three-byte rx/tx address + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, sizeof(rx_tx_addr)); + NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, sizeof(rx_tx_addr)); + NRF24L01_FlushTx(); + NRF24L01_FlushRx(); + NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit + NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowldgement on all data pipes + NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only + NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, FY326_SIZE); + NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_BIND_CHANNEL); + NRF24L01_SetBitrate(NRF24L01_BR_250K); + NRF24L01_SetPower(Model.tx_power); + + NRF24L01_Activate(0x73); + NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f); + NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07); +} + +static uint16_t fy326_callback() +{ + switch (phase) { + case FY326_INIT1: + MUSIC_Play(MUSIC_TELEMALARM1); + bind_counter = FY326_BIND_COUNT; + phase = FY326_BIND2; + send_packet(1); + return FY326_CHKTIME; + break; + + case FY326_BIND1: +#ifdef EMULATOR + if (1) { + packet[13] = 0x7e; +#else + if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR)) { // RX fifo data ready + NRF24L01_ReadPayload(packet, FY326_SIZE); +#endif + rxid = packet[13]; + txid[0] = 0xaa; + NRF24L01_SetTxRxMode(TXRX_OFF); + NRF24L01_SetTxRxMode(TX_EN); + PROTOCOL_SetBindState(0); + MUSIC_Play(MUSIC_DONE_BINDING); + phase = FY326_DATA; + } else if (bind_counter-- == 0) { + bind_counter = FY326_BIND_COUNT; + NRF24L01_SetTxRxMode(TXRX_OFF); + NRF24L01_SetTxRxMode(TX_EN); + send_packet(1); + phase = FY326_BIND2; + return FY326_CHKTIME; + } + break; + + case FY326_BIND2: +#ifdef EMULATOR + if (1) { +#else + if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_TX_DS)) { // TX data sent +#endif + // switch to RX mode + NRF24L01_SetTxRxMode(TXRX_OFF); + NRF24L01_FlushRx(); + NRF24L01_SetTxRxMode(RX_EN); + phase = FY326_BIND1; + } else { + return FY326_CHKTIME; + } + break; + + case FY326_DATA: + send_packet(0); + break; + } + return FY326_PERIOD; +} + +// Generate address to use from TX id and manufacturer id (STM32 unique id) +static void fy_txid() +{ + txid[0] = (MProtocol_id_master >> 24) & 0xFF; + txid[1] = ((MProtocol_id_master >> 16) & 0xFF); + txid[2] = (MProtocol_id_master >> 8) & 0xFF; + txid[3] = MProtocol_id_master & 0xFF; + for (uint8_t i = 0; i < sizeof(MProtocol_id_master); ++i) rand32_r(&MProtocol_id_master, 0); + txid[4] = MProtocol_id_master & 0xFF; + + rf_chans[0] = txid[0] & 0x0F; + rf_chans[1] = 0x10 + (txid[0] >> 4); + rf_chans[2] = 0x20 + (txid[1] & 0x0F); + rf_chans[3] = 0x30 + (txid[1] >> 4); + rf_chans[4] = 0x40 + (txid[2] >> 4); +} + +static uint16_t FY326_setup() +{ + BIND_IN_PROGRESS; + tx_power = Model.tx_power; + rxid = 0xaa; + phase = FY326_INIT1; + bind_counter = FY326_BIND_COUNT; + fy_txid(); + fy326_init(); + return INITIAL_WAIT; +} +#endif diff --git a/Multiprotocol/Nrf24l01_h377.ino b/Multiprotocol/Nrf24l01_h377.ino new file mode 100644 index 0000000..e8feaf3 --- /dev/null +++ b/Multiprotocol/Nrf24l01_h377.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. + +Deviation 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 Deviation. If not, see . +*/ + +#if defined (H377_NRF24L01_INO) +#include "iface_nrf24l01.h" + +#define BIND_COUNT 800 + + +#define TXID_H377_SIZE 5 + +#define FREQUENCE_NUM_H377 20 +#define SET_NUM_H377 9 +// available frequency must be in between 2402 and 2477 + +static uint8_t binding_ch=0x50; +static uint8_t hopping_frequency_data[SET_NUM_H377] = {0x1c,0x1b,0x1d,0x11,0x0e,0x0d,0x01,0x1d,0x15}; + +static const uint8_t binding_adr_rf[5]={0x32,0xaa,0x45,0x45,0x78}; + +static uint8_t rf_adr_buf[5]; +static uint8_t rf_adr_buf_data[SET_NUM_H377][5] = { + {0xad,0x9a,0xa6,0x69,0xb2},//ansheng + {0x92,0x9a,0x9d,0x69,0x99},//dc59 + {0x92,0xb2,0x9d,0x69,0x9a},//small two + {0xad,0x9a,0x5a,0x69,0x96},//james_1 + {0x95,0x9a,0x52,0x69,0x99},//james_2 + {0x52,0x52,0x52,0x69,0xb9},//james_3 + {0x52,0x52,0x52,0x52,0x55},//small two_1 + {0x92,0xB2,0x9D,0x69,0x9A},//small two_2 + {0x96,0x9A,0x45,0x69,0xB2}//small two_3 +}; + +static uint8_t bind_buf_array[10]; +static uint8_t bind_buf_array_data[SET_NUM_H377][4] = { + {0xcf,0x1c,0x19,0x1a}, + {0xff,0x48,0x19,0x19}, + {0xf3,0x4d,0x19,0x19}, + {0x9e,0x1f,0x19,0x19}, + {0x8d,0x3d,0x19,0x19}, + {0xbd,0x23,0x19,0x19}, + {0xF3,0x28,0x19,0x19}, + {0xF3,0x4D,0x19,0x19}, + {0x82,0x8D,0x19,0x19} +}; + + +static unsigned int ch_data[8]; +static uint8_t payload[10]; +static uint8_t counter1ms; + +static int select_ch_id = 0; + +static void h377_binding_packet(void) { //bind_buf_array + uint8_t i; + counter1ms = 0; + hopping_frequency_no = 0; + + for(i=0;i<5;i++) + bind_buf_array[i] = rf_adr_buf[i]; + + bind_buf_array[5] = hopping_frequency[0]; + + for(i=0;i<4;i++) + bind_buf_array[i+6] = bind_buf_array_data[select_ch_id][i]; +} + +static void h377_init() { + NRF24L01_Initialize(); + + NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable p0 rx + NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rf_adr_buf, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rf_adr_buf, 5); + NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 10); // payload size = 10 + //NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81); // binding packet must be set in channel 81 + NRF24L01_WriteReg(NRF24L01_05_RF_CH, binding_ch); // binding packet must be set in channel 81 + + // 2-bytes CRC, radio off + NRF24L01_SetTxRxMode(TX_EN); + NRF24L01_WriteReg(NRF24L01_00_CONFIG, BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP)); + NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address (byte -2) + NRF24L01_SetBitrate(0); // 1Mbps + NRF24L01_SetPower(); + NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit +} + +// H377 channel sequence: AILE ELEV THRO RUDD GEAR PITH, channel data value is from 0 to 1000 +static void h377_ch_data() { + uint32_t temp; + uint8_t i; + for (i = 0; i< 8; i++) { + temp = (uint32_t)Servo_data[i] * 450/PPM_MAX + 500; // max/min servo range is +-125% + if (i == 2) // It is clear that h377's thro stick is made reversely, so I adjust it here on purpose + temp = 1000 -temp; + //if (i == 0) // It is clear that h377's thro stick is made reversely, so I adjust it here on purpose + // temp = 1000 -temp; + //if (i == 1) // It is clear that h377's thro stick is made reversely, so I adjust it here on purpose + // temp = 1000 -temp; + if (temp < 0) + ch_data[i] = 0; + else if (temp > 1000) + ch_data[i] = 1000; + else + ch_data[i] = (unsigned int)temp; + payload[i] = (uint8_t)ch_data[i]; + } + payload[8] = (uint8_t)((ch_data[0]>>8)&0x0003); + payload[8] |= (uint8_t)((ch_data[1]>>6)&0x000c); + payload[8] |= (uint8_t)((ch_data[2]>>4)&0x0030); + payload[8] |= (uint8_t)((ch_data[3]>>2)&0x00c0); + + payload[9] = (uint8_t)((ch_data[4]>>8)&0x0003); + payload[9] |= (uint8_t)((ch_data[5]>>6)&0x000c); + payload[9] |= (uint8_t)((ch_data[6]>>4)&0x0030); + payload[9] |= (uint8_t)((ch_data[7]>>2)&0x00c0); +} + +static uint16_t h377_cb() { + counter1ms++; + if(counter1ms==1) { NRF24L01_FlushTx(); } + //------------------------- + else if(counter1ms==2) { + if (counter>0) { + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)binding_adr_rf, 5); + NRF24L01_WriteReg(NRF24L01_05_RF_CH, binding_ch); + } + } + else if(counter1ms==3) { + if (counter >0) { + counter--; + if (! counter) { BIND_DONE; } // binding finished, change tx add + NRF24L01_WritePayload(bind_buf_array,10); + } + } + else if (counter1ms==4) { if (counter > 0) { NRF24L01_FlushTx(); }} + //------------------------- + else if(counter1ms==5) { NRF24L01_SetPower(); } + //------------------------- + else if (counter1ms == 6) { + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rf_adr_buf, 5); + NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]); + hopping_frequency_no++; + if (hopping_frequency_no >= FREQUENCE_NUM_H377) { hopping_frequency_no = 0; } + } + else if (counter1ms == 7) { h377_ch_data(); } + else if(counter1ms>8) { + counter1ms = 0; + NRF24L01_WritePayload(payload,10); + } + return 1000; // send 1 binding packet and 1 data packet per 9ms +} + +// Linear feedback shift register with 32-bit Xilinx polinomial x^32 + x^22 + x^2 + x + 1 +static const uint32_t LFSR_FEEDBACK = 0x80200003ul; +static const uint32_t LFSR_INTAP = 32-1; + +static void update_lfsr(uint32_t *lfsr, uint8_t b) { + for (int i = 0; i < 8; ++i) { + *lfsr = (*lfsr >> 1) ^ ((-(*lfsr & 1u) & LFSR_FEEDBACK) ^ ~((uint32_t)(b & 1) << LFSR_INTAP)); + b >>= 1; + } +} + +// Generate internal id from TX id and manufacturer id (STM32 unique id) + + +static void H377_tx_id() { + for(int i=0;i<5;i++) + rf_adr_buf[i] = rf_adr_buf_data[select_ch_id][i]; + + hopping_frequency[0] = hopping_frequency_data[select_ch_id]; + + for (int i = 1; i < FREQUENCE_NUM_H377; i++) { + hopping_frequency[i] = hopping_frequency[i-1] + 3; + } +} + + + +static uint16_t h377_setup() { + select_ch_id = MProtocol_id_master%SET_NUM_H377; + + H377_tx_id();//rf_adr_buf hopping_frequency + + h377_binding_packet();//bind_buf_array (rf_adr_buf hopping_frequency) + + h377_init(); + + if(IS_AUTOBIND_FLAG_on) { + counter = BIND_COUNT; + BIND_IN_PROGRESS; + } + else { counter = 0; } + + return 1000; +} +#endif diff --git a/Multiprotocol/Nrf24l01_hm830.ino b/Multiprotocol/Nrf24l01_hm830.ino new file mode 100644 index 0000000..bb30d3a --- /dev/null +++ b/Multiprotocol/Nrf24l01_hm830.ino @@ -0,0 +1,267 @@ +/* + 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. + Deviation 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 Deviation. If not, see . + */ + +/* This protocol is for the HM Hobby HM830 RC Paper Airplane + Protocol spec: + Channel data: + AA BB CC DD EE FF GG + AA : Throttle Min=0x00 max =0x64 + BB : + bit 0,1,2: Left/Right magnitude, bit 5 Polarity (set = right) + bit 6: Accelerate + bit 7: Right button (also the ABC Button) + CC : bit 0 seems to be impacted by the Right button + DD + EE + FF : Trim (bit 0-5: Magnitude, bit 6 polarity (set = right) + GG : Checksum (CRC8 on bytes AA-FF), init = 0xa5, poly = 0x01 +*/ + +#ifdef HM830_NRF24L01_INO + +#include "iface_nrf24l01.h" + +enum { + HM830_BIND1A = 0, + HM830_BIND2A, + HM830_BIND3A, + HM830_BIND4A, + HM830_BIND5A, + HM830_BIND6A, + HM830_BIND7A, + HM830_DATA1, + HM830_DATA2, + HM830_DATA3, + HM830_DATA4, + HM830_DATA5, + HM830_DATA6, + HM830_DATA7, + HM830_BIND1B = 0x80, + HM830_BIND2B, + HM830_BIND3B, + HM830_BIND4B, + HM830_BIND5B, + HM830_BIND6B, + HM830_BIND7B, +}; + +static uint8_t init_vals_hm830[][2] = { + {NRF24L01_17_FIFO_STATUS, 0x00}, + {NRF24L01_16_RX_PW_P5, 0x07}, + {NRF24L01_15_RX_PW_P4, 0x07}, + {NRF24L01_14_RX_PW_P3, 0x07}, + {NRF24L01_13_RX_PW_P2, 0x07}, + {NRF24L01_12_RX_PW_P1, 0x07}, + {NRF24L01_11_RX_PW_P0, 0x07}, + {NRF24L01_0F_RX_ADDR_P5, 0xC6}, + {NRF24L01_0E_RX_ADDR_P4, 0xC5}, + {NRF24L01_0D_RX_ADDR_P3, 0xC4}, + {NRF24L01_0C_RX_ADDR_P2, 0xC3}, + {NRF24L01_09_CD, 0x00}, + {NRF24L01_08_OBSERVE_TX, 0x00}, + {NRF24L01_07_STATUS, 0x07}, +// {NRF24L01_06_RF_SETUP, 0x07}, + {NRF24L01_05_RF_CH, 0x18}, + {NRF24L01_04_SETUP_RETR, 0x3F}, + {NRF24L01_03_SETUP_AW, 0x03}, + {NRF24L01_02_EN_RXADDR, 0x3F}, + {NRF24L01_01_EN_AA, 0x3F}, + {NRF24L01_00_CONFIG, 0x0E}, +}; + +static uint8_t count; +static uint8_t rf_ch[] = {0x08, 0x35, 0x12, 0x3f, 0x1c, 0x49, 0x26}; +static uint8_t bind_addr[] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xc2}; + +static uint8_t crc8(uint32_t result, uint8_t *data, int len) { + int polynomial = 0x01; + for(int i = 0; i < len; i++) { + result = result ^ data[i]; + for(int j = 0; j < 8; j++) { + if(result & 0x80) { result = (result << 1) ^ polynomial; } + else { result = result << 1; } + } + } + return result & 0xff; +} + +static void HM830_init() { + NRF24L01_Initialize(); + for (uint32_t i = 0; i < sizeof(init_vals_hm830) / sizeof(init_vals_hm830[0]); i++) { NRF24L01_WriteReg(init_vals_hm830[i][0], init_vals_hm830[i][1]); } + + NRF24L01_SetTxRxMode(TX_EN); + NRF24L01_SetBitrate(0); + NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, bind_addr, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, bind_addr+1, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, bind_addr, 5); + NRF24L01_Activate(0x73); //Enable FEATURE + NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07); + NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3F); + //NRF24L01_ReadReg(NRF24L01_07_STATUS) ==> 0x07 + + NRF24L01_Activate(0x53); // switch bank back + + NRF24L01_FlushTx(); + //NRF24L01_ReadReg(NRF24L01_07_STATUS) ==> 0x0e + NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x0e); + //NRF24L01_ReadReg(NRF24L01_00_CONFIG); ==> 0x0e + NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0e); + NRF24L01_ReadReg(NRF24L01_01_EN_AA); // No Auto Acknoledgement +} + +static void build_bind_packet_hm830() { + for(int i = 0; i < 6; i++) { packet[i] = rx_tx_addr[i]; } + packet[6] = crc8(0xa5, packet, 6); +} + +static void build_data_packet() { + uint8_t ail_sign = 0, trim_sign = 0; + + throttle = (uint32_t)Servo_data[THROTTLE] * 50 / PPM_MAX + 50; + if (throttle < 0) { throttle = 0; } + + aileron = (uint32_t)Servo_data[AILERON] * 8 / PPM_MAX; + if (aileron < 0) { aileron = -aileron; ail_sign = 1; } + if (aileron > 7) { aileron = 7; } + + uint8_t turbo = (uint32_t)Servo_data[ELEVATOR] > 0 ? 1 : 0; + + uint8_t trim = ((uint32_t)Servo_data[RUDDER] * 0x1f / PPM_MAX); + if (trim < 0) { trim = -trim; trim_sign = 1; } + if (trim > 0x1f) { trim = 0x1f; } + + uint8_t rbutton = (uint32_t)Servo_data[4] > 0 ? 1 : 0; + packet[0] = throttle; + packet[1] = aileron; + if (ail_sign) { packet[1] |= 0x20; } + if (turbo) { packet[1] |= 0x40; } + if (rbutton) { packet[1] |= 0x80; } + packet[5] = trim; + if (trim_sign) { packet[5] |= 0x20;} + packet[6] = crc8(0xa5, packet, 6); +} + +static void send_packet_hm830() { + NRF24L01_ReadReg(NRF24L01_17_FIFO_STATUS); + NRF24L01_WritePayload(packet, 7); +} + +static uint16_t handle_binding() { + uint8_t status = NRF24L01_ReadReg(NRF24L01_07_STATUS); + if (status & 0x20) { + //Binding complete + phase = HM830_DATA1 + ((phase&0x7F)-HM830_BIND1A); + count = 0; + NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, rx_tx_addr+1, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5); + NRF24L01_FlushTx(); + build_data_packet(); + uint8_t rb = NRF24L01_ReadReg(NRF24L01_07_STATUS); //==> 0x0E + NRF24L01_WriteReg(NRF24L01_07_STATUS, rb); + rb = NRF24L01_ReadReg(NRF24L01_00_CONFIG); //==> 0x0E + NRF24L01_WriteReg(NRF24L01_00_CONFIG, rb); + send_packet_hm830(); + return 14000; + } + switch (phase) { + case HM830_BIND1A: + //Look for a Rx that is already bound + NRF24L01_SetPower(); + NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, rx_tx_addr+1, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5); + NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch[0]); + build_bind_packet_hm830(); + break; + case HM830_BIND1B: + //Look for a Rx that is not yet bound + NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, bind_addr, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, bind_addr+1, 5); + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, bind_addr, 5); + NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch[0]); + break; + case HM830_BIND2A: + case HM830_BIND3A: + case HM830_BIND4A: + case HM830_BIND5A: + case HM830_BIND6A: + case HM830_BIND7A: + case HM830_BIND2B: + case HM830_BIND3B: + case HM830_BIND4B: + case HM830_BIND5B: + case HM830_BIND6B: + case HM830_BIND7B: + NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch[(phase&0x7F)-HM830_BIND1A]); + break; + } + NRF24L01_FlushTx(); + uint8_t rb = NRF24L01_ReadReg(NRF24L01_07_STATUS); //==> 0x0E + NRF24L01_WriteReg(NRF24L01_07_STATUS, rb); + rb = NRF24L01_ReadReg(NRF24L01_00_CONFIG); //==> 0x0E + NRF24L01_WriteReg(NRF24L01_00_CONFIG, rb); + send_packet_hm830(); + phase++; + if (phase == HM830_BIND7B+1) { phase = HM830_BIND1A; } + else if (phase == HM830_BIND7A+1) { phase = HM830_BIND1B; } + return 20000; +} + +static uint16_t handle_data() { + uint8_t status = NRF24L01_ReadReg(NRF24L01_07_STATUS); + if (count <= 0 || !(status & 0x20)) { + if(count < 0 || ! (status & 0x20)) { + count = 0; + //We didn't get a response on this channel, try the next one + phase++; + if (phase-HM830_DATA1 > 6) { phase = HM830_DATA1; } + + NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch[0]); + NRF24L01_FlushTx(); + build_data_packet(); + uint8_t rb = NRF24L01_ReadReg(NRF24L01_07_STATUS); //==> 0x0E + NRF24L01_WriteReg(NRF24L01_07_STATUS, rb); + rb = NRF24L01_ReadReg(NRF24L01_00_CONFIG); //==> 0x0E + NRF24L01_WriteReg(NRF24L01_00_CONFIG, rb); + send_packet_hm830(); + return 14000; + } + } + build_data_packet(); + count++; + if(count == 98) { + count = -1; + NRF24L01_SetPower(); + } + uint8_t rb = NRF24L01_ReadReg(NRF24L01_07_STATUS); //==> 0x0E + NRF24L01_WriteReg(NRF24L01_07_STATUS, rb); + rb = NRF24L01_ReadReg(NRF24L01_00_CONFIG); //==> 0x0E + NRF24L01_WriteReg(NRF24L01_00_CONFIG, rb); + send_packet_hm830(); + return 20000; +} + + + +static uint16_t HM830_callback() { + if ((phase & 0x7F) < HM830_DATA1) { return handle_binding(); } + else { return handle_data(); } +} + + +static uint32_t HM830_setup(){ + count = 0; + // initialize_tx_id + + rx_tx_addr[4] = 0xee; + rx_tx_addr[5] = 0xc2; + HM830_init(); + phase = HM830_BIND1A; + + return 500; +} +#endif \ No newline at end of file diff --git a/Multiprotocol/Telemetry.ino b/Multiprotocol/Telemetry.ino index 39555d7..50553b4 100644 --- a/Multiprotocol/Telemetry.ino +++ b/Multiprotocol/Telemetry.ino @@ -1,166 +1,337 @@ //************************************* // FrSky Telemetry serial code * -// By Midelic on RCG * +// By Midelic on RCGroups * //************************************* #if defined TELEMETRY - -#define USER_MAX_BYTES 6 -#define MAX_PKTX 10 -uint8_t frame[18]; -uint8_t pass = 0; -uint8_t index; -uint8_t prev_index; -uint8_t pktx[MAX_PKTX]; - -void frskySendStuffed() -{ - Serial_write(0x7E); - for (uint8_t i = 0; i < 9; i++) + #if defined FRSKYX_CC2500_INO + #define SPORT_TELEMETRY + #endif + #if defined FRSKY_CC2500_INO + #define HUB_TELEMETRY + #endif + #if defined SPORT_TELEMETRY + #define SPORT_TELEMETRY + #define SPORT_TIME 12000 + uint32_t last=0; + uint8_t sport_counter=0; + uint8_t RxBt=0; + uint8_t rssi; + uint8_t ADC2; + #endif + #if defined HUB_TELEMETRY + #define MAX_PKTX 10 + uint8_t pktx[MAX_PKTX]; + uint8_t index; + uint8_t prev_index; + uint8_t pass = 0; + #endif + #define USER_MAX_BYTES 6 + uint8_t frame[18]; + + void frskySendStuffed() { - - if ((frame[i] == 0x7e) || (frame[i] == 0x7d)) + Serial_write(0x7E); + for (uint8_t i = 0; i < 9; i++) { - Serial_write(0x7D); - frame[i] ^= 0x20; + if ((frame[i] == 0x7e) || (frame[i] == 0x7d)) + { + Serial_write(0x7D); + frame[i] ^= 0x20; + } + Serial_write(frame[i]); } - Serial_write(frame[i]); + Serial_write(0x7E); } - Serial_write(0x7E); -} - -void compute_RSSIdbm(){ - RSSI_dBm = (((uint16_t)(pktt[len-2])*18)>>5); - if(pktt[len-2] >=128) - RSSI_dBm -= 82; - else - RSSI_dBm += 65; -} - -void frsky_check_telemetry(uint8_t *pkt,uint8_t len) -{ - if(pkt[1] != rx_tx_addr[3] || pkt[2] != rx_tx_addr[2] || len != pkt[0] + 3) - {//only packets with the required id and packet length - for(uint8_t i=3;i<6;i++) - pktt[i]=0; - return; - } - else - { - for (uint8_t i=3;i0) - telemetry_counter=(telemetry_counter+1)%32; - } -} - -void frsky_link_frame() -{ - frame[0] = 0xFE; - if ((cur_protocol[0]&0x1F)==MODE_FRSKY) - { - compute_RSSIdbm(); - frame[1] = pktt[3]; - frame[2] = pktt[4]; - frame[3] = (uint8_t)RSSI_dBm; - frame[4] = pktt[5]*2; - } - else - if ((cur_protocol[0]&0x1F)==MODE_HUBSAN) - { - frame[1] = v_lipo*2; //v_lipo; common 0x2A=42/10=4.2V - frame[2] = frame[1]; - frame[3] = 0x00; - frame[4] = (uint8_t)RSSI_dBm; - } - frame[5] = frame[6] = frame[7] = frame[8] = 0; - frskySendStuffed(); -} - -#if defined HUB_TELEMETRY -void frsky_user_frame() -{ - uint8_t indexx = 0, c=0, j=8, n=0, i; - - if(pktt[6]>0 && pktt[6]<=MAX_PKTX) - {//only valid hub frames - frame[0] = 0xFD; - frame[1] = 0; - frame[2] = pktt[7]; + + void compute_RSSIdbm(){ - switch(pass) - { - case 0: - indexx=pktt[6]; - for(i=0;i>5); + if(pktt[len-2] >=128) + RSSI_dBm -= 82; + else + RSSI_dBm += 65; + } + + void frsky_check_telemetry(uint8_t *pkt,uint8_t len) + { + if(pkt[1] != rx_tx_addr[3] || pkt[2] != rx_tx_addr[2] || len != pkt[0] + 3) + {//only packets with the required id and packet length + for(uint8_t i=3;i<6;i++) + pktt[i]=0; return; - frame[1] = index; + } + else + { + for (uint8_t i=3;i0) + telemetry_counter=(telemetry_counter+1)%32; + } + } + + void frsky_link_frame() + { + frame[0] = 0xFE; + if ((cur_protocol[0]&0x1F)==MODE_FRSKY) + { + compute_RSSIdbm(); + frame[1] = pktt[3]; + frame[2] = pktt[4]; + frame[3] = (uint8_t)RSSI_dBm; + frame[4] = pktt[5]*2; + } + else + if ((cur_protocol[0]&0x1F)==MODE_HUBSAN) + { + frame[1] = v_lipo*2; //v_lipo; common 0x2A=42/10=4.2V + frame[2] = frame[1]; + frame[3] = 0x00; + frame[4] = (uint8_t)RSSI_dBm; + } + frame[5] = frame[6] = frame[7] = frame[8] = 0; frskySendStuffed(); } - else - pass=0; -} -#endif - -void frskyUpdate() -{ - if(telemetry_link) - { - frsky_link_frame(); - telemetry_link=0; - return; - } + #if defined HUB_TELEMETRY - if(!telemetry_link && (cur_protocol[0]&0x1F) != MODE_HUBSAN ) - frsky_user_frame(); + void frsky_user_frame() + { + uint8_t indexx = 0, c=0, j=8, n=0, i; + + if(pktt[6]>0 && pktt[6]<=MAX_PKTX) + {//only valid hub frames + frame[0] = 0xFD; + frame[1] = 0; + frame[2] = pktt[7]; + + switch(pass) + { + case 0: + indexx=pktt[6]; + for(i=0;i0) + { + crc_s += p[i]; //0-1FF + crc_s += crc_s >> 8; //0-100 + crc_s &= 0x00ff; + } + } + } + + void sportIdle() + { + Serial_write(0x7e); + } + void sportSendFrame() + { + //at the moment only SWR RSSI,RxBt and A2. + sport_counter = (sport_counter + 1) %9; + + for (uint8_t i=5;i<8;i++) + frame[i]=0; + + switch (sport_counter) + { + case 0: // SWR + frame[0] = 0x98; + frame[1] = 0x10; + frame[2] = 0x05; + frame[3] = 0xf1; + frame[4] = 0x20;//dummy values if swr 20230f00 + frame[5] = 0x23; + frame[6] = 0x0F; + frame[7] = 0x00; + break; + case 1: // RSSI + frame[0] = 0x98; + frame[1] = 0x10; + frame[2] = 0x01; + frame[3] = 0xf1; + frame[4] = rssi; + break; + case 2: //BATT + frame[0] = 0x98; + frame[1] = 0x10; + frame[2] = 0x04; + frame[3] = 0xf1; + frame[4] = RxBt;//a1; + break; + case 3: //ADC2(A2) + frame[0] = 0x1A; + frame[1] = 0x10; + frame[2] = 0x03; + frame[3] = 0xf1; + frame[4] = ADC2;//a2;; + break; + default: + sportIdle(); + return; + } + sportSend(frame); + } + + void process_sport_data()//only for ADC2 + { + uint8_t j=7; + if(pktt[6]>0 && pktt[6]<=USER_MAX_BYTES) + { + for(uint8_t i=0;i<6;i++) + if(pktt[j++]==0x03) + if(pktt[j]==0xF1) + { + ADC2=pktt[j+1]; + break; + } + pktt[6]=0;//new frame + } + } + #endif + + + void frskyUpdate() + { + if(telemetry_link && (cur_protocol[0]&0x1F) != MODE_FRSKYX ) + { + frsky_link_frame(); + telemetry_link=0; + return; + } + #if defined HUB_TELEMETRY + if(!telemetry_link && (cur_protocol[0]&0x1F) != MODE_HUBSAN && (cur_protocol[0]&0x1F) != MODE_FRSKYX) + { + frsky_user_frame(); + return; + } + #endif + #if defined SPORT_TELEMETRY + if ((cur_protocol[0]&0x1F)==MODE_FRSKYX) + { + if(telemetry_link) + { + process_sport_data(); + if(pktt[4]>0x36) + rssi=pktt[4]/2; + else + RxBt=pktt[4]; + telemetry_link=0; + } + uint32_t now = micros(); + if ((now - last) > SPORT_TIME) + { + sportSendFrame(); + last = now; + } + } + #endif + } + #endif \ No newline at end of file diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index f0b4e61..6c28b62 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -24,7 +24,6 @@ //Uncomment to enable telemetry #define TELEMETRY -#define HUB_TELEMETRY //Uncomment to enable potar select @@ -34,29 +33,48 @@ #define POTAR_SELECT_M AILERON +//Comment if a module is not installed +#define A7105_INSTALLED +#define CYRF6936_INSTALLED +//#define CC2500_INSTALLED +#define NFR24L01_INSTALLED + //Comment a protocol to exclude it from compilation -//A7105 protocols -#define FLYSKY_A7105_INO -#define HUBSAN_A7105_INO -//CYRF6936 protocols -#define DEVO_CYRF6936_INO -#define DSM2_CYRF6936_INO -//CC2500 protocols -#define FRSKY_CC2500_INO -//#define FRSKYX_CC2500_INO -//NFR24L01 protocols -#define BAYANG_NRF24L01_INO -#define CG023_NRF24L01_INO -#define CX10_NRF24L01_INO -#define ESKY_NRF24L01_INO -#define HISKY_NRF24L01_INO -#define KN_NRF24L01_INO -#define SLT_NRF24L01_INO -#define SYMAX_NRF24L01_INO -#define V2X2_NRF24L01_INO -#define YD717_NRF24L01_INO -#define MT99XX_NRF24L01_INO -#define MJXQ_NRF24L01_INO +#ifdef A7105_INSTALLED + #define JOYSWAY_A7105_INO + + #define FLYSKY_A7105_INO + #define HUBSAN_A7105_INO +#endif +#ifdef CYRF6936_INSTALLED + #define J6PRO_CYRF6936_INO +// #define WK2x01_CYRF6936_INO + + #define DEVO_CYRF6936_INO + #define DSM2_CYRF6936_INO +#endif +#ifdef CC2500_INSTALLED + #define FRSKY_CC2500_INO + #define FRSKYX_CC2500_INO +#endif +#ifdef NFR24L01_INSTALLED + #define HM830_NRF24L01_INO + #define CFlie_NRF24L01_INO + #define H377_NRF24L01_INO + + #define BAYANG_NRF24L01_INO + #define CG023_NRF24L01_INO + #define CX10_NRF24L01_INO + #define ESKY_NRF24L01_INO + #define HISKY_NRF24L01_INO + #define KN_NRF24L01_INO + #define SLT_NRF24L01_INO + #define SYMAX_NRF24L01_INO + #define V2X2_NRF24L01_INO + #define YD717_NRF24L01_INO + #define MT99XX_NRF24L01_INO + #define MJXQ_NRF24L01_INO +#endif //Update this table to set which protocol and all associated settings are called for the corresponding dial number static const PPM_Parameters PPM_prot[15]= @@ -215,7 +233,7 @@ Option value between 0 and 255. 0xD7 or 0x00 for Frsky fine tuning. RUDDER, }; #endif -enum chan_order{ +enum chan_orders{ AUX1 =4, AUX2, AUX3, diff --git a/Multiprotocol/iface_nrf24l01.h b/Multiprotocol/iface_nrf24l01.h index 1acf438..92ff9e1 100644 --- a/Multiprotocol/iface_nrf24l01.h +++ b/Multiprotocol/iface_nrf24l01.h @@ -102,18 +102,7 @@ enum { #define REUSE_TX_PL 0xE3 //#define NOP 0xFF -/* -void NRF24L01_Initialize(); -byte NRF24L01_WriteReg(byte reg, byte data); -byte NRF24L01_WriteRegisterMulti(byte reg, byte data[], byte length); -byte NRF24L01_WritePayload(byte *data, byte len); -byte NRF24L01_ReadReg(byte reg); -byte NRF24L01_ReadRegisterMulti(byte reg, byte data[], byte length); -byte NRF24L01_ReadPayload(byte *data, byte len); +// XN297 emulation layer +#define XN297_UNSCRAMBLED 8 -byte NRF24L01_FlushTx(); -byte NRF24L01_FlushRx(); -byte NRF24L01_Activate(byte code); - -*/ #endif \ No newline at end of file diff --git a/Multiprotocol/multiprotocol.h b/Multiprotocol/multiprotocol.h index dcbbfbd..42ab887 100644 --- a/Multiprotocol/multiprotocol.h +++ b/Multiprotocol/multiprotocol.h @@ -15,7 +15,7 @@ // Check selected board type #ifndef ARDUINO_AVR_PRO - #error You must select the board type "Arduino Pro or Pro Mini" +// #error You must select the board type "Arduino Pro or Pro Mini" #endif #if F_CPU != 16000000L || not defined(__AVR_ATmega328P__) #error You must select the processor type "ATmega328(5V, 16MHz)" @@ -26,6 +26,14 @@ //****************** enum PROTOCOLS { + MODE_HM830=40, // =>NRF24L01 + MODE_CFLIE=41, // =>NRF24L01 + MODE_JOYSWAY = 42, // =>A7105 + MODE_J6PRO = 43, // =>CYRF6936 + MODE_H377=44, // =>NRF24L01 + MODE_WK2x01 = 45, // =>CYRF6936 + MODE_FY326=46, // =>NRF24L01 + MODE_SERIAL = 0, // Serial commands MODE_FLYSKY = 1, // =>A7105 MODE_HUBSAN = 2, // =>A7105 @@ -110,6 +118,12 @@ enum MJXQ X800 = 2, H26D = 3 }; +enum WK2X01 +{ + WK2801 = 0, + WK2601 = 1, + WK2401 = 2 +}; #define NONE 0 #define P_HIGH 1 diff --git a/README.md b/README.md index a4686c6..169902f 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,45 @@ Notes: - For serial, the dial switch is not needed and the bind button optionnal - Ajout d'un switch + transistor sur le TX ![Alt text](telemetryFRSKY.jpg) + + +#Protocoles ajoutés mais non testés (Issue de Deviation) +##CYRF6936 RF Module +###J6PRO +CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12 +---|---|---|---|---|---|---|---|---|---|---|--- +CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12 + +###WK2x01 +En cours ... + +##A7105 RF Module +###Joysway +CH1|CH2|CH3|CH4 +---|---|---|--- +A|E|T|R + +##NRF24L01 RF Module +###CFLIE +Modele: CrazyFlie Nano quad +CH1|CH2|CH3|CH4 +---|---|---|--- +A|E|T|R + +###Fy326 +Autobind +CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8 +---|---|---|---|---|---|---|--- +A|E|T|R|FLIP|HEADLESS|RTH|Calibrate + +###H377 +CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8 +---|---|---|---|---|---|---|--- +A|E|T|R|CH5|CH6|CH7|CH8 + +###HM830 +Modele: HM Hobby HM830 RC Paper Airplane +CH1|CH2|CH3|CH4|CH5 +---|---|---|--- +A|Turbo|T|Trim|Bouton ??? + diff --git a/sync.ffs_db b/sync.ffs_db index b007d39..af956ef 100644 Binary files a/sync.ffs_db and b/sync.ffs_db differ diff --git a/taranis_switches.png b/taranis_switches.png new file mode 100644 index 0000000..d1645d2 Binary files /dev/null and b/taranis_switches.png differ