diff --git a/src/configuration.h b/src/configuration.h index 2080e6e3..23eab0bf 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -188,11 +188,11 @@ along with this program. If not, see . #define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled #ifdef USE_SX1262 -#define SX1262_CS RF95_NSS // FIXME - we really should define LORA_CS instead -#define SX1262_DIO1 LORA_DIO1 -#define SX1262_BUSY LORA_DIO2 -#define SX1262_RESET LORA_RESET -#define SX1262_E22 // Not really an E22 but TTGO seems to be trying to clone that +#define SX126X_CS RF95_NSS // FIXME - we really should define LORA_CS instead +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that // Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface // code) #endif @@ -462,11 +462,11 @@ along with this program. If not, see . #define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled #ifdef USE_SX1262 -#define SX1262_CS 20 // CS0 on pinelora schematic, hooked to gpio D0 on ch341f -#define SX1262_DIO1 LORA_DIO1 -#define SX1262_BUSY LORA_DIO2 -#define SX1262_RESET LORA_RESET -// HOPE RFM90 does not have a TCXO therefore not SX1262_E22 +#define SX126X_CS 20 // CS0 on pinelora schematic, hooked to gpio D0 on ch341f +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +// HOPE RFM90 does not have a TCXO therefore not SX126X_E22 #endif #endif diff --git a/src/main.cpp b/src/main.cpp index a1de8dc6..0171631e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,7 @@ #include "RF95Interface.h" #include "SX1262Interface.h" +#include "SX1268Interface.h" #ifdef NRF52_SERIES #include "variant.h" @@ -491,10 +492,10 @@ void setup() } } -#ifdef SX1262_ANT_SW - // make analog PA vs not PA switch on SX1262 eval board work properly - pinMode(SX1262_ANT_SW, OUTPUT); - digitalWrite(SX1262_ANT_SW, 1); +#ifdef SX126X_ANT_SW + // make analog PA vs not PA switch on SX126x eval board work properly + pinMode(SX126X_ANT_SW, OUTPUT); + digitalWrite(SX126X_ANT_SW, 1); #endif // radio init MUST BE AFTER service.init, so we have our radio config settings (from nodedb init) @@ -512,9 +513,9 @@ void setup() } #endif -#if defined(SX1262_CS) +#if defined(USE_SX1262) if (!rIf) { - rIf = new SX1262Interface(SX1262_CS, SX1262_DIO1, SX1262_RESET, SX1262_BUSY, SPI); + rIf = new SX1262Interface(SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY, SPI); if (!rIf->init()) { DEBUG_MSG("Warning: Failed to find SX1262 radio\n"); delete rIf; @@ -525,6 +526,19 @@ void setup() } #endif +#if defined(USE_SX1268) + if (!rIf) { + rIf = new SX1268Interface(SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY, SPI); + if (!rIf->init()) { + DEBUG_MSG("Warning: Failed to find SX1268 radio\n"); + delete rIf; + rIf = NULL; + } else { + DEBUG_MSG("SX1268 Radio init succeeded, using SX1268 radio\n"); + } + } +#endif + #ifdef USE_SIM_RADIO if (!rIf) { rIf = new SimRadio; diff --git a/src/mesh/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp new file mode 100644 index 00000000..a50fbd6b --- /dev/null +++ b/src/mesh/InterfacesTemplates.cpp @@ -0,0 +1,6 @@ +#include "SX126xInterface.h" +#include "SX126xInterface.cpp" + +// We need this declaration for proper linking in derived classes +template class SX126xInterface; +template class SX126xInterface; \ No newline at end of file diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 8b7d9f11..5a1cce32 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -67,7 +67,7 @@ bool RF95Interface::init() #endif setTransmitEnable(false); - int res = lora->begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength); + int res = lora->begin(getFreq(), bw, sf, cr, syncWord, power, currentLimit, preambleLength); DEBUG_MSG("RF95 init result %d\n", res); // current limit was removed from module' ctor @@ -119,7 +119,7 @@ bool RF95Interface::reconfigure() err = lora->setPreambleLength(preambleLength); assert(err == ERR_NONE); - err = lora->setFrequency(freq); + err = lora->setFrequency(getFreq()); if (err != ERR_NONE) RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 7551473c..cf86ae01 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -318,18 +318,18 @@ void RadioInterface::applyModemConfig() // If user has manually specified a channel num, then use that, otherwise generate one by hashing the name const char *channelName = channels.getName(channels.getPrimaryIndex()); int channel_num = channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName) % myRegion->numChannels; - freq = myRegion->freq + radioConfig.preferences.frequency_offset + myRegion->spacing * channel_num; + float freq = myRegion->freq + radioConfig.preferences.frequency_offset + myRegion->spacing * channel_num; + + saveChannelNum(channel_num); + saveFreq(freq); DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num, power); DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq); DEBUG_MSG("Radio myRegion->spacing: %f\n", myRegion->spacing); DEBUG_MSG("Radio myRegion->numChannels: %d\n", myRegion->numChannels); DEBUG_MSG("Radio channel_num: %d\n", channel_num); - DEBUG_MSG("Radio frequency: %f\n", freq); + DEBUG_MSG("Radio frequency: %f\n", getFreq()); // the frequency could be overridden in RadioInterface::getFreq() for some modules DEBUG_MSG("Short packet time: %u msec\n", shortPacketMsec); - - saveChannelNum(channel_num); - saveFreq(freq); } /** diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index c0d7ec2d..542b575d 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -78,8 +78,6 @@ class RadioInterface void deliverToReceiver(MeshPacket *p); public: - float freq = 915.0; - /** pool is the pool we will alloc our rx packets from */ RadioInterface(); @@ -149,7 +147,7 @@ class RadioInterface /** * Get the frequency we saved. */ - float getFreq(); + virtual float getFreq(); /// Some boards (1st gen Pinetab Lora module) have broken IRQ wires, so we need to poll via i2c registers virtual bool isIRQPending() { return false; } diff --git a/src/mesh/SX1262Interface.cpp b/src/mesh/SX1262Interface.cpp index 76d44625..d0137280 100644 --- a/src/mesh/SX1262Interface.cpp +++ b/src/mesh/SX1262Interface.cpp @@ -2,256 +2,8 @@ #include "SX1262Interface.h" #include "error.h" -// Particular boards might define a different max power based on what their hardware can do -#ifndef SX1262_MAX_POWER -#define SX1262_MAX_POWER 22 -#endif - SX1262Interface::SX1262Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi) - : RadioLibInterface(cs, irq, rst, busy, spi, &lora), lora(&module) + : SX126xInterface(cs, irq, rst, busy, spi) { -} - -/// Initialise the Driver transport hardware and software. -/// Make sure the Driver is properly configured before calling init(). -/// \return true if initialisation succeeded. -bool SX1262Interface::init() -{ -#ifdef SX1262_POWER_EN - digitalWrite(SX1262_POWER_EN, HIGH); - pinMode(SX1262_POWER_EN, OUTPUT); -#endif - -#ifdef SX1262_RXEN // set not rx or tx mode - digitalWrite(SX1262_RXEN, LOW); // Set low before becoming an output - pinMode(SX1262_RXEN, OUTPUT); -#endif -#ifdef SX1262_TXEN - digitalWrite(SX1262_TXEN, LOW); - pinMode(SX1262_TXEN, OUTPUT); -#endif - -#ifndef SX1262_E22 - float tcxoVoltage = 0; // None - we use an XTAL -#else - // Use DIO3 to power tcxo per https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575 - float tcxoVoltage = 1.8; -#endif - bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC? - - RadioLibInterface::init(); - - if (power == 0) - power = SX1262_MAX_POWER; - - if (power > SX1262_MAX_POWER) // This chip has lower power limits than some - power = SX1262_MAX_POWER; - - limitPower(); - - int res = lora.begin(freq, bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage, useRegulatorLDO); - DEBUG_MSG("SX1262 init result %d\n", res); - - // current limit was removed from module' ctor - // override default value (60 mA) - res = lora.setCurrentLimit(currentLimit); - DEBUG_MSG("Current limit set to %f\n", currentLimit); - DEBUG_MSG("Current limit set result %d\n", res); - -#ifdef SX1262_TXEN - // lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX - if (res == ERR_NONE) - res = lora.setDio2AsRfSwitch(false); -#endif - -#if 0 - // Read/write a register we are not using (only used for FSK mode) to test SPI comms - uint8_t crcLSB = 0; - int err = lora.readRegister(SX126X_REG_CRC_POLYNOMIAL_LSB, &crcLSB, 1); - if(err != ERR_NONE) - RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); - - //if(crcLSB != 0x0f) - // RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); - - crcLSB = 0x5a; - err = lora.writeRegister(SX126X_REG_CRC_POLYNOMIAL_LSB, &crcLSB, 1); - if(err != ERR_NONE) - RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); - - err = lora.readRegister(SX126X_REG_CRC_POLYNOMIAL_LSB, &crcLSB, 1); - if(err != ERR_NONE) - RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); - - if(crcLSB != 0x5a) - RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); - // If we got this far register accesses (and therefore SPI comms) are good -#endif - - if (res == ERR_NONE) - res = lora.setCRC(SX126X_LORA_CRC_ON); - - if (res == ERR_NONE) - startReceive(); // start receiving - - return res == ERR_NONE; -} - -bool SX1262Interface::reconfigure() -{ - RadioLibInterface::reconfigure(); - - // set mode to standby - setStandby(); - - // configure publicly accessible settings - int err = lora.setSpreadingFactor(sf); - if (err != ERR_NONE) - RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); - - err = lora.setBandwidth(bw); - if (err != ERR_NONE) - RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); - - err = lora.setCodingRate(cr); - if (err != ERR_NONE) - RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); - - // Hmm - seems to lower SNR when the signal levels are high. Leaving off for now... - err = lora.setRxGain(true); - assert(err == ERR_NONE); - - err = lora.setSyncWord(syncWord); - assert(err == ERR_NONE); - - err = lora.setCurrentLimit(currentLimit); - assert(err == ERR_NONE); - - err = lora.setPreambleLength(preambleLength); - assert(err == ERR_NONE); - - err = lora.setFrequency(freq); - if (err != ERR_NONE) - RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); - - if (power > 22) // This chip has lower power limits than some - power = 22; - err = lora.setOutputPower(power); - assert(err == ERR_NONE); - - startReceive(); // restart receiving - - return ERR_NONE; -} - -void INTERRUPT_ATTR SX1262Interface::disableInterrupt() -{ - lora.clearDio1Action(); -} - -void SX1262Interface::setStandby() -{ - checkNotification(); // handle any pending interrupts before we force standby - - int err = lora.standby(); - assert(err == ERR_NONE); - -#ifdef SX1262_RXEN // we have RXEN/TXEN control - turn off RX and TX power - digitalWrite(SX1262_RXEN, LOW); -#endif -#ifdef SX1262_TXEN - digitalWrite(SX1262_TXEN, LOW); -#endif - - isReceiving = false; // If we were receiving, not any more - disableInterrupt(); - completeSending(); // If we were sending, not anymore -} - -/** - * Add SNR data to received messages - */ -void SX1262Interface::addReceiveMetadata(MeshPacket *mp) -{ - // DEBUG_MSG("PacketStatus %x\n", lora.getPacketStatus()); - mp->rx_snr = lora.getSNR(); - mp->rx_rssi = lround(lora.getRSSI()); -} - -/** We override to turn on transmitter power as needed. - */ -void SX1262Interface::configHardwareForSend() -{ -#ifdef SX1262_TXEN // we have RXEN/TXEN control - turn on TX power / off RX power - digitalWrite(SX1262_TXEN, HIGH); -#endif - - RadioLibInterface::configHardwareForSend(); -} - -// For power draw measurements, helpful to force radio to stay sleeping -// #define SLEEP_ONLY - -void SX1262Interface::startReceive() -{ -#ifdef SLEEP_ONLY - sleep(); -#else - - setStandby(); - -#ifdef SX1262_RXEN // we have RXEN/TXEN control - turn on RX power / off TX power - digitalWrite(SX1262_RXEN, HIGH); -#endif - - // int err = lora.startReceive(); - int err = lora.startReceiveDutyCycleAuto(); // We use a 32 bit preamble so this should save some power by letting radio sit in - // standby mostly. - assert(err == ERR_NONE); - - isReceiving = true; - - // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits - enableInterrupt(isrRxLevel0); -#endif -} - -/** Could we send right now (i.e. either not actively receving or transmitting)? */ -bool SX1262Interface::isActivelyReceiving() -{ - // The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet - // received and handled the interrupt for reading the packet/handling errors. - // FIXME: it would be better to check for preamble, but we currently have our ISR not set to fire for packets that - // never even get a valid header, so we don't want preamble to get set and stay set due to noise on the network. - - uint16_t irq = lora.getIrqStatus(); - bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID); - - // this is not correct - often always true - need to add an extra conditional - // size_t bytesPending = lora.getPacketLength(); - - // if (hasPreamble) DEBUG_MSG("rx hasPreamble\n"); - return hasPreamble; -} - -bool SX1262Interface::sleep() -{ - // Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet - DEBUG_MSG("sx1262 entering sleep mode (FIXME, don't keep config)\n"); - setStandby(); // Stop any pending operations - - // turn off TCXO if it was powered - // FIXME - this isn't correct - // lora.setTCXO(0); - - // put chipset into sleep mode (we've already disabled interrupts by now) - bool keepConfig = true; - lora.sleep(keepConfig); // Note: we do not keep the config, full reinit will be needed - -#ifdef SX1262_POWER_EN - digitalWrite(SX1262_POWER_EN, LOW); -#endif - - return true; } \ No newline at end of file diff --git a/src/mesh/SX1262Interface.h b/src/mesh/SX1262Interface.h index 05a93a33..0f029ada 100644 --- a/src/mesh/SX1262Interface.h +++ b/src/mesh/SX1262Interface.h @@ -1,62 +1,12 @@ #pragma once -#include "RadioLibInterface.h" +#include "SX126xInterface.h" /** * Our adapter for SX1262 radios */ -class SX1262Interface : public RadioLibInterface +class SX1262Interface : public SX126xInterface { - SX1262 lora; - public: SX1262Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi); - - /// Initialise the Driver transport hardware and software. - /// Make sure the Driver is properly configured before calling init(). - /// \return true if initialisation succeeded. - virtual bool init(); - - /// Apply any radio provisioning changes - /// Make sure the Driver is properly configured before calling init(). - /// \return true if initialisation succeeded. - virtual bool reconfigure(); - - /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. - virtual bool sleep(); - - bool isIRQPending() { return lora.getIrqStatus() != 0; } - - protected: - /** - * Glue functions called from ISR land - */ - virtual void disableInterrupt(); - - /** - * Enable a particular ISR callback glue function - */ - virtual void enableInterrupt(void (*callback)()) { lora.setDio1Action(callback); } - - /** are we actively receiving a packet (only called during receiving state) */ - virtual bool isActivelyReceiving(); - - /** - * Start waiting to receive a message - */ - virtual void startReceive(); - - /** - * We override to turn on transmitter power as needed. - */ - virtual void configHardwareForSend(); - - /** - * Add SNR data to received messages - */ - virtual void addReceiveMetadata(MeshPacket *mp); - - virtual void setStandby(); - - private: }; \ No newline at end of file diff --git a/src/mesh/SX1268Interface.cpp b/src/mesh/SX1268Interface.cpp new file mode 100644 index 00000000..66894312 --- /dev/null +++ b/src/mesh/SX1268Interface.cpp @@ -0,0 +1,9 @@ +#include "configuration.h" +#include "SX1268Interface.h" +#include "error.h" + +SX1268Interface::SX1268Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, + SPIClass &spi) + : SX126xInterface(cs, irq, rst, busy, spi) +{ +} \ No newline at end of file diff --git a/src/mesh/SX1268Interface.h b/src/mesh/SX1268Interface.h new file mode 100644 index 00000000..c5b56171 --- /dev/null +++ b/src/mesh/SX1268Interface.h @@ -0,0 +1,15 @@ +#pragma once + +#include "SX126xInterface.h" + +/** + * Our adapter for SX1268 radios + */ +class SX1268Interface : public SX126xInterface +{ + public: + /// override frequency of the SX1268 module regardless of the region (use EU433 value) + virtual float getFreq() { return 433.175f; } + + SX1268Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi); +}; \ No newline at end of file diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp new file mode 100644 index 00000000..b0c48d55 --- /dev/null +++ b/src/mesh/SX126xInterface.cpp @@ -0,0 +1,269 @@ +#include "configuration.h" +#include "SX126xInterface.h" +#include "error.h" + +// Particular boards might define a different max power based on what their hardware can do +#ifndef SX126X_MAX_POWER +#define SX126X_MAX_POWER 22 +#endif + +template +SX126xInterface::SX126xInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, + SPIClass &spi) + : RadioLibInterface(cs, irq, rst, busy, spi, &lora), lora(&module) +{ +} + +/// Initialise the Driver transport hardware and software. +/// Make sure the Driver is properly configured before calling init(). +/// \return true if initialisation succeeded. +template +bool SX126xInterface::init() +{ +#ifdef SX126X_POWER_EN + digitalWrite(SX126X_POWER_EN, HIGH); + pinMode(SX126X_POWER_EN, OUTPUT); +#endif + +#ifdef SX126X_RXEN // set not rx or tx mode + digitalWrite(SX126X_RXEN, LOW); // Set low before becoming an output + pinMode(SX126X_RXEN, OUTPUT); +#endif +#ifdef SX126X_TXEN + digitalWrite(SX126X_TXEN, LOW); + pinMode(SX126X_TXEN, OUTPUT); +#endif + +#ifndef SX126X_E22 + float tcxoVoltage = 0; // None - we use an XTAL +#else + // Use DIO3 to power tcxo per https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575 + float tcxoVoltage = 1.8; +#endif + bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC? + + RadioLibInterface::init(); + + if (power == 0) + power = SX126X_MAX_POWER; + + if (power > SX126X_MAX_POWER) // This chip has lower power limits than some + power = SX126X_MAX_POWER; + + limitPower(); + + int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage, useRegulatorLDO); + // \todo Display actual typename of the adapter, not just `SX126x` + DEBUG_MSG("SX126x init result %d\n", res); + + // current limit was removed from module' ctor + // override default value (60 mA) + res = lora.setCurrentLimit(currentLimit); + DEBUG_MSG("Current limit set to %f\n", currentLimit); + DEBUG_MSG("Current limit set result %d\n", res); + +#ifdef SX126X_TXEN + // lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX + if (res == ERR_NONE) + res = lora.setDio2AsRfSwitch(false); +#endif + +#if 0 + // Read/write a register we are not using (only used for FSK mode) to test SPI comms + uint8_t crcLSB = 0; + int err = lora.readRegister(SX126X_REG_CRC_POLYNOMIAL_LSB, &crcLSB, 1); + if(err != ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); + + //if(crcLSB != 0x0f) + // RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); + + crcLSB = 0x5a; + err = lora.writeRegister(SX126X_REG_CRC_POLYNOMIAL_LSB, &crcLSB, 1); + if(err != ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); + + err = lora.readRegister(SX126X_REG_CRC_POLYNOMIAL_LSB, &crcLSB, 1); + if(err != ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); + + if(crcLSB != 0x5a) + RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure); + // If we got this far register accesses (and therefore SPI comms) are good +#endif + + if (res == ERR_NONE) + res = lora.setCRC(SX126X_LORA_CRC_ON); + + if (res == ERR_NONE) + startReceive(); // start receiving + + return res == ERR_NONE; +} + +template +bool SX126xInterface::reconfigure() +{ + RadioLibInterface::reconfigure(); + + // set mode to standby + setStandby(); + + // configure publicly accessible settings + int err = lora.setSpreadingFactor(sf); + if (err != ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); + + err = lora.setBandwidth(bw); + if (err != ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); + + err = lora.setCodingRate(cr); + if (err != ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); + + // Hmm - seems to lower SNR when the signal levels are high. Leaving off for now... + err = lora.setRxGain(true); + assert(err == ERR_NONE); + + err = lora.setSyncWord(syncWord); + assert(err == ERR_NONE); + + err = lora.setCurrentLimit(currentLimit); + assert(err == ERR_NONE); + + err = lora.setPreambleLength(preambleLength); + assert(err == ERR_NONE); + + err = lora.setFrequency(getFreq()); + if (err != ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_InvalidRadioSetting); + + if (power > 22) // This chip has lower power limits than some + power = 22; + err = lora.setOutputPower(power); + assert(err == ERR_NONE); + + startReceive(); // restart receiving + + return ERR_NONE; +} + +template +void INTERRUPT_ATTR SX126xInterface::disableInterrupt() +{ + lora.clearDio1Action(); +} + +template +void SX126xInterface::setStandby() +{ + checkNotification(); // handle any pending interrupts before we force standby + + int err = lora.standby(); + assert(err == ERR_NONE); + +#ifdef SX126X_RXEN // we have RXEN/TXEN control - turn off RX and TX power + digitalWrite(SX126X_RXEN, LOW); +#endif +#ifdef SX126X_TXEN + digitalWrite(SX126X_TXEN, LOW); +#endif + + isReceiving = false; // If we were receiving, not any more + disableInterrupt(); + completeSending(); // If we were sending, not anymore +} + +/** + * Add SNR data to received messages + */ +template +void SX126xInterface::addReceiveMetadata(MeshPacket *mp) +{ + // DEBUG_MSG("PacketStatus %x\n", lora.getPacketStatus()); + mp->rx_snr = lora.getSNR(); + mp->rx_rssi = lround(lora.getRSSI()); +} + +/** We override to turn on transmitter power as needed. + */ +template +void SX126xInterface::configHardwareForSend() +{ +#ifdef SX126X_TXEN // we have RXEN/TXEN control - turn on TX power / off RX power + digitalWrite(SX126X_TXEN, HIGH); +#endif + + RadioLibInterface::configHardwareForSend(); +} + +// For power draw measurements, helpful to force radio to stay sleeping +// #define SLEEP_ONLY + +template +void SX126xInterface::startReceive() +{ +#ifdef SLEEP_ONLY + sleep(); +#else + + setStandby(); + +#ifdef SX126X_RXEN // we have RXEN/TXEN control - turn on RX power / off TX power + digitalWrite(SX126X_RXEN, HIGH); +#endif + + // int err = lora.startReceive(); + int err = lora.startReceiveDutyCycleAuto(); // We use a 32 bit preamble so this should save some power by letting radio sit in + // standby mostly. + assert(err == ERR_NONE); + + isReceiving = true; + + // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits + enableInterrupt(isrRxLevel0); +#endif +} + +/** Could we send right now (i.e. either not actively receving or transmitting)? */ +template +bool SX126xInterface::isActivelyReceiving() +{ + // The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet + // received and handled the interrupt for reading the packet/handling errors. + // FIXME: it would be better to check for preamble, but we currently have our ISR not set to fire for packets that + // never even get a valid header, so we don't want preamble to get set and stay set due to noise on the network. + + uint16_t irq = lora.getIrqStatus(); + bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID); + + // this is not correct - often always true - need to add an extra conditional + // size_t bytesPending = lora.getPacketLength(); + + // if (hasPreamble) DEBUG_MSG("rx hasPreamble\n"); + return hasPreamble; +} + +template +bool SX126xInterface::sleep() +{ + // Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet + // \todo Display actual typename of the adapter, not just `SX126x` + DEBUG_MSG("sx126x entering sleep mode (FIXME, don't keep config)\n"); + setStandby(); // Stop any pending operations + + // turn off TCXO if it was powered + // FIXME - this isn't correct + // lora.setTCXO(0); + + // put chipset into sleep mode (we've already disabled interrupts by now) + bool keepConfig = true; + lora.sleep(keepConfig); // Note: we do not keep the config, full reinit will be needed + +#ifdef SX126X_POWER_EN + digitalWrite(SX126X_POWER_EN, LOW); +#endif + + return true; +} \ No newline at end of file diff --git a/src/mesh/SX126xInterface.h b/src/mesh/SX126xInterface.h new file mode 100644 index 00000000..1b4a1004 --- /dev/null +++ b/src/mesh/SX126xInterface.h @@ -0,0 +1,68 @@ +#pragma once + +#include "RadioLibInterface.h" + +/** + * \brief Adapter for SX126x radio family. Implements common logic for child classes. + * \tparam T RadioLib module type for SX126x: SX1262, SX1268. + */ +template +class SX126xInterface : public RadioLibInterface +{ + public: + SX126xInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool init(); + + /// Apply any radio provisioning changes + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool reconfigure(); + + /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. + virtual bool sleep(); + + bool isIRQPending() { return lora.getIrqStatus() != 0; } + + protected: + + /** + * Specific module instance + */ + T lora; + + /** + * Glue functions called from ISR land + */ + virtual void disableInterrupt(); + + /** + * Enable a particular ISR callback glue function + */ + virtual void enableInterrupt(void (*callback)()) { lora.setDio1Action(callback); } + + /** are we actively receiving a packet (only called during receiving state) */ + virtual bool isActivelyReceiving(); + + /** + * Start waiting to receive a message + */ + virtual void startReceive(); + + /** + * We override to turn on transmitter power as needed. + */ + virtual void configHardwareForSend(); + + /** + * Add SNR data to received messages + */ + virtual void addReceiveMetadata(MeshPacket *mp); + + virtual void setStandby(); + + private: +}; \ No newline at end of file diff --git a/src/portduino/PortduinoGlue.cpp b/src/portduino/PortduinoGlue.cpp index d2caf695..46abd6b8 100644 --- a/src/portduino/PortduinoGlue.cpp +++ b/src/portduino/PortduinoGlue.cpp @@ -72,13 +72,13 @@ void portduinoSetup() gpioBind(loraIrq); // BUSY hw was busted on current board - just use the simulated pin (which will read low) - auto busy = new LinuxGPIOPin(SX1262_BUSY, "ch341", "slct", "loraBusy"); + auto busy = new LinuxGPIOPin(SX126X_BUSY, "ch341", "slct", "loraBusy"); busy->setSilent(); gpioBind(busy); - gpioBind(new LinuxGPIOPin(SX1262_RESET, "ch341", "ini", "loraReset")); + gpioBind(new LinuxGPIOPin(SX126X_RESET, "ch341", "ini", "loraReset")); - auto loraCs = new LinuxGPIOPin(SX1262_CS, "ch341", "cs0", "loraCs"); + auto loraCs = new LinuxGPIOPin(SX126X_CS, "ch341", "cs0", "loraCs"); loraCs->setSilent(); gpioBind(loraCs); } @@ -86,16 +86,16 @@ void portduinoSetup() #endif { - auto fakeBusy = new SimGPIOPin(SX1262_BUSY, "fakeBusy"); + auto fakeBusy = new SimGPIOPin(SX126X_BUSY, "fakeBusy"); fakeBusy->writePin(LOW); fakeBusy->setSilent(true); gpioBind(fakeBusy); - auto cs = new SimGPIOPin(SX1262_CS, "fakeLoraCS"); + auto cs = new SimGPIOPin(SX126X_CS, "fakeLoraCS"); cs->setSilent(true); gpioBind(cs); - gpioBind(new SimGPIOPin(SX1262_RESET, "fakeLoraReset")); + gpioBind(new SimGPIOPin(SX126X_RESET, "fakeLoraReset")); gpioBind(new SimGPIOPin(LORA_DIO1, "fakeLoraIrq")); } diff --git a/variants/WisCore_RAK4631_Board/variant.h b/variants/WisCore_RAK4631_Board/variant.h index e9e28dc1..8598dc1b 100644 --- a/variants/WisCore_RAK4631_Board/variant.h +++ b/variants/WisCore_RAK4631_Board/variant.h @@ -151,13 +151,13 @@ static const uint8_t SCK = PIN_SPI_SCK; */ // RAK4630 LoRa module -#define SX1262_CS (42) -#define SX1262_DIO1 (47) -#define SX1262_BUSY (46) -#define SX1262_RESET (38) -#define SX1262_TXEN (39) -#define SX1262_RXEN (37) -#define SX1262_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_CS (42) +#define SX126X_DIO1 (47) +#define SX126X_BUSY (46) +#define SX126X_RESET (38) +#define SX126X_TXEN (39) +#define SX126X_RXEN (37) +#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 // RAK1910 GPS module // If using the wisblock GPS module and pluged into Port A on WisBlock base diff --git a/variants/eink0.1/variant.h b/variants/eink0.1/variant.h index 1b30ca21..d07597a2 100644 --- a/variants/eink0.1/variant.h +++ b/variants/eink0.1/variant.h @@ -178,15 +178,15 @@ External serial flash WP25R1635FZUIL0 * Lora radio */ -#define SX1262_CS (0 + 24) // FIXME - we really should define LORA_CS instead -#define SX1262_DIO1 (0 + 20) +#define SX126X_CS (0 + 24) // FIXME - we really should define LORA_CS instead +#define SX126X_DIO1 (0 + 20) // Note DIO2 is attached internally to the module to an analog switch for TX/RX switching #define SX1262_DIO3 \ (0 + 21) // This is used as an *output* from the sx1262 and connected internally to power the tcxo, do not drive from the main // CPU? -#define SX1262_BUSY (0 + 17) -#define SX1262_RESET (0 + 25) -#define SX1262_E22 // Not really an E22 but TTGO seems to be trying to clone that +#define SX126X_BUSY (0 + 17) +#define SX126X_RESET (0 + 25) +#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that // Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface // code) diff --git a/variants/lora_isp4520/variant.h b/variants/lora_isp4520/variant.h index 9bf5cd50..d23a20a0 100644 --- a/variants/lora_isp4520/variant.h +++ b/variants/lora_isp4520/variant.h @@ -57,10 +57,10 @@ #define WIRE_INTERFACES_COUNT 0 // GPIOs the SX1262 is connected -#define SX1262_CS 1 // aka SPI_NSS -#define SX1262_DIO1 (4) -#define SX1262_BUSY (5) -#define SX1262_RESET (6) +#define SX126X_CS 1 // aka SPI_NSS +#define SX126X_DIO1 (4) +#define SX126X_BUSY (5) +#define SX126X_RESET (6) /* * Serial interfaces @@ -91,7 +91,7 @@ #define BATTERY_PIN 3 #define ADC_MULTIPLIER 1.436 -#define SX1262_E22 // Not really an E22 but this board clones using DIO3 for tcxo control +#define SX126X_E22 // Not really an E22 but this board clones using DIO3 for tcxo control #define NO_WIRE #define NO_GPS diff --git a/variants/lora_relay_v1/variant.h b/variants/lora_relay_v1/variant.h index f7ade958..74f3c5a6 100644 --- a/variants/lora_relay_v1/variant.h +++ b/variants/lora_relay_v1/variant.h @@ -117,21 +117,21 @@ static const uint8_t SCK = PIN_SPI_SCK; #define I2C_ADDR_BQ27441 0x55 // Battery gauge // CUSTOM GPIOs the SX1262 -#define SX1262_CS (32) +#define SX126X_CS (32) // If you would prefer to get console debug output over the JTAG ICE connection rather than the CDC-ACM USB serial device, just // define this. #define USE_SEGGER -#define SX1262_DIO1 (29) +#define SX126X_DIO1 (29) #define SX1262_DIO2 (30) -#define SX1262_BUSY (33) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) -#define SX1262_RESET (34) -// #define SX1262_ANT_SW (32 + 10) -#define SX1262_RXEN (14) -#define SX1262_TXEN (31) -#define SX1262_POWER_EN \ +#define SX126X_BUSY (33) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) +#define SX126X_RESET (34) +// #define SX126X_ANT_SW (32 + 10) +#define SX126X_RXEN (14) +#define SX126X_TXEN (31) +#define SX126X_POWER_EN \ (15) // FIXME, see warning hre https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay/blob/master/LORA_RELAY_NRF52840.ino -#define SX1262_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that +#define SX126X_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that #define ST7735_RESET (11) // Output #define ST7735_CS (12) diff --git a/variants/lora_relay_v2/variant.h b/variants/lora_relay_v2/variant.h index 6814fdb3..ec3e7e90 100644 --- a/variants/lora_relay_v2/variant.h +++ b/variants/lora_relay_v2/variant.h @@ -137,21 +137,21 @@ static const uint8_t SCK = PIN_SPI_SCK; #define I2C_ADDR_BQ27441 0x55 // Battery gauge // CUSTOM GPIOs the SX1262 -#define SX1262_CS (32) +#define SX126X_CS (32) // If you would prefer to get console debug output over the JTAG ICE connection rather than the CDC-ACM USB serial device, just // define this. #define USE_SEGGER -#define SX1262_DIO1 (29) +#define SX126X_DIO1 (29) #define SX1262_DIO2 (30) -#define SX1262_BUSY (33) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) -#define SX1262_RESET (34) -// #define SX1262_ANT_SW (32 + 10) -#define SX1262_RXEN (14) -#define SX1262_TXEN (31) -#define SX1262_POWER_EN \ +#define SX126X_BUSY (33) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) +#define SX126X_RESET (34) +// #define SX126X_ANT_SW (32 + 10) +#define SX126X_RXEN (14) +#define SX126X_TXEN (31) +#define SX126X_POWER_EN \ (15) // FIXME, see warning hre https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay/blob/master/LORA_RELAY_NRF52840.ino -#define SX1262_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that +#define SX126X_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that // ST7565 SPI #define ST7735_RESET (11) // Output diff --git a/variants/pca10056-rc-clock/variant.h b/variants/pca10056-rc-clock/variant.h index 9d35325c..b9c4f9bf 100644 --- a/variants/pca10056-rc-clock/variant.h +++ b/variants/pca10056-rc-clock/variant.h @@ -140,11 +140,11 @@ static const uint8_t SCK = PIN_SPI_SCK; #define EXTERNAL_FLASH_USE_QSPI // CUSTOM GPIOs the SX1262MB2CAS shield when installed on the NRF52840-DK development board -#define SX1262_CS (32 + 8) // P1.08 -#define SX1262_DIO1 (32 + 6) // P1.06 -#define SX1262_BUSY (32 + 4) // P1.04 -#define SX1262_RESET (0 + 3) // P0.03 -#define SX1262_ANT_SW (32 + 10) // P1.10 +#define SX126X_CS (32 + 8) // P1.08 +#define SX126X_DIO1 (32 + 6) // P1.06 +#define SX126X_BUSY (32 + 4) // P1.04 +#define SX126X_RESET (0 + 3) // P0.03 +#define SX126X_ANT_SW (32 + 10) // P1.10 // To debug via the segger JLINK console rather than the CDC-ACM serial device // #define USE_SEGGER diff --git a/variants/ppr/variant.h b/variants/ppr/variant.h index bbdda306..b055c3d8 100644 --- a/variants/ppr/variant.h +++ b/variants/ppr/variant.h @@ -129,15 +129,15 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PIN_WIRE_SCL (32) // CUSTOM GPIOs the SX1262 -#define SX1262_CS (10) -#define SX1262_DIO1 (20) +#define SX126X_CS (10) +#define SX126X_DIO1 (20) #define SX1262_DIO2 (26) -#define SX1262_BUSY (31) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) -#define SX1262_RESET (17) -// #define SX1262_ANT_SW (32 + 10) -#define SX1262_RXEN (22) -#define SX1262_TXEN (24) -#define SX1262_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that +#define SX126X_BUSY (31) // Supposed to be P0.18 but because of reworks, now on P0.31 (18) +#define SX126X_RESET (17) +// #define SX126X_ANT_SW (32 + 10) +#define SX126X_RXEN (22) +#define SX126X_TXEN (24) +#define SX126X_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that // ERC12864-10 LCD #define ERC12864_CS (32 + 4) diff --git a/variants/ppr1/variant.h b/variants/ppr1/variant.h index 04a6e3c0..b5d398c3 100644 --- a/variants/ppr1/variant.h +++ b/variants/ppr1/variant.h @@ -152,18 +152,18 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PIN_WIRE_SCL (32) // CUSTOM GPIOs the SX1262 -#define SX1262_CS (0 + 10) // FIXME - we really should define LORA_CS instead -#define SX1262_DIO1 (0 + 20) +#define SX126X_CS (0 + 10) // FIXME - we really should define LORA_CS instead +#define SX126X_DIO1 (0 + 20) #define SX1262_DIO2 (0 + 26) -#define SX1262_BUSY (0 + 19) -#define SX1262_RESET (0 + 17) -#define SX1262_TXEN (0 + 24) -#define SX1262_RXEN (0 + 22) -#define SX1262_E22 // Not really an E22 but this board clones using DIO3 for tcxo control +#define SX126X_BUSY (0 + 19) +#define SX126X_RESET (0 + 17) +#define SX126X_TXEN (0 + 24) +#define SX126X_RXEN (0 + 22) +#define SX126X_E22 // Not really an E22 but this board clones using DIO3 for tcxo control // FIXME, to prevent burning out parts I've set the power level super low, because I don't have // an antenna wired up -#define SX1262_MAX_POWER 1 +#define SX126X_MAX_POWER 1 #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...) diff --git a/variants/t-echo/variant.h b/variants/t-echo/variant.h index 40021891..beb9e550 100644 --- a/variants/t-echo/variant.h +++ b/variants/t-echo/variant.h @@ -180,21 +180,21 @@ External serial flash WP25R1635FZUIL0 * Lora radio */ -#define SX1262_CS (0 + 24) // FIXME - we really should define LORA_CS instead -#define SX1262_DIO1 (0 + 20) +#define SX126X_CS (0 + 24) // FIXME - we really should define LORA_CS instead +#define SX126X_DIO1 (0 + 20) // Note DIO2 is attached internally to the module to an analog switch for TX/RX switching #define SX1262_DIO3 \ (0 + 21) // This is used as an *output* from the sx1262 and connected internally to power the tcxo, do not drive from the main // CPU? -#define SX1262_BUSY (0 + 17) -#define SX1262_RESET (0 + 25) -#define SX1262_E22 // Not really an E22 but TTGO seems to be trying to clone that +#define SX126X_BUSY (0 + 17) +#define SX126X_RESET (0 + 25) +#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that // Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface // code) // #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...) -// #undef SX1262_CS +// #undef SX126X_CS // #define USE_SIM_RADIO // define to not use the lora radio hardware at all /*