First draft, works on bench

This commit is contained in:
AlessandroAU 2020-07-28 01:52:17 +10:00
parent de190f3349
commit d50b1854d0
8 changed files with 747 additions and 3 deletions

155
Multiprotocol/ExpressLRS.h Normal file
View File

@ -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 = x7+ x6+ x4+ x2+ x0 (0xD5) */ //from betaflight: https://github.com/betaflight/betaflight/blob/c8b5edb415c33916c91a7ccc8bd19c7276540cd1/src/test/unit/rx_crsf_unittest.cc#L65
#endif

View File

@ -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 0<c<nLeft
// find the c'th entry in the isAvailable array
// can skip 0 as that's the sync channel and is never available for normal allocation
index = 1;
int found = 0;
while (index < expresslrs_fhss_num_freqs)
{
if (expresslrs_fhss_is_available[index])
{
if (found == c)
break;
found++;
}
index++;
}
if (index == expresslrs_fhss_num_freqs)
{
// This should never happen
debugln("FAILED to find the available entry!\n");
// What to do? We don't want to hang as that will stop us getting to the wifi hotspot
// Use the sync channel
index = 0;
break;
}
} while (index == prev); // can't use index if it repeats the previous value
expresslrs_fhss_sequence[i] = index; // assign the value to the sequence array
expresslrs_fhss_is_available[index] = 0; // clear the flag
prev = index; // remember for next iteration
nLeft--; // reduce the count of available channels
if (nLeft == 0)
{
// we've assigned all of the channels, so reset for next cycle
expresslrs_fhss_calc_reset_available(expresslrs_fhss_is_available);
nLeft = expresslrs_fhss_num_freqs - 1;
}
}
debug(" %02d", expresslrs_fhss_sequence[i]);
if ((i + 1) % 10 == 0)
{
debugln(" ");
}
else
{
debug(" ");
}
} // for each element in FHSSsequence
debugln("");
debugln("List of Freqs:");
for (int i = 0; i < expresslrs_fhss_num_freqs; i++)
{
debugln("%d", expresslrs_fhss_get_array_val(i));
}
debugln("");
}
/* CRC8 implementation with polynom = x7+ x6+ x4+ x2+ x0 (0xD5) */ //from betaflight: https://github.com/betaflight/betaflight/blob/c8b5edb415c33916c91a7ccc8bd19c7276540cd1/src/test/unit/rx_crsf_unittest.cc#L65
const uint8_t expresslrs_crc8tab[256] = {
0x00, 0xD5, 0x7F, 0xAA, 0xFE, 0x2B, 0x81, 0x54, 0x29, 0xFC, 0x56, 0x83, 0xD7, 0x02, 0xA8, 0x7D,
0x52, 0x87, 0x2D, 0xF8, 0xAC, 0x79, 0xD3, 0x06, 0x7B, 0xAE, 0x04, 0xD1, 0x85, 0x50, 0xFA, 0x2F,
0xA4, 0x71, 0xDB, 0x0E, 0x5A, 0x8F, 0x25, 0xF0, 0x8D, 0x58, 0xF2, 0x27, 0x73, 0xA6, 0x0C, 0xD9,
0xF6, 0x23, 0x89, 0x5C, 0x08, 0xDD, 0x77, 0xA2, 0xDF, 0x0A, 0xA0, 0x75, 0x21, 0xF4, 0x5E, 0x8B,
0x9D, 0x48, 0xE2, 0x37, 0x63, 0xB6, 0x1C, 0xC9, 0xB4, 0x61, 0xCB, 0x1E, 0x4A, 0x9F, 0x35, 0xE0,
0xCF, 0x1A, 0xB0, 0x65, 0x31, 0xE4, 0x4E, 0x9B, 0xE6, 0x33, 0x99, 0x4C, 0x18, 0xCD, 0x67, 0xB2,
0x39, 0xEC, 0x46, 0x93, 0xC7, 0x12, 0xB8, 0x6D, 0x10, 0xC5, 0x6F, 0xBA, 0xEE, 0x3B, 0x91, 0x44,
0x6B, 0xBE, 0x14, 0xC1, 0x95, 0x40, 0xEA, 0x3F, 0x42, 0x97, 0x3D, 0xE8, 0xBC, 0x69, 0xC3, 0x16,
0xEF, 0x3A, 0x90, 0x45, 0x11, 0xC4, 0x6E, 0xBB, 0xC6, 0x13, 0xB9, 0x6C, 0x38, 0xED, 0x47, 0x92,
0xBD, 0x68, 0xC2, 0x17, 0x43, 0x96, 0x3C, 0xE9, 0x94, 0x41, 0xEB, 0x3E, 0x6A, 0xBF, 0x15, 0xC0,
0x4B, 0x9E, 0x34, 0xE1, 0xB5, 0x60, 0xCA, 0x1F, 0x62, 0xB7, 0x1D, 0xC8, 0x9C, 0x49, 0xE3, 0x36,
0x19, 0xCC, 0x66, 0xB3, 0xE7, 0x32, 0x98, 0x4D, 0x30, 0xE5, 0x4F, 0x9A, 0xCE, 0x1B, 0xB1, 0x64,
0x72, 0xA7, 0x0D, 0xD8, 0x8C, 0x59, 0xF3, 0x26, 0x5B, 0x8E, 0x24, 0xF1, 0xA5, 0x70, 0xDA, 0x0F,
0x20, 0xF5, 0x5F, 0x8A, 0xDE, 0x0B, 0xA1, 0x74, 0x09, 0xDC, 0x76, 0xA3, 0xF7, 0x22, 0x88, 0x5D,
0xD6, 0x03, 0xA9, 0x7C, 0x28, 0xFD, 0x57, 0x82, 0xFF, 0x2A, 0x80, 0x55, 0x01, 0xD4, 0x7E, 0xAB,
0x84, 0x51, 0xFB, 0x2E, 0x7A, 0xAF, 0x05, 0xD0, 0xAD, 0x78, 0xD2, 0x07, 0x53, 0x86, 0x2C, 0xF9};
uint8_t expresslrs_crc(uint8_t *data, int length)
{
uint8_t crc = 0;
for (uint8_t i = 0; i < length; i++)
{
crc = expresslrs_crc8tab[crc ^ *data++];
}
return crc;
}
#endif

View File

@ -84,6 +84,7 @@ const char STR_TIGER[] ="Tiger";
const char STR_XK[] ="XK";
const char STR_XN297DUMP[] ="XN297DP";
const char STR_FRSKYR9[] ="FrSkyR9";
const char STR_ELRS[] ="ELRS";
const char STR_PROPEL[] ="Propel";
const char STR_SKYARTEC[] ="Skyartc";
const char STR_KYOSHO[] ="Kyosho";
@ -142,6 +143,7 @@ const char STR_SUBTYPE_HOTT[] = "\x07""Sync\0 ""No_Sync";
const char STR_SUBTYPE_PELIKAN[] = "\x04""Pro\0""Lite";
const char STR_SUBTYPE_V761[] = "\x03""3CH""4CH";
const char STR_SUBTYPE_RLINK[] = "\x07""Surface";
const char STR_SUBTYPE_ELRS[] = "\x08""915AU200""915AU100""915AU50\0""915FC200""915FC100""915FC50\0""868EU200""868EU100""868EU50\0";
enum
{
@ -251,6 +253,9 @@ const mm_protocol_definition multi_protocols[] = {
#if defined(FRSKYR9_SX1276_INO)
{PROTO_FRSKY_R9, STR_FRSKYR9, 8, STR_SUBTYPE_FRSKYR9, OPTION_NONE },
#endif
#if defined(ELRS_SX1276_INO)
{PROTO_ELRS, STR_ELRS, 9, STR_SUBTYPE_ELRS, OPTION_RFCHAN },
#endif
#if defined(FX816_NRF24L01_INO)
{PROTO_FX816, STR_FX816, 1, STR_SUBTYPE_FX816, OPTION_NONE },
#endif

View File

@ -101,6 +101,7 @@ enum PROTOCOLS
PROTO_Q90C = 72, // =>NRF24L01 or CC2500
PROTO_KYOSHO = 73, // =>A7105
PROTO_RLINK = 74, // =>CC2500
PROTO_ELRS = 75, // =>SX1276
PROTO_FAKE = 126, // =>CC2500+NRF24L01
PROTO_TEST = 127, // =>CC2500

View File

@ -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);

View File

@ -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
void SX1276_ClearIRQFlags()
{
SX1276_WriteReg(SX1276_12_REGIRQFLAGS, 0b11111111);
}
#endif

View File

@ -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 ***/

View File

@ -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