From d50b1854d066a5033e6666addb328c41e86242cf Mon Sep 17 00:00:00 2001 From: AlessandroAU Date: Tue, 28 Jul 2020 01:52:17 +1000 Subject: [PATCH] First draft, works on bench --- Multiprotocol/ExpressLRS.h | 155 +++++++++ Multiprotocol/ExpressLRS.ino | 540 ++++++++++++++++++++++++++++++++ Multiprotocol/Multi_Names.ino | 5 + Multiprotocol/Multiprotocol.h | 1 + Multiprotocol/Multiprotocol.ino | 6 + Multiprotocol/SX1276_SPI.ino | 37 ++- Multiprotocol/_Config.h | 5 +- Multiprotocol/iface_sx1276.h | 1 + 8 files changed, 747 insertions(+), 3 deletions(-) create mode 100644 Multiprotocol/ExpressLRS.h create mode 100644 Multiprotocol/ExpressLRS.ino diff --git a/Multiprotocol/ExpressLRS.h b/Multiprotocol/ExpressLRS.h new file mode 100644 index 0000000..f5c37d4 --- /dev/null +++ b/Multiprotocol/ExpressLRS.h @@ -0,0 +1,155 @@ + +#ifndef _ExpressLRS_H_ +#define _ExpressLRS_H_ + +#include "iface_SX1276.h" + +#define One_Bit_Switches + +extern uint8_t UID[6]; +extern uint8_t CRCCaesarCipher; +extern uint8_t DeviceAddr; + +extern uint8_t ExpressLRS_TXdataBuffer[8]; +extern uint8_t ExpressLRS_NonceTX; + +#define ELRS_RC_DATA_PACKET 0b00 +#define ELRS_MSP_DATA_PACKET 0b01 +#define ELRS_TLM_PACKET 0b11 +#define ELRS_SYNC_PACKET 0b10 + +#define ELRS_RC_DATA_PACKET 0b00 +#define ELRS_MSP_DATA_PACKET 0b01 +#define ELRS_TLM_PACKET 0b11 +#define ELRS_SYNC_PACKET 0b10 + +typedef enum +{ + RATE_200HZ = 2, + RATE_100HZ = 4, + RATE_50HZ = 5, +} expresslrs_RFrates_e; // Max value of 16 since only 4 bits have been assigned in the sync package. + +#define RATE_MAX 3 + + +typedef struct expresslrs_mod_settings_s +{ + uint8_t index; + expresslrs_RFrates_e enum_rate; // Max value of 16 since only 4 bits have been assigned in the sync package. + uint8_t bw; + uint8_t sf; + uint8_t cr; + uint32_t interval; //interval in us seconds that corresponds to that frequnecy + uint8_t FHSShopInterval; // every X packets we hope to a new frequnecy. Max value of 16 since only 4 bits have been assigned in the sync package. + uint8_t PreambleLen; + uint32_t SyncPktInterval; + +} expresslrs_mod_settings_t; + + +extern expresslrs_mod_settings_s ExpressLRS_AirRateConfig[RATE_MAX]; + +expresslrs_mod_settings_s *get_elrs_airRateConfig(int8_t index) +{ + if (index < 0) // Protect against out of bounds rate + { + return &ExpressLRS_AirRateConfig[0]; // Set to first entry in the array (200HZ) + } + else if (index > (RATE_MAX - 1)) + { + return &ExpressLRS_AirRateConfig[RATE_MAX - 1]; // Set to last usable entry in the array (currently 50HZ) + } + return &ExpressLRS_AirRateConfig[index]; +} + +extern expresslrs_mod_settings_s *ExpressLRS_currAirRate_Modparams; + +void ExpressLRS_SetRFLinkRate(uint8_t rate); + +//////////////////////////////////////////// RNG ROUTINES /////////////////////////////////////////// + +#define RNG_MAX 0x7FFF +extern unsigned long ExpressLRS_seed; + +int32_t expresslrs_rng(void); // returns values between 0 and 0x7FFF, NB rngN depends on this output range, so if we change the behaviour rngN will need updating +void expresslrs_rngSeed(long newSeed); // returns 0 <= x < max where max <= 256, (actual upper limit is higher, but there is one and I haven't thought carefully about what it is) +unsigned int expresslrs_rngN(unsigned int max); + +long expresslrs_rng8Bit(void); +long expresslrs_rng5Bit(void); +long expresslrs_rng0to2(void); + +//////////////////////////////////////////// FHSS ROUTINES /////////////////////////////////////////// + +extern uint32_t expresslrs_fhss_start_freq; +extern uint32_t expresslrs_fhss_interval; +extern uint32_t expresslrs_fhss_num_freqs; + +extern uint8_t expresslrs_fhss_ptr; //value of current index in expresslrs_fhss_sequence array + +#define NR_SEQUENCE_ENTRIES 256 // length of peseudo random hop seq +extern uint8_t expresslrs_fhss_sequence[NR_SEQUENCE_ENTRIES]; + +void expresslrs_fhss_init_freq(uint8_t regulatory_domain); + +/* 868 EU Frequency bands taken from https://wetten.overheid.nl/BWBR0036378/2016-12-28#Bijlagen + * Note: these frequencies fall in the license free H-band, but in combination with 500kHz + * LoRa modem bandwidth used by ExpressLRS (EU allows up to 125kHz modulation BW only) they + * will never pass RED certification and they are ILLEGAL to use. USE at your own risk + * + * Therefore we simply maximize the usage of available spectrum so laboratory testing of the software won't disturb existing + * 868MHz ISM band traffic too much. + + 863275000, // band H1, 863 - 865MHz, 0.1% duty cycle or CSMA techniques, 25mW EIRP + 863800000, + 864325000, + 864850000, + 865375000, // Band H2, 865 - 868.6MHz, 1.0% dutycycle or CSMA, 25mW EIRP + 865900000, + 866425000, + 866950000, + 867475000, + 868000000, + 868525000, // Band H3, 868.7-869.2MHz, 0.1% dutycycle or CSMA, 25mW EIRP + 869050000, + 869575000}; +*/ + +uint32_t expresslrs_fhss_get_array_val(uint8_t index); +uint8_t expresslrs_fhss_get_index(); // get the current index of the FHSS pointer +uint32_t expresslrs_fhss_inital_freq(); // get inital freq (index 0) +uint32_t expresslrs_fhss_get_curr_freq(); // return curr freq +uint32_t expresslrs_fhss_get_next_freq(); // incriment pointer and return new freq + +/** +Requirements for freq sequence generator: +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); +void expresslrs_fhss_generate_sequence(); +uint8_t expresslrs_crc(uint8_t *data, int length); + +//////////////////////////////////////////// CRC ROUTINES /////////////////////////////////////////// + +extern const uint8_t expresslrs_crc8tab[256]; /* CRC8 implementation with polynom = x​7​+ x​6​+ x​4​+ x​2​+ x​0 ​(0xD5) */ //from betaflight: https://github.com/betaflight/betaflight/blob/c8b5edb415c33916c91a7ccc8bd19c7276540cd1/src/test/unit/rx_crsf_unittest.cc#L65 + +#endif diff --git a/Multiprotocol/ExpressLRS.ino b/Multiprotocol/ExpressLRS.ino new file mode 100644 index 0000000..4c696cd --- /dev/null +++ b/Multiprotocol/ExpressLRS.ino @@ -0,0 +1,540 @@ +#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 0NRF24L01 or CC2500 PROTO_KYOSHO = 73, // =>A7105 PROTO_RLINK = 74, // =>CC2500 + PROTO_ELRS = 75, // =>SX1276 PROTO_FAKE = 126, // =>CC2500+NRF24L01 PROTO_TEST = 127, // =>CC2500 diff --git a/Multiprotocol/Multiprotocol.ino b/Multiprotocol/Multiprotocol.ino index 9fdc022..9107aaf 100644 --- a/Multiprotocol/Multiprotocol.ino +++ b/Multiprotocol/Multiprotocol.ino @@ -1613,6 +1613,12 @@ static void protocol_init() remote_callback = FrSkyR9_callback; break; #endif + #if defined(ELRS_SX1276_INO) + case PROTO_ELRS: + next_callback = initExpressLRS(); + remote_callback = ExpressLRS_callback; + break; + #endif #endif } debugln("Protocol selected: %d, sub proto %d, rxnum %d, option %d", protocol, sub_protocol, RX_num, option); diff --git a/Multiprotocol/SX1276_SPI.ino b/Multiprotocol/SX1276_SPI.ino index b0fbf2b..646bd7c 100644 --- a/Multiprotocol/SX1276_SPI.ino +++ b/Multiprotocol/SX1276_SPI.ino @@ -48,8 +48,9 @@ uint8_t SX1276_Reset() //TODO when pin is not wired #ifdef SX1276_RST_pin SX1276_RST_off; - delayMicroseconds(200); + delayMicroseconds(500); SX1276_RST_on; + delayMicroseconds(500); #endif return 0; } @@ -97,6 +98,21 @@ void SX1276_SetTxRxMode(uint8_t mode) #endif } +void SX1276_SetFrequency_expresslrs(uint32_t freq) // freqs given by bottom function are about 500khz high +{ + #define FREQ_STEP 61.03515625 + + int32_t FRQ = ((uint32_t)((double)freq / (double)FREQ_STEP)); + + uint8_t FRQ_MSB = (uint8_t)((FRQ >> 16) & 0xFF); + uint8_t FRQ_MID = (uint8_t)((FRQ >> 8) & 0xFF); + uint8_t FRQ_LSB = (uint8_t)(FRQ & 0xFF); + + uint8_t outbuff[3] = {FRQ_MSB, FRQ_MID, FRQ_LSB}; + + SX1276_WriteRegisterMulti(SX1276_06_FRFMSB, outbuff, sizeof(outbuff)); +} + void SX1276_SetFrequency(uint32_t frequency) { uint32_t f = frequency / 61; @@ -190,6 +206,18 @@ void SX1276_SetPreambleLength(uint16_t length) SX1276_WriteRegisterMulti(SX1276_20_PREAMBLEMSB, data, 2); } +void SX1276_SetSyncWord(uint8_t syncWord) +{ + + if (syncWord == 0x34) //sync word reserved for LoRaWAN networks + { + syncWord++; + debugln("Reserved Syncword detected in UID config, Using 0x35 instead"); + } + + SX1276_WriteReg(SX1276_39_REGSYNCWORD, syncWord); +} + void SX1276_SetDetectionThreshold(uint8_t threshold) { SX1276_WriteReg(SX1276_37_DETECTIONTHRESHOLD, threshold); @@ -243,4 +271,9 @@ void SX1276_WritePayloadToFifo(uint8_t* payload, uint8_t length) SX1276_WriteRegisterMulti(SX1276_00_FIFO, payload, length); } -#endif \ No newline at end of file +void SX1276_ClearIRQFlags() +{ + SX1276_WriteReg(SX1276_12_REGIRQFLAGS, 0b11111111); +} + +#endif diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index a118190..045ac7c 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -242,7 +242,10 @@ #define ZSX_NRF24L01_INO //The protocols below need a SX1276 to be installed -#define FRSKYR9_SX1276_INO +#define FRSKYR9_SX1276_INO +#define ELRS_SX1276_INO + + /***************************/ /*** PROTOCOLS SETTINGS ***/ diff --git a/Multiprotocol/iface_sx1276.h b/Multiprotocol/iface_sx1276.h index 56c5280..0799d9e 100644 --- a/Multiprotocol/iface_sx1276.h +++ b/Multiprotocol/iface_sx1276.h @@ -39,6 +39,7 @@ enum SX1276_26_MODEMCONFIG3 = 0x26, SX1276_31_DETECTOPTIMIZE = 0x31, SX1276_37_DETECTIONTHRESHOLD = 0x37, + SX1276_39_REGSYNCWORD = 0x39, SX1276_40_DIOMAPPING1 = 0x40, SX1276_42_VERSION = 0x42, SX1276_4D_PADAC = 0x4D