From 9c6fa9d152d96106583a67a822ed6b2451ef897f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominic=20H=C3=B6glinger?= Date: Tue, 23 Sep 2025 20:31:46 +0200 Subject: [PATCH] TactilityC: Expose Radio HAL --- TactilityC/Include/tt_hal_radio.h | 149 ++++++++++++++- TactilityC/Source/tt_hal_radio.cpp | 292 ++++++++++++++++++++++++++--- 2 files changed, 409 insertions(+), 32 deletions(-) diff --git a/TactilityC/Include/tt_hal_radio.h b/TactilityC/Include/tt_hal_radio.h index 805a33a3..994b4dda 100644 --- a/TactilityC/Include/tt_hal_radio.h +++ b/TactilityC/Include/tt_hal_radio.h @@ -1,17 +1,74 @@ #pragma once +#include "tt_hal_device.h" + #ifdef __cplusplus extern "C" { #endif typedef void* RadioHandle; +enum RadioState { + RADIO_PENDING_ON, + RADIO_ON, + RADIO_ERROR, + RADIO_PENDING_OFF, + RADIO_OFF +}; + enum Modulation { MODULATION_LORA, MODULATION_FSK, MODULATION_LRFHSS }; +enum RadioParameter { + RADIO_POWER, + RADIO_FREQUENCY, + RADIO_BANDWIDTH, + RADIO_SPREADFACTOR, + RADIO_CODINGRATE, + RADIO_SYNCWORD, + RADIO_PREAMBLES, + RADIO_FREQDIV, + RADIO_DATARATE, + RADIO_ADDRWIDTH, + RADIO_NARROWGRID +}; + +enum RadioParameterStatus { + RADIO_PARAM_UNAVAILABLE, + RADIO_PARAM_VALERROR, + RADIO_PARAM_SUCCESS +}; + +enum RadioTxState { + RADIO_TX_QUEUED, + RADIO_TX_PENDING_TRANSMIT, + RADIO_TX_TRANSMITTED, + RADIO_TX_TIMEOUT, + RADIO_TX_ERROR +}; + +typedef int32_t RadioRxSubscriptionId; +typedef int32_t RadioTxId; + +struct RadioRxPacket { + const uint8_t *data; + uint32_t size; + float rssi; + float snr; +}; + +struct RadioTxPacket { + uint8_t *data; + uint32_t size; + uint32_t address; +}; + +typedef void (*RadioTxStateCallback)(RadioTxId id, RadioTxState state); +typedef void (*RadioOnReceiveCallback)(DeviceId id, const RadioRxPacket* packet); + /** * Allocate a radio driver object for the specified radioId. * @param[in] radioId the identifier of the radio device @@ -26,11 +83,21 @@ RadioHandle tt_hal_radio_alloc(DeviceId radioId); void tt_hal_radio_free(RadioHandle handle); /** - * Set the modulation for the radio driver object. - * @param[in] modulation the modulation type + * Get the state for the radio driver object. * @param[in] handle the radio driver handle + * @return the state of the radio */ -void tt_hal_radio_set_modulation(RadioHandle handle, Modulation modulation); +RadioState tt_hal_radio_get_state(RadioHandle handle); + +/** + * Set the modulation for the radio driver object. + * The radio must not be started and it must be either capable + * of reception or transmission of passed modulation. + * @param[in] handle the radio driver handle + * @param[in] modulation the modulation type + * @return true if the modulation could be set, false otherwise + */ +bool tt_hal_radio_set_modulation(RadioHandle handle, Modulation modulation); /** * Get the modulation for the radio driver object. @@ -39,6 +106,82 @@ void tt_hal_radio_set_modulation(RadioHandle handle, Modulation modulation); */ Modulation tt_hal_radio_get_modulation(RadioHandle handle); +/** + * Try to set a parameter for the radio driver object. + * The radio must not be started and it must be in the modulation mode + * for the respective parameter. + * @param[in] handle the radio driver handle + * @param[in] parameter the parameter + * @param[in] value the value to set + * @return status of the parameter set operation + */ +RadioParameterStatus tt_hal_radio_set_parameter(RadioHandle handle, RadioParameter parameter, float value); + +/** + * Try to get a parameter for the radio driver object. + * The radio must be in the modulation mode for the respective parameter, + * else the parameter is unavailable. + * @param[in] handle the radio driver handle + * @param[in] parameter the parameter + * @param[out] value retrieved value will be stored at this pointer + * @return status of the parameter get operation + */ +RadioParameterStatus tt_hal_radio_get_parameter(RadioHandle handle, RadioParameter parameter, float *value); + +/** + * Check whenever the radio driver object can transmit a certain modulation. + * @param[in] handle the radio driver handle + * @param[in] modulation the modulation type + * @return true if capable, false otherwise + */ +bool tt_hal_radio_can_transmit(RadioHandle handle, Modulation modulation); + +/** + * Check whenever the radio driver object can receive a certain modulation. + * @param[in] handle the radio driver handle + * @param[in] modulation the modulation type + * @return true if capable, false otherwise + */ +bool tt_hal_radio_can_receive(RadioHandle handle, Modulation modulation); + +/** + * Starts the radio driver object. + * @param[in] handle the radio driver handle + * @return true if the radio could be started, false otherwise + */ +bool tt_hal_radio_start(RadioHandle handle); + +/** + * Stops the radio driver object. + * @param[in] handle the radio driver handle + * @return true if the radio could be stopped, false otherwise + */ +bool tt_hal_radio_stop(RadioHandle handle); + +/** + * Put a packet in the transmission queue of the radio driver object. + * @param[in] handle the radio driver handle + * @param[in] packet packet to send (no special requirement for memory of data) + * @param[in] callback function to call on transmission state change for the packet + * @return the identifier for the transmission + */ +RadioTxId tt_hal_radio_transmit(RadioHandle handle, RadioTxPacket packet, RadioTxStateCallback callback); + +/** + * Subscribe for any received packet that the radio driver object receives. + * @param[in] handle the radio driver handle + * @param[in] callback function to call on reception of a packet + * @return the identifier for the subscription + */ +RadioRxSubscriptionId radio_subscribe_receive(RadioHandle handle, RadioOnReceiveCallback callback); + +/** + * Unsubscribe for any received packet that the radio driver object receives. + * @param[in] handle the radio driver handle + * @param[in] id the identifier for the subscription + */ +void radio_unsubscribe_receive(RadioHandle handle, RadioRxSubscriptionId id); + #ifdef __cplusplus } #endif diff --git a/TactilityC/Source/tt_hal_radio.cpp b/TactilityC/Source/tt_hal_radio.cpp index 687b5e9a..bfccaed4 100644 --- a/TactilityC/Source/tt_hal_radio.cpp +++ b/TactilityC/Source/tt_hal_radio.cpp @@ -2,34 +2,19 @@ #include "Tactility/Check.h" #include "Tactility/hal/Device.h" -#include "Tactility/hal/display/DisplayDevice.h" -#include "Tactility/hal/display/DisplayDriver.h" +#include "Tactility/hal/radio/RadioDevice.h" -static Modulation fromCpp(tt::hal::radio::RadioDevice::Modulation modulation) { - switch (modulation) { - case tt::hal::radio::RadioDevice::Modulation::LoRa: - return MODULATION_LORA; - case tt::hal::radio::RadioDevice::Modulation::Fsk: - return MODULATION_FSK; - case tt::hal::radio::RadioDevice::Modulation::LrFhss: - return MODULATION_LRFHSS; - default: - tt_crash("Modulation not supported"); - } -} -static tt::hal::radio::RadioDevice::Modulation modulation toCpp(Modulation modulation) { - switch (modulation) { - case MODULATION_LORA: - return tt::hal::radio::RadioDevice::Modulation::LoRa; - case MODULATION_FSK: - return tt::hal::radio::RadioDevice::Modulation::Fsk; - case MODULATION_LRFHSS: - return tt::hal::radio::RadioDevice::Modulation::LrFhss; - default: - tt_crash("Modulation not supported"); - } -} +static RadioState fromCpp(tt::hal::radio::RadioDevice::State state); +static tt::hal::radio::RadioDevice::State toCpp(RadioState state); +static Modulation fromCpp(tt::hal::radio::RadioDevice::Modulation modulation); +static tt::hal::radio::RadioDevice::Modulation toCpp(Modulation modulation); +static RadioParameter fromCpp(tt::hal::radio::RadioDevice::Parameter parameter); +static tt::hal::radio::RadioDevice::Parameter toCpp(RadioParameter parameter); +static RadioParameterStatus fromCpp(tt::hal::radio::RadioDevice::ParameterStatus status); +static tt::hal::radio::RadioDevice::ParameterStatus toCpp(RadioParameterStatus status); +static RadioTxState fromCpp(tt::hal::radio::RadioDevice::TransmissionState state); +static tt::hal::radio::RadioDevice::TransmissionState toCpp(RadioTxState state); struct DeviceWrapper { std::shared_ptr device; @@ -47,7 +32,7 @@ static std::shared_ptr findValidRadioDevice(tt::hal extern "C" { RadioHandle tt_hal_radio_alloc(DeviceId radioId) { - auto radio = findValidRadioDevice(id); + auto radio = findValidRadioDevice(radioId); return new DeviceWrapper(radio); } @@ -56,13 +41,262 @@ extern "C" { delete wrapper; } - void tt_hal_radio_set_modulation(RadioHandle handle, Modulation modulation) { + RadioState tt_hal_radio_get_state(RadioHandle handle) { auto wrapper = static_cast(handle); - wrapper->device->setModulation(toCpp(modulation)); + return fromCpp(wrapper->device->getState()); + } + + bool tt_hal_radio_set_modulation(RadioHandle handle, Modulation modulation) { + auto wrapper = static_cast(handle); + return wrapper->device->setModulation(toCpp(modulation)); } Modulation tt_hal_radio_get_modulation(RadioHandle handle) { auto wrapper = static_cast(handle); return fromCpp(wrapper->device->getModulation()); } + + RadioParameterStatus tt_hal_radio_set_parameter(RadioHandle handle, RadioParameter parameter, float value) { + auto wrapper = static_cast(handle); + return fromCpp(wrapper->device->setParameter(toCpp(parameter), value)); + } + + RadioParameterStatus tt_hal_radio_get_parameter(RadioHandle handle, RadioParameter parameter, float *value) { + auto wrapper = static_cast(handle); + // This is a programming error not an input error, thus assert. + // TODO: Does TactC even know assert? Maybe putting a crash is the optimal solution. + assert(value); + return fromCpp(wrapper->device->getParameter(toCpp(parameter), *value)); + } + + bool tt_hal_radio_can_transmit(RadioHandle handle, Modulation modulation) { + auto wrapper = static_cast(handle); + return wrapper->device->canTransmit(toCpp(modulation)); + } + + bool tt_hal_radio_can_receive(RadioHandle handle, Modulation modulation) { + auto wrapper = static_cast(handle); + return wrapper->device->canReceive(toCpp(modulation)); + } + + bool tt_hal_radio_start(RadioHandle handle) { + auto wrapper = static_cast(handle); + return wrapper->device->start(); + } + + bool tt_hal_radio_stop(RadioHandle handle) { + auto wrapper = static_cast(handle); + return wrapper->device->stop(); + } + + RadioTxId tt_hal_radio_transmit(RadioHandle handle, RadioTxPacket packet, RadioTxStateCallback callback) { + auto wrapper = static_cast(handle); + auto ttPacket = tt::hal::radio::TxPacket{ + .data = std::vector(packet.data, packet.data + packet.size), + .address = packet.address + }; + return wrapper->device->transmit(ttPacket, [callback](tt::hal::radio::RadioDevice::TxId id, tt::hal::radio::RadioDevice::TransmissionState state) { + if (callback) { + callback(id, fromCpp(state)); + } + }); + } + + RadioRxSubscriptionId radio_subscribe_receive(RadioHandle handle, RadioOnReceiveCallback callback) { + auto wrapper = static_cast(handle); + return wrapper->device->subscribeRx([callback](tt::hal::Device::Id id, const tt::hal::radio::RxPacket& ttPacket) { + if (callback) { + auto ttcPacket = RadioRxPacket{ + .data = ttPacket.data.data(), + .size = ttPacket.data.size(), + .rssi = ttPacket.rssi, + .snr = ttPacket.snr + }; + callback(id, &ttcPacket); + } + }); + } + + void radio_unsubscribe_receive(RadioHandle handle, RadioRxSubscriptionId id) { + auto wrapper = static_cast(handle); + wrapper->device->unsubscribeRx(id); + } +} + +static RadioState fromCpp(tt::hal::radio::RadioDevice::State state) { + switch (state) { + case tt::hal::radio::RadioDevice::State::PendingOn: + return RADIO_PENDING_ON; + case tt::hal::radio::RadioDevice::State::On: + return RADIO_ON; + case tt::hal::radio::RadioDevice::State::Error: + return RADIO_ERROR; + case tt::hal::radio::RadioDevice::State::PendingOff: + return RADIO_PENDING_OFF; + case tt::hal::radio::RadioDevice::State::Off: + return RADIO_OFF; + default: + tt_crash("Radio state not supported"); + } +} + +static tt::hal::radio::RadioDevice::State toCpp(RadioState state) { + switch (state) { + case RADIO_PENDING_ON: + return tt::hal::radio::RadioDevice::State::PendingOn; + case RADIO_ON: + return tt::hal::radio::RadioDevice::State::On; + case RADIO_ERROR: + return tt::hal::radio::RadioDevice::State::Error; + case RADIO_PENDING_OFF: + return tt::hal::radio::RadioDevice::State::PendingOff; + case RADIO_OFF: + return tt::hal::radio::RadioDevice::State::Off; + default: + tt_crash("Radio state not supported"); + } +} + +static Modulation fromCpp(tt::hal::radio::RadioDevice::Modulation modulation) { + switch (modulation) { + case tt::hal::radio::RadioDevice::Modulation::LoRa: + return MODULATION_LORA; + case tt::hal::radio::RadioDevice::Modulation::Fsk: + return MODULATION_FSK; + case tt::hal::radio::RadioDevice::Modulation::LrFhss: + return MODULATION_LRFHSS; + default: + tt_crash("Modulation not supported"); + } +} + +static tt::hal::radio::RadioDevice::Modulation toCpp(Modulation modulation) { + switch (modulation) { + case MODULATION_LORA: + return tt::hal::radio::RadioDevice::Modulation::LoRa; + case MODULATION_FSK: + return tt::hal::radio::RadioDevice::Modulation::Fsk; + case MODULATION_LRFHSS: + return tt::hal::radio::RadioDevice::Modulation::LrFhss; + default: + tt_crash("Modulation not supported"); + } +} + +static RadioParameter fromCpp(tt::hal::radio::RadioDevice::Parameter parameter) { + switch (parameter) { + case tt::hal::radio::RadioDevice::Parameter::Power: + return RADIO_POWER; + case tt::hal::radio::RadioDevice::Parameter::Frequency: + return RADIO_FREQUENCY; + case tt::hal::radio::RadioDevice::Parameter::Bandwidth: + return RADIO_BANDWIDTH; + case tt::hal::radio::RadioDevice::Parameter::SpreadFactor: + return RADIO_SPREADFACTOR; + case tt::hal::radio::RadioDevice::Parameter::CodingRate: + return RADIO_CODINGRATE; + case tt::hal::radio::RadioDevice::Parameter::SyncWord: + return RADIO_SYNCWORD; + case tt::hal::radio::RadioDevice::Parameter::PreambleLength: + return RADIO_PREAMBLES; + case tt::hal::radio::RadioDevice::Parameter::FrequencyDeviation: + return RADIO_FREQDIV; + case tt::hal::radio::RadioDevice::Parameter::DataRate: + return RADIO_DATARATE; + case tt::hal::radio::RadioDevice::Parameter::AddressWidth: + return RADIO_ADDRWIDTH; + case tt::hal::radio::RadioDevice::Parameter::NarrowGrid: + return RADIO_NARROWGRID; + default: + tt_crash("Parameter not supported"); + } +} + +static tt::hal::radio::RadioDevice::Parameter toCpp(RadioParameter parameter) { + switch (parameter) { + case RADIO_POWER: + return tt::hal::radio::RadioDevice::Parameter::Power; + case RADIO_FREQUENCY: + return tt::hal::radio::RadioDevice::Parameter::Frequency; + case RADIO_BANDWIDTH: + return tt::hal::radio::RadioDevice::Parameter::Bandwidth; + case RADIO_SPREADFACTOR: + return tt::hal::radio::RadioDevice::Parameter::SpreadFactor; + case RADIO_CODINGRATE: + return tt::hal::radio::RadioDevice::Parameter::CodingRate; + case RADIO_SYNCWORD: + return tt::hal::radio::RadioDevice::Parameter::SyncWord; + case RADIO_PREAMBLES: + return tt::hal::radio::RadioDevice::Parameter::PreambleLength; + case RADIO_FREQDIV: + return tt::hal::radio::RadioDevice::Parameter::FrequencyDeviation; + case RADIO_DATARATE: + return tt::hal::radio::RadioDevice::Parameter::DataRate; + case RADIO_ADDRWIDTH: + return tt::hal::radio::RadioDevice::Parameter::AddressWidth; + case RADIO_NARROWGRID: + return tt::hal::radio::RadioDevice::Parameter::NarrowGrid; + default: + tt_crash("Parameter not supported"); + } +} + +static RadioParameterStatus fromCpp(tt::hal::radio::RadioDevice::ParameterStatus status) { + switch (status) { + case tt::hal::radio::RadioDevice::ParameterStatus::Unavailable: + return RADIO_PARAM_UNAVAILABLE; + case tt::hal::radio::RadioDevice::ParameterStatus::ValueError: + return RADIO_PARAM_VALERROR; + case tt::hal::radio::RadioDevice::ParameterStatus::Success: + return RADIO_PARAM_SUCCESS; + default: + tt_crash("Parameter status not supported"); + } +} + +static tt::hal::radio::RadioDevice::ParameterStatus toCpp(RadioParameterStatus status) { + switch (status) { + case RADIO_PARAM_UNAVAILABLE: + return tt::hal::radio::RadioDevice::ParameterStatus::Unavailable; + case RADIO_PARAM_VALERROR: + return tt::hal::radio::RadioDevice::ParameterStatus::ValueError; + case RADIO_PARAM_SUCCESS: + return tt::hal::radio::RadioDevice::ParameterStatus::Success; + default: + tt_crash("Parameter status not supported"); + } +} + +static RadioTxState fromCpp(tt::hal::radio::RadioDevice::TransmissionState state) { + switch (state) { + case tt::hal::radio::RadioDevice::TransmissionState::Queued: + return RADIO_TX_QUEUED; + case tt::hal::radio::RadioDevice::TransmissionState::PendingTransmit: + return RADIO_TX_PENDING_TRANSMIT; + case tt::hal::radio::RadioDevice::TransmissionState::Transmitted: + return RADIO_TX_TRANSMITTED; + case tt::hal::radio::RadioDevice::TransmissionState::Timeout: + return RADIO_TX_TIMEOUT; + case tt::hal::radio::RadioDevice::TransmissionState::Error: + return RADIO_TX_ERROR; + default: + tt_crash("Transmission state not supported"); + } +} + +static tt::hal::radio::RadioDevice::TransmissionState toCpp(RadioTxState state) { + switch (state) { + case RADIO_TX_QUEUED: + return tt::hal::radio::RadioDevice::TransmissionState::Queued; + case RADIO_TX_PENDING_TRANSMIT: + return tt::hal::radio::RadioDevice::TransmissionState::PendingTransmit; + case RADIO_TX_TRANSMITTED: + return tt::hal::radio::RadioDevice::TransmissionState::Transmitted; + case RADIO_TX_TIMEOUT: + return tt::hal::radio::RadioDevice::TransmissionState::Timeout; + case RADIO_TX_ERROR: + return tt::hal::radio::RadioDevice::TransmissionState::Error; + default: + tt_crash("Transmission state not supported"); + } }