This is a copy of the community maintained fork of the open firmware which powers RNode devices. This version will have support for the hardware made by Mees Electronics.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

788 lines
25 KiB

// 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 <Arduino.h>
#include <SPI.h>
#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