/* This project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Multiprotocol is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>. */ #if defined(MLINK_CYRF6936_INO) #include "iface_cyrf6936.h" //#define MLINK_FORCE_ID #define MLINK_BIND_COUNT 696 // around 20s #define MLINK_NUM_FREQ 78 #define MLINK_BIND_CHANNEL 0x01 #define MLINK_PACKET_SIZE 8 enum { MLINK_BIND_TX=0, MLINK_BIND_PREP_RX, MLINK_BIND_RX, MLINK_PREP_DATA, MLINK_SEND1, MLINK_SEND2, MLINK_SEND3, MLINK_CHECK3, MLINK_RX, MLINK_BUILD4, }; uint8_t MLINK_Data_Code[16], MLINK_CRC_Init, MLINK_Unk_6_2; const uint8_t PROGMEM MLINK_init_vals[][2] = { //Init from dump { CYRF_01_TX_LENGTH, 0x08 }, // Length of packet { CYRF_02_TX_CTRL, 0x40 }, // Clear TX Buffer { CYRF_03_TX_CFG, 0x3C }, //0x3E in normal mode, 0x3C in bind mode: SDR 64 chip codes (=8 bytes data code used) { CYRF_05_RX_CTRL, 0x00 }, { CYRF_06_RX_CFG, 0x93 }, // AGC enabled, overwrite enable, valid flag enable { CYRF_0B_PWR_CTRL, 0x00 }, //{ CYRF_0C_XTAL_CTRL, 0x00 }, // Set to GPIO on reset //{ CYRF_0D_IO_CFG, 0x00 }, // Set to GPIO on reset //{ CYRF_0E_GPIO_CTRL, 0x00 }, // Set by the CYRF_SetTxRxMode function { CYRF_0F_XACT_CFG, 0x04 }, // end state idle { CYRF_10_FRAMING_CFG, 0x00 }, // SOP disabled { CYRF_11_DATA32_THOLD, 0x05 }, // not used??? { CYRF_12_DATA64_THOLD, 0x0F }, // 64 Chip Data PN Code Correlator Threshold { CYRF_14_EOP_CTRL, 0x05 }, // 5 consecutive noncorrelations symbol for EOP { CYRF_15_CRC_SEED_LSB, 0x00 }, // not used??? { CYRF_16_CRC_SEED_MSB, 0x00 }, // not used??? { CYRF_1B_TX_OFFSET_LSB,0x00 }, { CYRF_1C_TX_OFFSET_MSB,0x00 }, { CYRF_1D_MODE_OVERRIDE,0x00 }, { CYRF_1E_RX_OVERRIDE, 0x14 }, // RX CRC16 is disabled and Force Receive Data Rate { CYRF_1F_TX_OVERRIDE, 0x04 }, // TX CRC16 is disabled { CYRF_26_XTAL_CFG, 0x08 }, { CYRF_29_RX_ABORT, 0x00 }, { CYRF_32_AUTO_CAL_TIME,0x3C }, { CYRF_35_AUTOCAL_OFFSET,0x14 }, { CYRF_39_ANALOG_CTRL, 0x03 }, // Receive invert and all slow }; static void __attribute__((unused)) MLINK_cyrf_config() { for(uint8_t i = 0; i < sizeof(MLINK_init_vals) / 2; i++) CYRF_WriteRegister(pgm_read_byte_near(&MLINK_init_vals[i][0]), pgm_read_byte_near(&MLINK_init_vals[i][1])); CYRF_WritePreamble(0x333304); CYRF_SetTxRxMode(TX_EN); } static void __attribute__((unused)) MLINK_send_bind_packet() { uint8_t p_c=packet_count>>1; memset(packet, p_c<0x16?0x00:0xFF, MLINK_PACKET_SIZE-1); packet[0]=0x0F; // bind packet[1]=p_c; switch(p_c) { case 0x00: packet[2]=0x40; //unknown but seems constant packet[4]=0x01; //unknown but seems constant packet[5]=0x03; //unknown but seems constant packet[6]=0xE3; //unknown but seems constant break; case 0x05: packet[6]=MLINK_CRC_Init; //CRC init value break; case 0x06: packet[2]=MLINK_Unk_6_2; //unknown and different //Start of hopping frequencies for(uint8_t i=0;i<4;i++) packet[i+3]=hopping_frequency[i]; break; case 0x15: packet[6]=0x51; //unknown but seems constant break; case 0x16: packet[2]=0x51; //unknown but seems constant packet[3]=0xEC; //unknown but seems constant packet[4]=0x05; //unknown but seems constant break; case 0x1A: packet[1]=0xFF; memset(&packet[2],0x00,5); break; } if(p_c>=0x01 && p_c<=0x04) {//DATA_CODE uint8_t p_c_5=(p_c-1)*5; for(uint8_t i=0;i<5;i++) if(i+p_c_5<16) packet[i+2]=MLINK_Data_Code[i+p_c_5]; } else if(p_c>=0x07 && p_c<=0x15) {//Hopping frequencies uint8_t p_c_5=5*(p_c-6)-1; for(uint8_t i=0;i<5;i++) if(i+p_c_5<MLINK_NUM_FREQ) packet[i+2]=hopping_frequency[i+p_c_5]; } else if(p_c>0x19) { packet[1]=0xFF; memset(&packet[2], 0x00, MLINK_PACKET_SIZE-3); } //Calculate CRC crc8=0xFF; // Init = 0xFF for(uint8_t i=0;i<MLINK_PACKET_SIZE-1;i++) crc8_update(bit_reverse(packet[i])); packet[7] = bit_reverse(crc8); // CRC reflected out //Debug #if 0 debug("P(%02d):",p_c); for(uint8_t i=0;i<8;i++) debug(" %02X",packet[i]); debugln(""); #endif //Send packet CYRF_WriteDataPacketLen(packet, MLINK_PACKET_SIZE); } static void __attribute__((unused)) MLINK_send_data_packet() { static uint8_t tog=0; uint8_t start; #ifdef FAILSAFE_ENABLE static uint8_t fs=0; if(IS_FAILSAFE_VALUES_on && phase==MLINK_SEND1) { fs=10; // Original radio is sending 70 packets FAILSAFE_VALUES_off; } if(fs) {// Failsafe packets switch(phase) { case MLINK_SEND2: packet[0]=0x06; start=17; break; case MLINK_SEND3: packet[0]=0x84; start=5; fs--; break; default: //MLINK_SEND1: packet[0]=0x05; start=11; break; } //Pack 6 channels per packet for(uint8_t i=0;i<6;i++) { uint8_t val=start<16 ? convert_channel_16b_nolimit(start,426 >> 4,3448 >> 4,true) : 0x00; start--; // switch to next channel packet[i+1]=val; } } else #endif {// Normal packets if(hopping_frequency_no==0) tog=1; //Channels to be sent if(phase==MLINK_SEND1 || ((hopping_frequency_no%5==0) && (phase==MLINK_SEND2))) { if((hopping_frequency_no&1)==0) packet[0] = 0x09; //10,8,6 else packet[0] = 0x01; //11,9,7 } else if(phase==MLINK_SEND2) { if(tog) packet[0] = 0x02; //x,15,13 else packet[0] = 0x0A; //x,14,12 tog^=1; } else {//phase==MLINK_SEND3 if((hopping_frequency_no&1)==0) packet[0] = 0x88; //4,2,0 else packet[0] = 0x80; //5,3,1 } //Start channel start=4+6*(packet[0]&3); if((packet[0]&0x08)==0) start++; //Channels 426..1937..3448 for(uint8_t i=0;i<3;i++) { uint16_t val=start<16 ? convert_channel_16b_nolimit(start,426,3448,false) : 0x0000; start-=2; // switch to next channel packet[i*2+1]=val>>8; packet[i*2+2]=val; } } //Calculate CRC crc8=bit_reverse(hopping_frequency_no + MLINK_CRC_Init); // Init = relected freq index + offset for(uint8_t i=0;i<MLINK_PACKET_SIZE-1;i++) crc8_update(bit_reverse(packet[i])); packet[7] = bit_reverse(crc8); // CRC reflected out //Send CYRF_WriteDataPacketLen(packet, MLINK_PACKET_SIZE); //Debug #if 0 debug("P(%02d):",hopping_frequency_no); for(uint8_t i=0;i<8;i++) debug(" %02X",packet[i]); debugln(""); #endif } #ifdef MLINK_HUB_TELEMETRY static void __attribute__((unused)) MLINK_Send_Telemetry() { // not sure how MLINK telemetry works, the 2 RXs I have are sending something completly different... telemetry_counter += 2; // TX LQI counter telemetry_link = 1; if(packet_in[0]==0x13) { // RX-9-DR : 13 1A C8 00 01 64 00 uint8_t id; for(uint8_t i=1; i<5; i+=3) {//2 sensors per packet id=0x00; switch(packet_in[i]&0x0F) { case 1: //voltage if((packet_in[i]&0xF0) == 0x00) v_lipo1 = packet_in[i+1]; // Rx_Batt*20 else v_lipo2 = packet_in[i+1]; break; case 2: //current id = 0x28; break; case 3: //vario id = 0x30; break; case 5: //rpm id = 0x03; break; case 6: //temp id = 0x02; break; case 10: //lqi RX_RSSI=RX_LQI=packet_in[i+1]>>1; break; } #if defined HUB_TELEMETRY if(id) { uint16_t val=((packet_in[i+2]&0x80)<<8)|((packet_in[i+2]&0x7F)<<7)|(packet_in[i+1]>>1); //remove the alarm LSB bit, move the sign bit to MSB frsky_send_user_frame(id, val, val>>8); } #endif } } else if(packet_in[0]==0x03) { // RX-5 : 03 15 23 00 00 01 02 //Incoming packet values RX_RSSI = packet_in[2]<<1; // Looks to be the RX RSSI value RX_LQI = packet_in[5]; // Looks to be connection lost } else RX_RSSI = TX_LQI; // Read TX RSSI TX_RSSI = CYRF_ReadRegister(CYRF_13_RSSI)&0x1F; if(telemetry_lost) { telemetry_lost = 0; packet_count = 50; telemetry_counter = 100; } } #endif #ifdef MLINK_FW_TELEMETRY static void __attribute__((unused)) MLINK_Send_Telemetry() { telemetry_counter += 2; // TX LQI counter telemetry_link = 4; // Read TX RSSI TX_RSSI = CYRF_ReadRegister(CYRF_13_RSSI)&0x1F; if(telemetry_lost) { telemetry_lost = 0; packet_count = 50; telemetry_counter = 100; } } #endif uint16_t MLINK_callback() { uint8_t status; uint16_t start; switch(phase) { case MLINK_BIND_RX: //debugln("RX"); status=CYRF_ReadRegister(CYRF_05_RX_CTRL); if( (status&0x80) == 0 ) {//Packet received len=CYRF_ReadRegister(CYRF_09_RX_COUNT); debugln("L=%02X",len) if( len==8 ) { CYRF_ReadDataPacketLen(packet, len*2); debug("RX="); for(uint8_t i=0;i<8;i++) debug(" %02X",packet[i*2]); debugln(""); //Check CRC crc8=0xFF; // Init = 0xFF for(uint8_t i=0;i<MLINK_PACKET_SIZE-1;i++) crc8_update(bit_reverse(packet[i<<1])); if(packet[14] == bit_reverse(crc8)) {// CRC is ok debugln("CRC ok"); if(packet[0]==0x7F) packet_count=3; // Start sending bind payload else if(packet_count > 0x19*2) { if(packet[0] == 0x8F) packet_count++; else if(packet[0] == 0x9F) packet_count=0x80; // End bind else packet_count=0; // Restart bind... } } } } else packet_count=0; CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Enable RX abort CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24); // Force end state CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Disable RX abort phase=MLINK_BIND_TX; // Retry sending bind packet CYRF_SetTxRxMode(TX_EN); // Transmit mode if(packet_count) return 18136; case MLINK_BIND_TX: if(--bind_counter==0 || packet_count>=0x1B*2) { // Switch to normal mode BIND_DONE; phase=MLINK_PREP_DATA; return 22720; } MLINK_send_bind_packet(); if(packet_count == 0 || packet_count > 0x19*2) { phase++; // MLINK_BIND_PREP_RX return 4700; // Original is 4900 } packet_count++; if(packet_count&1) return 6000; return 22720; case MLINK_BIND_PREP_RX: start=micros(); while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 200) // Wait max 200µs for TX to finish if((CYRF_ReadRegister(CYRF_02_TX_CTRL) & 0x80) == 0x00) break; // Packet transmission complete CYRF_SetTxRxMode(RX_EN); // Receive mode CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x82); // Prepare to receive phase++; //MLINK_BIND_RX if(packet_count > 0x19*2) return 28712; // Give more time to the RX to confirm that the bind is ok... return 28712-4700; case MLINK_PREP_DATA: CYRF_ConfigDataCode(MLINK_Data_Code); MLINK_CRC_Init += 0xED; hopping_frequency_no = 0x00; CYRF_ConfigRFChannel(hopping_frequency[hopping_frequency_no]); CYRF_SetPower(0x38); #if defined(MLINK_HUB_TELEMETRY) || defined(MLINK_FW_TELEMETRY) packet_count = 0; telemetry_lost = 1; #endif phase++; case MLINK_SEND1: MLINK_send_data_packet(); phase++; return 4880+1111; case MLINK_SEND2: MLINK_send_data_packet(); phase++; if(hopping_frequency_no%5==0) return 4617+1017; return 4617+1422; case MLINK_SEND3: MLINK_send_data_packet(); phase++; return 4611; case MLINK_CHECK3: //Switch to next channel hopping_frequency_no++; if(hopping_frequency_no>=MLINK_NUM_FREQ) hopping_frequency_no=0; CYRF_ConfigRFChannel(hopping_frequency[hopping_frequency_no]); //Receive telemetry if(hopping_frequency_no%5==0) {//Receive telemetry CYRF_SetTxRxMode(RX_EN); // Receive mode CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x82); // Prepare to receive phase++; //MLINK_RX return 8038+2434+410-1000; } else CYRF_SetPower(0x38); phase=MLINK_SEND1; return 4470; case MLINK_RX: #if defined(MLINK_HUB_TELEMETRY) || defined(MLINK_FW_TELEMETRY) //TX LQI calculation packet_count++; if(packet_count>=50) { packet_count=0; TX_LQI=telemetry_counter; if(telemetry_counter==0) telemetry_lost = 1; telemetry_counter = 0; } #endif status=CYRF_ReadRegister(CYRF_05_RX_CTRL); debug("T(%02X):",status); if( (status&0x80) == 0 ) {//Packet received len=CYRF_ReadRegister(CYRF_09_RX_COUNT); debug("(%X)",len) if( len && len <= MLINK_PACKET_SIZE ) { CYRF_ReadDataPacketLen(packet_in, len*2); #if defined(MLINK_HUB_TELEMETRY) || defined(MLINK_FW_TELEMETRY) if(len==MLINK_PACKET_SIZE) { for(uint8_t i=0;i<8;i++) //Check CRC crc8=bit_reverse(MLINK_CRC_Init); for(uint8_t i=0;i<MLINK_PACKET_SIZE-1;i++) { packet_in[i]=packet_in[i<<1]; crc8_update(bit_reverse(packet_in[i])); debug(" %02X",packet_in[i]); } if(packet_in[14] == bit_reverse(crc8)) // Packet CRC is ok MLINK_Send_Telemetry(); else debug(" NOK"); } #endif } } debugln(""); CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Enable RX abort CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24); // Force end state CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Disable RX abort CYRF_SetTxRxMode(TX_EN); // Transmit mode phase=MLINK_SEND2; return 1000; } return 1000; } static void __attribute__((unused)) MLINK_shuffle_freqs(uint32_t seed, uint8_t *hop) { randomSeed(seed); for(uint8_t i=0; i < MLINK_NUM_FREQ/2; i++) { uint8_t r = random(0xfefefefe) % (MLINK_NUM_FREQ/2); uint8_t tmp = hop[r]; hop[r] = hop[i]; hop[i] = tmp; } } void MLINK_init() { MLINK_cyrf_config(); //Init ID and RF freqs for(uint8_t i=0; i < MLINK_NUM_FREQ/2; i++) { hopping_frequency[i ] = (i<<1) + 3; hopping_frequency[i+MLINK_NUM_FREQ/2] = (i<<1) + 3; } // part1 memcpy(MLINK_Data_Code ,rx_tx_addr,4); MLINK_shuffle_freqs(MProtocol_id, hopping_frequency); // part2 MProtocol_id ^= 0x6FBE3201; set_rx_tx_addr(MProtocol_id); memcpy(MLINK_Data_Code+4,rx_tx_addr,4); MLINK_shuffle_freqs(MProtocol_id, &hopping_frequency[MLINK_NUM_FREQ/2]); // part3 MLINK_CRC_Init = rx_tx_addr[3]; //value sent during bind then used to init the CRC MLINK_Unk_6_2 = 0x3A; //unknown value sent during bind but doesn't seem to matter #ifdef MLINK_FORCE_ID if(RX_num) { //Cockpit SX memcpy(MLINK_Data_Code,"\x4C\x97\x9D\xBF\xB8\x3D\xB5\xBE",8); memcpy(hopping_frequency,"\x0D\x41\x09\x43\x17\x2D\x05\x31\x13\x3B\x1B\x3D\x0B\x41\x11\x45\x09\x2B\x17\x4D\x19\x3F\x03\x3F\x0F\x37\x1F\x47\x1B\x49\x07\x35\x27\x2F\x15\x33\x23\x39\x1F\x33\x19\x45\x0D\x2D\x11\x35\x0B\x47\x25\x3D\x21\x37\x1D\x3B\x05\x2F\x21\x39\x23\x4B\x03\x31\x25\x29\x07\x4F\x1D\x4B\x15\x4D\x13\x4F\x0F\x49\x29\x2B\x27\x43",MLINK_NUM_FREQ); MLINK_Unk_6_2 = 0x3A; //unknown value sent during bind but doesn't seem to matter MLINK_CRC_Init = 0x07; //value sent during bind then used to init the CRC } else { //HFM3 memcpy(MLINK_Data_Code,"\xC0\x90\x8F\xBB\x7C\x8E\x2B\x8E",8); memcpy(hopping_frequency,"\x05\x41\x27\x4B\x17\x33\x11\x39\x0F\x3F\x05\x2F\x13\x2D\x25\x31\x1F\x2D\x25\x35\x03\x41\x1B\x43\x09\x3D\x1F\x29\x1D\x35\x0D\x3B\x19\x49\x23\x3B\x17\x47\x1D\x2B\x13\x37\x0B\x31\x23\x33\x29\x3F\x07\x37\x07\x43\x11\x2B\x1B\x39\x0B\x4B\x03\x4F\x21\x47\x0F\x4D\x15\x45\x21\x4F\x09\x3D\x19\x2F\x15\x45\x0D\x49\x27\x4D",MLINK_NUM_FREQ); MLINK_Unk_6_2 = 0x02; //unknown value but doesn't seem to matter MLINK_CRC_Init = 0x3E; //value sent during bind then used to init the CRC } //Other TX //MLINK_Unk_6_2 = 0x7e; //unknown value but doesn't seem to matter //MLINK_CRC_Init = 0xA2; //value sent during bind then used to init the CRC #endif for(uint8_t i=0;i<8;i++) MLINK_Data_Code[i+8]=MLINK_Data_Code[7-i]; debug("ID:") for(uint8_t i=0;i<16;i++) debug(" %02X", MLINK_Data_Code[i]); debugln(""); debugln("CRC init: %02X", MLINK_CRC_Init) debug("RF:") for(uint8_t i=0;i<MLINK_NUM_FREQ;i++) debug(" %02X", hopping_frequency[i]); debugln(""); if(IS_BIND_IN_PROGRESS) { packet_count = 0; bind_counter = MLINK_BIND_COUNT; CYRF_ConfigDataCode((uint8_t*)"\x6F\xBE\x32\x01\xDB\xF1\x2B\x01\xE3\x5C\xFA\x02\x97\x93\xF9\x02"); //Bind data code CYRF_ConfigRFChannel(MLINK_BIND_CHANNEL); phase = MLINK_BIND_TX; } else phase = MLINK_PREP_DATA; } #endif