diff --git a/Boards/LilygoTLoraPager/CMakeLists.txt b/Boards/LilygoTLoraPager/CMakeLists.txt index 02e89f02..d926dcc4 100644 --- a/Boards/LilygoTLoraPager/CMakeLists.txt +++ b/Boards/LilygoTLoraPager/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lcd ST7796 BQ25896 BQ27220 TCA8418 DRV2605 PwmBacklight driver esp_adc + REQUIRES Tactility esp_lcd ST7796 BQ25896 BQ27220 TCA8418 DRV2605 SX126x PwmBacklight driver esp_adc ) diff --git a/Boards/LilygoTLoraPager/Source/LilygoTloraPager.cpp b/Boards/LilygoTLoraPager/Source/LilygoTloraPager.cpp index a2e6f48a..9a04676e 100644 --- a/Boards/LilygoTLoraPager/Source/LilygoTloraPager.cpp +++ b/Boards/LilygoTLoraPager/Source/LilygoTloraPager.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #define TPAGER_SPI_TRANSFER_SIZE_LIMIT (480 * 222 * (LV_COLOR_DEPTH / 8)) @@ -22,6 +23,17 @@ static DeviceVector createDevices() { auto tca8418 = std::make_shared(I2C_NUM_0); auto keyboard = std::make_shared(tca8418); + auto sx1262 = std::make_shared("SX1262", Sx1262::Configuration{ + .spiHostDevice = SPI2_HOST, + .spiFrequency = 10'000'000, + .csPin = GPIO_NUM_36, + .resetPin = GPIO_NUM_47, + .busyPin = GPIO_NUM_48, + .irqPin = GPIO_NUM_14, + .tcxoVoltage = 3.0, + .useRegulatorLdo = false + }); + return std::vector> { tca8418, std::make_shared(I2C_NUM_0), @@ -31,7 +43,8 @@ static DeviceVector createDevices() { createTpagerSdCard(), createDisplay(), keyboard, - std::make_shared() + std::make_shared(), + sx1262 }; } diff --git a/Drivers/RadioLibCompat/Source/RadiolibTactilityHal.cpp b/Drivers/RadioLibCompat/Source/RadiolibTactilityHal.cpp index 58996b15..b7eda5d5 100644 --- a/Drivers/RadioLibCompat/Source/RadiolibTactilityHal.cpp +++ b/Drivers/RadioLibCompat/Source/RadiolibTactilityHal.cpp @@ -114,20 +114,24 @@ long RadiolibTactilityHal::pulseIn(uint32_t pin, uint32_t state, unsigned long t } void RadiolibTactilityHal::spiBegin() { - spi_device_interface_config_t devcfg = {}; - devcfg.clock_speed_hz = 1 * 1000 * 1000; // 1MHz - devcfg.mode = 0; - devcfg.spics_io_num = csPin; - devcfg.queue_size = 1; - esp_err_t ret = spi_bus_add_device(spiHostDevice, &devcfg, &spiDeviceHandle); - if (ret != ESP_OK) { - TT_LOG_E(TAG, "Failed to add SPI device: %s", esp_err_to_name(ret)); + if (!spiInitialized) { + TT_LOG_I(TAG, "SPI Begin!"); + spi_device_interface_config_t devcfg = {}; + devcfg.clock_speed_hz = spiFrequency; + devcfg.mode = 0; + devcfg.spics_io_num = csPin; + devcfg.queue_size = 1; + esp_err_t ret = spi_bus_add_device(spiHostDevice, &devcfg, &spiDeviceHandle); + if (ret != ESP_OK) { + TT_LOG_E(TAG, "Failed to add SPI device: %s", esp_err_to_name(ret)); + } + spiInitialized = true; } } void RadiolibTactilityHal::spiBeginTransaction() { - // not needed - in ESP32 Arduino core, this function - // repeats clock div, mode and bit order configuration + // This function is used to set up the transaction (speed, bit order, mode, ...). + // With the ESP-IDF HAL this is automatically done, so no code needed. } void RadiolibTactilityHal::spiTransfer(uint8_t* out, size_t len, uint8_t* in) { @@ -151,5 +155,8 @@ void RadiolibTactilityHal::spiEndTransaction() { } void RadiolibTactilityHal::spiEnd() { - + if (spiInitialized) { + spi_bus_remove_device(spiDeviceHandle); + spiInitialized = false; + } } diff --git a/Drivers/RadioLibCompat/Source/RadiolibTactilityHal.h b/Drivers/RadioLibCompat/Source/RadiolibTactilityHal.h index e26ee1bb..a8575868 100644 --- a/Drivers/RadioLibCompat/Source/RadiolibTactilityHal.h +++ b/Drivers/RadioLibCompat/Source/RadiolibTactilityHal.h @@ -16,12 +16,14 @@ class RadiolibTactilityHal : public RadioLibHal { private: spi_host_device_t spiHostDevice; + int spiFrequency; gpio_num_t csPin; spi_device_handle_t spiDeviceHandle; std::shared_ptr lock; + bool spiInitialized; public: - explicit RadiolibTactilityHal(spi_host_device_t spiHostDevice, gpio_num_t csPin) + explicit RadiolibTactilityHal(spi_host_device_t spiHostDevice, int spiFrequency, gpio_num_t csPin, std::shared_ptr spiLock) : RadioLibHal( GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, @@ -30,8 +32,12 @@ public: GPIO_INTR_POSEDGE, GPIO_INTR_NEGEDGE) , spiHostDevice(spiHostDevice) + , spiFrequency(spiFrequency) , csPin(csPin) - , lock(tt::hal::spi::getLock(spiHostDevice)) {} + , lock(spiLock) + , spiInitialized(false) { + if (!lock) lock = tt::hal::spi::getLock(spiHostDevice); + } void init() override; void term() override; diff --git a/Drivers/SX126x/Source/Sx1262.cpp b/Drivers/SX126x/Source/Sx1262.cpp index 5289ac95..d45cf6b3 100644 --- a/Drivers/SX126x/Source/Sx1262.cpp +++ b/Drivers/SX126x/Source/Sx1262.cpp @@ -6,7 +6,7 @@ constexpr const char* TAG = "Sx1262"; void IRAM_ATTR dio1handler(void* context) { - ((Sx1262*)context)->setRxEvent(); + ((Sx1262*)context)->dio1Event(); } bool Sx1262::configure(const Parameter parameter, const float value) { @@ -15,24 +15,31 @@ bool Sx1262::configure(const Parameter parameter, const float value) { switch (parameter) { case Power: power = value; + TT_LOG_I(TAG, "Configure %s=%d", toString(parameter), power); return true; case Frequency: frequency = value; + TT_LOG_I(TAG, "Configure %s=%f", toString(parameter), frequency); return true; case Bandwidth: bandwidth = value; + TT_LOG_I(TAG, "Configure %s=%f", toString(parameter), bandwidth); return true; case SpreadFactor: spreadFactor = value; + TT_LOG_I(TAG, "Configure %s=%d", toString(parameter), spreadFactor); return true; case CodingRate: codingRate = value; + TT_LOG_I(TAG, "Configure %s=%d", toString(parameter), codingRate); return true; case SyncWord: syncWord = value; + TT_LOG_I(TAG, "Configure %s=%X", toString(parameter), syncWord); return true; case PreambleLength: preambleLength = value; + TT_LOG_I(TAG, "Configure %s=%d", toString(parameter), preambleLength); return true; case DataRate: bitRate = value; @@ -48,6 +55,21 @@ bool Sx1262::configure(const Parameter parameter, const float value) { } void Sx1262::registerDio1Isr() { + hal.pinMode(GPIO_NUM_9, GPIO_MODE_OUTPUT); + gpio_set_level(GPIO_NUM_9, 0); + + gpio_hal_context_t gpiohal; + gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0); + + gpio_config_t conf = { + .pin_bit_mask = (1ULL<pin[configuration.irqPin].int_type, + }; + gpio_config(&conf); + // We cannot use the RadioLib API to register this action, // as it does not have the capability to pass an instance pointer via context. // A trampoline has been tried, but is not linkable to be in IRAM_ATTR (dangerous relocation). @@ -61,8 +83,35 @@ 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); +} int32_t Sx1262::threadMain(const Modulation modulation) { + using enum ExchangeState; uint16_t rc = RADIOLIB_ERR_NONE; @@ -78,6 +127,13 @@ 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, @@ -101,41 +157,85 @@ int32_t Sx1262::threadMain(const Modulation modulation) { return -1; } + registerDio1Isr(); setState(State::On); TT_LOG_I(TAG, "SX1262 device ready to receive!"); + exchangeState = Receive; while (!isThreadInterrupted()) { radio.startReceive(); - getEventFlag().wait(RADIO_TERMINATE_BIT, RADIO_RECEIVED_BIT); + 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); // Thread might've been interrupted in the meanwhile if (isThreadInterrupted()) { break; } - uint16_t rxSize = radio.getPacketLength(true); - uint8_t *dataBuffer = new uint8_t[rxSize]; - uint16_t rc = radio.readData(dataBuffer, rxSize); - if (rc != RADIOLIB_ERR_NONE) { - TT_LOG_E(TAG, "Error receiving data, RadioLib returned %hi", rc); - } else { - float rssi = radio.getRSSI(); - float snr = radio.getSNR(); - TT_LOG_I(TAG, "LoRa RX size=%d RSSI=%f SNR=%f", rxSize, rssi, snr); - std::string message((char*)dataBuffer, rxSize); - TT_LOG_I(TAG, "msg=%s", message.c_str()); - auto rxPacket = tt::hal::radio::RxPacket { - .data = dataBuffer, - .size = rxSize, - .rssi = rssi, - .snr = snr - }; + if ((eventFlags & RADIO_TRANSMIT_QUEUED_BIT) && (getTxQueueSize() > 0)) { + currentTx = popNextQueuedTx(); + radio.standby(); + uint16_t rc = radio.startTransmit(currentTx.data.data(), currentTx.data.size()); + if (rc == RADIOLIB_ERR_NONE) { + exchangeState = TransmitWaiting; + currentTx.callback(currentTx.id, TransmissionState::PendingTransmit); - publishRx(rxPacket); + 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); + uint8_t *dataBuffer = new uint8_t[rxSize]; + uint16_t rc = radio.readData(dataBuffer, 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); + std::string message((char*)dataBuffer, rxSize); + TT_LOG_I(TAG, "msg=%s", message.c_str()); + auto rxPacket = tt::hal::radio::RxPacket { + .data = dataBuffer, + .size = rxSize, + .rssi = rssi, + .snr = snr + }; + + publishRx(rxPacket); + } + + delete[] dataBuffer; + + // A delay is needed before a new command + vTaskDelay(pdMS_TO_TICKS(100)); + gpio_set_level(GPIO_NUM_9, 0); + } else { + TT_LOG_W(TAG, "Unhandled event, flag=%X", eventFlags); } - delete[] dataBuffer; } + unregisterDio1Isr(); return 0; } diff --git a/Drivers/SX126x/Source/Sx1262.h b/Drivers/SX126x/Source/Sx1262.h index 5ea66bcd..2c7e3d3e 100644 --- a/Drivers/SX126x/Source/Sx1262.h +++ b/Drivers/SX126x/Source/Sx1262.h @@ -13,6 +13,7 @@ class Sx1262 final : public tt::hal::radio::RadioDevice { public: struct Configuration { spi_host_device_t spiHostDevice; + int spiFrequency; gpio_num_t csPin; gpio_num_t resetPin; gpio_num_t busyPin; @@ -22,6 +23,14 @@ public: }; private: + static constexpr auto SX1262_DIO1_EVENT_BIT = BIT8; + + enum class ExchangeState { + Idle, + Receive, + //TransmitInitiated, + TransmitWaiting + }; std::string name; const Configuration configuration; @@ -29,7 +38,9 @@ private: RadiolibTactilityHal hal; Module radioModule; SX1262 radio; - IsrTrampolines::SlotNumber isrTrampolineSlot; + TxPacket currentTx; + ExchangeState exchangeState; + int8_t power = 0; float frequency = 0.0; float bandwidth = 0.0; @@ -46,19 +57,16 @@ private: public: - explicit Sx1262(const std::string& name, const Configuration& configuration) - : RadioDevice(name, 1024) + explicit Sx1262(const std::string& name, const Configuration& configuration, std::shared_ptr lock = nullptr) + : RadioDevice(name, 4096) , name(name) , configuration(configuration) - , hal(configuration.spiHostDevice, configuration.csPin) + , hal(configuration.spiHostDevice, configuration.spiFrequency, configuration.csPin, lock) , radioModule(&hal, configuration.csPin, configuration.irqPin, configuration.resetPin, configuration.busyPin) - , radio(&radioModule) { - registerDio1Isr(); - } + , radio(&radioModule) + , exchangeState(ExchangeState::Idle) {} - ~Sx1262() override { - unregisterDio1Isr(); - } + ~Sx1262() override = default; std::string getName() const override { return name; } @@ -70,7 +78,10 @@ public: return (modulation == Modulation::Fsk) || (modulation == Modulation::LoRa); } - void IRAM_ATTR setRxEvent() { getEventFlag().set(RADIO_RECEIVED_BIT); } + bool hasReceived(); + + void dio1Event(); + //void IRAM_ATTR setRxEvent() { rxFlag = true; } protected: diff --git a/Tactility/Include/Tactility/hal/radio/RadioDevice.h b/Tactility/Include/Tactility/hal/radio/RadioDevice.h index 96f8ce0e..3d54e8b4 100644 --- a/Tactility/Include/Tactility/hal/radio/RadioDevice.h +++ b/Tactility/Include/Tactility/hal/radio/RadioDevice.h @@ -1,10 +1,13 @@ #pragma once #include "../Device.h" + #include #include #include #include + +#include #include namespace tt::hal::radio { @@ -39,6 +42,8 @@ public: }; typedef int RxSubscriptionId; + typedef int TxId; + enum class State { PendingOn, @@ -48,8 +53,24 @@ public: Off }; -private: + enum class TransmissionState { + Queued, + PendingTransmit, + Transmitted, + Timeout, + Error + }; + using TxStateCallback = std::function; + +protected: + struct TxPacket { + TxId id; + std::vector data; + TxStateCallback callback; + }; + +private: struct RxSubscription { RxSubscriptionId id; std::shared_ptr> onData; @@ -63,12 +84,16 @@ private: 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_RECEIVED_BIT = BIT1; + 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; } @@ -76,6 +101,23 @@ protected: bool isThreadInterrupted() const; void setState(State newState); + size_t getTxQueueSize() const { + auto lock = mutex.asScopedLock(); + lock.lock(); + const auto size = txQueue.size(); + return size; + } + + TxPacket popNextQueuedTx() { + auto lock = mutex.asScopedLock(); + lock.lock(); + + auto tx = std::move(txQueue.front()); + txQueue.pop_front(); + + return tx; + } + void publishRx(const RxPacket& packet); public: @@ -94,6 +136,21 @@ public: bool start(const Modulation modulation); bool stop(); + TxId transmit(uint8_t *data, const size_t size, TxStateCallback callback) { + return transmit(std::vector(data, data + size), callback); + } + + TxId transmit(const std::vector& data, TxStateCallback callback) { + auto lock = mutex.asScopedLock(); + lock.lock(); + const auto txId = lastTxId; + txQueue.push_back(TxPacket{.id = txId, .data = data, .callback = callback}); + callback(txId, TransmissionState::Queued); + lastTxId++; + getEventFlag().set(RADIO_TRANSMIT_QUEUED_BIT); + return txId; + } + RxSubscriptionId subscribeRx(const std::function& onData) { auto lock = mutex.asScopedLock(); lock.lock();