// SPDX-License-Identifier: Apache-2.0 #pragma once #include #include #include #include #ifdef __cplusplus extern "C" { #endif struct Device; /** * Device type and API for sub-GHz packet transceivers such as the Semtech SX126x family. * The API is LoRa-centric but exposes the modem's other modulation schemes as well. */ enum LoraModulation { LORA_MODULATION_NONE = 0, LORA_MODULATION_FSK, LORA_MODULATION_LORA, LORA_MODULATION_LR_FHSS, }; /** * Tunable radio parameters. Values are int32_t; the unit is documented per parameter. * Frequencies and rates use their base SI unit (Hz, bit/s) on purpose: a float can't hold * a value like 906875000 Hz (906.875 MHz) exactly, so integers avoid the rounding a * fractional MHz/kHz representation would introduce. * * Which parameters are available depends on the driver and the selected modulation. */ enum LoraParameter { /** TX output power in dBm */ LORA_PARAMETER_POWER = 0, /** Boosted RX gain mode: 0 = off, 1 = on */ LORA_PARAMETER_BOOSTED_GAIN, /** Carrier frequency in Hz */ LORA_PARAMETER_FREQUENCY, /** Bandwidth in Hz */ LORA_PARAMETER_BANDWIDTH, /** LoRa spreading factor (7-12) */ LORA_PARAMETER_SPREADING_FACTOR, /** LoRa coding rate denominator (5-8 for 4/5 to 4/8) */ LORA_PARAMETER_CODING_RATE, /** * LoRa sync word (0x00-0xFF). Distinguishes otherwise-identical networks: a receiver * only accepts packets whose sync word matches. Conventionally 0x12 for private * networks and 0x34 for public/LoRaWAN. LoRa modulation only. */ LORA_PARAMETER_SYNC_WORD, /** Preamble length in symbols (LoRa) or bits (FSK) */ LORA_PARAMETER_PREAMBLE_LENGTH, /** FSK frequency deviation from the carrier in Hz */ LORA_PARAMETER_FREQUENCY_DEVIATION, /** FSK bit rate in bits per second */ LORA_PARAMETER_DATA_RATE, /** LR-FHSS grid spacing: 0 = 25 kHz (wide), 1 = 3.9 kHz (narrow) */ LORA_PARAMETER_NARROW_GRID, /** * PA over-current protection limit in mA. Fail-safe: a low limit caps the current the * PA can push into a bad or disconnected antenna, but also caps the achievable output * power. Drivers keep a conservative default; a board-aware consumer that knows its * antenna and PA can raise it to reach full output power. */ LORA_PARAMETER_CURRENT_LIMIT, }; enum LoraRadioState { LORA_RADIO_STATE_OFF, LORA_RADIO_STATE_ON_PENDING, LORA_RADIO_STATE_ON, LORA_RADIO_STATE_OFF_PENDING, LORA_RADIO_STATE_ERROR, }; /** Identifies a queued transmission. Unique per device until it wraps around. */ typedef int32_t LoraTxId; enum LoraTransmissionState { /** Accepted into the TX queue */ LORA_TRANSMISSION_STATE_QUEUED, /** Handed to the modem, waiting for TX-done */ LORA_TRANSMISSION_STATE_TRANSMIT_PENDING, /** TX-done confirmed by the modem */ LORA_TRANSMISSION_STATE_TRANSMITTED, /** No TX-done within the driver's timeout */ LORA_TRANSMISSION_STATE_TIMEOUT, /** The modem rejected the transmission */ LORA_TRANSMISSION_STATE_ERROR, }; struct LoraRxPacket { /** Packet payload. Only valid for the duration of the RX callback. */ const uint8_t* data; size_t length; /** Received signal strength in dBm */ float rssi; /** Signal-to-noise ratio in dB */ float snr; }; /** * Callbacks are invoked without any driver lock held, either from the driver's radio * thread (RX, TX progress, state) or from the thread calling transmit()/set_enabled() * (the QUEUED TX event and enable/disable state changes). Taking consumer locks in a * callback is therefore safe, but keep callbacks short: RX processing stalls while * they run. After remove_*_callback returns, a callback that was already in flight * may still complete once — disable the radio before destroying callback context. */ typedef void (*LoraStateCallback)(struct Device* device, void* context, enum LoraRadioState state); typedef void (*LoraRxCallback)(struct Device* device, void* context, const struct LoraRxPacket* packet); typedef void (*LoraTxCallback)(struct Device* device, void* context, LoraTxId id, enum LoraTransmissionState state); struct LoraApi { /** * Get the radio state of the device. * @param[in] device the lora device * @param[out] state the radio state * @return ERROR_NONE on success */ error_t (*get_radio_state)(struct Device* device, enum LoraRadioState* state); /** * Turn the radio on or off. Requires a modulation to be set before enabling. * Turning on is asynchronous: observe the radio state to know when it's up. * @param[in] device the lora device * @param[in] enabled true to turn the radio on * @return ERROR_NONE on success * @retval ERROR_INVALID_STATE when enabling without a modulation set */ error_t (*set_enabled)(struct Device* device, bool enabled); /** * Set the modulation scheme. Only allowed while the radio is off. * @param[in] device the lora device * @param[in] modulation the modulation scheme * @return ERROR_NONE on success * @retval ERROR_INVALID_STATE when the radio is on or turning on * @retval ERROR_NOT_SUPPORTED when the device supports neither TX nor RX for this modulation */ error_t (*set_modulation)(struct Device* device, enum LoraModulation modulation); /** * Get the current modulation scheme. * @param[in] device the lora device * @param[out] modulation the modulation scheme * @return ERROR_NONE on success */ error_t (*get_modulation)(struct Device* device, enum LoraModulation* modulation); /** * @param[in] device the lora device * @param[in] modulation the modulation scheme * @return true when the device can transmit with the given modulation */ bool (*can_transmit)(struct Device* device, enum LoraModulation modulation); /** * @param[in] device the lora device * @param[in] modulation the modulation scheme * @return true when the device can receive with the given modulation */ bool (*can_receive)(struct Device* device, enum LoraModulation modulation); /** * Set a radio parameter. See LoraParameter for units. * Parameters apply to the current modulation and take effect the next time the radio turns on. * @param[in] device the lora device * @param[in] parameter the parameter to set * @param[in] value the value to set * @return ERROR_NONE on success * @retval ERROR_NOT_SUPPORTED when the parameter doesn't apply to the device or modulation * @retval ERROR_OUT_OF_RANGE when the value is invalid for the parameter */ error_t (*set_parameter)(struct Device* device, enum LoraParameter parameter, int32_t value); /** * Get a radio parameter. See LoraParameter for units. * @param[in] device the lora device * @param[in] parameter the parameter to get * @param[out] value the current value * @return ERROR_NONE on success * @retval ERROR_NOT_SUPPORTED when the parameter doesn't apply to the device or modulation */ error_t (*get_parameter)(struct Device* device, enum LoraParameter parameter, int32_t* value); /** * Queue a packet for transmission. The data is copied. * Progress is reported through the TX callbacks, starting with QUEUED. * @param[in] device the lora device * @param[in] data the packet payload * @param[in] length the payload length in bytes * @param[out] id the id assigned to this transmission (optional, can be NULL) * @return ERROR_NONE on success */ error_t (*transmit)(struct Device* device, const uint8_t* data, size_t length, LoraTxId* id); /** * Add a callback for received packets. * @param[in] device the lora device * @param[in] callback_context the context to pass to the callback * @param[in] callback the callback function * @return ERROR_NONE on success */ error_t (*add_rx_callback)(struct Device* device, void* callback_context, LoraRxCallback callback); /** * Remove a callback for received packets. * @param[in] device the lora device * @param[in] callback the callback function * @return ERROR_NONE on success */ error_t (*remove_rx_callback)(struct Device* device, LoraRxCallback callback); /** * Add a callback for radio state changes. * @param[in] device the lora device * @param[in] callback_context the context to pass to the callback * @param[in] callback the callback function * @return ERROR_NONE on success */ error_t (*add_state_callback)(struct Device* device, void* callback_context, LoraStateCallback callback); /** * Remove a callback for radio state changes. * @param[in] device the lora device * @param[in] callback the callback function * @return ERROR_NONE on success */ error_t (*remove_state_callback)(struct Device* device, LoraStateCallback callback); /** * Add a callback for transmission progress. * @param[in] device the lora device * @param[in] callback_context the context to pass to the callback * @param[in] callback the callback function * @return ERROR_NONE on success */ error_t (*add_tx_callback)(struct Device* device, void* callback_context, LoraTxCallback callback); /** * Remove a callback for transmission progress. * @param[in] device the lora device * @param[in] callback the callback function * @return ERROR_NONE on success */ error_t (*remove_tx_callback)(struct Device* device, LoraTxCallback callback); }; extern const struct DeviceType LORA_TYPE; /** @return the first registered lora device, regardless of started state, or NULL if none exists */ struct Device* lora_find_first_registered_device(void); error_t lora_get_radio_state(struct Device* device, enum LoraRadioState* state); error_t lora_set_enabled(struct Device* device, bool enabled); error_t lora_set_modulation(struct Device* device, enum LoraModulation modulation); error_t lora_get_modulation(struct Device* device, enum LoraModulation* modulation); bool lora_can_transmit(struct Device* device, enum LoraModulation modulation); bool lora_can_receive(struct Device* device, enum LoraModulation modulation); error_t lora_set_parameter(struct Device* device, enum LoraParameter parameter, int32_t value); error_t lora_get_parameter(struct Device* device, enum LoraParameter parameter, int32_t* value); error_t lora_transmit(struct Device* device, const uint8_t* data, size_t length, LoraTxId* id); error_t lora_add_rx_callback(struct Device* device, void* callback_context, LoraRxCallback callback); error_t lora_remove_rx_callback(struct Device* device, LoraRxCallback callback); error_t lora_add_state_callback(struct Device* device, void* callback_context, LoraStateCallback callback); error_t lora_remove_state_callback(struct Device* device, LoraStateCallback callback); error_t lora_add_tx_callback(struct Device* device, void* callback_context, LoraTxCallback callback); error_t lora_remove_tx_callback(struct Device* device, LoraTxCallback callback); #ifdef __cplusplus } #endif