#if defined(ELRS_SX1276_INO) #include "iface_sx1276.h" #include "ExpressLRS.h" uint8_t UID[6] = {10, 183, 12, 124, 56, 3}; // default UID for ExpressLRS (for testing) uint8_t CRCCaesarCipher = UID[4]; uint8_t DeviceAddr = 0b111111 & UID[5]; uint8_t expresslrs_tx_data_buf[8] = {0}; uint8_t expresslrs_nonce_tx = 0; uint32_t SyncPacketLastSent = 0; expresslrs_mod_settings_s *ExpressLRS_currAirRate_Modparams = &ExpressLRS_AirRateConfig[0]; void expresslrs_dio0_isr() { expresslrs_nonce_tx++; SX1276_ClearIRQFlags(); expresslrs_fhss_handle(); } uint16_t initExpressLRS() { debugln("Setting ExpressLRS LoRa reg defaults"); SX1276_Reset(); SX1276_SetMode(true, false, SX1276_OPMODE_SLEEP); SX1276_SetMode(true, false, SX1276_OPMODE_STDBY); //SX1276_DetectChip(); // for debug #define expresslrs_num_rf_rates 3 #define expresslrs_num_reg_domains 3 uint8_t reg_domain = int(sub_protocol / expresslrs_num_reg_domains); uint8_t rf_rate = sub_protocol % expresslrs_num_rf_rates; expresslrs_fhss_init_freq(reg_domain); expresslrs_ota_set_rfrate((expresslrs_RFrates_e)rf_rate); debugln("%s%d", "Reg Domain: ", reg_domain); debugln("%s%d", "RFrate: ", rf_rate); debugln("%s%d", "Interval: ", ExpressLRS_currAirRate_Modparams->interval); SX1276_SetSyncWord(UID[3]); SX1276_WriteReg(SX1276_22_PAYLOAD_LENGTH, 8); SX1276_WriteReg(SX1276_40_DIOMAPPING1, 0b11000000); //undocumented "hack", looking at Table 18 from datasheet SX127X_REG_DIO_MAPPING_1 = 11 appears to be unspported by infact it generates an intterupt on both RXdone and TXdone, this saves switching modes. SX1276_SetHopPeriod(0); // 0 = disabled, we hop frequencies manually SX1276_SetPaDac(true); SX1276_SetPaConfig(true, 7, 0); SX1276_SetTxRxMode(TX_EN); SX1276_SetFrequency_expresslrs(expresslrs_fhss_inital_freq()); attachInterrupt(SX1276_DIO0_pin, expresslrs_dio0_isr, RISING); //attache expresslrs_dio0_isr() func return ExpressLRS_currAirRate_Modparams->interval; } uint16_t ExpressLRS_callback() { expresslrs_switch_update_vals(); expresslrs_build_ota_pkt(); return ExpressLRS_currAirRate_Modparams->interval; } ///////////////////////////////////////////////////////////////////// OTA Func ///////////////////////////////////////////////////////////////////// static inline uint8_t UINT11_to_BIT(uint16_t Val) { if (Val > 1024) return 1; else return 0; }; void expresslrs_fhss_handle() { uint8_t modresult = (expresslrs_nonce_tx) % ExpressLRS_currAirRate_Modparams->FHSShopInterval; if (modresult == 0) // if it time to hop, do so. { SX1276_SetFrequency_expresslrs(expresslrs_fhss_get_next_freq()); } } // TODO, for now we just impliment the hybrid switch mode // Hybrid switches // The first switch is treated as a low-latency switch to be used for arm/disarm. // It is sent with every packet. The remaining 7 switch channels are sent // in the same change-prioritized round-robin fashion as described for sequential // switches above. All switches are encoded for 3 position support. All analog // channels are reduced to 10bit resolution to free up space in the rc packet // for switches. // current and sent switch values, used for prioritising sequential switch transmission #define N_SWITCHES 8 uint8_t currentSwitches[N_SWITCHES] = {0}; uint8_t sentSwitches[N_SWITCHES] = {0}; uint8_t nextSwitchIndex = 0; // for round-robin sequential switches void expresslrs_switch_update_vals() { #define INPUT_RANGE 2048 const uint16_t SWITCH_DIVISOR = INPUT_RANGE / 3; // input is 0 - 2048 for (int i = 0; i < N_SWITCHES; i++) { currentSwitches[i] = (Channel_data[i + 4] - 32) / SWITCH_DIVISOR; } } void expresslrs_switch_just_sent(uint8_t index, uint8_t value) { sentSwitches[index] = value; } uint8_t expresslrs_switch_next_index() { int firstSwitch = 0; // sequential switches includes switch 0 firstSwitch = 1; // skip 0 since it is sent on every packet // look for a changed switch int i; for (i = firstSwitch; i < N_SWITCHES; i++) { if (currentSwitches[i] != sentSwitches[i]) break; } // if we didn't find a changed switch, we get here with i==N_SWITCHES if (i == N_SWITCHES) { i = nextSwitchIndex; } // keep track of which switch to send next if there are no changed switches // during the next call. nextSwitchIndex = (i + 1) % 8; // for hydrid switches 0 is sent on every packet, so we can skip // that value for the round-robin if (nextSwitchIndex == 0) { nextSwitchIndex = 1; } return i; } void expresslrs_build_ota_hybrid_switches(uint8_t *Buffer, uint8_t addr) { uint8_t PacketHeaderAddr; PacketHeaderAddr = (addr << 2) + ELRS_RC_DATA_PACKET; Buffer[0] = PacketHeaderAddr; Buffer[1] = ((Channel_data[0] - 32) >> 3); Buffer[2] = ((Channel_data[1] - 32) >> 3); Buffer[3] = ((Channel_data[2] - 32) >> 3); Buffer[4] = ((Channel_data[3] - 32) >> 3); Buffer[5] = (((Channel_data[0] - 32) & 0b110) << 5) + (((Channel_data[1] - 32) & 0b110) << 3) + (((Channel_data[2] - 32) & 0b110) << 1) + (((Channel_data[3] - 32) & 0b110) >> 1); // switch 0 is sent on every packet - intended for low latency arm/disarm Buffer[6] = (currentSwitches[0] & 0b11) << 5; // note this leaves the top bit of byte 6 unused // find the next switch to send uint8_t nextSwitchIndex = expresslrs_switch_next_index() & 0b111; // mask for paranoia uint8_t value = currentSwitches[nextSwitchIndex] & 0b11; // mask for paranoia // put the bits into buf[6]. nextSwitchIndex is in the range 1 through 7 so takes 3 bits // currentSwitches[nextSwitchIndex] is in the range 0 through 2, takes 2 bits. Buffer[6] += (nextSwitchIndex << 2) + value; // update the sent value expresslrs_switch_just_sent(nextSwitchIndex, value); } void ExpressLRS_GenerateSyncPacketData() { uint8_t PacketHeaderAddr; PacketHeaderAddr = (DeviceAddr << 2) + ELRS_SYNC_PACKET; expresslrs_tx_data_buf[0] = PacketHeaderAddr; expresslrs_tx_data_buf[1] = expresslrs_fhss_get_index(); expresslrs_tx_data_buf[2] = expresslrs_nonce_tx; expresslrs_tx_data_buf[3] = ((ExpressLRS_currAirRate_Modparams->index & 0b111) << 5) + (0b000 << 2); // disable TLM expresslrs_tx_data_buf[4] = UID[3]; expresslrs_tx_data_buf[5] = UID[4]; expresslrs_tx_data_buf[6] = UID[5]; } void expresslrs_build_ota_11bit_switches() { uint8_t PacketHeaderAddr; PacketHeaderAddr = (DeviceAddr << 2) + ELRS_RC_DATA_PACKET; expresslrs_tx_data_buf[0] = PacketHeaderAddr; expresslrs_tx_data_buf[1] = ((Channel_data[0] - 32) >> 3); //- 32 is needed to make the scales correct with respect to the multi channel scaling expresslrs_tx_data_buf[2] = ((Channel_data[1] - 32) >> 3); expresslrs_tx_data_buf[3] = ((Channel_data[2] - 32) >> 3); expresslrs_tx_data_buf[4] = ((Channel_data[3] - 32) >> 3); expresslrs_tx_data_buf[5] = (((Channel_data[0] - 32) & 0b00000111) << 5) + (((Channel_data[1] - 32) & 0b111) << 2) + (((Channel_data[2] - 32) & 0b110) >> 1); expresslrs_tx_data_buf[6] = (((Channel_data[2] - 32) & 0b001) << 7) + (((Channel_data[3] - 32) & 0b111) << 4); // 4 bits left over for something else? #ifdef One_Bit_Switches expresslrs_tx_data_buf[6] += UINT11_to_BIT(Channel_data[4] - 32) << 3; expresslrs_tx_data_buf[6] += UINT11_to_BIT(Channel_data[5] - 32) << 2; expresslrs_tx_data_buf[6] += UINT11_to_BIT(Channel_data[6] - 32) << 1; expresslrs_tx_data_buf[6] += UINT11_to_BIT(Channel_data[7] - 32) << 0; #endif } void expresslrs_build_ota_pkt() { #ifdef MULTI_SYNC //TODO #endif if ((millis() > (SyncPacketLastSent + ExpressLRS_currAirRate_Modparams->SyncPktInterval)) && (expresslrs_fhss_get_curr_freq() == expresslrs_fhss_inital_freq()) && ((expresslrs_nonce_tx) % ExpressLRS_currAirRate_Modparams->FHSShopInterval == 1)) // sync just after we changed freqs (helps with hwTimer.init() being in sync from the get go) { ExpressLRS_GenerateSyncPacketData(); SyncPacketLastSent = millis(); } else { expresslrs_build_ota_hybrid_switches(expresslrs_tx_data_buf, DeviceAddr); // TODO impliment diff switch modes // #if defined HYBRID_SWITCHES_8 // expresslrs_build_ota_hybrid_switches(&Radio, &crsf, DeviceAddr); // #elif defined SEQ_SWITCHES // GenerateChannelDataSeqSwitch(&Radio, &crsf, DeviceAddr); // #else // expresslrs_build_ota_11bit_switches(); // #endif } ///// Next, Calculate the CRC and put it into the buffer ///// uint8_t crc = expresslrs_crc(expresslrs_tx_data_buf, 7) + CRCCaesarCipher; expresslrs_tx_data_buf[7] = crc; SX1276_WritePayloadToFifo(expresslrs_tx_data_buf, 8); SX1276_SetMode(true, false, SX1276_OPMODE_TX); } ///////////////////////////////////////////////////////////////////// RF Params ///////////////////////////////////////////////////////////////////// expresslrs_mod_settings_s ExpressLRS_AirRateConfig[RATE_MAX] = { {0, RATE_200HZ, SX1276_MODEM_CONFIG1_BW_500KHZ, 6, SX1276_MODEM_CONFIG1_CODING_RATE_4_7, 5000, 2, 8, 2000}, {1, RATE_100HZ, SX1276_MODEM_CONFIG1_BW_500KHZ, 7, SX1276_MODEM_CONFIG1_CODING_RATE_4_7, 10000, 2, 8, 2000}, {2, RATE_50HZ, SX1276_MODEM_CONFIG1_BW_500KHZ, 8, SX1276_MODEM_CONFIG1_CODING_RATE_4_7, 20000, 2, 8, 2000}}; void expresslrs_ota_set_rfrate(uint8_t rate) // Set speed of RF link (hz) { expresslrs_mod_settings_s *const ModParams = get_elrs_airRateConfig(rate); if (ModParams->sf == 6) { SX1276_SetDetectionThreshold(SX1276_MODEM_DETECTION_THRESHOLD_SF6); SX1276_SetDetectOptimize(true, SX1276_DETECT_OPTIMIZE_SF6); } else { SX1276_SetDetectionThreshold(SX1276_MODEM_DETECTION_THRESHOLD_SF7_TO_SF12); SX1276_SetDetectOptimize(true, SX1276_DETECT_OPTIMIZE_SF7_TO_SF12); } SX1276_ConfigModem1(ModParams->bw, ModParams->cr, true); SX1276_ConfigModem2(ModParams->sf, false, false); SX1276_ConfigModem3(false, true); SX1276_SetPreambleLength(ModParams->PreambleLen); ExpressLRS_currAirRate_Modparams = ModParams; } //////////////////////////////////////////// RNG ROUTINES //////////////////////////////////////////// #define RNG_MAX 0x7FFF unsigned long ExpressLRS_seed = 0; int32_t expresslrs_rng(void) { const long m = 2147483648; const long a = 214013; const long c = 2531011; ExpressLRS_seed = (a * ExpressLRS_seed + c) % m; return ExpressLRS_seed >> 16; } void expresslrs_rngSeed(long newSeed) { ExpressLRS_seed = newSeed; } unsigned int expresslrs_rngN(unsigned int max) { unsigned long x = expresslrs_rng(); unsigned int result = (x * max) / RNG_MAX; return result; } long expresslrs_rng8Bit(void) { return expresslrs_rng() & 0b11111111; // 0..255 returned } long expresslrs_rng5Bit(void) { return expresslrs_rng() & 0b11111; // 0..31 returned } long expresslrs_rng0to2(void) { int randomNumber = expresslrs_rng() & 0b11; // 0..2 returned while (randomNumber == 3) { randomNumber = expresslrs_rng() & 0b11; } return randomNumber; } //////////////////////////////////////////// FHSS ROUTINES //////////////////////////////////////////// uint32_t expresslrs_fhss_start_freq; uint32_t expresslrs_fhss_interval; uint32_t expresslrs_fhss_num_freqs; uint8_t expresslrs_fhss_ptr = 0; //value of current index in expresslrs_fhss_sequence array #define NR_SEQUENCE_ENTRIES 256 uint8_t expresslrs_fhss_sequence[NR_SEQUENCE_ENTRIES] = {0}; void expresslrs_fhss_init_freq(uint8_t regulatory_domain) { switch (regulatory_domain) { case 0: //915 AU expresslrs_fhss_start_freq = 915500000; expresslrs_fhss_interval = 600000; expresslrs_fhss_num_freqs = 20; debugln("915 AU"); break; case 1: // 915 FCC expresslrs_fhss_start_freq = 903500000; expresslrs_fhss_interval = 600000; expresslrs_fhss_num_freqs = 40; debugln("915 FCC"); break; case 2: //868 EU expresslrs_fhss_start_freq = 863275000; expresslrs_fhss_interval = 525000; expresslrs_fhss_num_freqs = 13; debugln("868 EU"); break; } expresslrs_fhss_generate_sequence(); // generate the pseudo random hop seq } uint32_t expresslrs_fhss_get_array_val(uint8_t index) { return ((index * expresslrs_fhss_interval) + expresslrs_fhss_start_freq); } uint8_t expresslrs_fhss_get_index() { return expresslrs_fhss_ptr; } uint32_t expresslrs_fhss_inital_freq() { return expresslrs_fhss_start_freq; } uint32_t expresslrs_fhss_get_curr_freq() { return expresslrs_fhss_get_array_val(expresslrs_fhss_sequence[expresslrs_fhss_ptr]); } uint32_t expresslrs_fhss_get_next_freq() { expresslrs_fhss_ptr++; return expresslrs_fhss_get_curr_freq(); } /** Requirements: 1. 0 every n hops 2. No two repeated channels 3. Equal occurance of each (or as even as possible) of each channel 4. Pesudorandom Approach: Initialise an array of flags indicating which channels have not yet been assigned and a counter of how many channels are available Iterate over the FHSSsequence array using index if index is a multiple of SYNC_INTERVAL assign the sync channel index (0) otherwise, generate a random number between 0 and the number of channels left to be assigned find the index of the nth remaining channel if the index is a repeat, generate a new random number if the index is not a repeat, assing it to the FHSSsequence array, clear the availability flag and decrement the available count if there are no available channels left, reset the flags array and the count */ // Set all of the flags in the array to true, except for the first one // which corresponds to the sync channel and is never available for normal expresslrs_fhss_calc_reset_available // allocation. void expresslrs_fhss_calc_reset_available(uint8_t *array) { // channel 0 is the sync channel and is never considered available array[0] = 0; // all other entires to 1 for (unsigned int i = 1; i < expresslrs_fhss_num_freqs; i++) array[i] = 1; } void expresslrs_fhss_generate_sequence() { debug("Number of FHSS frequencies ="); debugln(" %d", expresslrs_fhss_num_freqs); long macSeed = ((long)UID[2] << 24) + ((long)UID[3] << 16) + ((long)UID[4] << 8) + UID[5]; expresslrs_rngSeed(macSeed); uint8_t expresslrs_fhss_is_available[expresslrs_fhss_num_freqs]; expresslrs_fhss_calc_reset_available(expresslrs_fhss_is_available); // Fill the FHSSsequence with channel indices // The 0 index is special - the 'sync' channel. The sync channel appears every // syncInterval hops. The other channels are randomly distributed between the // sync channels int SYNC_INTERVAL = expresslrs_fhss_num_freqs - 1; int nLeft = expresslrs_fhss_num_freqs - 1; // how many channels are left to be allocated. Does not include the sync channel unsigned int prev = 0; // needed to prevent repeats of the same index // for each slot in the sequence table for (int i = 0; i < NR_SEQUENCE_ENTRIES; i++) { if (i % SYNC_INTERVAL == 0) { // assign sync channel 0 expresslrs_fhss_sequence[i] = 0; prev = 0; } else { // pick one of the available channels. May need to loop to avoid repeats unsigned int index; do { int c = expresslrs_rngN(nLeft); // returnc 0