diff --git a/Drivers/RadioLibCompat/Source/RadiolibThreadedDevice.cpp b/Drivers/RadioLibCompat/Source/RadiolibThreadedDevice.cpp new file mode 100644 index 00000000..a86dbebe --- /dev/null +++ b/Drivers/RadioLibCompat/Source/RadiolibThreadedDevice.cpp @@ -0,0 +1,99 @@ +#include "RadiolibThreadedDevice.h" +#include + +constexpr const char* TAG = "RadiolibThreadedDevice"; + +bool RadiolibThreadedDevice::start(const Modulation modulation) { + auto lock = getMutex().asScopedLock(); + + if (!isCapableOf(modulation)) { + TT_LOG_E(TAG, "Can't start device \"%s\", not capable of modulation \"%s\"", getName().c_str(), toString(modulation)); + return false; + } + + lock.lock(); + + if (thread != nullptr && thread->getState() != tt::Thread::State::Stopped) { + TT_LOG_W(TAG, "Already started"); + return true; + } + + threadInterrupted = false; + + TT_LOG_I(TAG, "Starting thread"); + setState(State::PendingOn); + + thread = std::make_unique( + threadName, + threadSize, + [this, modulation]() { + return this->threadMain(modulation); + } + ); + thread->setPriority(tt::Thread::Priority::High); + thread->start(); + + TT_LOG_I(TAG, "Starting finished"); + return true; +} + +bool RadiolibThreadedDevice::stop() { + auto lock = getMutex().asScopedLock(); + lock.lock(); + + setState(State::PendingOff); + + if (thread != nullptr) { + threadInterrupted = true; + interruptSignal(); + + // Detach thread, it will auto-delete when leaving the current scope + auto old_thread = std::move(thread); + + if (old_thread->getState() != tt::Thread::State::Stopped) { + // Unlock so thread can lock + lock.unlock(); + // Wait for thread to finish + old_thread->join(); + // Re-lock to continue logic below + lock.lock(); + } + } + + setState(State::Off); + + return true; +} + +bool RadiolibThreadedDevice::isThreadInterrupted() const { + auto lock = getMutex().asScopedLock(); + lock.lock(); + return threadInterrupted; +} + +int32_t RadiolibThreadedDevice::threadMain(const Modulation modulation) { + + int rc = doBegin(modulation); + if (rc != 0) { + return rc; + } + setState(State::On); + + while (!isThreadInterrupted()) { + doListen(); + + // Thread might've been interrupted in the meanwhile + if (isThreadInterrupted()) { + break; + } + + if (getTxQueueSize() > 0) { + doTransmit(); + } else { + doReceive(); + } + } + + doEnd(); + return 0; +} diff --git a/Drivers/RadioLibCompat/Source/RadiolibThreadedDevice.h b/Drivers/RadioLibCompat/Source/RadiolibThreadedDevice.h new file mode 100644 index 00000000..6830c6e4 --- /dev/null +++ b/Drivers/RadioLibCompat/Source/RadiolibThreadedDevice.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +class RadiolibThreadedDevice : public tt::hal::radio::RadioDevice { + +private: + std::string threadName; + size_t threadSize; + std::unique_ptr _Nullable thread; + bool threadInterrupted = false; + +protected: + virtual int32_t threadMain(const Modulation modulation); + bool isThreadInterrupted() const; + + virtual void interruptSignal() = 0; + + virtual int doBegin(const Modulation modulation) = 0; + virtual void doEnd() = 0; + virtual void doTransmit() = 0; + virtual void doListen() = 0; + virtual void doReceive() = 0; + +public: + explicit RadiolibThreadedDevice(const std::string& threadName, const size_t threadSize) + : threadName(threadName) + , threadSize(threadSize) + {} + + ~RadiolibThreadedDevice() override = default; + + virtual bool start(const Modulation modulation) override; + virtual bool stop() override; + +}; diff --git a/Drivers/SX126x/Source/Sx1262.cpp b/Drivers/SX126x/Source/Sx1262.cpp index e08fcde9..a7033a84 100644 --- a/Drivers/SX126x/Source/Sx1262.cpp +++ b/Drivers/SX126x/Source/Sx1262.cpp @@ -83,36 +83,21 @@ void Sx1262::unregisterDio1Isr() { gpio_wakeup_disable(configuration.irqPin); gpio_set_intr_type(configuration.irqPin, GPIO_INTR_DISABLE); } -/* -void IRAM_ATTR Sx1262::dio1Event() { - static const auto DRAM_ATTR rxbit = RADIO_RECEIVED_BIT; - static const auto DRAM_ATTR txbit = RADIO_TRANSMITTED_BIT; - - switch (exchangeState) { - case ExchangeState::Receive: - getEventFlag().set(rxbit); - break; - case ExchangeState::TransmitWaiting: - getEventFlag().set(txbit); - break; - default: - break; - } - - gpio_set_level(GPIO_NUM_9, 1); -}*/ - void IRAM_ATTR Sx1262::dio1Event() { static const auto DRAM_ATTR bit = SX1262_DIO1_EVENT_BIT; - getEventFlag().set(bit); - - gpio_set_level(GPIO_NUM_9, 1); + events.set(bit); } -int32_t Sx1262::threadMain(const Modulation modulation) { - using enum ExchangeState; +void Sx1262::txQueuedSignal() { + events.set(SX1262_QUEUED_TX_BIT); +} +void Sx1262::interruptSignal() { + events.set(SX1262_INTERRUPT_BIT); +} + +int Sx1262::doBegin(const Modulation modulation) { uint16_t rc = RADIOLIB_ERR_NONE; if (modulation == Modulation::LoRa) { @@ -127,13 +112,6 @@ int32_t Sx1262::threadMain(const Modulation modulation) { configuration.tcxoVoltage, configuration.useRegulatorLdo ); - /* - radio.forceLDRO(false); - radio.setCRC(true); - radio.invertIQ(false); - radio.setWhitening(true, 0x00FF); - radio.explicitHeader();*/ - } else if (modulation == Modulation::Fsk) { rc = radio.beginFSK( frequency, @@ -158,79 +136,69 @@ int32_t Sx1262::threadMain(const Modulation modulation) { } registerDio1Isr(); - setState(State::On); + return 0; +} - TT_LOG_I(TAG, "SX1262 device ready to receive!"); - exchangeState = Receive; +void Sx1262::doEnd() { + unregisterDio1Isr(); +} - while (!isThreadInterrupted()) { - radio.startReceive(); - TT_LOG_I(TAG, "WAIT FLAG"); - auto eventFlags = getEventFlag().wait(RADIO_TERMINATE_BIT | SX1262_DIO1_EVENT_BIT | RADIO_TRANSMIT_QUEUED_BIT); - TT_LOG_W(TAG, "Event, flag=%X", eventFlags); +void Sx1262::doTransmit() { + currentTx = popNextQueuedTx(); + radio.standby(); + uint16_t rc = radio.startTransmit(currentTx.packet.data.data(), currentTx.packet.data.size()); + if (rc == RADIOLIB_ERR_NONE) { + currentTx.callback(currentTx.id, TransmissionState::PendingTransmit); + + TT_LOG_I(TAG, "WAIT TX FLAG"); + auto txEventFlags = events.wait(SX1262_INTERRUPT_BIT | SX1262_DIO1_EVENT_BIT, tt::EventFlag::WaitAny, pdMS_TO_TICKS(2000)); + TT_LOG_W(TAG, "Event, flag=%X", txEventFlags); // Thread might've been interrupted in the meanwhile if (isThreadInterrupted()) { - break; + return; } - - if ((eventFlags & RADIO_TRANSMIT_QUEUED_BIT) && (getTxQueueSize() > 0)) { - currentTx = popNextQueuedTx(); - radio.standby(); - uint16_t rc = radio.startTransmit(currentTx.packet.data.data(), currentTx.packet.data.size()); - if (rc == RADIOLIB_ERR_NONE) { - exchangeState = TransmitWaiting; - currentTx.callback(currentTx.id, TransmissionState::PendingTransmit); - - TT_LOG_I(TAG, "WAIT TX FLAG"); - auto txEventFlags = getEventFlag().wait(RADIO_TERMINATE_BIT | SX1262_DIO1_EVENT_BIT, tt::EventFlag::WaitAny, pdMS_TO_TICKS(2000)); - TT_LOG_W(TAG, "Event, flag=%X", txEventFlags); - - // Thread might've been interrupted in the meanwhile - if (isThreadInterrupted()) { - break; - } - if (txEventFlags & SX1262_DIO1_EVENT_BIT) { - currentTx.callback(currentTx.id, TransmissionState::Transmitted); - } else { - currentTx.callback(currentTx.id, TransmissionState::Timeout); - } - exchangeState = Receive; - - } else { - TT_LOG_E(TAG, "Error transmitting id=%d, rc=%hi", currentTx.id, rc); - currentTx.callback(currentTx.id, TransmissionState::Error); - exchangeState = Receive; - } - } else if (eventFlags & SX1262_DIO1_EVENT_BIT) { - uint16_t rxSize = radio.getPacketLength(true); - std::vector data(rxSize); - uint16_t rc = radio.readData(data.data(), rxSize); - if (rc != RADIOLIB_ERR_NONE) { - TT_LOG_E(TAG, "Error receiving data, RadioLib returned %hi", rc); - } else if(rxSize == 0) { - //TT_LOG_W(TAG, "Received data length 0"); - } else { - float rssi = radio.getRSSI(); - float snr = radio.getSNR(); - TT_LOG_I(TAG, "LoRa RX size=%d RSSI=%f SNR=%f", rxSize, rssi, snr); - auto rxPacket = tt::hal::radio::RxPacket { - .data = data, - .rssi = rssi, - .snr = snr - }; - - publishRx(rxPacket); - } - - // A delay is needed before a new command - vTaskDelay(pdMS_TO_TICKS(100)); - gpio_set_level(GPIO_NUM_9, 0); + if (txEventFlags & SX1262_DIO1_EVENT_BIT) { + currentTx.callback(currentTx.id, TransmissionState::Transmitted); } else { - TT_LOG_W(TAG, "Unhandled event, flag=%X", eventFlags); + currentTx.callback(currentTx.id, TransmissionState::Timeout); } + + } else { + TT_LOG_E(TAG, "Error transmitting id=%d, rc=%hi", currentTx.id, rc); + currentTx.callback(currentTx.id, TransmissionState::Error); + } +} + +void Sx1262::doListen() { + radio.startReceive(); + TT_LOG_I(TAG, "WAIT FLAG"); + auto eventFlags = events.wait(SX1262_INTERRUPT_BIT | SX1262_DIO1_EVENT_BIT | SX1262_QUEUED_TX_BIT); + TT_LOG_W(TAG, "Event, flag=%X", eventFlags); +} + +void Sx1262::doReceive() { + uint16_t rxSize = radio.getPacketLength(true); + std::vector data(rxSize); + uint16_t rc = radio.readData(data.data(), rxSize); + if (rc != RADIOLIB_ERR_NONE) { + TT_LOG_E(TAG, "Error receiving data, RadioLib returned %hi", rc); + } else if(rxSize == 0) { + //TT_LOG_W(TAG, "Received data length 0"); + } else { + float rssi = radio.getRSSI(); + float snr = radio.getSNR(); + TT_LOG_I(TAG, "LoRa RX size=%d RSSI=%f SNR=%f", rxSize, rssi, snr); + auto rxPacket = tt::hal::radio::RxPacket { + .data = data, + .rssi = rssi, + .snr = snr + }; + + publishRx(rxPacket); } - unregisterDio1Isr(); - return 0; + // A delay is needed before a new command + vTaskDelay(pdMS_TO_TICKS(100)); } + diff --git a/Drivers/SX126x/Source/Sx1262.h b/Drivers/SX126x/Source/Sx1262.h index bc2b49a8..78460aa8 100644 --- a/Drivers/SX126x/Source/Sx1262.h +++ b/Drivers/SX126x/Source/Sx1262.h @@ -1,14 +1,16 @@ #pragma once -#include #include +#include +#include #include #include "RadiolibTactilityHal.h" +#include "RadiolibThreadedDevice.h" #include -class Sx1262 final : public tt::hal::radio::RadioDevice { +class Sx1262 final : public RadiolibThreadedDevice { public: struct Configuration { @@ -23,23 +25,18 @@ public: }; private: - static constexpr auto SX1262_DIO1_EVENT_BIT = BIT8; - - enum class ExchangeState { - Idle, - Receive, - //TransmitInitiated, - TransmitWaiting - }; + static constexpr auto SX1262_INTERRUPT_BIT = BIT0; + static constexpr auto SX1262_DIO1_EVENT_BIT = BIT1; + static constexpr auto SX1262_QUEUED_TX_BIT = BIT2; std::string name; const Configuration configuration; std::shared_ptr lock; + tt::EventFlag events; RadiolibTactilityHal hal; Module radioModule; SX1262 radio; TxItem currentTx; - ExchangeState exchangeState; int8_t power = 0; float frequency = 0.0; @@ -51,20 +48,29 @@ private: float bitRate = 0.0; float frequencyDeviation = 0.0; - int32_t threadMain(); void registerDio1Isr(); void unregisterDio1Isr(); +protected: + virtual void txQueuedSignal() override; + virtual void interruptSignal() override; + + virtual int doBegin(const Modulation modulation) override; + virtual void doEnd() override; + virtual void doTransmit() override; + virtual void doListen() override; + virtual void doReceive() override; + public: explicit Sx1262(const std::string& name, const Configuration& configuration, std::shared_ptr lock = nullptr) - : RadioDevice(name, 4096) + : RadiolibThreadedDevice(name, 4096) , name(name) , configuration(configuration) , hal(configuration.spiHostDevice, configuration.spiFrequency, configuration.csPin, lock) , radioModule(&hal, configuration.csPin, configuration.irqPin, configuration.resetPin, configuration.busyPin) , radio(&radioModule) - , exchangeState(ExchangeState::Idle) {} + {} ~Sx1262() override = default; @@ -78,12 +84,5 @@ public: return (modulation == Modulation::Fsk) || (modulation == Modulation::LoRa); } - bool hasReceived(); - void dio1Event(); - //void IRAM_ATTR setRxEvent() { rxFlag = true; } - -protected: - - int32_t threadMain(const Modulation modulation) override; }; diff --git a/Tactility/Include/Tactility/hal/radio/RadioDevice.h b/Tactility/Include/Tactility/hal/radio/RadioDevice.h index 455fa3e2..b6531686 100644 --- a/Tactility/Include/Tactility/hal/radio/RadioDevice.h +++ b/Tactility/Include/Tactility/hal/radio/RadioDevice.h @@ -2,8 +2,6 @@ #include "../Device.h" -#include -#include #include #include @@ -80,31 +78,19 @@ private: std::shared_ptr> onData; }; - std::string threadName; - size_t threadSize; State state; - EventFlag events; Mutex mutex = Mutex(Mutex::Type::Recursive); - std::unique_ptr _Nullable thread; - bool threadInterrupted = false; std::vector rxSubscriptions; std::deque txQueue; TxId lastTxId = 0; RxSubscriptionId lastRxSubscriptionId = 0; protected: - - static constexpr auto RADIO_TERMINATE_BIT = BIT0; - static constexpr auto RADIO_TRANSMIT_QUEUED_BIT = BIT1; - static constexpr auto RADIO_RECEIVED_BIT = BIT2; - static constexpr auto RADIO_TRANSMITTED_BIT = BIT3; - - virtual int32_t threadMain(const Modulation modulation) = 0; - Mutex &getMutex() { return mutex; } - EventFlag &getEventFlag() { return events; } - bool isThreadInterrupted() const; + const Mutex &getMutex() const { return mutex; } void setState(State newState); + virtual void txQueuedSignal() = 0; + size_t getTxQueueSize() const { auto lock = mutex.asScopedLock(); lock.lock(); @@ -125,10 +111,8 @@ protected: void publishRx(const RxPacket& packet); public: - explicit RadioDevice(const std::string& threadName, const size_t threadSize) - : threadName(threadName) - , threadSize(threadSize) - , state(State::Off) {} + explicit RadioDevice() + : state(State::Off) {} ~RadioDevice() override = default; @@ -137,8 +121,8 @@ public: virtual bool configure(const Parameter parameter, const float value) = 0; virtual bool isCapableOf(const Modulation modulation) = 0; - bool start(const Modulation modulation); - bool stop(); + virtual bool start(const Modulation modulation) = 0; + virtual bool stop() = 0; TxId transmit(const TxPacket& packet, TxStateCallback callback) { auto lock = mutex.asScopedLock(); @@ -147,7 +131,7 @@ public: txQueue.push_back(TxItem{.id = txId, .packet = packet, .callback = callback}); callback(txId, TransmissionState::Queued); lastTxId++; - getEventFlag().set(RADIO_TRANSMIT_QUEUED_BIT); + txQueuedSignal(); return txId; } diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 0161f762..fdc686f6 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -60,6 +60,7 @@ namespace app { namespace boot { extern const AppManifest manifest; } namespace calculator { extern const AppManifest manifest; } namespace chat { extern const AppManifest manifest; } + namespace chirp { extern const AppManifest manifest; } namespace development { extern const AppManifest manifest; } namespace display { extern const AppManifest manifest; } namespace files { extern const AppManifest manifest; } @@ -121,6 +122,7 @@ static void registerSystemApps() { addApp(app::wifiapsettings::manifest); addApp(app::wificonnect::manifest); addApp(app::wifimanage::manifest); + addApp(app::chirp::manifest); #if defined(CONFIG_TINYUSB_MSC_ENABLED) && CONFIG_TINYUSB_MSC_ENABLED addApp(app::usbsettings::manifest); diff --git a/Tactility/Source/hal/radio/RadioDevice.cpp b/Tactility/Source/hal/radio/RadioDevice.cpp index ec750ed8..cb8a30c7 100644 --- a/Tactility/Source/hal/radio/RadioDevice.cpp +++ b/Tactility/Source/hal/radio/RadioDevice.cpp @@ -5,74 +5,6 @@ namespace tt::hal::radio { constexpr const char* TAG = "RadioDevice"; -bool RadioDevice::start(const Modulation modulation) { - auto lock = mutex.asScopedLock(); - - if (!isCapableOf(modulation)) { - TT_LOG_E(TAG, "Can't start device \"%s\", not capable of modulation \"%s\"", getName().c_str(), toString(modulation)); - return false; - } - - lock.lock(); - - if (thread != nullptr && thread->getState() != Thread::State::Stopped) { - TT_LOG_W(TAG, "Already started"); - return true; - } - - threadInterrupted = false; - - TT_LOG_I(TAG, "Starting thread"); - setState(State::PendingOn); - - thread = std::make_unique( - threadName, - threadSize, - [this, modulation]() { - return this->threadMain(modulation); - } - ); - thread->setPriority(tt::Thread::Priority::High); - thread->start(); - - TT_LOG_I(TAG, "Starting finished"); - return true; -} - -bool RadioDevice::stop() { - auto lock = mutex.asScopedLock(); - lock.lock(); - - setState(State::PendingOff); - - if (thread != nullptr) { - threadInterrupted = true; - getEventFlag().set(RADIO_TERMINATE_BIT); - - // Detach thread, it will auto-delete when leaving the current scope - auto old_thread = std::move(thread); - - if (old_thread->getState() != Thread::State::Stopped) { - // Unlock so thread can lock - lock.unlock(); - // Wait for thread to finish - old_thread->join(); - // Re-lock to continue logic below - lock.lock(); - } - } - - setState(State::Off); - - return true; -} - -bool RadioDevice::isThreadInterrupted() const { - auto lock = mutex.asScopedLock(); - lock.lock(); - return threadInterrupted; -} - RadioDevice::State RadioDevice::getState() const { auto lock = mutex.asScopedLock(); lock.lock();