Compare commits
4 Commits
cbfc7305f3
...
8473c178e6
| Author | SHA1 | Date | |
|---|---|---|---|
| 8473c178e6 | |||
| 09ef4c39d4 | |||
| b437c66fa3 | |||
| 6a250edcdb |
@ -24,7 +24,7 @@ DeviceVector createDevices() {
|
||||
auto tca8418 = std::make_shared<Tca8418>(I2C_NUM_0);
|
||||
auto keyboard = std::make_shared<TpagerKeyboard>(tca8418);
|
||||
|
||||
auto sx1262 = std::make_shared<Sx1262>("SX1262", Sx1262::Configuration{
|
||||
auto sx1262 = std::make_shared<Sx1262>(Sx1262::Configuration{
|
||||
.spiHostDevice = SPI2_HOST,
|
||||
.spiFrequency = 10'000'000,
|
||||
.csPin = GPIO_NUM_36,
|
||||
|
||||
@ -54,11 +54,16 @@ void RadiolibTactilityHal::attachInterrupt(uint32_t interruptNum, void (*interru
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_install_isr_service((int)ESP_INTR_FLAG_IRAM);
|
||||
if (!isrServiceInitialized) {
|
||||
gpio_install_isr_service((int)ESP_INTR_FLAG_IRAM);
|
||||
isrServiceInitialized = true;
|
||||
}
|
||||
gpio_set_intr_type((gpio_num_t)interruptNum, (gpio_int_type_t)(mode & 0x7));
|
||||
|
||||
// this uses function typecasting, which is not defined when the functions have different signatures
|
||||
// untested and might not work
|
||||
// TODO: I think the wisest course of action is forbidding registration via RadioLib entirely,
|
||||
// as it doesn't suit Tactility with its lack of context passing
|
||||
gpio_isr_handler_add((gpio_num_t)interruptNum, (void (*)(void*))interruptCb, NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -21,9 +21,10 @@ private:
|
||||
spi_device_handle_t spiDeviceHandle;
|
||||
std::shared_ptr<tt::Lock> lock;
|
||||
bool spiInitialized;
|
||||
bool isrServiceInitialized;
|
||||
|
||||
public:
|
||||
explicit RadiolibTactilityHal(spi_host_device_t spiHostDevice, int spiFrequency, gpio_num_t csPin, std::shared_ptr<tt::Lock> spiLock)
|
||||
explicit RadiolibTactilityHal(spi_host_device_t spiHostDevice, int spiFrequency, gpio_num_t csPin)
|
||||
: RadioLibHal(
|
||||
GPIO_MODE_INPUT,
|
||||
GPIO_MODE_OUTPUT,
|
||||
@ -34,10 +35,9 @@ public:
|
||||
, spiHostDevice(spiHostDevice)
|
||||
, spiFrequency(spiFrequency)
|
||||
, csPin(csPin)
|
||||
, lock(spiLock)
|
||||
, spiInitialized(false) {
|
||||
if (!lock) lock = tt::hal::spi::getLock(spiHostDevice);
|
||||
}
|
||||
, lock(tt::hal::spi::getLock(spiHostDevice))
|
||||
, spiInitialized(false)
|
||||
, isrServiceInitialized(false) {}
|
||||
|
||||
void init() override;
|
||||
void term() override;
|
||||
|
||||
@ -3,14 +3,8 @@
|
||||
|
||||
constexpr const char* TAG = "RadiolibThreadedDevice";
|
||||
|
||||
bool RadiolibThreadedDevice::start(const Modulation modulation) {
|
||||
bool RadiolibThreadedDevice::start() {
|
||||
auto lock = getMutex().asScopedLock();
|
||||
|
||||
if (!canTransmit(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)) {
|
||||
@ -26,8 +20,8 @@ bool RadiolibThreadedDevice::start(const Modulation modulation) {
|
||||
thread = std::make_unique<tt::Thread>(
|
||||
threadName,
|
||||
threadSize,
|
||||
[this, modulation]() {
|
||||
return this->threadMain(modulation);
|
||||
[this]() {
|
||||
return this->threadMain();
|
||||
}
|
||||
);
|
||||
thread->setPriority(tt::Thread::Priority::High);
|
||||
@ -71,9 +65,9 @@ bool RadiolibThreadedDevice::isThreadInterrupted() const {
|
||||
return threadInterrupted;
|
||||
}
|
||||
|
||||
int32_t RadiolibThreadedDevice::threadMain(const Modulation modulation) {
|
||||
int32_t RadiolibThreadedDevice::threadMain() {
|
||||
|
||||
int rc = doBegin(modulation);
|
||||
int rc = doBegin(getModulation());
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ private:
|
||||
bool threadInterrupted = false;
|
||||
|
||||
protected:
|
||||
virtual int32_t threadMain(const Modulation modulation);
|
||||
virtual int32_t threadMain();
|
||||
bool isThreadInterrupted() const;
|
||||
|
||||
virtual void interruptSignal() = 0;
|
||||
@ -31,7 +31,6 @@ public:
|
||||
|
||||
~RadiolibThreadedDevice() override = default;
|
||||
|
||||
virtual bool start(const Modulation modulation) override;
|
||||
virtual bool start() override;
|
||||
virtual bool stop() override;
|
||||
|
||||
};
|
||||
|
||||
@ -5,94 +5,266 @@
|
||||
|
||||
constexpr const char* TAG = "Sx1262";
|
||||
|
||||
template<typename T>
|
||||
static constexpr Sx1262::ParameterStatus checkLimitsAndApply(T &target, const float value, const float lower, const float upper, const unsigned step = 0) {
|
||||
if ((value >= lower) && (value <= upper)) {
|
||||
if (step != 0) {
|
||||
int ivalue = static_cast<int>(value);
|
||||
if ((ivalue % step) != 0) {
|
||||
return Sx1262::ParameterStatus::ValueError;
|
||||
}
|
||||
}
|
||||
|
||||
target = static_cast<T>(value);
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
}
|
||||
return Sx1262::ParameterStatus::ValueError;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr Sx1262::ParameterStatus checkValuesAndApply(T &target, const float value, std::initializer_list<float> valids) {
|
||||
for (float valid : valids) {
|
||||
if (value == valid) {
|
||||
target = static_cast<T>(value);
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
}
|
||||
}
|
||||
return Sx1262::ParameterStatus::ValueError;
|
||||
}
|
||||
|
||||
void IRAM_ATTR dio1handler(void* context) {
|
||||
((Sx1262*)context)->dio1Event();
|
||||
}
|
||||
|
||||
bool Sx1262::setParameter(const Parameter parameter, const float value) {
|
||||
Sx1262::ParameterStatus Sx1262::setLoraParameter(const Parameter parameter, const float value) {
|
||||
using enum Parameter;
|
||||
|
||||
switch (parameter) {
|
||||
case Power:
|
||||
power = value;
|
||||
return true;
|
||||
return checkLimitsAndApply(power, value, -9.0, 22.0);
|
||||
case Frequency:
|
||||
frequency = value;
|
||||
return true;
|
||||
return checkLimitsAndApply(frequency, value, 150.0, 960.0);
|
||||
case Bandwidth:
|
||||
bandwidth = value;
|
||||
return true;
|
||||
return checkValuesAndApply(bandwidth, value, {
|
||||
7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0, 500.0
|
||||
});
|
||||
case SpreadFactor:
|
||||
spreadFactor = value;
|
||||
return true;
|
||||
return checkLimitsAndApply(spreadFactor, value, 7.0, 12.0, 1);
|
||||
case CodingRate:
|
||||
codingRate = value;
|
||||
return true;
|
||||
return checkLimitsAndApply(codingRate, value, 5.0, 8.0, 1);
|
||||
case SyncWord:
|
||||
syncWord = value;
|
||||
return true;
|
||||
return checkLimitsAndApply(syncWord, value, 0.0, 255.0, 1);
|
||||
case PreambleLength:
|
||||
preambleLength = value;
|
||||
return true;
|
||||
case DataRate:
|
||||
bitRate = value;
|
||||
return true;
|
||||
case FrequencyDeviation:
|
||||
frequencyDeviation = value;
|
||||
return true;
|
||||
case NarrowGrid:
|
||||
narrowGrid = value;
|
||||
return true;
|
||||
return checkLimitsAndApply(preambleLength, value, 0.0, 65535.0, 1);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TT_LOG_W(TAG, "Tried to set unsupported parameter \"%s\" to %f", toString(parameter), value);
|
||||
|
||||
return false;
|
||||
TT_LOG_W(TAG, "Tried to set unsupported LoRa parameter \"%s\" to %f", toString(parameter), value);
|
||||
return Sx1262::ParameterStatus::Unavailable;
|
||||
}
|
||||
|
||||
bool Sx1262::getParameter(const Parameter parameter, float &value) const {
|
||||
Sx1262::ParameterStatus Sx1262::setFskParameter(const Parameter parameter, const float value) {
|
||||
using enum Parameter;
|
||||
|
||||
switch (parameter) {
|
||||
case Power:
|
||||
return checkLimitsAndApply(power, value, -9.0, 22.0);
|
||||
case Frequency:
|
||||
return checkLimitsAndApply(frequency, value, 150.0, 960.0);
|
||||
case Bandwidth:
|
||||
return checkValuesAndApply(bandwidth, value, {
|
||||
4.8, 5.8, 7.3, 9.7, 11.7, 14.6, 19.5, 23.4, 29.3, 39.0, 46.9, 58.6, 78.2
|
||||
});
|
||||
case PreambleLength:
|
||||
return checkLimitsAndApply(preambleLength, value, 0.0, 65535.0, 1);
|
||||
case DataRate:
|
||||
return checkLimitsAndApply(bitRate, value, 0.6, 300.0);
|
||||
case FrequencyDeviation:
|
||||
return checkLimitsAndApply(frequencyDeviation, value, 0.0, 200.0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TT_LOG_W(TAG, "Tried to set unsupported FSK parameter \"%s\" to %f", toString(parameter), value);
|
||||
return Sx1262::ParameterStatus::Unavailable;
|
||||
}
|
||||
|
||||
Sx1262::ParameterStatus Sx1262::setLrFhssParameter(const Parameter parameter, const float value) {
|
||||
using enum Parameter;
|
||||
|
||||
switch (parameter) {
|
||||
case Bandwidth:
|
||||
return checkValuesAndApply(bandwidth, value, {
|
||||
39.06, 85.94, 136.72, 183.59, 335.94, 386.72, 722.66, 773.44, 1523.4, 1574.2
|
||||
});
|
||||
case CodingRate:
|
||||
return checkValuesAndApply(codingRate, value, {
|
||||
RADIOLIB_SX126X_LR_FHSS_CR_5_6,
|
||||
RADIOLIB_SX126X_LR_FHSS_CR_2_3,
|
||||
RADIOLIB_SX126X_LR_FHSS_CR_1_2,
|
||||
RADIOLIB_SX126X_LR_FHSS_CR_1_3
|
||||
});
|
||||
case NarrowGrid:
|
||||
return checkLimitsAndApply(narrowGrid, value, 0.0, 1.0, 1);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TT_LOG_W(TAG, "Tried to set unsupported LR-FHSS parameter \"%s\" to %f", toString(parameter), value);
|
||||
return Sx1262::ParameterStatus::Unavailable;
|
||||
}
|
||||
|
||||
|
||||
Sx1262::ParameterStatus Sx1262::setParameter(const Parameter parameter, const float value) {
|
||||
const auto currentModulation = getModulation();
|
||||
|
||||
switch (currentModulation) {
|
||||
case Modulation::LoRa:
|
||||
return setLoraParameter(parameter, value);
|
||||
case Modulation::Fsk:
|
||||
return setFskParameter(parameter, value);
|
||||
case Modulation::LrFhss:
|
||||
return setLrFhssParameter(parameter, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Shouldn't be reachable, return failsafe value
|
||||
return Sx1262::ParameterStatus::Unavailable;
|
||||
}
|
||||
|
||||
Sx1262::ParameterStatus Sx1262::getLoraParameter(const Parameter parameter, float &value) const {
|
||||
using enum Parameter;
|
||||
|
||||
switch (parameter) {
|
||||
case Power:
|
||||
value = power;
|
||||
return true;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case Frequency:
|
||||
value = frequency;
|
||||
return true;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case Bandwidth:
|
||||
value = bandwidth;
|
||||
return true;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case SpreadFactor:
|
||||
value = spreadFactor;
|
||||
return true;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case CodingRate:
|
||||
value = codingRate;
|
||||
return true;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case SyncWord:
|
||||
value = syncWord;
|
||||
return true;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case PreambleLength:
|
||||
value = preambleLength;
|
||||
return true;
|
||||
case DataRate:
|
||||
value = bitRate;
|
||||
return true;
|
||||
case FrequencyDeviation:
|
||||
value = frequencyDeviation;
|
||||
return true;
|
||||
case NarrowGrid:
|
||||
value = narrowGrid;
|
||||
return true;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TT_LOG_W(TAG, "Tried to get unsupported parameter \"%s\"", toString(parameter));
|
||||
TT_LOG_W(TAG, "Tried to get unsupported LoRa parameter \"%s\"", toString(parameter));
|
||||
return Sx1262::ParameterStatus::Unavailable;
|
||||
}
|
||||
|
||||
return false;
|
||||
Sx1262::ParameterStatus Sx1262::getFskParameter(const Parameter parameter, float &value) const {
|
||||
using enum Parameter;
|
||||
|
||||
switch (parameter) {
|
||||
case Power:
|
||||
value = power;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case Frequency:
|
||||
value = frequency;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case Bandwidth:
|
||||
value = bandwidth;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case DataRate:
|
||||
value = bitRate;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case FrequencyDeviation:
|
||||
value = frequencyDeviation;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TT_LOG_W(TAG, "Tried to get unsupported FSK parameter \"%s\"", toString(parameter));
|
||||
return Sx1262::ParameterStatus::Unavailable;
|
||||
}
|
||||
|
||||
Sx1262::ParameterStatus Sx1262::getLrFhssParameter(const Parameter parameter, float &value) const {
|
||||
using enum Parameter;
|
||||
|
||||
switch (parameter) {
|
||||
case Power:
|
||||
value = power;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case Bandwidth:
|
||||
value = bandwidth;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case CodingRate:
|
||||
value = codingRate;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
case NarrowGrid:
|
||||
value = narrowGrid;
|
||||
return Sx1262::ParameterStatus::Success;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TT_LOG_W(TAG, "Tried to get unsupported LR-FHSS parameter \"%s\"", toString(parameter));
|
||||
return Sx1262::ParameterStatus::Unavailable;
|
||||
}
|
||||
|
||||
|
||||
Sx1262::ParameterStatus Sx1262::getParameter(const Parameter parameter, float &value) const {
|
||||
const auto currentModulation = getModulation();
|
||||
|
||||
switch (currentModulation) {
|
||||
case Modulation::LoRa:
|
||||
return getLoraParameter(parameter, value);
|
||||
case Modulation::Fsk:
|
||||
return getFskParameter(parameter, value);
|
||||
case Modulation::LrFhss:
|
||||
return getLrFhssParameter(parameter, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Shouldn't be reachable, return failsafe value
|
||||
return Sx1262::ParameterStatus::Unavailable;
|
||||
}
|
||||
|
||||
tt::hal::radio::Unit Sx1262::getParameterUnit(const Parameter parameter) const {
|
||||
using enum Parameter;
|
||||
using Unit = tt::hal::radio::Unit;
|
||||
|
||||
switch (parameter) {
|
||||
case Power:
|
||||
return Unit(Unit::Name::DecibelMilliwatts);
|
||||
case Frequency:
|
||||
return Unit(Unit::Prefix::Mega, Unit::Name::Herz);
|
||||
case Bandwidth:
|
||||
return Unit(Unit::Prefix::Kilo, Unit::Name::Herz);
|
||||
case SpreadFactor:
|
||||
case CodingRate: // no break
|
||||
case SyncWord: // no break
|
||||
case PreambleLength: // no break
|
||||
return Unit(Unit::Name::None);
|
||||
case DataRate:
|
||||
return Unit(Unit::Prefix::Kilo, Unit::Name::BitsPerSecond);
|
||||
case FrequencyDeviation:
|
||||
return Unit(Unit::Prefix::Kilo, Unit::Name::Herz);
|
||||
case NarrowGrid:
|
||||
return Unit(Unit::Name::None);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TT_LOG_W(TAG, "Tried to get unit for unsupported parameter \"%s\"", toString(parameter));
|
||||
return Unit(Unit::Name::None);
|
||||
}
|
||||
|
||||
void Sx1262::registerDio1Isr() {
|
||||
@ -181,7 +353,6 @@ int Sx1262::doBegin(const Modulation modulation) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
currentModem = modulation;
|
||||
registerDio1Isr();
|
||||
return 0;
|
||||
}
|
||||
@ -198,11 +369,18 @@ void Sx1262::doTransmit() {
|
||||
TT_LOG_W(TAG, "RadioLib returned %hi on standby", rc);
|
||||
}
|
||||
|
||||
rc = radio.startTransmit(currentTx.packet.data.data(), currentTx.packet.data.size());
|
||||
if (getModulation() == Modulation::Fsk) {
|
||||
rc = radio.startTransmit(currentTx.packet.data.data(), currentTx.packet.data.size(),
|
||||
currentTx.packet.address);
|
||||
} else {
|
||||
rc = radio.startTransmit(currentTx.packet.data.data(), currentTx.packet.data.size());
|
||||
}
|
||||
|
||||
if (rc == RADIOLIB_ERR_NONE) {
|
||||
currentTx.callback(currentTx.id, TransmissionState::PendingTransmit);
|
||||
|
||||
auto txEventFlags = events.wait(SX1262_INTERRUPT_BIT | SX1262_DIO1_EVENT_BIT, tt::EventFlag::WaitAny, pdMS_TO_TICKS(2000));
|
||||
auto txEventFlags = events.wait(SX1262_INTERRUPT_BIT | SX1262_DIO1_EVENT_BIT, tt::EventFlag::WaitAny,
|
||||
pdMS_TO_TICKS(SX1262_TX_TIMEOUT_MILLIS));
|
||||
|
||||
// Thread might've been interrupted in the meanwhile
|
||||
if (isThreadInterrupted()) {
|
||||
@ -223,7 +401,7 @@ void Sx1262::doTransmit() {
|
||||
}
|
||||
|
||||
void Sx1262::doListen() {
|
||||
if (currentModem != Modulation::LrFhss) {
|
||||
if (getModulation() != Modulation::LrFhss) {
|
||||
radio.startReceive();
|
||||
events.wait(SX1262_INTERRUPT_BIT | SX1262_DIO1_EVENT_BIT | SX1262_QUEUED_TX_BIT);
|
||||
} else {
|
||||
@ -234,7 +412,7 @@ void Sx1262::doListen() {
|
||||
|
||||
void Sx1262::doReceive() {
|
||||
// LR-FHSS modem only supports TX
|
||||
if (currentModem == Modulation::LrFhss) return;
|
||||
if (getModulation() == Modulation::LrFhss) return;
|
||||
|
||||
uint16_t rxSize = radio.getPacketLength(true);
|
||||
std::vector<uint8_t> data(rxSize);
|
||||
|
||||
@ -25,7 +25,9 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
static constexpr auto SX1262_DEFAULT_NAME = "SX1262";
|
||||
static constexpr auto SX1262_COOLDOWN_MILLIS = 100;
|
||||
static constexpr auto SX1262_TX_TIMEOUT_MILLIS = 2000;
|
||||
static constexpr auto SX1262_INTERRUPT_BIT = BIT0;
|
||||
static constexpr auto SX1262_DIO1_EVENT_BIT = BIT1;
|
||||
static constexpr auto SX1262_QUEUED_TX_BIT = BIT2;
|
||||
@ -38,7 +40,6 @@ private:
|
||||
Module radioModule;
|
||||
SX1262 radio;
|
||||
TxItem currentTx;
|
||||
Modulation currentModem;
|
||||
|
||||
int8_t power = 0;
|
||||
float frequency = 0.0;
|
||||
@ -54,6 +55,14 @@ private:
|
||||
void registerDio1Isr();
|
||||
void unregisterDio1Isr();
|
||||
|
||||
ParameterStatus setLoraParameter(const Parameter parameter, const float value);
|
||||
ParameterStatus setFskParameter(const Parameter parameter, const float value);
|
||||
ParameterStatus setLrFhssParameter(const Parameter parameter, const float value);
|
||||
ParameterStatus getLoraParameter(const Parameter parameter, float &value) const;
|
||||
ParameterStatus getFskParameter(const Parameter parameter, float &value) const;
|
||||
ParameterStatus getLrFhssParameter(const Parameter parameter, float &value) const;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void txQueuedSignal() override;
|
||||
virtual void interruptSignal() override;
|
||||
@ -66,24 +75,23 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
explicit Sx1262(const std::string& name, const Configuration& configuration, std::shared_ptr<tt::Lock> lock = nullptr)
|
||||
explicit Sx1262(const Configuration& configuration, const std::string& name = SX1262_DEFAULT_NAME)
|
||||
: RadiolibThreadedDevice(name, 4096)
|
||||
, name(name)
|
||||
, configuration(configuration)
|
||||
, hal(configuration.spiHostDevice, configuration.spiFrequency, configuration.csPin, lock)
|
||||
, hal(configuration.spiHostDevice, configuration.spiFrequency, configuration.csPin)
|
||||
, radioModule(&hal, configuration.csPin, configuration.irqPin, configuration.resetPin, configuration.busyPin)
|
||||
, radio(&radioModule)
|
||||
, currentModem(Modulation::None)
|
||||
{}
|
||||
|
||||
~Sx1262() override = default;
|
||||
|
||||
std::string getName() const override { return name; }
|
||||
|
||||
std::string getDescription() const override { return "Semtech SX1262 LoRa, FSK and LR-FHSS capable radio"; }
|
||||
|
||||
bool setParameter(const Parameter parameter, const float value) override;
|
||||
bool getParameter(const Parameter parameter, float &value) const override;
|
||||
ParameterStatus setParameter(const Parameter parameter, const float value) override;
|
||||
ParameterStatus getParameter(const Parameter parameter, float &value) const override;
|
||||
tt::hal::radio::Unit getParameterUnit(const Parameter parameter) const override;
|
||||
|
||||
bool canTransmit(const Modulation modulation) override {
|
||||
return (modulation == Modulation::Fsk) ||
|
||||
|
||||
@ -34,23 +34,29 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void clear() { parameters.clear(); }
|
||||
|
||||
bool apply(RadioDevice &radio) {
|
||||
bool successful = true;
|
||||
for (const auto& [parameter, value] : parameters) {
|
||||
// No break on error chosen to apply all parameters,
|
||||
// a bad one doesn't make the successive tries any more invalid
|
||||
successful &= radio.setParameter(parameter, value);
|
||||
successful &= (radio.setParameter(parameter, value) == RadioDevice::ParameterStatus::Success);
|
||||
}
|
||||
return successful;
|
||||
}
|
||||
|
||||
bool load(const RadioDevice &radio) {
|
||||
bool successful = true;
|
||||
for (const auto& [parameter, value] : parameters) {
|
||||
successful &= radio.getParameter(parameter, parameters[parameter]);
|
||||
void load(const RadioDevice &radio) {
|
||||
// This loop has to be ajusted for each new parameter.
|
||||
// Could be made more maintainable with a template enum iterator in an utility header.
|
||||
for (RadioDevice::Parameter p = RadioDevice::Parameter::Power;
|
||||
p < RadioDevice::Parameter::NarrowGrid;
|
||||
p = static_cast<RadioDevice::Parameter>((size_t)p + 1)) {
|
||||
float value = 0.0;
|
||||
if (radio.getParameter(p, value) == RadioDevice::ParameterStatus::Success) {
|
||||
set(p, value);
|
||||
}
|
||||
}
|
||||
return successful;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Device.h"
|
||||
#include "Unit.h"
|
||||
|
||||
#include <Tactility/Mutex.h>
|
||||
#include <Tactility/Thread.h>
|
||||
@ -45,6 +46,12 @@ public:
|
||||
NarrowGrid
|
||||
};
|
||||
|
||||
enum class ParameterStatus {
|
||||
Unavailable,
|
||||
ValueError,
|
||||
Success
|
||||
};
|
||||
|
||||
typedef int RxSubscriptionId;
|
||||
typedef int TxId;
|
||||
|
||||
@ -81,6 +88,7 @@ private:
|
||||
};
|
||||
|
||||
State state;
|
||||
Modulation modulation;
|
||||
Mutex mutex = Mutex(Mutex::Type::Recursive);
|
||||
std::vector<RxSubscription> rxSubscriptions;
|
||||
std::deque<TxItem> txQueue;
|
||||
@ -114,18 +122,21 @@ protected:
|
||||
|
||||
public:
|
||||
explicit RadioDevice()
|
||||
: state(State::Off) {}
|
||||
: state(State::Off), modulation(Modulation::None) {}
|
||||
|
||||
~RadioDevice() override = default;
|
||||
|
||||
Type getType() const override { return Type::Radio; }
|
||||
|
||||
virtual bool setParameter(const Parameter parameter, const float value) = 0;
|
||||
virtual bool getParameter(const Parameter parameter, float &value) const = 0;
|
||||
bool setModulation(const Modulation newModulation);
|
||||
Modulation getModulation() const;
|
||||
virtual ParameterStatus setParameter(const Parameter parameter, const float value) = 0;
|
||||
virtual ParameterStatus getParameter(const Parameter parameter, float &value) const = 0;
|
||||
virtual Unit getParameterUnit(const Parameter parameter) const = 0;
|
||||
virtual bool canTransmit(const Modulation modulation) = 0;
|
||||
virtual bool canReceive(const Modulation modulation) = 0;
|
||||
|
||||
virtual bool start(const Modulation modulation) = 0;
|
||||
virtual bool start() = 0;
|
||||
virtual bool stop() = 0;
|
||||
|
||||
TxId transmit(const TxPacket& packet, TxStateCallback callback) {
|
||||
|
||||
46
Tactility/Include/Tactility/hal/radio/Unit.h
Normal file
46
Tactility/Include/Tactility/hal/radio/Unit.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tt::hal::radio {
|
||||
|
||||
class Unit {
|
||||
public:
|
||||
enum class Prefix {
|
||||
Femto,
|
||||
Pico,
|
||||
Nano,
|
||||
Milli,
|
||||
None,
|
||||
Kilo,
|
||||
Mega,
|
||||
Giga,
|
||||
Terra,
|
||||
Peta
|
||||
};
|
||||
|
||||
enum class Name
|
||||
{
|
||||
None,
|
||||
BitsPerSecond,
|
||||
BytesPerSecond,
|
||||
Herz,
|
||||
Decibel,
|
||||
DecibelMilliwatts
|
||||
};
|
||||
|
||||
const Prefix prefix;
|
||||
const Name unit;
|
||||
|
||||
explicit Unit(const Prefix si, const Name unit)
|
||||
: prefix(si), unit(unit) {}
|
||||
explicit Unit(const Name unit)
|
||||
: prefix(Prefix::None), unit(unit) {}
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
const char* toString(Unit::Prefix prefix);
|
||||
const char* toString(Unit::Name unit);
|
||||
|
||||
}
|
||||
@ -428,17 +428,18 @@ public:
|
||||
|
||||
void configureFromForm() {
|
||||
using enum tt::hal::radio::RadioDevice::Parameter;
|
||||
using enum tt::hal::radio::RadioDevice::ParameterStatus;
|
||||
|
||||
std::string buffer;
|
||||
int value = 0;
|
||||
bool configured = true;
|
||||
buffer = lv_textarea_get_text(frequencyInput);
|
||||
if (!buffer.empty()) {
|
||||
configured &= loraDevice->setParameter(Frequency, std::stof(buffer));
|
||||
configured &= (loraDevice->setParameter(Frequency, std::stof(buffer)) == Success);
|
||||
}
|
||||
buffer = lv_textarea_get_text(bandwidthInput);
|
||||
if (!buffer.empty()) {
|
||||
configured &= loraDevice->setParameter(Bandwidth, std::stof(buffer));
|
||||
configured &= (loraDevice->setParameter(Bandwidth, std::stof(buffer)) == Success);
|
||||
}
|
||||
buffer = lv_textarea_get_text(syncwordInput);
|
||||
if (!buffer.empty()) {
|
||||
@ -446,20 +447,20 @@ public:
|
||||
std::stringstream ss(buffer);
|
||||
ss >> std::hex >> syncWord;
|
||||
|
||||
configured &= loraDevice->setParameter(SyncWord, std::stoi(buffer, nullptr, 16));
|
||||
configured &= (loraDevice->setParameter(SyncWord, std::stoi(buffer, nullptr, 16)) == Success);
|
||||
}
|
||||
value = lv_slider_get_value(deBitsInput);
|
||||
configured &= loraDevice->setParameter(CodingRate, value);
|
||||
configured &= (loraDevice->setParameter(CodingRate, value) == Success);
|
||||
|
||||
value = lv_slider_get_value(sfInput);
|
||||
configured &= loraDevice->setParameter(SpreadFactor, value);
|
||||
configured &= (loraDevice->setParameter(SpreadFactor, value) == Success);
|
||||
|
||||
value = lv_slider_get_value(preambleChirpsInput);
|
||||
configured &= loraDevice->setParameter(PreambleLength, value);
|
||||
configured &= (loraDevice->setParameter(PreambleLength, value) == Success);
|
||||
|
||||
buffer = lv_textarea_get_text(txPowInput);
|
||||
if (!buffer.empty()) {
|
||||
configured &= loraDevice->setParameter(Power, std::stof(buffer));
|
||||
configured &= (loraDevice->setParameter(Power, std::stof(buffer)) == Success);
|
||||
}
|
||||
}
|
||||
|
||||
@ -468,8 +469,9 @@ public:
|
||||
loraDevice = loraDevs[lv_dropdown_get_selected(loraDeviceInput)];
|
||||
if (loraDevice) {
|
||||
disableForm();
|
||||
loraDevice->setModulation(tt::hal::radio::RadioDevice::Modulation::LoRa);
|
||||
configureFromForm();
|
||||
loraDevice->start(tt::hal::radio::RadioDevice::Modulation::LoRa);
|
||||
loraDevice->start();
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
if (loraDevice->getState() != tt::hal::radio::RadioDevice::State::On) {
|
||||
lv_obj_clear_state(loraDeviceOn, LV_STATE_CHECKED);
|
||||
|
||||
@ -5,6 +5,31 @@ namespace tt::hal::radio {
|
||||
|
||||
constexpr const char* TAG = "RadioDevice";
|
||||
|
||||
bool RadioDevice::setModulation(const RadioDevice::Modulation newModulation) {
|
||||
// A bool is chosen over an enum class because:
|
||||
// - this is not tied to user input and
|
||||
// - the programmer can infer why it didn't work using
|
||||
// other methods such as getState() and canTransmit/Receive()
|
||||
const auto state = getState();
|
||||
if ((state == State::PendingOn) || (state == State::On)) {
|
||||
return false;
|
||||
} else if (!(canTransmit(newModulation) || canReceive(newModulation))) {
|
||||
return false;
|
||||
} else {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
modulation = newModulation;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RadioDevice::Modulation RadioDevice::getModulation() const {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
return modulation;
|
||||
}
|
||||
|
||||
RadioDevice::State RadioDevice::getState() const {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
60
Tactility/Source/hal/radio/Unit.cpp
Normal file
60
Tactility/Source/hal/radio/Unit.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "Tactility/hal/radio/Unit.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace tt::hal::radio {
|
||||
|
||||
std::string Unit::toString() const {
|
||||
return std::string(toString(prefix))+std::string(toString(unit));
|
||||
}
|
||||
|
||||
const char* toString(Unit::Prefix prefix) {
|
||||
using enum Unit::Prefix;
|
||||
|
||||
switch (prefix) {
|
||||
case Femto:
|
||||
return "f";
|
||||
case Pico:
|
||||
return "p";
|
||||
case Nano:
|
||||
return "n";
|
||||
case Milli:
|
||||
return "m";
|
||||
case None:
|
||||
return "";
|
||||
case Kilo:
|
||||
return "k";
|
||||
case Mega:
|
||||
return "M";
|
||||
case Giga:
|
||||
return "G";
|
||||
case Terra:
|
||||
return "T";
|
||||
case Peta:
|
||||
return "P";
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
const char* toString(Unit::Name unit) {
|
||||
using enum Unit::Name;
|
||||
|
||||
switch (unit) {
|
||||
case None:
|
||||
return "";
|
||||
case BitsPerSecond:
|
||||
return "bps";
|
||||
case BytesPerSecond:
|
||||
return "Bps";
|
||||
case Herz:
|
||||
return "Hz";
|
||||
case Decibel:
|
||||
return "dB";
|
||||
case DecibelMilliwatts:
|
||||
return "dBm";
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user