ChirpChatter: Update to improved Radio API
+ Add hexdump decode + Make progress/status functional + Transmit supported
This commit is contained in:
parent
04edfa7c99
commit
24e33368b2
@ -14,6 +14,12 @@
|
||||
#include <iomanip>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <lvgl.h>
|
||||
|
||||
extern const lv_obj_class_t lv_label_class;
|
||||
@ -66,36 +72,49 @@ static void debugDefocus(lv_event_t *event) {
|
||||
}
|
||||
}
|
||||
|
||||
bool isPrintableData(const std::vector<uint8_t>& data) {
|
||||
return std::all_of(data.begin(), data.end(),
|
||||
[](uint8_t byte) {
|
||||
char c = static_cast<char>(byte);
|
||||
return std::isprint(static_cast<unsigned char>(c));
|
||||
});
|
||||
}
|
||||
|
||||
std::string hexdump(const std::vector<uint8_t>& data) {
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
oss << std::hex << std::uppercase << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<unsigned>(data[i]);
|
||||
if (i + 1 != data.size()) oss << ' ';
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
class LoraView {
|
||||
public:
|
||||
using DeviceActivationCallback = std::function<void(hal::radio::RadioDevice&)>;
|
||||
using DeviceActivationCallback = std::function<void(std::shared_ptr<tt::hal::radio::RadioDevice>)>;
|
||||
private:
|
||||
//using LoraParameters = tt::hal::lora::LoraParameters;
|
||||
|
||||
std::vector<std::string> loraDevNames;
|
||||
std::vector<std::shared_ptr<tt::hal::radio::RadioDevice>> loraDevs;
|
||||
std::shared_ptr<tt::hal::radio::RadioDevice> loraDevice;
|
||||
|
||||
DeviceActivationCallback cbDevActive;
|
||||
DeviceActivationCallback cbDevInactive;
|
||||
|
||||
std::vector<std::string> getLoraDevNames() {
|
||||
std::vector<std::string> lora_names;
|
||||
/*size_t dev_index = 0;
|
||||
void queryLoraDevs() {
|
||||
auto radios = tt::hal::findDevices<tt::hal::radio::RadioDevice>(tt::hal::Device::Type::Radio);
|
||||
loraDevNames.clear();
|
||||
loraDevs.clear();
|
||||
|
||||
std::vector<hal::lora::LoraConfiguration> lora_configs;
|
||||
loraService->getLoraConfigurations(lora_configs);
|
||||
|
||||
for (const auto& lora_config: lora_configs) {
|
||||
std::string name(lora_config.name);
|
||||
if (!name.empty()) {
|
||||
lora_names.push_back(name);
|
||||
} else {
|
||||
std::stringstream stream;
|
||||
stream << "Device " << std::to_string(dev_index);
|
||||
lora_names.push_back(stream.str());
|
||||
for (const auto& radio: radios) {
|
||||
if (radio->isCapableOf(tt::hal::radio::RadioDevice::Modulation::LoRa)) {
|
||||
loraDevNames.push_back(radio->getName());
|
||||
loraDevs.push_back(radio);
|
||||
}
|
||||
dev_index++;
|
||||
}*/
|
||||
return lora_names;
|
||||
}
|
||||
}
|
||||
|
||||
lv_obj_t* initDropdownInput(int row, const char* const label, const char* const items) {
|
||||
@ -212,7 +231,7 @@ private:
|
||||
frequencyInput = initFormInput(1, "Frequency", "869.525", "MHz");
|
||||
bandwidthInput = initFormInput(2, "Bandwidth", "250", "kHz");
|
||||
syncwordInput = initFormInput(3, "Sync Word", "2B", "hex");
|
||||
deBitsInput = initSliderInput(4, "DE Bits", 0, 4, 2);
|
||||
deBitsInput = initSliderInput(4, "Coding Rate", 4, 8, 5);
|
||||
sfInput = initSliderInput(5, "Spread Factor", 7, 12, 11);
|
||||
preambleChirpsInput = initSliderInput(6, "Preamble Chirps", 4, 32, 16);
|
||||
txPowInput = initFormInput(7, "TX Power", "27", "dBm");
|
||||
@ -345,19 +364,17 @@ public:
|
||||
lv_obj_t* txPowInput;
|
||||
|
||||
LoraView(lv_obj_t *parent) {
|
||||
loraDevNames = getLoraDevNames();
|
||||
queryLoraDevs();
|
||||
initUi(parent);
|
||||
/*
|
||||
setParameters(LoraParameters {
|
||||
.spreadFactor = 11,
|
||||
.deBits = 2,
|
||||
.syncWord = 0x2B,
|
||||
.preambleLength = 8,
|
||||
.power = 22,
|
||||
.tcxoVoltage = 3.0,
|
||||
.frequency = 869.525,
|
||||
.bandwidth = 250.0
|
||||
});*/
|
||||
setParameters(
|
||||
869.525,
|
||||
250.0,
|
||||
0x2B,
|
||||
16,
|
||||
5,
|
||||
11,
|
||||
22
|
||||
);
|
||||
}
|
||||
|
||||
void onDeviceActivation(DeviceActivationCallback cb) {
|
||||
@ -367,45 +384,86 @@ public:
|
||||
void onDeviceDeactivation(DeviceActivationCallback cb) {
|
||||
cbDevInactive = cb;
|
||||
}
|
||||
/*
|
||||
void setParameters(LoraParameters params) {
|
||||
std::string buf;
|
||||
loraParams = params;
|
||||
|
||||
buf = std::format("{:.6f}", loraParams.frequency);
|
||||
void setParameters(float frequency, float bandwidth, uint8_t syncWord, uint16_t preambleLength, uint8_t codingRate, uint8_t spreadFactor, int8_t power) {
|
||||
std::string buf;
|
||||
|
||||
buf = std::format("{:.6f}", frequency);
|
||||
lv_textarea_set_text(frequencyInput, buf.c_str());
|
||||
buf = std::format("{:.2f}", loraParams.bandwidth);
|
||||
buf = std::format("{:.2f}", bandwidth);
|
||||
lv_textarea_set_text(bandwidthInput, buf.c_str());
|
||||
buf = std::format("{:X}", loraParams.syncWord);
|
||||
buf = std::format("{:X}", syncWord);
|
||||
lv_textarea_set_text(syncwordInput, buf.c_str());
|
||||
lv_slider_set_value(preambleChirpsInput, loraParams.preambleLength, LV_ANIM_OFF);
|
||||
lv_slider_set_value(deBitsInput, loraParams.deBits, LV_ANIM_OFF);
|
||||
lv_slider_set_value(sfInput, loraParams.spreadFactor, LV_ANIM_OFF);
|
||||
buf = std::format("{:d}", loraParams.power);
|
||||
lv_slider_set_value(preambleChirpsInput, preambleLength, LV_ANIM_OFF);
|
||||
lv_slider_set_value(deBitsInput, codingRate, LV_ANIM_OFF);
|
||||
lv_slider_set_value(sfInput, spreadFactor, LV_ANIM_OFF);
|
||||
buf = std::format("{:d}", power);
|
||||
lv_textarea_set_text(txPowInput, buf.c_str());
|
||||
}*/
|
||||
}
|
||||
|
||||
void configureFromForm() {
|
||||
using enum tt::hal::radio::RadioDevice::Parameter;
|
||||
|
||||
std::string buffer;
|
||||
int value = 0;
|
||||
bool configured = true;
|
||||
buffer = lv_textarea_get_text(frequencyInput);
|
||||
if (!buffer.empty()) {
|
||||
configured &= loraDevice->configure(Frequency, std::stof(buffer));
|
||||
}
|
||||
buffer = lv_textarea_get_text(bandwidthInput);
|
||||
if (!buffer.empty()) {
|
||||
configured &= loraDevice->configure(Bandwidth, std::stof(buffer));
|
||||
}
|
||||
buffer = lv_textarea_get_text(syncwordInput);
|
||||
if (!buffer.empty()) {
|
||||
uint8_t syncWord = 0;
|
||||
std::stringstream ss(buffer);
|
||||
ss >> std::hex >> syncWord;
|
||||
|
||||
configured &= loraDevice->configure(SyncWord, std::stoi(buffer, nullptr, 16));
|
||||
}
|
||||
value = lv_slider_get_value(deBitsInput);
|
||||
configured &= loraDevice->configure(CodingRate, value);
|
||||
|
||||
value = lv_slider_get_value(sfInput);
|
||||
configured &= loraDevice->configure(SpreadFactor, value);
|
||||
|
||||
value = lv_slider_get_value(preambleChirpsInput);
|
||||
configured &= loraDevice->configure(PreambleLength, value);
|
||||
|
||||
buffer = lv_textarea_get_text(txPowInput);
|
||||
if (!buffer.empty()) {
|
||||
configured &= loraDevice->configure(Power, std::stof(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
void enableDevice()
|
||||
{
|
||||
/*
|
||||
loraDevice = loraService->getDevice(loraDevNames[lv_dropdown_get_selected(loraDeviceInput)]);
|
||||
loraDevice = loraDevs[lv_dropdown_get_selected(loraDeviceInput)];
|
||||
if (loraDevice) {
|
||||
disableForm();
|
||||
loraDevice->start(loraParams);
|
||||
cbDevActive(*loraDevice);
|
||||
configureFromForm();
|
||||
loraDevice->start(tt::hal::radio::RadioDevice::Modulation::LoRa);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
if (loraDevice->getState() != tt::hal::radio::RadioDevice::State::On) {
|
||||
lv_obj_clear_state(loraDeviceOn, LV_STATE_CHECKED);
|
||||
enableForm();
|
||||
} else {
|
||||
cbDevActive(loraDevice);
|
||||
}
|
||||
} else {
|
||||
lv_obj_clear_state(loraDeviceOn, LV_STATE_CHECKED);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void disableDevice()
|
||||
{/*
|
||||
{
|
||||
if (loraDevice) {
|
||||
loraDevice->stop();
|
||||
cbDevInactive(*loraDevice);
|
||||
cbDevInactive(loraDevice);
|
||||
}
|
||||
enableForm();
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
@ -414,6 +472,7 @@ class ChirpChatterApp : public App {
|
||||
lv_obj_t* sidebar = nullptr;
|
||||
lv_obj_t* mainView = nullptr;
|
||||
lv_obj_t* progressBar = nullptr;
|
||||
lv_obj_t* progressText = nullptr;
|
||||
|
||||
lv_obj_t* messageList = nullptr;
|
||||
lv_obj_t* inputField = nullptr;
|
||||
@ -422,6 +481,7 @@ class ChirpChatterApp : public App {
|
||||
LoraView* loraView = nullptr;
|
||||
|
||||
hal::radio::RadioDevice::RxSubscriptionId rxSubId;
|
||||
std::shared_ptr<tt::hal::radio::RadioDevice> loraDevice;
|
||||
|
||||
template<CCViews T>
|
||||
lv_obj_t* createSidebarButton(lv_obj_t* parent, const char* image_file) {
|
||||
@ -498,9 +558,14 @@ class ChirpChatterApp : public App {
|
||||
|
||||
lv_obj_t* msg_label = lv_label_create(msg_container);
|
||||
|
||||
std::string buf((char*)packet.data, packet.size);
|
||||
|
||||
lv_label_set_text(msg_label, buf.c_str());
|
||||
std::vector<uint8_t> data(packet.data, packet.data + packet.size);
|
||||
std::string messageBuf = "";
|
||||
if (isPrintableData(data)) {
|
||||
messageBuf = std::string((char*)packet.data, packet.size);
|
||||
} else {
|
||||
messageBuf = hexdump(data);
|
||||
}
|
||||
lv_label_set_text(msg_label, messageBuf.c_str());
|
||||
lv_obj_set_width(msg_label, lv_pct(100));
|
||||
lv_label_set_long_mode(msg_label, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_align(msg_label, LV_TEXT_ALIGN_LEFT, 0);
|
||||
@ -565,13 +630,15 @@ public:
|
||||
lv_obj_add_style(grid, &style_grid, LV_PART_MAIN);
|
||||
|
||||
// Create toolbar
|
||||
auto* toolbar = tt::lvgl::toolbar_create(grid, "Transmitting message...");
|
||||
auto* toolbar = tt::lvgl::toolbar_create(grid, "Welcome to ChirpChatter!");
|
||||
lv_obj_set_size(toolbar, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
||||
lv_obj_set_grid_cell(toolbar, LV_GRID_ALIGN_STRETCH, 0, 2,
|
||||
LV_GRID_ALIGN_STRETCH, 0, 1);
|
||||
|
||||
auto* text = lv_obj_get_child_by_type(toolbar, 0, &lv_label_class);
|
||||
lv_obj_set_style_text_font(text, &lv_font_montserrat_12, 0);
|
||||
progressText = lv_obj_get_child_by_type(toolbar, 0, &lv_label_class);
|
||||
lv_obj_align(progressText, LV_ALIGN_TOP_LEFT, 0, 0);
|
||||
lv_obj_set_style_text_font(progressText, &lv_font_montserrat_12, 0);
|
||||
lv_obj_set_size(progressText, lv_pct(75), LV_SIZE_CONTENT);
|
||||
|
||||
// Create sidebar
|
||||
sidebar = lv_obj_create(grid);
|
||||
@ -585,12 +652,15 @@ public:
|
||||
lv_obj_set_flex_flow(sidebar, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
// Create progress bar
|
||||
/*progressBar = lv_bar_create(toolbar);
|
||||
lv_obj_align(progressBar, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
|
||||
lv_obj_set_size(progressBar, lv_pct(100), 20);
|
||||
lv_obj_set_style_radius(progressBar, 0, LV_PART_MAIN);
|
||||
lv_bar_set_start_value(progressBar, 50, LV_ANIM_ON);
|
||||
*/
|
||||
progressBar = lv_bar_create(toolbar);
|
||||
//lv_obj_set_flex_flow(toolbar, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_align(progressBar, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_set_size(progressBar, lv_pct(25), 36);
|
||||
lv_obj_set_style_radius(progressBar, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_radius(progressBar, 0, LV_PART_INDICATOR | LV_STATE_DEFAULT);
|
||||
lv_bar_set_range(progressBar, 0, 100);
|
||||
lv_bar_set_value(progressBar, 100, LV_ANIM_OFF);
|
||||
|
||||
auto paths = context.getPaths();
|
||||
auto icon_msgs_path = paths->getSystemPathLvgl("icon_msgs.png");
|
||||
createSidebarButton<CCView_Msgs>(sidebar, icon_msgs_path.c_str());
|
||||
@ -687,8 +757,14 @@ public:
|
||||
//loraView->onDeviceActivation(std::bind(&ChirpChatterApp::onDeviceActivation, this));
|
||||
//loraView->onDeviceDeactivation(std::bind(&ChirpChatterApp::onDeviceDeactivation, this));
|
||||
|
||||
//loraView->onDeviceActivation([this](hal::lora::LoraDevice& dev) { this->onDeviceActivation(dev); });
|
||||
//loraView->onDeviceDeactivation([this](hal::lora::LoraDevice& dev) { this->onDeviceDeactivation(dev); });
|
||||
loraView->onDeviceActivation([this](std::shared_ptr<tt::hal::radio::RadioDevice> dev) { this->onDeviceActivation(dev); });
|
||||
loraView->onDeviceDeactivation([this](std::shared_ptr<tt::hal::radio::RadioDevice> dev) { this->onDeviceDeactivation(dev); });
|
||||
|
||||
lv_obj_add_event_cb(send_btn, [](lv_event_t * e) {
|
||||
lv_obj_t* input = lv_event_get_target_obj(e);
|
||||
ChirpChatterApp* self = (ChirpChatterApp*)lv_event_get_user_data(e);
|
||||
self->sendMessage();
|
||||
}, LV_EVENT_SHORT_CLICKED, (void*)this);
|
||||
|
||||
changeView(CCView_Msgs);
|
||||
}
|
||||
@ -697,15 +773,57 @@ public:
|
||||
addPacketMessage(packet);
|
||||
}
|
||||
|
||||
void onDeviceActivation(hal::radio::RadioDevice& dev) {
|
||||
//rxSubId = dev.subscribeRx(std::bind(&ChirpChatterApp::onRxPacket, this));
|
||||
rxSubId = dev.subscribeRx([this](hal::Device::Id id, const hal::radio::RxPacket& packet) {
|
||||
void onDeviceActivation(std::shared_ptr<tt::hal::radio::RadioDevice> dev) {
|
||||
rxSubId = dev->subscribeRx([this](hal::Device::Id id, const hal::radio::RxPacket& packet) {
|
||||
this->onRxPacket(id, packet);
|
||||
});
|
||||
loraDevice = dev;
|
||||
std::ostringstream oss;
|
||||
oss << "Device \"" << dev->getName() << "\" online";
|
||||
|
||||
lv_label_set_text(progressText, oss.str().c_str());
|
||||
}
|
||||
|
||||
void onDeviceDeactivation(std::shared_ptr<tt::hal::radio::RadioDevice> dev) {
|
||||
dev->unsubscribeRx(rxSubId);
|
||||
loraDevice = nullptr;
|
||||
|
||||
lv_label_set_text(progressText, "Offline");
|
||||
}
|
||||
|
||||
void sendMessage() {
|
||||
std::string message = lv_textarea_get_text(inputField);
|
||||
std::vector<uint8_t> data(message.begin(), message.end());
|
||||
loraDevice->transmit(data, [this](hal::radio::RadioDevice::TxId id, hal::radio::RadioDevice::TransmissionState state) {
|
||||
this->onTxStatus(id, state);
|
||||
});
|
||||
}
|
||||
|
||||
void onDeviceDeactivation(hal::radio::RadioDevice& dev) {
|
||||
dev.unsubscribeRx(rxSubId);
|
||||
void onTxStatus(hal::radio::RadioDevice::TxId id, hal::radio::RadioDevice::TransmissionState state) {
|
||||
using enum hal::radio::RadioDevice::TransmissionState;
|
||||
|
||||
switch (state) {
|
||||
case Queued:
|
||||
lv_label_set_text(progressText, "Message queued...");
|
||||
lv_bar_set_value(progressBar, 25, LV_ANIM_ON);
|
||||
break;
|
||||
case PendingTransmit:
|
||||
lv_label_set_text(progressText, "Message transmitting...");
|
||||
lv_bar_set_value(progressBar, 50, LV_ANIM_ON);
|
||||
break;
|
||||
case Transmitted:
|
||||
lv_label_set_text(progressText, "Message transmitted!\nReturn to receive.");
|
||||
lv_bar_set_value(progressBar, 100, LV_ANIM_ON);
|
||||
break;
|
||||
case Timeout:
|
||||
lv_label_set_text(progressText, "Message transmit timed out!");
|
||||
lv_bar_set_value(progressBar, 100, LV_ANIM_ON);
|
||||
break;
|
||||
case Error:
|
||||
lv_label_set_text(progressText, "Error transmitting message!");
|
||||
lv_bar_set_value(progressBar, 100, LV_ANIM_ON);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void changeView(const CCViews view) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user