firmware/src/mesh/SX126xInterface.cpp
GUVWAF 8c68d888c8
SX126x: Try next Interface when chip not found (#2363)
* If chip was not found, return false for init()

* SX1268: Only overwrite frequency when out of bounds
Happens when region is still UNSET
2023-03-18 07:23:37 -05:00

299 lines
10 KiB
C++

#include "SX126xInterface.h"
#include "configuration.h"
#include "error.h"
#include "mesh/NodeDB.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 <typename T>
SX126xInterface<T>::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)
{
LOG_WARN("SX126xInterface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
}
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
template <typename T> bool SX126xInterface<T>::init()
{
#ifdef SX126X_POWER_EN
digitalWrite(SX126X_POWER_EN, HIGH);
pinMode(SX126X_POWER_EN, 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`
LOG_INFO("SX126x init result %d\n", res);
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND)
return false;
LOG_INFO("Frequency set to %f\n", getFreq());
LOG_INFO("Bandwidth set to %f\n", bw);
LOG_INFO("Power output set to %d\n", power);
// current limit was removed from module' ctor
// override default value (60 mA)
res = lora.setCurrentLimit(currentLimit);
LOG_DEBUG("Current limit set to %f\n", currentLimit);
LOG_DEBUG("Current limit set result %d\n", res);
#ifdef SX126X_E22
// E22 Emulation explicitly requires DIO2 as RF switch, so set it to TRUE again for good measure. In case somebody defines
// SX126X_TX for an E22 Module
if (res == RADIOLIB_ERR_NONE)
res = lora.setDio2AsRfSwitch(true);
#endif
#if defined(SX126X_TXEN) && (SX126X_TXEN != RADIOLIB_NC)
// lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX
if (res == RADIOLIB_ERR_NONE) {
res = lora.setDio2AsRfSwitch(false);
lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
}
#endif
if (config.lora.sx126x_rx_boosted_gain) {
uint16_t result = lora.setRxBoostedGainMode(true);
LOG_INFO("Set Rx Boosted Gain mode; result: %d\n", result);
} else {
uint16_t result = lora.setRxBoostedGainMode(false);
LOG_INFO("Set Rx Power Saving Gain mode; result: %d\n", result);
}
#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 != RADIOLIB_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 != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(CriticalErrorCode_SX1262Failure);
err = lora.readRegister(SX126X_REG_CRC_POLYNOMIAL_LSB, &crcLSB, 1);
if(err != RADIOLIB_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 == RADIOLIB_ERR_NONE)
res = lora.setCRC(RADIOLIB_SX126X_LORA_CRC_ON);
if (res == RADIOLIB_ERR_NONE)
startReceive(); // start receiving
return res == RADIOLIB_ERR_NONE;
}
template <typename T> bool SX126xInterface<T>::reconfigure()
{
RadioLibInterface::reconfigure();
// set mode to standby
setStandby();
// configure publicly accessible settings
int err = lora.setSpreadingFactor(sf);
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setBandwidth(bw);
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setCodingRate(cr);
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
// Hmm - seems to lower SNR when the signal levels are high. Leaving off for now...
// TODO: Confirm gain registers are okay now
// err = lora.setRxGain(true);
// assert(err == RADIOLIB_ERR_NONE);
err = lora.setSyncWord(syncWord);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setCurrentLimit(currentLimit);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setPreambleLength(preambleLength);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setFrequency(getFreq());
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
if (power > SX126X_MAX_POWER) // This chip has lower power limits than some
power = SX126X_MAX_POWER;
err = lora.setOutputPower(power);
assert(err == RADIOLIB_ERR_NONE);
startReceive(); // restart receiving
return RADIOLIB_ERR_NONE;
}
template <typename T> void INTERRUPT_ATTR SX126xInterface<T>::disableInterrupt()
{
lora.clearDio1Action();
}
template <typename T> void SX126xInterface<T>::setStandby()
{
checkNotification(); // handle any pending interrupts before we force standby
int err = lora.standby();
if (err != RADIOLIB_ERR_NONE)
LOG_DEBUG("SX126x standby failed with error %d\n", err);
assert(err == RADIOLIB_ERR_NONE);
isReceiving = false; // If we were receiving, not any more
activeReceiveStart = 0;
disableInterrupt();
completeSending(); // If we were sending, not anymore
}
/**
* Add SNR data to received messages
*/
template <typename T> void SX126xInterface<T>::addReceiveMetadata(meshtastic_MeshPacket *mp)
{
// LOG_DEBUG("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 <typename T> void SX126xInterface<T>::configHardwareForSend()
{
RadioLibInterface::configHardwareForSend();
}
// For power draw measurements, helpful to force radio to stay sleeping
// #define SLEEP_ONLY
template <typename T> void SX126xInterface<T>::startReceive()
{
#ifdef SLEEP_ONLY
sleep();
#else
setStandby();
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
// Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving
int err = lora.startReceiveDutyCycleAuto(preambleLength, 8,
RADIOLIB_SX126X_IRQ_RX_DEFAULT | RADIOLIB_SX126X_IRQ_RADIOLIB_PREAMBLE_DETECTED |
RADIOLIB_SX126X_IRQ_HEADER_VALID);
assert(err == RADIOLIB_ERR_NONE);
isReceiving = true;
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
#endif
}
/** Is the channel currently active? */
template <typename T> bool SX126xInterface<T>::isChannelActive()
{
// check if we can detect a LoRa preamble on the current channel
int16_t result;
setStandby();
result = lora.scanChannel();
if (result == RADIOLIB_LORA_DETECTED)
return true;
assert(result != RADIOLIB_ERR_WRONG_MODEM);
return false;
}
/** Could we send right now (i.e. either not actively receving or transmitting)? */
template <typename T> bool SX126xInterface<T>::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.
uint16_t irq = lora.getIrqStatus();
bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_RADIOLIB_PREAMBLE_DETECTED));
// Handle false detections
if (detected) {
uint32_t now = millis();
if (!activeReceiveStart) {
activeReceiveStart = now;
} else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID)) {
// The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
activeReceiveStart = 0;
LOG_DEBUG("Ignore false preamble detection.\n");
return false;
} else if (now - activeReceiveStart > maxPacketTimeMsec) {
// We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
activeReceiveStart = 0;
LOG_DEBUG("Ignore false header detection.\n");
return false;
}
}
// if (detected) LOG_DEBUG("rx detected\n");
return detected;
}
template <typename T> bool SX126xInterface<T>::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`
LOG_DEBUG("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;
}