RadioSet: Split off main source into Utils, SettingsView
This commit is contained in:
parent
506c840933
commit
dfadaab667
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "SettingsView.h"
|
||||||
|
|
||||||
#include "tt_app.h"
|
#include "tt_app.h"
|
||||||
#include "tt_hal_radio.h"
|
#include "tt_hal_radio.h"
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
|
|
||||||
class TermView;
|
class TermView;
|
||||||
class SettingsView;
|
|
||||||
|
|
||||||
class RadioSet {
|
class RadioSet {
|
||||||
lv_obj_t* mainView = nullptr;
|
lv_obj_t* mainView = nullptr;
|
||||||
|
|||||||
889
ExternalApps/RadioSet/main/Source/SettingsView.cpp
Normal file
889
ExternalApps/RadioSet/main/Source/SettingsView.cpp
Normal file
@ -0,0 +1,889 @@
|
|||||||
|
#include "SettingsView.h"
|
||||||
|
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
static lv_obj_t* createGridDropdownInput(lv_obj_t *container, int row, const char* const label, const char* const items) {
|
||||||
|
lv_obj_t* label_obj = lv_label_create(container);
|
||||||
|
lv_label_set_text(label_obj, label);
|
||||||
|
lv_obj_set_grid_cell(label_obj,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label_obj, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
|
lv_obj_t* input = lv_dropdown_create(container);
|
||||||
|
lv_obj_set_grid_cell(input,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(input, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
lv_dropdown_set_options(input, items);
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParameterInput {
|
||||||
|
static constexpr auto LV_STATE_INVALID = LV_STATE_USER_1;
|
||||||
|
typedef void (*Callback)(void* ctx);
|
||||||
|
|
||||||
|
const RadioHandle handle;
|
||||||
|
const RadioParameter param;
|
||||||
|
Callback userChangeCallback = nullptr;
|
||||||
|
void* userChangeCtx = nullptr;
|
||||||
|
|
||||||
|
static void apply_error_style(lv_obj_t* obj) {
|
||||||
|
static bool init = false;
|
||||||
|
static lv_style_t style_invalid;
|
||||||
|
if (!init) {
|
||||||
|
lv_style_init(&style_invalid);
|
||||||
|
lv_style_set_border_color(&style_invalid, lv_color_hex(0xFFBF00));
|
||||||
|
lv_style_set_border_width(&style_invalid, 2);
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
lv_obj_add_style(obj, &style_invalid, LV_STATE_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterInput(RadioHandle handle, const RadioParameter param)
|
||||||
|
: handle(handle)
|
||||||
|
, param(param) {}
|
||||||
|
|
||||||
|
virtual ~ParameterInput() = default;
|
||||||
|
|
||||||
|
void onUserChange(Callback cb, void* ctx) {
|
||||||
|
userChangeCallback = cb;
|
||||||
|
userChangeCtx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitUserChange() {
|
||||||
|
if (userChangeCallback) {
|
||||||
|
userChangeCallback(userChangeCtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void storeToRadio() = 0;
|
||||||
|
virtual void updatePreview() = 0;
|
||||||
|
virtual void activate() = 0;
|
||||||
|
virtual void deactivate() = 0;
|
||||||
|
virtual void setValue(float value) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NumericParameterInput : public ParameterInput {
|
||||||
|
|
||||||
|
lv_obj_t* label = nullptr;
|
||||||
|
lv_obj_t* input = nullptr;
|
||||||
|
lv_obj_t* unitlabel = nullptr;
|
||||||
|
char* fmt;
|
||||||
|
|
||||||
|
NumericParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, char* fmt = "%f", char* unit_override = nullptr)
|
||||||
|
: ParameterInput(handle, param)
|
||||||
|
, fmt(fmt) {
|
||||||
|
initUi(container, row, unit_override);
|
||||||
|
loadFromRadio();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~NumericParameterInput() {
|
||||||
|
//lv_obj_clean(label);
|
||||||
|
//lv_obj_clean(input);
|
||||||
|
//lv_obj_clean(unitlabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initUi(lv_obj_t* container, int row, char* unit_override) {
|
||||||
|
const int height = LV_SIZE_CONTENT;
|
||||||
|
label = lv_label_create(container);
|
||||||
|
lv_label_set_text(label, toString(param));
|
||||||
|
lv_obj_set_grid_cell(label,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label, lv_pct(100), height);
|
||||||
|
|
||||||
|
input = lv_textarea_create(container);
|
||||||
|
lv_obj_set_grid_cell(input,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(input, lv_pct(100), height);
|
||||||
|
lv_textarea_set_accepted_chars(input, "0123456789.+-");
|
||||||
|
loadFromRadio();
|
||||||
|
lv_textarea_set_one_line(input, true);
|
||||||
|
apply_error_style(input);
|
||||||
|
|
||||||
|
unitlabel = lv_label_create(container);
|
||||||
|
lv_obj_set_grid_cell(unitlabel,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(unitlabel, lv_pct(100), height);
|
||||||
|
lv_obj_set_style_text_align(unitlabel , LV_TEXT_ALIGN_LEFT, 0);
|
||||||
|
|
||||||
|
if (unit_override) {
|
||||||
|
lv_label_set_text(unitlabel, unit_override);
|
||||||
|
} else {
|
||||||
|
char unit[64] = {0};
|
||||||
|
tt_hal_radio_get_parameter_unit_str(handle, param, unit, sizeof(unit));
|
||||||
|
lv_label_set_text(unitlabel, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_add_event_cb(input, [](lv_event_t * e) {
|
||||||
|
NumericParameterInput* self = (NumericParameterInput*)lv_event_get_user_data(e);
|
||||||
|
self->storeToRadio();
|
||||||
|
self->emitUserChange();
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFromRadio() {
|
||||||
|
float value;
|
||||||
|
if (tt_hal_radio_get_parameter(handle, param, &value) == RADIO_PARAM_SUCCESS) {
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void storeToRadio() override {
|
||||||
|
float value;
|
||||||
|
if (sscanf(lv_textarea_get_text(input), "%f", &value) == 1) {
|
||||||
|
if (tt_hal_radio_set_parameter(handle, param, value) != RADIO_PARAM_SUCCESS) {
|
||||||
|
lv_obj_add_state(input, LV_STATE_INVALID);
|
||||||
|
} else {
|
||||||
|
lv_obj_clear_state(input, LV_STATE_INVALID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lv_obj_add_state(input, LV_STATE_INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePreview() override {}
|
||||||
|
|
||||||
|
virtual void activate() override {
|
||||||
|
lv_obj_clear_state(input, LV_STATE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void deactivate() override {
|
||||||
|
lv_obj_add_state(input, LV_STATE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setValue(float value) override {
|
||||||
|
Str txt;
|
||||||
|
txt.appendf(fmt, value);
|
||||||
|
lv_textarea_set_text(input, txt.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SliderParameterInput : public ParameterInput {
|
||||||
|
char* fmt = nullptr;
|
||||||
|
lv_obj_t* label = nullptr;
|
||||||
|
lv_obj_t* slider = nullptr;
|
||||||
|
lv_obj_t* preview = nullptr;
|
||||||
|
|
||||||
|
SliderParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, int min, int max, char* fmt = "%i")
|
||||||
|
: ParameterInput(handle, param)
|
||||||
|
, fmt(fmt) {
|
||||||
|
initUi(container, row, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SliderParameterInput() {
|
||||||
|
//lv_obj_clean(label);
|
||||||
|
//lv_obj_clean(slider);
|
||||||
|
//lv_obj_clean(preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initUi(lv_obj_t* container, int row, int min, int max) {
|
||||||
|
label = lv_label_create(container);
|
||||||
|
lv_label_set_text(label, toString(param));
|
||||||
|
lv_obj_set_grid_cell(label,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
|
slider = lv_slider_create(container);
|
||||||
|
lv_obj_set_grid_cell(slider,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(slider, lv_pct(100), 10);
|
||||||
|
lv_slider_set_range(slider, min, max);
|
||||||
|
apply_error_style(slider);
|
||||||
|
|
||||||
|
preview = lv_label_create(container);
|
||||||
|
lv_obj_set_grid_cell(preview,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(preview, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_style_text_align(preview , LV_TEXT_ALIGN_LEFT, 0);
|
||||||
|
|
||||||
|
loadFromRadio();
|
||||||
|
|
||||||
|
lv_obj_add_event_cb(slider, [](lv_event_t * e) {
|
||||||
|
lv_obj_t* slider = lv_event_get_target_obj(e);
|
||||||
|
SliderParameterInput* self = (SliderParameterInput*)lv_event_get_user_data(e);
|
||||||
|
self->updatePreview();
|
||||||
|
self->storeToRadio();
|
||||||
|
self->emitUserChange();
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFromRadio() {
|
||||||
|
float value;
|
||||||
|
if (tt_hal_radio_get_parameter(handle, param, &value) == RADIO_PARAM_SUCCESS) {
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void storeToRadio() override {
|
||||||
|
if (tt_hal_radio_set_parameter(handle, param, lv_slider_get_value(slider)) != RADIO_PARAM_SUCCESS) {
|
||||||
|
lv_obj_add_state(slider, LV_STATE_INVALID);
|
||||||
|
} else {
|
||||||
|
lv_obj_clear_state(slider, LV_STATE_INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePreview() override {
|
||||||
|
char buf[64] = {0};
|
||||||
|
lv_snprintf(buf, sizeof(buf), fmt, lv_slider_get_value(slider));
|
||||||
|
lv_label_set_text(preview, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void activate() override {
|
||||||
|
lv_obj_clear_state(slider, LV_STATE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void deactivate() override {
|
||||||
|
lv_obj_add_state(slider, LV_STATE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setValue(float value) override {
|
||||||
|
lv_slider_set_value(slider, value, LV_ANIM_ON);
|
||||||
|
updatePreview();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct SliderSelectParameterInput : public ParameterInput {
|
||||||
|
static constexpr float SELECT_END = -1;
|
||||||
|
const float* selections;
|
||||||
|
const size_t selectionsSize;
|
||||||
|
|
||||||
|
char unit[64] = {0};
|
||||||
|
char* fmt;
|
||||||
|
lv_obj_t* label = nullptr;
|
||||||
|
lv_obj_t* slider = nullptr;
|
||||||
|
lv_obj_t* preview = nullptr;
|
||||||
|
|
||||||
|
static constexpr size_t get_selection_num(const float selections[]) {
|
||||||
|
constexpr size_t MAX_SELECTIONS = 32;
|
||||||
|
|
||||||
|
if (selections) {
|
||||||
|
for (size_t i = 0; i < MAX_SELECTIONS; ++i) {
|
||||||
|
if (selections[i] == SELECT_END) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SliderSelectParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, const float selections[] = nullptr, char* fmt = "%f")
|
||||||
|
: ParameterInput(handle, param)
|
||||||
|
, selections(selections)
|
||||||
|
, selectionsSize(get_selection_num(selections))
|
||||||
|
, fmt(fmt) {
|
||||||
|
initUi(container, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SliderSelectParameterInput() {
|
||||||
|
//lv_obj_clean(label);
|
||||||
|
//lv_obj_clean(slider);
|
||||||
|
//lv_obj_clean(preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_selection_index(const float value) {
|
||||||
|
for (int i = 0; i < selectionsSize; ++i) {
|
||||||
|
if (selections[i] == value) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initUi(lv_obj_t* container, int row) {
|
||||||
|
label = lv_label_create(container);
|
||||||
|
lv_label_set_text(label, toString(param));
|
||||||
|
lv_obj_set_grid_cell(label,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
|
slider = lv_slider_create(container);
|
||||||
|
lv_obj_set_grid_cell(slider,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(slider, lv_pct(100), 10);
|
||||||
|
lv_slider_set_range(slider, 0, selectionsSize - 1);
|
||||||
|
apply_error_style(slider);
|
||||||
|
|
||||||
|
preview = lv_label_create(container);
|
||||||
|
lv_obj_set_grid_cell(preview,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(preview, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_style_text_align(preview , LV_TEXT_ALIGN_LEFT, 0);
|
||||||
|
|
||||||
|
tt_hal_radio_get_parameter_unit_str(handle, param, unit, sizeof(unit));
|
||||||
|
|
||||||
|
loadFromRadio();
|
||||||
|
|
||||||
|
lv_obj_add_event_cb(slider, [](lv_event_t * e) {
|
||||||
|
lv_obj_t* slider = lv_event_get_target_obj(e);
|
||||||
|
SliderSelectParameterInput* self = (SliderSelectParameterInput*)lv_event_get_user_data(e);
|
||||||
|
self->updatePreview();
|
||||||
|
self->storeToRadio();
|
||||||
|
self->emitUserChange();
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFromRadio() {
|
||||||
|
float value;
|
||||||
|
if (tt_hal_radio_get_parameter(handle, param, &value) == RADIO_PARAM_SUCCESS) {
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void storeToRadio() override {
|
||||||
|
if (tt_hal_radio_set_parameter(handle, param, selections[lv_slider_get_value(slider)]) != RADIO_PARAM_SUCCESS) {
|
||||||
|
lv_obj_add_state(slider, LV_STATE_INVALID);
|
||||||
|
} else {
|
||||||
|
lv_obj_clear_state(slider, LV_STATE_INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePreview() override {
|
||||||
|
Str text;
|
||||||
|
text.appendf(fmt, selections[lv_slider_get_value(slider)]);
|
||||||
|
text.append(unit);
|
||||||
|
lv_label_set_text(preview, text.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void activate() override {
|
||||||
|
lv_obj_clear_state(slider, LV_STATE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void deactivate() override {
|
||||||
|
lv_obj_add_state(slider, LV_STATE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setValue(float value) override {
|
||||||
|
lv_slider_set_value(slider, get_selection_index(value), LV_ANIM_ON);
|
||||||
|
updatePreview();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FlagParameterInput : public ParameterInput {
|
||||||
|
lv_obj_t* label = nullptr;
|
||||||
|
lv_obj_t* input = nullptr;
|
||||||
|
|
||||||
|
FlagParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row)
|
||||||
|
: ParameterInput(handle, param) {
|
||||||
|
initUi(container, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~FlagParameterInput() {
|
||||||
|
//lv_obj_clean(label);
|
||||||
|
//lv_obj_clean(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initUi(lv_obj_t* container, int row) {
|
||||||
|
label = lv_label_create(container);
|
||||||
|
lv_label_set_text(label, toString(param));
|
||||||
|
lv_obj_set_grid_cell(label,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
|
input = lv_switch_create(container);
|
||||||
|
lv_obj_set_grid_cell(input,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(input, 30, 20);
|
||||||
|
apply_error_style(input);
|
||||||
|
loadFromRadio();
|
||||||
|
lv_obj_add_event_cb(input, [](lv_event_t * e) {
|
||||||
|
lv_obj_t* slider = lv_event_get_target_obj(e);
|
||||||
|
FlagParameterInput* self = (FlagParameterInput*)lv_event_get_user_data(e);
|
||||||
|
self->storeToRadio();
|
||||||
|
self->emitUserChange();
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFromRadio() {
|
||||||
|
float value;
|
||||||
|
if (tt_hal_radio_get_parameter(handle, param, &value) == RADIO_PARAM_SUCCESS) {
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void storeToRadio() override {
|
||||||
|
float value = lv_obj_has_state(input, LV_STATE_CHECKED) ? 1.0 : 0.0;
|
||||||
|
if (tt_hal_radio_set_parameter(handle, param, value) != RADIO_PARAM_SUCCESS) {
|
||||||
|
lv_obj_add_state(input, LV_STATE_INVALID);
|
||||||
|
} else {
|
||||||
|
lv_obj_clear_state(input, LV_STATE_INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePreview() override {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void activate() override {
|
||||||
|
lv_obj_clear_state(input, LV_STATE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void deactivate() override {
|
||||||
|
lv_obj_add_state(input, LV_STATE_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setValue(float value) override {
|
||||||
|
if (value != 0.0) {
|
||||||
|
lv_obj_add_state(input, LV_STATE_CHECKED);
|
||||||
|
} else {
|
||||||
|
lv_obj_clear_state(input, LV_STATE_CHECKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static ParameterInput* makeLoraInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row) {
|
||||||
|
static constexpr float bw_values[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0, 500.0, SliderSelectParameterInput::SELECT_END};
|
||||||
|
// LoRa is standardized, so we get to use fancy inputs
|
||||||
|
switch (param) {
|
||||||
|
case RADIO_BANDWIDTH:
|
||||||
|
return new SliderSelectParameterInput(handle, param, container, row, bw_values, "%.1f");
|
||||||
|
case RADIO_SPREADFACTOR:
|
||||||
|
return new SliderParameterInput(handle, param, container, row, 7, 12);
|
||||||
|
case RADIO_CODINGRATE:
|
||||||
|
return new SliderParameterInput(handle, param, container, row, 5, 8);
|
||||||
|
case RADIO_SYNCWORD:
|
||||||
|
return new SliderParameterInput(handle, param, container, row, 0, 255, "%02X");
|
||||||
|
case RADIO_PREAMBLES:
|
||||||
|
return new SliderParameterInput(handle, param, container, row, 0, 0xFFFF);
|
||||||
|
default:
|
||||||
|
return new NumericParameterInput(handle, param, container, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParameterInput* makeLrFhssInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row) {
|
||||||
|
switch (param) {;
|
||||||
|
case RADIO_NARROWGRID:
|
||||||
|
return new FlagParameterInput(handle, param, container, row);
|
||||||
|
default:
|
||||||
|
return new NumericParameterInput(handle, param, container, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParameterInput* makeBaseInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row) {
|
||||||
|
switch (param) {
|
||||||
|
case RADIO_POWER: //no break
|
||||||
|
case RADIO_FREQUENCY:
|
||||||
|
return new NumericParameterInput(handle, param, container, row);
|
||||||
|
case RADIO_BOOSTEDGAIN:
|
||||||
|
return new FlagParameterInput(handle, param, container, row);
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParameterInput* makeParameterInput(RadioHandle handle, const RadioParameter param, const Modulation modulation, lv_obj_t* container, int row) {
|
||||||
|
auto base_input = makeBaseInput(handle, param, container, row);
|
||||||
|
if (base_input) return base_input;
|
||||||
|
|
||||||
|
switch (modulation) {
|
||||||
|
case MODULATION_LORA:
|
||||||
|
return makeLoraInput(handle, param, container, row);
|
||||||
|
case MODULATION_LRFHSS:
|
||||||
|
return makeLrFhssInput(handle, param, container, row);
|
||||||
|
default:
|
||||||
|
return new NumericParameterInput(handle, param, container, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::addPreset(Preset* preset) {
|
||||||
|
presets.pushBack(preset);
|
||||||
|
presetsByModulation[preset->modulation].pushBack(preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::queryRadios() {
|
||||||
|
DeviceId devices[MAX_RADIOS];
|
||||||
|
uint16_t device_count = 0;
|
||||||
|
if(!tt_hal_device_find(DEVICE_TYPE_RADIO, devices, &device_count, MAX_RADIOS)) {
|
||||||
|
// TT_LOG_W(TAG, "No radios registered with the system?");
|
||||||
|
} else {
|
||||||
|
size_t radios_allocated = 0;
|
||||||
|
for (size_t i = 0; (i < device_count) && (i < MAX_RADIOS); ++i) {
|
||||||
|
auto radio = tt_hal_radio_alloc(devices[i]);
|
||||||
|
if (radio) {
|
||||||
|
// TT_LOG_I(TAG, "Discovered radio \"%s\"", tt_hal_radio_get_name(radio));
|
||||||
|
radios[radios_allocated] = radio;
|
||||||
|
radios_allocated++;
|
||||||
|
} else {
|
||||||
|
// TT_LOG_E(TAG, "Error allocating radio handle for id=%d", devId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
radioCount = radios_allocated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::getRadioNames(Str &names, const char* const separator) {
|
||||||
|
int count = 1;
|
||||||
|
names.clear();
|
||||||
|
//for (auto radio : radios) {
|
||||||
|
for (size_t i = 0; i < radioCount; ++i) {
|
||||||
|
Str name(tt_hal_radio_get_name(radios[i]));
|
||||||
|
auto last = (i == (radioCount - 1));
|
||||||
|
if (name == "") {
|
||||||
|
name.appendf("Unknown Radio %d", count);
|
||||||
|
}
|
||||||
|
names.append(name.c_str());
|
||||||
|
count++;
|
||||||
|
if (!last) {
|
||||||
|
names.append(separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SettingsView::getModemAvailableIndex(Modulation m) {
|
||||||
|
for (size_t i = 0; i < modemsAvailableCount; ++i) {
|
||||||
|
if (modemsAvailable[i] == m) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t* SettingsView::initParameterFormGeneric(lv_obj_t *parent, const Modulation modem) {
|
||||||
|
lv_obj_t *container = propertiesForm;
|
||||||
|
if (container) {
|
||||||
|
lv_obj_clean(container);
|
||||||
|
lv_obj_del(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
paramsAvailableCount = 0;
|
||||||
|
container = lv_obj_create(parent);
|
||||||
|
lv_obj_set_style_pad_all(container, 0, 0);
|
||||||
|
lv_obj_set_layout(container, LV_LAYOUT_GRID);
|
||||||
|
lv_obj_align(container, LV_ALIGN_TOP_MID, 0, 0);
|
||||||
|
|
||||||
|
const int grid_row_size = 40;
|
||||||
|
const int grid_col_size = 60;
|
||||||
|
static constexpr size_t row_dsc_last = RADIO_NARROWGRID + 1;
|
||||||
|
static lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), grid_col_size, LV_GRID_TEMPLATE_LAST};
|
||||||
|
static lv_coord_t row_dsc[row_dsc_last] = {0};
|
||||||
|
for (size_t i = 0; i < row_dsc_last; ++i) {
|
||||||
|
row_dsc[i] = grid_row_size; //LV_GRID_FR(1);
|
||||||
|
}
|
||||||
|
row_dsc[row_dsc_last - 1] = LV_GRID_TEMPLATE_LAST;
|
||||||
|
lv_obj_set_grid_dsc_array(container, col_dsc, row_dsc);
|
||||||
|
|
||||||
|
char unit_buffer[32] = {0};
|
||||||
|
|
||||||
|
// Clean up any input
|
||||||
|
for (size_t i = 0; i < MAX_PARAMS; ++i) {
|
||||||
|
// As this is a LUT, only some may be set
|
||||||
|
if (paramInputs[i]) {
|
||||||
|
delete paramInputs[i];
|
||||||
|
paramInputs[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RadioParameter param = RADIO_POWER;
|
||||||
|
param <= RADIO_NARROWGRID;
|
||||||
|
param = static_cast<RadioParameter>((size_t)param + 1)) {
|
||||||
|
float value = 0.0;
|
||||||
|
Str value_buffer;
|
||||||
|
auto status = tt_hal_radio_get_parameter(radioSelected, param, &value);
|
||||||
|
if (status == RADIO_PARAM_SUCCESS) {
|
||||||
|
auto input = makeParameterInput(radioSelected, param, modem, container, paramsAvailableCount);
|
||||||
|
input->onUserChange([](void* ctx) {
|
||||||
|
SettingsView* self = (SettingsView*)ctx;
|
||||||
|
self->onParameterInput();
|
||||||
|
}, this);
|
||||||
|
paramInputs[param] = input;
|
||||||
|
//lv_group_focus_obj(input);
|
||||||
|
paramsAvailable[paramsAvailableCount] = param;
|
||||||
|
paramsAvailableCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row_dsc[paramsAvailableCount] = LV_GRID_TEMPLATE_LAST;
|
||||||
|
lv_obj_set_grid_dsc_array(container, col_dsc, row_dsc);
|
||||||
|
lv_obj_set_size(container, lv_pct(100), lv_pct(100));
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::selectModulation(int modemIndex) {
|
||||||
|
lv_dropdown_set_selected(modemDropdown, modemIndex);
|
||||||
|
if (tt_hal_radio_set_modulation(radioSelected, modemsAvailable[modemIndex])) {
|
||||||
|
propertiesForm = initParameterFormGeneric(mainPanel, modemsAvailable[modemIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePresets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::selectPreset(int presetIndex) {
|
||||||
|
// The first index is always "No preset" or "None available"
|
||||||
|
// Other indices are the presets for the current modulation + 1
|
||||||
|
|
||||||
|
auto modem = tt_hal_radio_get_modulation(radioSelected);
|
||||||
|
auto& presets = presetsByModulation[modem];
|
||||||
|
if ((presetIndex > 0) && ((presetIndex - 1) < presets.size())) {
|
||||||
|
auto preset = presets[presetIndex - 1];
|
||||||
|
|
||||||
|
for (auto iter = preset->items.begin(); iter != preset->items.end(); iter++) {
|
||||||
|
if (paramInputs[iter->parameter]) {
|
||||||
|
paramInputs[iter->parameter]->setValue(iter->value);
|
||||||
|
paramInputs[iter->parameter]->storeToRadio();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
crash("Selected preset does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::onParameterInput() {
|
||||||
|
// As the user did an input, this makes any applied
|
||||||
|
// preset inconsistent, revert back to "None".
|
||||||
|
lv_dropdown_set_selected(modemPresetDropdown, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::updatePresets() {
|
||||||
|
auto modemIndexConfigured = tt_hal_radio_get_modulation(radioSelected);
|
||||||
|
|
||||||
|
Str preset_list("Select...");
|
||||||
|
auto& presets = presetsByModulation[modemIndexConfigured];
|
||||||
|
if (!presets.empty()) {
|
||||||
|
preset_list.append("\n");
|
||||||
|
}
|
||||||
|
for (auto iter = presets.begin(); iter != presets.end(); iter++) {
|
||||||
|
auto place_sep = !is_last(iter, presets);
|
||||||
|
auto& name = (*iter)->name;
|
||||||
|
preset_list.append(name.c_str());
|
||||||
|
if (place_sep) {
|
||||||
|
preset_list.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preset_list.empty()) {
|
||||||
|
lv_obj_add_state(modemPresetDropdown, LV_STATE_DISABLED);
|
||||||
|
lv_dropdown_set_options(modemPresetDropdown, "None");
|
||||||
|
} else {
|
||||||
|
lv_obj_clear_state(modemPresetDropdown, LV_STATE_DISABLED);
|
||||||
|
lv_dropdown_set_options(modemPresetDropdown, preset_list.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::selectRadio(int index) {
|
||||||
|
if (radioStateSubId > -1) {
|
||||||
|
tt_hal_radio_unsubscribe_state(radioSelected, radioStateSubId);
|
||||||
|
}
|
||||||
|
|
||||||
|
radioSelected = radios[index];
|
||||||
|
crashassert(radioSelected, "Radio selected not allocated");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_MODEMS; ++i) {
|
||||||
|
modemsAvailable[i] = MODULATION_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Str modulation_list;
|
||||||
|
modemsAvailableCount = 1;
|
||||||
|
modemsAvailable[0] = MODULATION_NONE;
|
||||||
|
modulation_list.append(LV_SYMBOL_MINUS);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
modulation_list.append(LV_SYMBOL_MINUS);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
modulation_list.append("Disabled\n");
|
||||||
|
|
||||||
|
|
||||||
|
for (Modulation mod = FIRST_MODULATION;
|
||||||
|
mod <= LAST_MODULATION;
|
||||||
|
mod = static_cast<Modulation>((size_t)mod + 1)) {
|
||||||
|
bool canRx = tt_hal_radio_can_receive(radioSelected, mod);
|
||||||
|
bool canTx = tt_hal_radio_can_transmit(radioSelected, mod);
|
||||||
|
bool place_sep = (canRx || canTx) && (mod != LAST_MODULATION);
|
||||||
|
|
||||||
|
if (!canRx && !canTx) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
modemsAvailable[modemsAvailableCount] = mod;
|
||||||
|
modemsAvailableCount++;
|
||||||
|
|
||||||
|
if (canRx) {
|
||||||
|
modulation_list.append(LV_SYMBOL_DOWNLOAD);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
} else {
|
||||||
|
modulation_list.append(LV_SYMBOL_MINUS);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canTx) {
|
||||||
|
modulation_list.append(LV_SYMBOL_UPLOAD);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
} else {
|
||||||
|
modulation_list.append(LV_SYMBOL_MINUS);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
modulation_list.append(toString(mod));
|
||||||
|
if (place_sep) {
|
||||||
|
modulation_list.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_dropdown_set_options(modemDropdown, modulation_list.c_str());
|
||||||
|
auto modemIndexConfigured = getModemAvailableIndex(tt_hal_radio_get_modulation(radioSelected));
|
||||||
|
if (modemIndexConfigured > -1) {
|
||||||
|
lv_dropdown_set_selected(modemDropdown, modemIndexConfigured);
|
||||||
|
selectModulation(modemIndexConfigured);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectedRadioState(tt_hal_radio_get_state(radioSelected));
|
||||||
|
|
||||||
|
radioStateSubId = tt_hal_radio_subscribe_state(radioSelected, [](DeviceId id, RadioState state, void* ctx) {
|
||||||
|
SettingsView* self = (SettingsView*)ctx;
|
||||||
|
self->updateSelectedRadioState(state);
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t* SettingsView::initDeviceForm(lv_obj_t *parent) {
|
||||||
|
lv_obj_t *container = lv_obj_create(parent);
|
||||||
|
lv_obj_set_size(container, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_style_pad_all(container, 0, 0);
|
||||||
|
|
||||||
|
const int grid_row_size = 40;
|
||||||
|
const int grid_col_size = 45;
|
||||||
|
static lv_coord_t lora_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), grid_col_size, LV_GRID_TEMPLATE_LAST};
|
||||||
|
static lv_coord_t lora_row_dsc[] = {
|
||||||
|
grid_row_size,
|
||||||
|
grid_row_size,
|
||||||
|
grid_row_size,
|
||||||
|
grid_row_size,
|
||||||
|
LV_GRID_TEMPLATE_LAST};
|
||||||
|
lv_obj_set_layout(container, LV_LAYOUT_GRID);
|
||||||
|
lv_obj_set_grid_dsc_array(container, lora_col_dsc, lora_row_dsc);
|
||||||
|
|
||||||
|
Str radio_names;
|
||||||
|
getRadioNames(radio_names, "\n");
|
||||||
|
radioDropdown = createGridDropdownInput(container, 0, "Device", radio_names.c_str());
|
||||||
|
radioSwitch = lv_switch_create(container);
|
||||||
|
lv_obj_set_grid_cell(radioSwitch,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, 0, 1);
|
||||||
|
lv_obj_set_size(radioSwitch, lv_pct(100), 20);
|
||||||
|
|
||||||
|
lv_obj_t* state_text = lv_label_create(container);
|
||||||
|
lv_label_set_text(state_text, "State");
|
||||||
|
lv_obj_set_grid_cell(state_text,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, 1, 1);
|
||||||
|
lv_obj_set_size(state_text, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
|
radioStateLabel = lv_label_create(container);
|
||||||
|
lv_label_set_text(radioStateLabel, "Unknown");
|
||||||
|
lv_obj_set_grid_cell(radioStateLabel,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, 1, 1);
|
||||||
|
lv_obj_set_size(radioStateLabel, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
|
modemDropdown = createGridDropdownInput(container, 2, "Modulation", "None available");
|
||||||
|
modemPresetDropdown = createGridDropdownInput(container, 3, "Preset", "None");
|
||||||
|
|
||||||
|
lv_obj_add_event_cb(modemDropdown, [](lv_event_t * e) {
|
||||||
|
SettingsView* self = (SettingsView*)lv_event_get_user_data(e);
|
||||||
|
lv_obj_t* input = lv_event_get_target_obj(e);
|
||||||
|
self->selectModulation(lv_dropdown_get_selected(input));
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
|
||||||
|
lv_obj_add_event_cb(modemPresetDropdown, [](lv_event_t * e) {
|
||||||
|
SettingsView* self = (SettingsView*)lv_event_get_user_data(e);
|
||||||
|
lv_obj_t* input = lv_event_get_target_obj(e);
|
||||||
|
self->selectPreset(lv_dropdown_get_selected(input));
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
|
||||||
|
lv_obj_add_event_cb(radioSwitch, [](lv_event_t * e) {
|
||||||
|
lv_obj_t* input = lv_event_get_target_obj(e);
|
||||||
|
SettingsView* self = (SettingsView*)lv_event_get_user_data(e);
|
||||||
|
self->enableSelectedRadio(lv_obj_has_state(input, LV_STATE_CHECKED));
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
|
||||||
|
selectRadio(0);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::updateSelectedRadioState(RadioState state) {
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case RADIO_PENDING_ON:
|
||||||
|
lv_label_set_text(radioStateLabel, "Activating...");
|
||||||
|
break;
|
||||||
|
case RADIO_ON:
|
||||||
|
lv_label_set_text(radioStateLabel, "Activated");
|
||||||
|
break;
|
||||||
|
case RADIO_ERROR:
|
||||||
|
lv_label_set_text(radioStateLabel, "Error");
|
||||||
|
break;
|
||||||
|
case RADIO_PENDING_OFF:
|
||||||
|
lv_label_set_text(radioStateLabel, "Deactivating...");
|
||||||
|
break;
|
||||||
|
case RADIO_OFF:
|
||||||
|
lv_label_set_text(radioStateLabel, "Deactivated");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lv_label_set_text(radioStateLabel, "Unknown");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case RADIO_OFF:
|
||||||
|
case RADIO_ERROR:
|
||||||
|
activateConfig();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
deactivateConfig();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsView::enableSelectedRadio(bool enable) {
|
||||||
|
if (radioSelected) {
|
||||||
|
if (enable) {
|
||||||
|
return tt_hal_radio_start(radioSelected);
|
||||||
|
} else {
|
||||||
|
return tt_hal_radio_stop(radioSelected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::activateConfig() {
|
||||||
|
lv_obj_clear_state(modemDropdown, LV_STATE_DISABLED);
|
||||||
|
lv_obj_clear_state(radioSwitch, LV_STATE_CHECKED);
|
||||||
|
lv_obj_clear_state(modemPresetDropdown, LV_STATE_DISABLED);
|
||||||
|
for (size_t i = 0; i < MAX_PARAMS; ++i) {
|
||||||
|
if (paramInputs[i]) {
|
||||||
|
paramInputs[i]->activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::deactivateConfig() {
|
||||||
|
lv_obj_add_state(radioSwitch, LV_STATE_CHECKED);
|
||||||
|
lv_obj_add_state(modemDropdown, LV_STATE_DISABLED);
|
||||||
|
lv_obj_add_state(modemPresetDropdown, LV_STATE_DISABLED);
|
||||||
|
for (size_t i = 0; i < MAX_PARAMS; ++i) {
|
||||||
|
if (paramInputs[i]) {
|
||||||
|
paramInputs[i]->deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsView::initUi(lv_obj_t *parent) {
|
||||||
|
mainPanel = lv_obj_create(parent);
|
||||||
|
lv_obj_set_size(mainPanel, lv_pct(100), lv_pct(80));
|
||||||
|
lv_obj_set_flex_flow(mainPanel, LV_FLEX_FLOW_COLUMN);
|
||||||
|
lv_obj_align(mainPanel, LV_ALIGN_TOP_MID, 0, 0);
|
||||||
|
lv_obj_set_style_border_width(mainPanel, 0, 0);
|
||||||
|
lv_obj_set_style_pad_all(mainPanel, 0, 0);
|
||||||
|
|
||||||
|
deviceForm = initDeviceForm(mainPanel);
|
||||||
|
}
|
||||||
72
ExternalApps/RadioSet/main/Source/SettingsView.h
Normal file
72
ExternalApps/RadioSet/main/Source/SettingsView.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Preset.h"
|
||||||
|
|
||||||
|
#include <tt_hal_radio.h>
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
class ParameterInput;
|
||||||
|
|
||||||
|
class SettingsView {
|
||||||
|
static constexpr size_t MAX_RADIOS = 32;
|
||||||
|
static constexpr Modulation FIRST_MODULATION = MODULATION_NONE;
|
||||||
|
static constexpr Modulation LAST_MODULATION = MODULATION_LRFHSS;
|
||||||
|
static constexpr size_t MAX_MODEMS = LAST_MODULATION + 1;
|
||||||
|
static constexpr size_t MAX_PARAMS = RADIO_NARROWGRID + 1;
|
||||||
|
|
||||||
|
RadioHandle radios[MAX_RADIOS] = {0};
|
||||||
|
size_t radioCount = 0;
|
||||||
|
|
||||||
|
RadioHandle radioSelected = nullptr;
|
||||||
|
RadioStateSubscriptionId radioStateSubId = -1;
|
||||||
|
Modulation modemsAvailable[MAX_MODEMS] = {};
|
||||||
|
size_t modemsAvailableCount = 0;
|
||||||
|
RadioParameter paramsAvailable[MAX_PARAMS] = {};
|
||||||
|
ParameterInput* paramInputs[MAX_PARAMS] = {0};
|
||||||
|
size_t paramsAvailableCount = 0;
|
||||||
|
|
||||||
|
LinkedList<Preset*> presets;
|
||||||
|
LinkedList<Preset*> presetsByModulation[MAX_MODEMS];
|
||||||
|
|
||||||
|
lv_obj_t* mainPanel = nullptr;
|
||||||
|
lv_obj_t* deviceForm = nullptr;
|
||||||
|
lv_obj_t* radioDropdown = nullptr;
|
||||||
|
lv_obj_t* radioSwitch = nullptr;
|
||||||
|
lv_obj_t* radioStateLabel = nullptr;
|
||||||
|
lv_obj_t* modemDropdown = nullptr;
|
||||||
|
lv_obj_t* modemPresetDropdown = nullptr;
|
||||||
|
|
||||||
|
lv_obj_t *propertiesForm = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void addPreset(Preset* preset);
|
||||||
|
void queryRadios();
|
||||||
|
void getRadioNames(Str &names, const char* const separator);
|
||||||
|
|
||||||
|
int getModemAvailableIndex(Modulation m);
|
||||||
|
|
||||||
|
lv_obj_t* initParameterFormGeneric(lv_obj_t *parent, const Modulation modem);
|
||||||
|
|
||||||
|
void selectModulation(int modemIndex);
|
||||||
|
void selectPreset(int presetIndex);
|
||||||
|
|
||||||
|
void onParameterInput();
|
||||||
|
void updatePresets();
|
||||||
|
|
||||||
|
void selectRadio(int index);
|
||||||
|
|
||||||
|
lv_obj_t *initDeviceForm(lv_obj_t *parent);
|
||||||
|
|
||||||
|
void updateSelectedRadioState(RadioState state);
|
||||||
|
bool enableSelectedRadio(bool enable);
|
||||||
|
|
||||||
|
void activateConfig();
|
||||||
|
void deactivateConfig();
|
||||||
|
|
||||||
|
void initUi(lv_obj_t *parent);
|
||||||
|
|
||||||
|
explicit SettingsView(lv_obj_t *parent) {
|
||||||
|
queryRadios();
|
||||||
|
initUi(parent);
|
||||||
|
}
|
||||||
|
};
|
||||||
63
ExternalApps/RadioSet/main/Source/Utils.cpp
Normal file
63
ExternalApps/RadioSet/main/Source/Utils.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
#include <tt_app_alertdialog.h>
|
||||||
|
|
||||||
|
void crash(const char* const message) {
|
||||||
|
tt_app_alertdialog_start("RadioSet has crashed!", message, nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void crashassert(bool assertion, const char* const message) {
|
||||||
|
if (!assertion) {
|
||||||
|
crash(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *const toString(Modulation m) {
|
||||||
|
switch (m) {
|
||||||
|
case MODULATION_NONE:
|
||||||
|
return "None";
|
||||||
|
case MODULATION_LORA:
|
||||||
|
return "LoRa";
|
||||||
|
case MODULATION_FSK:
|
||||||
|
return "FSK";
|
||||||
|
case MODULATION_LRFHSS:
|
||||||
|
return "LR-FHSS";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crash("Unknown modulation passed.");
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
char *const toString(RadioParameter p) {
|
||||||
|
switch (p) {
|
||||||
|
case RADIO_POWER:
|
||||||
|
return "Power";
|
||||||
|
case RADIO_BOOSTEDGAIN:
|
||||||
|
return "RX Boosted Gain";
|
||||||
|
case RADIO_FREQUENCY:
|
||||||
|
return "Center Frequency";
|
||||||
|
case RADIO_BANDWIDTH:
|
||||||
|
return "Bandwidth";
|
||||||
|
case RADIO_SPREADFACTOR:
|
||||||
|
return "Spread Factor";
|
||||||
|
case RADIO_CODINGRATE:
|
||||||
|
return "Coding Rate";
|
||||||
|
case RADIO_SYNCWORD:
|
||||||
|
return "Sync Word";
|
||||||
|
case RADIO_PREAMBLES:
|
||||||
|
return "Preamble Length";
|
||||||
|
case RADIO_FREQDIV:
|
||||||
|
return "Frequency Deviation";
|
||||||
|
case RADIO_DATARATE:
|
||||||
|
return "Data Rate";
|
||||||
|
case RADIO_ADDRWIDTH:
|
||||||
|
return "Address Width";
|
||||||
|
case RADIO_NARROWGRID:
|
||||||
|
return "Narrow Grid";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crash("Unknown parameter passed.");
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
19
ExternalApps/RadioSet/main/Source/Utils.h
Normal file
19
ExternalApps/RadioSet/main/Source/Utils.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tt_hal_radio.h>
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
Iterator next(Iterator i) {
|
||||||
|
return i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator, typename Container>
|
||||||
|
bool is_last(Iterator i, const Container& c) {
|
||||||
|
return (i != c.end()) && (next(i) == c.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void crash(const char* const message);
|
||||||
|
void crashassert(bool assertion, const char* const message);
|
||||||
|
|
||||||
|
char *const toString(Modulation m);
|
||||||
|
char *const toString(RadioParameter p);
|
||||||
Loading…
x
Reference in New Issue
Block a user