#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 #ifndef SX128X_MAX_POWER #define SX128X_MAX_POWER 13 #endif template SX128xInterface::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 bool SX128xInterface::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 (settingsMap[rxen] != RADIOLIB_NC) { pinMode(settingsMap[rxen], OUTPUT); digitalWrite(settingsMap[rxen], LOW); // Set low before becoming an output } if (settingsMap[txen] != RADIOLIB_NC) { pinMode(settingsMap[txen], OUTPUT); digitalWrite(settingsMap[txen], 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(); if (power > SX128X_MAX_POWER) // This chip has lower power limits than some power = SX128X_MAX_POWER; limitPower(); 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 ((config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (res == RADIOLIB_ERR_INVALID_FREQUENCY)) { LOG_WARN("Radio chip 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. Skipping 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 && settingsMap[rxen] != RADIOLIB_NC && settingsMap[txen] != RADIOLIB_NC) { lora.setRfSwitchPins(settingsMap[rxen], settingsMap[txen]); } #endif if (res == RADIOLIB_ERR_NONE) res = lora.setCRC(2); if (res == RADIOLIB_ERR_NONE) startReceive(); // start receiving return res == RADIOLIB_ERR_NONE; } template bool SX128xInterface::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); 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 void INTERRUPT_ATTR SX128xInterface::disableInterrupt() { lora.clearDio1Action(); } template bool SX128xInterface::wideLora() { return true; } template void SX128xInterface::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 (settingsMap[rxen] != RADIOLIB_NC) { digitalWrite(settingsMap[rxen], LOW); } if (settingsMap[txen] != RADIOLIB_NC) { digitalWrite(settingsMap[txen], 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 void SX128xInterface::addReceiveMetadata(meshtastic_MeshPacket *mp) { // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); mp->rx_snr = lora.getSNR(); mp->rx_rssi = lround(lora.getRSSI()); } /** We override to turn on transmitter power as needed. */ template void SX128xInterface::configHardwareForSend() { #if ARCH_PORTDUINO if (settingsMap[txen] != RADIOLIB_NC) { digitalWrite(settingsMap[txen], HIGH); } if (settingsMap[rxen] != RADIOLIB_NC) { digitalWrite(settingsMap[rxen], 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 void SX128xInterface::startReceive() { #ifdef SLEEP_ONLY sleep(); #else setStandby(); #if ARCH_PORTDUINO if (settingsMap[rxen] != RADIOLIB_NC) { digitalWrite(settingsMap[rxen], HIGH); } if (settingsMap[txen] != RADIOLIB_NC) { digitalWrite(settingsMap[txen], 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 // We use the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving int err = lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED); 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 bool SX128xInterface::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; 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 bool SX128xInterface::isActivelyReceiving() { return receiveDetected(lora.getIrqStatus(), RADIOLIB_SX128X_IRQ_HEADER_VALID, RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED); } template bool SX128xInterface::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