mirror of
https://github.com/meshtastic/firmware.git
synced 2026-02-02 07:01:54 +00:00
Using long interleaving is not a breaking change, the receiver node is able to use the lora header to know if LI encoding is used or not and will decode LI packets correctly. However the problem is SX127x and other first generation LoRa IP which do not support LI at all, for theses it is a breaking change. HOWEVER due to the sync word bug the LR11x0 already can't talk with SX127x, so if we enable LI on theses no one would be able to tell. Same for SX128x altho this is because it works on 2.4Ghz which is incompatible with SX127x. Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
328 lines
11 KiB
C++
328 lines
11 KiB
C++
#if RADIOLIB_EXCLUDE_SX128X != 1
|
|
#include "SX128xInterface.h"
|
|
#include "Throttle.h"
|
|
#include "configuration.h"
|
|
#include "error.h"
|
|
#include "mesh/NodeDB.h"
|
|
|
|
#if ARCH_PORTDUINO
|
|
#include "PortduinoGlue.h"
|
|
#endif
|
|
|
|
// Particular boards might define a different max power based on what their hardware can do
|
|
#if ARCH_PORTDUINO
|
|
#define SX128X_MAX_POWER portduino_config.sx128x_max_power
|
|
#endif
|
|
#ifndef SX128X_MAX_POWER
|
|
#define SX128X_MAX_POWER 13
|
|
#endif
|
|
|
|
template <typename T>
|
|
SX128xInterface<T>::SX128xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
|
RADIOLIB_PIN_TYPE busy)
|
|
: RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module)
|
|
{
|
|
LOG_DEBUG("SX128xInterface(cs=%d, irq=%d, rst=%d, busy=%d)", 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 SX128xInterface<T>::init()
|
|
{
|
|
#ifdef SX128X_POWER_EN
|
|
pinMode(SX128X_POWER_EN, OUTPUT);
|
|
digitalWrite(SX128X_POWER_EN, HIGH);
|
|
#endif
|
|
|
|
#ifdef RF95_FAN_EN
|
|
pinMode(RF95_FAN_EN, OUTPUT);
|
|
digitalWrite(RF95_FAN_EN, 1);
|
|
#endif
|
|
|
|
#if ARCH_PORTDUINO
|
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
|
pinMode(portduino_config.lora_rxen_pin.pin, OUTPUT);
|
|
digitalWrite(portduino_config.lora_rxen_pin.pin, LOW); // Set low before becoming an output
|
|
}
|
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
|
pinMode(portduino_config.lora_txen_pin.pin, OUTPUT);
|
|
digitalWrite(portduino_config.lora_txen_pin.pin, LOW); // Set low before becoming an output
|
|
}
|
|
#else
|
|
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode
|
|
pinMode(SX128X_RXEN, OUTPUT);
|
|
digitalWrite(SX128X_RXEN, LOW); // Set low before becoming an output
|
|
#endif
|
|
#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC)
|
|
pinMode(SX128X_TXEN, OUTPUT);
|
|
digitalWrite(SX128X_TXEN, LOW);
|
|
#endif
|
|
#endif
|
|
|
|
RadioLibInterface::init();
|
|
|
|
limitPower(SX128X_MAX_POWER);
|
|
|
|
preambleLength = 12; // 12 is the default for this chip, 32 does not RX at all
|
|
|
|
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength);
|
|
// \todo Display actual typename of the adapter, not just `SX128x`
|
|
LOG_INFO("SX128x init result %d", res);
|
|
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED)
|
|
return false;
|
|
|
|
if ((config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (res == RADIOLIB_ERR_INVALID_FREQUENCY)) {
|
|
LOG_WARN("Radio only supports 2.4GHz LoRa. Adjusting Region and rebooting");
|
|
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_LORA_24;
|
|
nodeDB->saveToDisk(SEGMENT_CONFIG);
|
|
delay(2000);
|
|
#if defined(ARCH_ESP32)
|
|
ESP.restart();
|
|
#elif defined(ARCH_NRF52)
|
|
NVIC_SystemReset();
|
|
#else
|
|
LOG_ERROR("FIXME implement reboot for this platform. Skip for now");
|
|
#endif
|
|
}
|
|
|
|
LOG_INFO("Frequency set to %f", getFreq());
|
|
LOG_INFO("Bandwidth set to %f", bw);
|
|
LOG_INFO("Power output set to %d", power);
|
|
|
|
#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC) && defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC)
|
|
if (res == RADIOLIB_ERR_NONE) {
|
|
lora.setRfSwitchPins(SX128X_RXEN, SX128X_TXEN);
|
|
}
|
|
#elif ARCH_PORTDUINO
|
|
if (res == RADIOLIB_ERR_NONE && portduino_config.lora_rxen_pin.pin != RADIOLIB_NC &&
|
|
portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
|
lora.setRfSwitchPins(portduino_config.lora_rxen_pin.pin, portduino_config.lora_txen_pin.pin);
|
|
}
|
|
#endif
|
|
|
|
if (res == RADIOLIB_ERR_NONE)
|
|
res = lora.setCRC(2);
|
|
|
|
if (res == RADIOLIB_ERR_NONE)
|
|
startReceive(); // start receiving
|
|
|
|
return res == RADIOLIB_ERR_NONE;
|
|
}
|
|
|
|
template <typename T> bool SX128xInterface<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, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it
|
|
if (err != RADIOLIB_ERR_NONE)
|
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
|
|
|
err = lora.setSyncWord(syncWord);
|
|
if (err != RADIOLIB_ERR_NONE)
|
|
LOG_ERROR("SX128X setSyncWord %s%d", radioLibErr, err);
|
|
assert(err == RADIOLIB_ERR_NONE);
|
|
|
|
err = lora.setPreambleLength(preambleLength);
|
|
if (err != RADIOLIB_ERR_NONE)
|
|
LOG_ERROR("SX128X setPreambleLength %s%d", radioLibErr, err);
|
|
assert(err == RADIOLIB_ERR_NONE);
|
|
|
|
err = lora.setFrequency(getFreq());
|
|
if (err != RADIOLIB_ERR_NONE)
|
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
|
|
|
if (power > SX128X_MAX_POWER) // This chip has lower power limits than some
|
|
power = SX128X_MAX_POWER;
|
|
|
|
err = lora.setOutputPower(power);
|
|
if (err != RADIOLIB_ERR_NONE)
|
|
LOG_ERROR("SX128X setOutputPower %s%d", radioLibErr, err);
|
|
assert(err == RADIOLIB_ERR_NONE);
|
|
|
|
startReceive(); // restart receiving
|
|
|
|
return RADIOLIB_ERR_NONE;
|
|
}
|
|
|
|
template <typename T> void SX128xInterface<T>::disableInterrupt()
|
|
{
|
|
lora.clearDio1Action();
|
|
}
|
|
|
|
template <typename T> bool SX128xInterface<T>::wideLora()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
template <typename T> void SX128xInterface<T>::setStandby()
|
|
{
|
|
checkNotification(); // handle any pending interrupts before we force standby
|
|
|
|
int err = lora.standby();
|
|
|
|
if (err != RADIOLIB_ERR_NONE)
|
|
LOG_ERROR("SX128x standby %s%d", radioLibErr, err);
|
|
assert(err == RADIOLIB_ERR_NONE);
|
|
#if ARCH_PORTDUINO
|
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
|
digitalWrite(portduino_config.lora_rxen_pin.pin, LOW);
|
|
}
|
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
|
digitalWrite(portduino_config.lora_txen_pin.pin, LOW);
|
|
}
|
|
#else
|
|
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power
|
|
digitalWrite(SX128X_RXEN, LOW);
|
|
#endif
|
|
#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC)
|
|
digitalWrite(SX128X_TXEN, LOW);
|
|
#endif
|
|
#endif
|
|
isReceiving = false; // If we were receiving, not any more
|
|
activeReceiveStart = 0;
|
|
disableInterrupt();
|
|
completeSending(); // If we were sending, not anymore
|
|
RadioLibInterface::setStandby();
|
|
}
|
|
|
|
/**
|
|
* Add SNR data to received messages
|
|
*/
|
|
template <typename T> void SX128xInterface<T>::addReceiveMetadata(meshtastic_MeshPacket *mp)
|
|
{
|
|
// LOG_DEBUG("PacketStatus %x", lora.getPacketStatus());
|
|
mp->rx_snr = lora.getSNR();
|
|
mp->rx_rssi = lround(lora.getRSSI());
|
|
LOG_DEBUG("Corrected frequency offset: %f", lora.getFrequencyError());
|
|
}
|
|
|
|
/** We override to turn on transmitter power as needed.
|
|
*/
|
|
template <typename T> void SX128xInterface<T>::configHardwareForSend()
|
|
{
|
|
#if ARCH_PORTDUINO
|
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
|
digitalWrite(portduino_config.lora_txen_pin.pin, HIGH);
|
|
}
|
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
|
digitalWrite(portduino_config.lora_rxen_pin.pin, LOW);
|
|
}
|
|
|
|
#else
|
|
#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn on TX power / off RX power
|
|
digitalWrite(SX128X_TXEN, HIGH);
|
|
#endif
|
|
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC)
|
|
digitalWrite(SX128X_RXEN, LOW);
|
|
#endif
|
|
#endif
|
|
|
|
RadioLibInterface::configHardwareForSend();
|
|
}
|
|
|
|
// For power draw measurements, helpful to force radio to stay sleeping
|
|
// #define SLEEP_ONLY
|
|
|
|
template <typename T> void SX128xInterface<T>::startReceive()
|
|
{
|
|
#ifdef SLEEP_ONLY
|
|
sleep();
|
|
#else
|
|
|
|
setStandby();
|
|
|
|
#if ARCH_PORTDUINO
|
|
if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
|
|
digitalWrite(portduino_config.lora_rxen_pin.pin, HIGH);
|
|
}
|
|
if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
|
|
digitalWrite(portduino_config.lora_txen_pin.pin, LOW);
|
|
}
|
|
|
|
#else
|
|
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn on RX power / off TX power
|
|
digitalWrite(SX128X_RXEN, HIGH);
|
|
#endif
|
|
#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC)
|
|
digitalWrite(SX128X_TXEN, LOW);
|
|
#endif
|
|
#endif
|
|
|
|
int err = lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS);
|
|
|
|
if (err != RADIOLIB_ERR_NONE)
|
|
LOG_ERROR("SX128X startReceive %s%d", radioLibErr, err);
|
|
assert(err == RADIOLIB_ERR_NONE);
|
|
|
|
RadioLibInterface::startReceive();
|
|
|
|
// 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 SX128xInterface<T>::isChannelActive()
|
|
{
|
|
// check if we can detect a LoRa preamble on the current channel
|
|
ChannelScanConfig_t cfg = {.cad = {.symNum = NUM_SYM_CAD_24GHZ,
|
|
.detPeak = 0,
|
|
.detMin = 0,
|
|
.exitMode = 0,
|
|
.timeout = 0,
|
|
.irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS,
|
|
.irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK}};
|
|
int16_t result;
|
|
|
|
setStandby();
|
|
result = lora.scanChannel(cfg);
|
|
if (result == RADIOLIB_LORA_DETECTED)
|
|
return true;
|
|
if (result != RADIOLIB_CHANNEL_FREE)
|
|
LOG_ERROR("SX128X scanChannel %s%d", radioLibErr, result);
|
|
assert(result != RADIOLIB_ERR_WRONG_MODEM);
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
|
template <typename T> bool SX128xInterface<T>::isActivelyReceiving()
|
|
{
|
|
return receiveDetected(lora.getIrqStatus(), RADIOLIB_SX128X_IRQ_HEADER_VALID, RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED);
|
|
}
|
|
|
|
template <typename T> bool SX128xInterface<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 `SX128x`
|
|
LOG_DEBUG("SX128x entering sleep mode"); // (FIXME, don't keep config)
|
|
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 SX128X_POWER_EN
|
|
digitalWrite(SX128X_POWER_EN, LOW);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
#endif |