// Copyright (c) Sandeep Mistry. All rights reserved. // Licensed under the MIT license. // Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva // Obviously still under the MIT license. #ifndef RADIO_H #define RADIO_H #include #include #include "Interfaces.h" #include "Boards.h" #include "src/misc/FIFOBuffer.h" #define MAX_PKT_LENGTH 255 // TX #define PA_OUTPUT_RFO_PIN 0 #define PA_OUTPUT_PA_BOOST_PIN 1 // Default LoRa settings #define PHY_HEADER_LORA_SYMBOLS 20 #define PHY_CRC_LORA_BITS 16 #define LORA_PREAMBLE_SYMBOLS_MIN 18 #define LORA_PREAMBLE_TARGET_MS 24 #define LORA_PREAMBLE_FAST_DELTA 18 #define LORA_FAST_THRESHOLD_BPS 30E3 #define LORA_LIMIT_THRESHOLD_BPS 60E3 // DCD #define STATUS_INTERVAL_MS 3 #define DCD_SAMPLES 2500 #define UTIL_UPDATE_INTERVAL_MS 1000 #define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS) #define AIRTIME_LONGTERM 3600 #define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000) #define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES) #define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS) #define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS // CSMA Parameters #define CSMA_SIFS_MS 0 #define CSMA_POST_TX_YIELD_SLOTS 3 #define CSMA_SLOT_MAX_MS 100 #define CSMA_SLOT_MIN_MS 24 #define CSMA_SLOT_MIN_FAST_DELTA 18 #define CSMA_SLOT_SYMBOLS 12 #define CSMA_CW_BANDS 4 #define CSMA_CW_MIN 0 #define CSMA_CW_PER_BAND_WINDOWS 15 #define CSMA_BAND_1_MAX_AIRTIME 7 #define CSMA_BAND_N_MIN_AIRTIME 85 #define CSMA_INFR_THRESHOLD_DB 12 #define LED_ID_TRIG 16 #define NOISE_FLOOR_SAMPLES 64 #define RSSI_OFFSET 157 #define PHY_HEADER_LORA_SYMBOLS 8 #define MODEM_TIMEOUT_MULT 1.1 // Status flags const uint8_t SIG_DETECT = 0x01; const uint8_t SIG_SYNCED = 0x02; const uint8_t RX_ONGOING = 0x04; // forward declare Utilities.h LED functions void led_rx_on(); void led_rx_off(); void led_id_on(); void led_id_off(); void led_indicate_airtime_lock(); void kiss_indicate_channel_stats(uint8_t index); void kiss_indicate_csma_stats(uint8_t index); #if PLATFORM == PLATFORM_ESP32 // get update_lock for ESP32 extern portMUX_TYPE update_lock; #endif class RadioInterface : public Stream { public: // todo: in the future define _spiModem and _spiSettings from here for inheritence by child classes RadioInterface(uint8_t index) : _index(index), _sf(0x07), _radio_locked(false), _radio_online(false), _st_airtime_limit(0.0), _lt_airtime_limit(0.0), _airtime_lock(false), _airtime(0.0), _longterm_airtime(0.0), _last_packet_cost(0.0), _local_channel_util(0.0), _total_channel_util(0.0), _longterm_channel_util(0.0), _last_status_update(0), _stat_signal_detected(false), _stat_signal_synced(false),_stat_rx_ongoing(false), _last_dcd(0), _dcd_count(0), _dcd(false), _dcd_led(false), _dcd_waiting(false), _dcd_sample(0), _csma_slot_ms(CSMA_SLOT_MIN_MS), _preambleLength(LORA_PREAMBLE_SYMBOLS_MIN), _lora_symbol_time_ms(0.0), _lora_preamble_time_ms(0), _lora_header_time_ms(0), _lora_symbol_rate(0.0), _lora_us_per_byte(0.0), _bitrate(0), _packet{0}, _onReceive(NULL), _txp(0), _ldro(false), _limit_rate(false), _interference_detected(false), _avoid_interference(true), _difs_ms(CSMA_SIFS_MS + 2 * _csma_slot_ms), _difs_wait_start(0), _cw_wait_start(0), _cw_wait_target(0), _cw_wait_passed(0), _csma_cw(-1), _cw_band(1), _cw_min(0), _cw_max(CSMA_CW_PER_BAND_WINDOWS), _noise_floor_sampled(false), _noise_floor_sample(0), _noise_floor_buffer({0}), _noise_floor(-292), _led_id_filter(0), _preamble_detected_at(0) {}; virtual void reset() = 0; virtual int begin() = 0; virtual void end() = 0; virtual int beginPacket(int implicitHeader = false) = 0; virtual int endPacket() = 0; virtual int packetRssi(uint8_t pkt_snr_raw = 0xFF) = 0; virtual int currentRssi() = 0; virtual uint8_t packetRssiRaw() = 0; virtual uint8_t currentRssiRaw() = 0; virtual uint8_t packetSnrRaw() = 0; virtual float packetSnr() = 0; virtual long packetFrequencyError() = 0; // from Print virtual size_t write(uint8_t byte) = 0; virtual size_t write(const uint8_t *buffer, size_t size) = 0; // from Stream virtual int available() = 0; virtual int read() = 0; virtual int peek() = 0; virtual void flush() = 0; virtual void onReceive(void(*callback)(uint8_t, int)) = 0; virtual void receive(int size = 0) = 0; virtual void standby() = 0; virtual void sleep() = 0; virtual bool preInit() = 0; virtual int8_t getTxPower() = 0; virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN) = 0; virtual uint32_t getFrequency() = 0; virtual void setFrequency(uint32_t frequency) = 0; virtual void setSpreadingFactor(int sf) = 0; virtual uint8_t getSpreadingFactor() = 0; virtual uint32_t getSignalBandwidth() = 0; virtual void setSignalBandwidth(uint32_t sbw) = 0; virtual void setCodingRate4(int denominator) = 0; virtual uint8_t getCodingRate4() = 0; virtual void setPreambleLength(long length) = 0; virtual bool dcd() = 0; virtual void enableCrc() = 0; virtual void disableCrc() = 0; virtual void enableTCXO() = 0; virtual void disableTCXO() = 0; virtual uint8_t random() = 0; virtual void setSPIFrequency(uint32_t frequency) = 0; void updateBitrate() { if (!_radio_online) { _bitrate = 0; } else { _lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf)); _lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0; _bitrate = (uint32_t)(_sf * ( (4.0/(float)getCodingRate4()) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0); _lora_us_per_byte = 1000000.0/((float)_bitrate/8.0); bool fast_rate = _bitrate > LORA_FAST_THRESHOLD_BPS; _limit_rate = _bitrate > LORA_LIMIT_THRESHOLD_BPS; int csma_slot_min_ms = CSMA_SLOT_MIN_MS; float lora_preamble_target_ms = LORA_PREAMBLE_TARGET_MS; if (fast_rate) { csma_slot_min_ms -= CSMA_SLOT_MIN_FAST_DELTA; lora_preamble_target_ms -= LORA_PREAMBLE_FAST_DELTA; } _csma_slot_ms = _lora_symbol_time_ms*CSMA_SLOT_SYMBOLS; if (_csma_slot_ms > CSMA_SLOT_MAX_MS) { _csma_slot_ms = CSMA_SLOT_MAX_MS; } if (_csma_slot_ms < CSMA_SLOT_MIN_MS) { _csma_slot_ms = csma_slot_min_ms; } _difs_ms = CSMA_SIFS_MS + 2*_csma_slot_ms; float target_preamble_symbols = lora_preamble_target_ms/_lora_symbol_time_ms; if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) { target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN; } else { target_preamble_symbols = (ceil)(target_preamble_symbols); } setPreambleLength(target_preamble_symbols); _lora_preamble_time_ms = (ceil)(_preambleLength * _lora_symbol_time_ms); _lora_header_time_ms = (ceil)(PHY_HEADER_LORA_SYMBOLS * _lora_symbol_time_ms); } } virtual void handleDio0Rise() = 0; virtual bool getPacketValidity() = 0; uint32_t getBitrate() { return _bitrate; }; uint8_t getIndex() { return _index; }; void setRadioLock(bool lock) { _radio_locked = lock; }; bool getRadioLock() { return _radio_locked; }; void setRadioOnline(bool online) { _radio_online = online; }; bool getRadioOnline() { return _radio_online; }; void setSTALock(float at) { _st_airtime_limit = at; }; float getSTALock() { return _st_airtime_limit; }; void setLTALock(float at) { _lt_airtime_limit = at; }; float getLTALock() { return _lt_airtime_limit; }; bool calculateALock() { _airtime_lock = false; if (_st_airtime_limit != 0.0 && _airtime >= _st_airtime_limit) { _airtime_lock = true; } if (_lt_airtime_limit != 0.0 && _longterm_airtime >= _lt_airtime_limit) { _airtime_lock = true; } return _airtime_lock; }; void updateAirtime() { uint16_t cb = current_airtime_bin(); uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; } uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } _airtime_bins[nb] = 0; _airtime = (float)(_airtime_bins[cb]+_airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS); uint32_t longterm_airtime_sum = 0; for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { longterm_airtime_sum += _airtime_bins[bin]; } _longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS; float longterm_channel_util_sum = 0.0; for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { longterm_channel_util_sum += _longterm_bins[bin]; } _longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS; updateCSMAParameters(); kiss_indicate_channel_stats(_index); }; float getAirtime(uint16_t written) { float lora_symbols = 0; float packet_cost_ms = 0.0; if (interfaces[_index] == SX1276 || interfaces[_index] == SX1278) { lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*_sf + 8 + PHY_HEADER_LORA_SYMBOLS); lora_symbols /= 4*(_sf-2*_ldro); lora_symbols *= getCodingRate4(); lora_symbols += _preambleLength + 0.25 + 8; packet_cost_ms += lora_symbols * _lora_symbol_time_ms; } else if (interfaces[_index] == SX1262 || interfaces[_index] == SX1280) { if (_sf < 7) { lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*_sf + PHY_HEADER_LORA_SYMBOLS); lora_symbols /= 4*_sf; lora_symbols *= getCodingRate4(); lora_symbols += _preambleLength + 2.25 + 8; packet_cost_ms += lora_symbols * _lora_symbol_time_ms; } else { lora_symbols += (8*written + PHY_CRC_LORA_BITS - 4*_sf + 8 + PHY_HEADER_LORA_SYMBOLS); lora_symbols /= 4*(_sf-2*_ldro); lora_symbols *= getCodingRate4(); lora_symbols += _preambleLength + 0.25 + 8; packet_cost_ms += lora_symbols * _lora_symbol_time_ms; } } _last_packet_cost = packet_cost_ms; return packet_cost_ms; } void addAirtime() { uint16_t cb = current_airtime_bin(); uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } _airtime_bins[cb] += _last_packet_cost; _airtime_bins[nb] = 0; }; void updateModemStatus() { #if MCU_VARIANT == MCU_ESP32 portENTER_CRITICAL(&update_lock); #elif MCU_VARIANT == MCU_NRF52 portENTER_CRITICAL(); #endif bool carrier_detected = dcd(); int current_rssi = currentRssi(); _last_status_update = millis(); #if MCU_VARIANT == MCU_ESP32 portEXIT_CRITICAL(&update_lock); #elif MCU_VARIANT == MCU_NRF52 portEXIT_CRITICAL(); #endif _interference_detected = !carrier_detected && (current_rssi > (_noise_floor+CSMA_INFR_THRESHOLD_DB)); if (_interference_detected) { if (_led_id_filter < LED_ID_TRIG) { _led_id_filter += 1; } } else { if (_led_id_filter > 0) {_led_id_filter -= 1; } } if (carrier_detected) { _dcd = true; } else { _dcd = false; } _dcd_led = _dcd; if (_dcd_led) { led_rx_on(); } else { if (_interference_detected) { if (_led_id_filter >= LED_ID_TRIG && _noise_floor_sampled) { led_id_on(); } } else { if (_airtime_lock) { led_indicate_airtime_lock(); } else { led_rx_off(); led_id_off(); } } } } void updateNoiseFloor() { int current_rssi = currentRssi(); if (!_dcd) { if (!_noise_floor_sampled || current_rssi < _noise_floor + CSMA_INFR_THRESHOLD_DB) { _noise_floor_buffer[_noise_floor_sample] = current_rssi; _noise_floor_sample = _noise_floor_sample+1; if (_noise_floor_sample >= NOISE_FLOOR_SAMPLES) { _noise_floor_sample %= NOISE_FLOOR_SAMPLES; _noise_floor_sampled = true; } if (_noise_floor_sampled) { _noise_floor = 0; for (int ni = 0; ni < NOISE_FLOOR_SAMPLES; ni++) { _noise_floor += _noise_floor_buffer[ni]; } _noise_floor /= NOISE_FLOOR_SAMPLES; } } } } void checkModemStatus() { if (millis()-_last_status_update >= STATUS_INTERVAL_MS) { updateModemStatus(); updateNoiseFloor(); _util_samples[_dcd_sample] = _dcd; _dcd_sample = (_dcd_sample+1)%DCD_SAMPLES; if (_dcd_sample % UTIL_UPDATE_INTERVAL == 0) { int util_count = 0; for (int ui = 0; ui < DCD_SAMPLES; ui++) { if (_util_samples[ui]) util_count++; } _local_channel_util = (float)util_count / (float)DCD_SAMPLES; _total_channel_util = _local_channel_util + _airtime; if (_total_channel_util > 1.0) _total_channel_util = 1.0; int16_t cb = current_airtime_bin(); uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } if (_total_channel_util > _longterm_bins[cb]) _longterm_bins[cb] = _total_channel_util; _longterm_bins[nb] = 0.0; updateAirtime(); } } }; void updateCSMAParameters() { int airtime_pct = (int)(_airtime*100); int new_cw_band = _cw_band; if (airtime_pct <= CSMA_BAND_1_MAX_AIRTIME) { new_cw_band = 1; } else { int at = airtime_pct + CSMA_BAND_1_MAX_AIRTIME; new_cw_band = map(at, CSMA_BAND_1_MAX_AIRTIME, CSMA_BAND_N_MIN_AIRTIME, 2, CSMA_CW_BANDS); } if (new_cw_band > CSMA_CW_BANDS) { new_cw_band = CSMA_CW_BANDS; } if (new_cw_band != _cw_band) { _cw_band = (uint8_t)(new_cw_band); _cw_min = (_cw_band-1) * CSMA_CW_PER_BAND_WINDOWS; _cw_max = (_cw_band) * CSMA_CW_PER_BAND_WINDOWS - 1; kiss_indicate_csma_stats(_index); } } void setDCD(bool dcd) { _dcd = dcd; }; bool getDCD() { return _dcd; }; void setDCDWaiting(bool dcd_waiting) { _dcd_waiting = dcd_waiting; }; bool getDCDWaiting() { return _dcd_waiting; }; float getAirtime() { return _airtime; }; float getLongtermAirtime() { return _longterm_airtime; }; float getTotalChannelUtil() { return _total_channel_util; }; float getLongtermChannelUtil() { return _longterm_channel_util; }; void setCSMASlotMS(int slot_size) { _csma_slot_ms = slot_size; }; int getCSMASlotMS() { return _csma_slot_ms; }; float getSymbolTime() { return _lora_symbol_time_ms; }; float getSymbolRate() { return _lora_symbol_rate; }; long getPreambleLength() { return _preambleLength; }; void setAvdInterference(bool cfg) { _avoid_interference = cfg; }; bool getAvdInterference() { return _avoid_interference; }; bool getInterference() { return _interference_detected; }; int getNoiseFloor() { return _noise_floor; }; unsigned long getDifsMS() { return _difs_ms; }; uint8_t getCWBand() { return _cw_band; }; uint8_t getCWMin() { return _cw_min; }; uint8_t getCWMax() { return _cw_max; }; uint8_t getCW() { return _csma_cw; }; void setCW(uint8_t cw) { _csma_cw = cw; }; void setCWWaitTarget(unsigned long target) { _cw_wait_target = target; }; unsigned long getCWWaitTarget() { return _cw_wait_target; }; unsigned long getDifsWaitStart() { return _difs_wait_start; }; void setDifsWaitStart(unsigned long start) { _difs_wait_start = start; }; unsigned long getCWWaitStart() { return _cw_wait_start; }; void setCWWaitStart(unsigned long start) { _cw_wait_start = start; }; void addCWWaitPassed(unsigned long start) { _cw_wait_passed += start; }; void resetCWWaitPassed() { _cw_wait_passed = 0; }; bool getCWWaitStatus() { return _cw_wait_passed < _cw_wait_target; }; bool getLimitRate() { return _limit_rate; }; protected: virtual void explicitHeaderMode() = 0; virtual void implicitHeaderMode() = 0; uint8_t _index; uint32_t _bitrate; int8_t _txp; uint8_t _sf; bool _radio_locked; bool _radio_online; float _st_airtime_limit; float _lt_airtime_limit; bool _airtime_lock; uint16_t _airtime_bins[AIRTIME_BINS] = {0}; uint16_t _longterm_bins[AIRTIME_BINS] = {0}; float _airtime; float _longterm_airtime; float _last_packet_cost; float _local_channel_util; float _total_channel_util; float _longterm_channel_util; uint32_t _last_status_update; bool _stat_signal_detected; bool _stat_signal_synced; bool _stat_rx_ongoing; uint32_t _last_dcd; uint16_t _dcd_count; bool _dcd; bool _dcd_led; bool _dcd_waiting; bool _util_samples[DCD_SAMPLES] = {false}; int _dcd_sample; long _preambleLength; float _lora_symbol_time_ms; float _lora_symbol_rate; float _lora_us_per_byte; long _lora_preamble_time_ms; long _lora_header_time_ms; bool _ldro; bool _limit_rate; bool _interference_detected; bool _avoid_interference; int _csma_slot_ms; unsigned long _difs_ms; unsigned long _difs_wait_start; unsigned long _cw_wait_start; unsigned long _cw_wait_target; unsigned long _cw_wait_passed; int _csma_cw; uint8_t _cw_band; uint8_t _cw_min; uint8_t _cw_max; bool _noise_floor_sampled; int _noise_floor_sample; int _noise_floor_buffer[NOISE_FLOOR_SAMPLES]; int _noise_floor; uint8_t _led_id_filter; unsigned long _preamble_detected_at; uint8_t _packet[255]; void (*_onReceive)(uint8_t, int); }; class sx126x : public RadioInterface { public: sx126x(uint8_t index, SPIClass* spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen); void reset(); int begin(); void end(); int beginPacket(int implicitHeader = false); int endPacket(); int packetRssi(uint8_t pkt_snr_raw = 0xFF); int currentRssi(); uint8_t packetRssiRaw(); uint8_t currentRssiRaw(); uint8_t packetSnrRaw(); float packetSnr(); long packetFrequencyError(); // from Print size_t write(uint8_t byte); size_t write(const uint8_t *buffer, size_t size); // from Stream int available(); int read(); int peek(); void flush(); void onReceive(void(*callback)(uint8_t, int)); void receive(int size = 0); void standby(); void sleep(); bool preInit(); int8_t getTxPower(); void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); uint32_t getFrequency(); void setFrequency(uint32_t frequency); void setSpreadingFactor(int sf); uint8_t getSpreadingFactor(); uint32_t getSignalBandwidth(); void setSignalBandwidth(uint32_t sbw); void setCodingRate4(int denominator); uint8_t getCodingRate4(); void setPreambleLength(long length); bool dcd(); void enableCrc(); void disableCrc(); void enableTCXO(); void disableTCXO(); byte random(); void setSPIFrequency(uint32_t frequency); void dumpRegisters(Stream& out); void handleDio0Rise(); private: void writeBuffer(const uint8_t* buffer, size_t size); void readBuffer(uint8_t* buffer, size_t size); void loraMode(); void rxAntEnable(); void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); void setSyncWord(uint16_t sw); void waitOnBusy(); void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); void explicitHeaderMode(); void implicitHeaderMode(); uint8_t readRegister(uint16_t address); void writeRegister(uint16_t address, uint8_t value); uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); static void onDio0Rise(); void handleLowDataRate(); void optimizeModemSensitivity(); void calibrate(void); void calibrate_image(uint32_t frequency); bool getPacketValidity(); private: SPISettings _spiSettings; SPIClass* _spiModem; int _ss; int _sclk; int _mosi; int _miso; int _reset; int _dio0; int _rxen; int _busy; uint32_t _frequency; uint8_t _bw; uint8_t _cr; int _packetIndex; int _implicitHeaderMode; int _payloadLength; int _crcMode; int _fifo_tx_addr_ptr; int _fifo_rx_addr_ptr; bool _preinit_done; bool _tcxo; bool _dio2_as_rf_switch; }; class sx127x : public RadioInterface { public: sx127x(uint8_t index, SPIClass* spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy); void reset(); int begin(); void end(); int beginPacket(int implicitHeader = false); int endPacket(); int packetRssi(uint8_t pkt_snr_raw = 0xFF); int packetRssi(); int currentRssi(); uint8_t packetRssiRaw(); uint8_t currentRssiRaw(); uint8_t packetSnrRaw(); float packetSnr(); long packetFrequencyError(); // from Print size_t write(uint8_t byte); size_t write(const uint8_t *buffer, size_t size); // from Stream int available(); int read(); int peek(); void flush(); void onReceive(void(*callback)(uint8_t, int)); void receive(int size = 0); void standby(); void sleep(); bool preInit(); int8_t getTxPower(); void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); uint32_t getFrequency(); void setFrequency(uint32_t frequency); void setSpreadingFactor(int sf); uint8_t getSpreadingFactor(); uint32_t getSignalBandwidth(); void setSignalBandwidth(uint32_t sbw); void setCodingRate4(int denominator); uint8_t getCodingRate4(); void setPreambleLength(long length); bool dcd(); void enableCrc(); void disableCrc(); void enableTCXO(); void disableTCXO(); byte random(); void setSPIFrequency(uint32_t frequency); void handleDio0Rise(); bool getPacketValidity(); private: void setSyncWord(uint8_t sw); void explicitHeaderMode(); void implicitHeaderMode(); uint8_t readRegister(uint8_t address); void writeRegister(uint8_t address, uint8_t value); uint8_t singleTransfer(uint8_t address, uint8_t value); static void onDio0Rise(); void handleLowDataRate(); void optimizeModemSensitivity(); private: SPISettings _spiSettings; SPIClass* _spiModem; int _ss; int _sclk; int _mosi; int _miso; int _reset; int _dio0; int _busy; uint32_t _frequency; int _packetIndex; int _implicitHeaderMode; bool _preinit_done; uint8_t _cr; uint32_t _bw; }; class sx128x : public RadioInterface { public: sx128x(uint8_t index, SPIClass* spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen); void reset(); int begin(); void end(); int beginPacket(int implicitHeader = false); int endPacket(); int packetRssi(uint8_t pkt_snr_raw = 0xFF); int currentRssi(); uint8_t packetRssiRaw(); uint8_t currentRssiRaw(); uint8_t packetSnrRaw(); float packetSnr(); long packetFrequencyError(); // from Print size_t write(uint8_t byte); size_t write(const uint8_t *buffer, size_t size); // from Stream int available(); int read(); int peek(); void flush(); void onReceive(void(*callback)(uint8_t, int)); void receive(int size = 0); void standby(); void sleep(); bool preInit(); int8_t getTxPower(); void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); uint32_t getFrequency(); void setFrequency(uint32_t frequency); void setSpreadingFactor(int sf); uint8_t getSpreadingFactor(); uint32_t getSignalBandwidth(); void setSignalBandwidth(uint32_t sbw); void setCodingRate4(int denominator); uint8_t getCodingRate4(); void setPreambleLength(long length); bool dcd(); void enableCrc(); void disableCrc(); void enableTCXO(); void disableTCXO(); byte random(); void setSPIFrequency(uint32_t frequency); void dumpRegisters(Stream& out); void handleDio0Rise(); bool getPacketValidity(); private: void writeBuffer(const uint8_t* buffer, size_t size); void readBuffer(uint8_t* buffer, size_t size); void txAntEnable(); void rxAntEnable(); void loraMode(); void waitOnBusy(); void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); void setSyncWord(int sw); void explicitHeaderMode(); void implicitHeaderMode(); uint8_t readRegister(uint16_t address); void writeRegister(uint16_t address, uint8_t value); uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); static void onDio0Rise(); void handleLowDataRate(); void optimizeModemSensitivity(); private: SPISettings _spiSettings; SPIClass* _spiModem; int _ss; int _sclk; int _mosi; int _miso; int _reset; int _dio0; int _rxen; int _txen; int _busy; int _modem; uint32_t _frequency; uint8_t _bw; uint8_t _cr; int _packetIndex; int _implicitHeaderMode; int _payloadLength; int _crcMode; int _fifo_tx_addr_ptr; int _fifo_rx_addr_ptr; bool _preinit_done; int _rxPacketLength; bool _tcxo; uint8_t _preamble_e; uint8_t _preamble_m; uint32_t _last_preamble; }; #endif