From 4b95c981bbc1b8fbbd9eeb8c89e533aa61ef26a9 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Fri, 8 Aug 2025 20:01:31 +1000 Subject: [PATCH 01/46] * UI revamp for companion radios --- examples/companion_radio/MyMesh.cpp | 15 + examples/companion_radio/MyMesh.h | 16 +- examples/companion_radio/UITask.cpp | 704 ++++++++++++++--------- examples/companion_radio/UITask.h | 62 +- examples/companion_radio/icons.h | 118 ++++ examples/companion_radio/main.cpp | 7 +- src/helpers/esp32/SerialBLEInterface.cpp | 2 + src/helpers/esp32/SerialBLEInterface.h | 2 + src/helpers/nrf52/SerialBLEInterface.cpp | 10 + src/helpers/nrf52/T114Board.h | 4 + src/helpers/nrf52/ThinkNodeM1Board.h | 4 + src/helpers/ui/DisplayDriver.h | 6 + src/helpers/ui/GxEPDDisplay.cpp | 13 +- src/helpers/ui/GxEPDDisplay.h | 1 + src/helpers/ui/MomentaryButton.cpp | 74 +++ src/helpers/ui/MomentaryButton.h | 25 + src/helpers/ui/ST7789Display.cpp | 11 + src/helpers/ui/ST7789Display.h | 1 + src/helpers/ui/UIScreen.h | 21 + variants/heltec_v3/platformio.ini | 55 +- variants/heltec_v3/target.cpp | 1 + variants/heltec_v3/target.h | 2 + variants/t114/platformio.ini | 1 + variants/t114/target.cpp | 1 + variants/t114/target.h | 2 + variants/xiao_c3/platformio.ini | 5 +- 26 files changed, 840 insertions(+), 323 deletions(-) create mode 100644 examples/companion_radio/icons.h create mode 100644 src/helpers/ui/MomentaryButton.cpp create mode 100644 src/helpers/ui/MomentaryButton.h create mode 100644 src/helpers/ui/UIScreen.h diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 901e3db5..75828d5e 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -267,6 +267,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path } memcpy(p->pubkey_prefix, contact.id.pub_key, sizeof(p->pubkey_prefix)); + strcpy(p->name, contact.name); p->recv_timestamp = getRTCClock()->getCurrentTime(); p->path_len = path_len; memcpy(p->path, path, p->path_len); @@ -275,6 +276,20 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); } +static int sort_by_recent(const void *a, const void *b) { + return ((AdvertPath *) b)->recv_timestamp - ((AdvertPath *) a)->recv_timestamp; +} + +int MyMesh::getRecentlyHeard(AdvertPath dest[], int max_num) { + if (max_num > ADVERT_PATH_TABLE_SIZE) max_num = ADVERT_PATH_TABLE_SIZE; + qsort(advert_paths, ADVERT_PATH_TABLE_SIZE, sizeof(advert_paths[0]), sort_by_recent); + + for (int i = 0; i < max_num; i++) { + dest[i] = advert_paths[i]; + } + return max_num; +} + void MyMesh::onContactPathUpdated(const ContactInfo &contact) { out_frame[0] = PUSH_CODE_PATH_UPDATED; memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 01889223..42819813 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -77,6 +77,14 @@ #define REQ_TYPE_KEEP_ALIVE 0x02 #define REQ_TYPE_GET_TELEMETRY_DATA 0x03 +struct AdvertPath { + uint8_t pubkey_prefix[7]; + uint8_t path_len; + char name[32]; + uint32_t recv_timestamp; + uint8_t path[MAX_PATH_SIZE]; +}; + class MyMesh : public BaseChatMesh, public DataStoreHost { public: MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store); @@ -93,6 +101,8 @@ public: bool advert(); void enterCLIRescue(); + int getRecentlyHeard(AdvertPath dest[], int max_num); + protected: float getAirtimeBudgetFactor() const override; int getInterferenceThreshold() const override; @@ -201,12 +211,6 @@ private: AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table int next_ack_idx; - struct AdvertPath { - uint8_t pubkey_prefix[7]; - uint8_t path_len; - uint32_t recv_timestamp; - uint8_t path[MAX_PATH_SIZE]; - }; #define ADVERT_PATH_TABLE_SIZE 16 AdvertPath advert_paths[ADVERT_PATH_TABLE_SIZE]; // circular table }; diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index a7f03a26..9f5fe5b1 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -1,8 +1,8 @@ #include "UITask.h" -#include #include #include "NodePrefs.h" #include "MyMesh.h" +#include "target.h" #define AUTO_OFF_MILLIS 15000 // 15 seconds #define BOOT_SCREEN_MILLIS 3000 // 3 seconds @@ -13,80 +13,366 @@ #define LED_CYCLE_MILLIS 4000 #endif -#ifndef USER_BTN_PRESSED -#define USER_BTN_PRESSED LOW -#endif +#define LONG_PRESS_MILLIS 1200 -// 'meshcore', 128x13px -static const uint8_t meshcore_logo [] PROGMEM = { - 0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, - 0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe, - 0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc, - 0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00, - 0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00, - 0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8, - 0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8, - 0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0, - 0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00, - 0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00, - 0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8, - 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8, - 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8, +#define PRESS_LABEL "long press" + +#include "icons.h" + +class SplashScreen : public UIScreen { + UITask* _task; + unsigned long dismiss_after; + char _version_info[12]; + +public: + SplashScreen(UITask* task) : _task(task) { + // strip off dash and commit hash by changing dash to null terminator + // e.g: v1.2.3-abcdef -> v1.2.3 + const char *ver = FIRMWARE_VERSION; + const char *dash = strchr(ver, '-'); + + int len = dash ? dash - ver : strlen(ver); + if (len >= sizeof(_version_info)) len = sizeof(_version_info) - 1; + memcpy(_version_info, ver, len); + _version_info[len] = 0; + + dismiss_after = millis() + BOOT_SCREEN_MILLIS; + } + + int render(DisplayDriver& display) override { + // meshcore logo + display.setColor(DisplayDriver::BLUE); + int logoWidth = 128; + display.drawXbm((display.width() - logoWidth) / 2, 3, meshcore_logo, logoWidth, 13); + + // version info + display.setColor(DisplayDriver::LIGHT); + display.setTextSize(2); + display.drawTextCentered(display.width()/2, 22, _version_info); + + display.setTextSize(1); + display.drawTextCentered(display.width()/2, 42, FIRMWARE_BUILD_DATE); + + return 1000; + } + + void poll() override { + if (millis() >= dismiss_after) { + _task->gotoHomeScreen(); + } + } +}; + +class HomeScreen : public UIScreen { + enum HomePage { + FIRST, + RECENT, + RADIO, + BLUETOOTH, + ADVERT, + SHUTDOWN, + Count // keep as last + }; + + UITask* _task; + mesh::RTCClock* _rtc; + SensorManager* _sensors; + NodePrefs* _node_prefs; + uint8_t _page; + bool _shutdown_init; + AdvertPath recent[4]; + + void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) { + // Convert millivolts to percentage + const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V) + const int maxMilliVolts = 4200; // Maximum voltage (e.g., 4.2V) + int batteryPercentage = ((batteryMilliVolts - minMilliVolts) * 100) / (maxMilliVolts - minMilliVolts); + if (batteryPercentage < 0) batteryPercentage = 0; // Clamp to 0% + if (batteryPercentage > 100) batteryPercentage = 100; // Clamp to 100% + + // battery icon + int iconWidth = 24; + int iconHeight = 10; + int iconX = display.width() - iconWidth - 5; // Position the icon near the top-right corner + int iconY = 0; + display.setColor(DisplayDriver::GREEN); + + // battery outline + display.drawRect(iconX, iconY, iconWidth, iconHeight); + + // battery "cap" + display.fillRect(iconX + iconWidth, iconY + (iconHeight / 4), 3, iconHeight / 2); + + // fill the battery based on the percentage + int fillWidth = (batteryPercentage * (iconWidth - 4)) / 100; + display.fillRect(iconX + 2, iconY + 2, fillWidth, iconHeight - 4); + } + +public: + HomeScreen(UITask* task, mesh::RTCClock* rtc, SensorManager* sensors, NodePrefs* node_prefs) + : _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0), _shutdown_init(false) { } + + void poll() override { + if (_shutdown_init && !_task->isButtonPressed()) { // must wait for USR button to be released + _task->shutdown(); + } + } + + int render(DisplayDriver& display) override { + char tmp[80]; + // node name + display.setCursor(0, 0); + display.setTextSize(1); + display.setColor(DisplayDriver::GREEN); + display.print(_node_prefs->node_name); + + // battery voltage + renderBatteryIndicator(display, _task->getBattMilliVolts()); + + // curr page indicator + int y = 14; + int x = display.width() / 2 - 25; + for (uint8_t i = 0; i < HomePage::Count; i++, x += 10) { + if (i == _page) { + display.fillRect(x-1, y-1, 3, 3); + } else { + display.fillRect(x, y, 1, 1); + } + } + + if (_page == HomePage::FIRST) { + display.setColor(DisplayDriver::YELLOW); + display.setTextSize(2); + sprintf(tmp, "MSG: %d", _task->getMsgCount()); + display.drawTextCentered(display.width() / 2, 20, tmp); + + if (_task->hasConnection()) { + display.setColor(DisplayDriver::GREEN); + display.setTextSize(1); + display.drawTextCentered(display.width() / 2, 43, "< Connected >"); + } else if (the_mesh.getBLEPin() != 0) { // BT pin + display.setColor(DisplayDriver::RED); + display.setTextSize(2); + sprintf(tmp, "Pin:%d", the_mesh.getBLEPin()); + display.drawTextCentered(display.width() / 2, 43, tmp); + } + } else if (_page == HomePage::RECENT) { + the_mesh.getRecentlyHeard(recent, 4); + display.setColor(DisplayDriver::GREEN); + int y = 20; + for (int i = 0; i < 4; i++, y += 11) { + auto a = &recent[i]; + if (a->name[0] == 0) continue; // empty slot + display.setCursor(0, y); + display.print(a->name); + int secs = _rtc->getCurrentTime() - a->recv_timestamp; + if (secs < 60) { + sprintf(tmp, "%ds", secs); + } else if (secs < 60*60) { + sprintf(tmp, "%dm", secs / 60); + } else { + sprintf(tmp, "%dh", secs / (60*60)); + } + display.setCursor(display.width() - display.getTextWidth(tmp) - 1, y); + display.print(tmp); + } + } else if (_page == HomePage::RADIO) { + display.setColor(DisplayDriver::YELLOW); + display.setTextSize(1); + // freq / sf + display.setCursor(0, 20); + sprintf(tmp, "FQ: %06.3f SF: %d", _node_prefs->freq, _node_prefs->sf); + display.print(tmp); + + display.setCursor(0, 31); + sprintf(tmp, "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr); + display.print(tmp); + + // tx power, noise floor + display.setCursor(0, 42); + sprintf(tmp, "TX: %ddBm", _node_prefs->tx_power_dbm); + display.print(tmp); + display.setCursor(0, 53); + sprintf(tmp, "Noise floor: %d", radio_driver.getNoiseFloor()); + display.print(tmp); + } else if (_page == HomePage::BLUETOOTH) { + display.setColor(DisplayDriver::GREEN); + display.drawXbm((display.width() - 32) / 2, 18, + _task->isSerialEnabled() ? bluetooth_on : bluetooth_off, + 32, 32); + display.setTextSize(1); + display.drawTextCentered(display.width() / 2, 64 - 11, "toggle: " PRESS_LABEL); + } else if (_page == HomePage::ADVERT) { + display.setColor(DisplayDriver::GREEN); + display.drawXbm((display.width() - 32) / 2, 18, advert_icon, 32, 32); + display.drawTextCentered(display.width() / 2, 64 - 11, "advert: " PRESS_LABEL); + } else if (_page == HomePage::SHUTDOWN) { + display.setColor(DisplayDriver::GREEN); + display.setTextSize(1); + if (_shutdown_init) { + display.drawTextCentered(display.width() / 2, 34, "shutting down..."); + } else { + display.drawXbm((display.width() - 32) / 2, 18, power_icon, 32, 32); + display.drawTextCentered(display.width() / 2, 64 - 11, "off: " PRESS_LABEL); + } + } + return 5000; // next render after 5000 ms + } + + bool handleInput(char c) override { + if (c == KEY_LEFT) { + _page = (_page + HomePage::Count - 1) % HomePage::Count; + return true; + } + if (c == KEY_RIGHT || c == KEY_SELECT) { + _page = (_page + 1) % HomePage::Count; + if (_page == HomePage::RECENT) { + _task->showAlert("Recent adverts", 800); + } + return true; + } + if (c == KEY_ENTER && _page == HomePage::BLUETOOTH) { + if (_task->isSerialEnabled()) { // toggle Bluetooth on/off + _task->disableSerial(); + } else { + _task->enableSerial(); + } + return true; + } + if (c == KEY_ENTER && _page == HomePage::ADVERT) { + #ifdef PIN_BUZZER + _task->soundBuzzer(UIEventType::ack); + #endif + if (the_mesh.advert()) { + _task->showAlert("Advert sent!", 1000); + } else { + _task->showAlert("Advert failed..", 1000); + } + return true; + } + if (c == KEY_ENTER && _page == HomePage::SHUTDOWN) { + _shutdown_init = true; // need to wait for button to be released + return true; + } + return false; + } +}; + +class MsgPreviewScreen : public UIScreen { + UITask* _task; + mesh::RTCClock* _rtc; + + struct MsgEntry { + uint32_t timestamp; + char origin[62]; + char msg[78]; + }; + #define MAX_UNREAD_MSGS 32 + int num_unread; + MsgEntry unread[MAX_UNREAD_MSGS]; + +public: + MsgPreviewScreen(UITask* task, mesh::RTCClock* rtc) : _task(task), _rtc(rtc) { num_unread = 0; } + + void addPreview(uint8_t path_len, const char* from_name, const char* msg) { + if (num_unread >= MAX_UNREAD_MSGS) return; // full + + auto p = &unread[num_unread++]; + p->timestamp = _rtc->getCurrentTime(); + if (path_len == 0xFF) { + sprintf(p->origin, "(D) %s:", from_name); + } else { + sprintf(p->origin, "(%d) %s:", (uint32_t) path_len, from_name); + } + StrHelper::strncpy(p->msg, msg, sizeof(p->msg)); + } + + int render(DisplayDriver& display) override { + char tmp[16]; + display.setCursor(0, 0); + display.setTextSize(1); + display.setColor(DisplayDriver::GREEN); + sprintf(tmp, "Unread: %d", num_unread); + display.print(tmp); + + auto p = &unread[0]; + + int secs = _rtc->getCurrentTime() - p->timestamp; + if (secs < 60) { + sprintf(tmp, "%ds", secs); + } else if (secs < 60*60) { + sprintf(tmp, "%dm", secs / 60); + } else { + sprintf(tmp, "%dh", secs / (60*60)); + } + display.setCursor(display.width() - display.getTextWidth(tmp), 0); + display.print(tmp); + + display.drawRect(0, 11, display.width(), 1); // horiz line + + display.setCursor(0, 14); + display.setColor(DisplayDriver::YELLOW); + display.print(p->origin); + + display.setCursor(0, 25); + display.setColor(DisplayDriver::LIGHT); + display.printWordWrap(p->msg, display.width()); + + return 1000; // next render after 1000 ms + } + + bool handleInput(char c) override { + if (c == KEY_SELECT || c == KEY_RIGHT) { + num_unread--; + if (num_unread == 0) { + _task->gotoHomeScreen(); + } else { + // delete first/curr item from unread queue + for (int i = 0; i < num_unread; i++) { + unread[i] = unread[i + 1]; + } + } + return true; + } + if (c == KEY_ENTER) { + num_unread = 0; // clear unread queue + _task->gotoHomeScreen(); + return true; + } + return false; + } }; void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs) { _display = display; _sensors = sensors; _auto_off = millis() + AUTO_OFF_MILLIS; - clearMsgPreview(); + +#if defined(PIN_USER_BTN) + user_btn.begin(); +#endif + _node_prefs = node_prefs; if (_display != NULL) { _display->turnOn(); } - // strip off dash and commit hash by changing dash to null terminator - // e.g: v1.2.3-abcdef -> v1.2.3 - char *version = strdup(FIRMWARE_VERSION); - char *dash = strchr(version, '-'); - if (dash) { - *dash = 0; - } - - // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, FIRMWARE_BUILD_DATE); - #ifdef PIN_BUZZER buzzer.begin(); #endif - // Initialize digital button if available -#ifdef PIN_USER_BTN - _userButton = new Button(PIN_USER_BTN, USER_BTN_PRESSED); - _userButton->begin(); - - // Set up digital button callbacks - _userButton->onShortPress([this]() { handleButtonShortPress(); }); - _userButton->onDoublePress([this]() { handleButtonDoublePress(); }); - _userButton->onTriplePress([this]() { handleButtonTriplePress(); }); - _userButton->onQuadruplePress([this]() { handleButtonQuadruplePress(); }); - _userButton->onLongPress([this]() { handleButtonLongPress(); }); - _userButton->onAnyPress([this]() { handleButtonAnyPress(); }); -#endif - - // Initialize analog button if available -#ifdef PIN_USER_BTN_ANA - _userButtonAnalog = new Button(PIN_USER_BTN_ANA, USER_BTN_PRESSED, true, 20); - _userButtonAnalog->begin(); - - // Set up analog button callbacks - _userButtonAnalog->onShortPress([this]() { handleButtonShortPress(); }); - _userButtonAnalog->onDoublePress([this]() { handleButtonDoublePress(); }); - _userButtonAnalog->onTriplePress([this]() { handleButtonTriplePress(); }); - _userButtonAnalog->onQuadruplePress([this]() { handleButtonQuadruplePress(); }); - _userButtonAnalog->onLongPress([this]() { handleButtonLongPress(); }); - _userButtonAnalog->onAnyPress([this]() { handleButtonAnyPress(); }); -#endif ui_started_at = millis(); + _alert_expiry = 0; + + splash = new SplashScreen(this); + home = new HomeScreen(this, &rtc_clock, sensors, node_prefs); + msg_preview = new MsgPreviewScreen(this, &rtc_clock); + setCurrScreen(splash); +} + +void UITask::showAlert(const char* text, int duration_millis) { + strcpy(_alert, text); + _alert_expiry = millis() + duration_millis; } void UITask::soundBuzzer(UIEventType bet) { @@ -109,147 +395,28 @@ switch(bet){ break; } #endif -// Serial.print("DBG: Buzzzzzz -> "); -// Serial.println((int) bet); } void UITask::msgRead(int msgcount) { _msgcount = msgcount; if (msgcount == 0) { - clearMsgPreview(); + gotoHomeScreen(); } } -void UITask::clearMsgPreview() { - _origin[0] = 0; - _msg[0] = 0; - _need_refresh = true; -} - void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) { _msgcount = msgcount; - if (path_len == 0xFF) { - sprintf(_origin, "(F) %s", from_name); - } else { - sprintf(_origin, "(%d) %s", (uint32_t) path_len, from_name); - } - StrHelper::strncpy(_msg, text, sizeof(_msg)); + ((MsgPreviewScreen *) msg_preview)->addPreview(path_len, from_name, text); + setCurrScreen(msg_preview); if (_display != NULL) { if (!_display->isOn()) _display->turnOn(); _auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer - _need_refresh = true; + _next_refresh = 0; // trigger refresh } } -void UITask::renderBatteryIndicator(uint16_t batteryMilliVolts) { - // Convert millivolts to percentage - const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V) - const int maxMilliVolts = 4200; // Maximum voltage (e.g., 4.2V) - int batteryPercentage = ((batteryMilliVolts - minMilliVolts) * 100) / (maxMilliVolts - minMilliVolts); - if (batteryPercentage < 0) batteryPercentage = 0; // Clamp to 0% - if (batteryPercentage > 100) batteryPercentage = 100; // Clamp to 100% - - // battery icon - int iconWidth = 24; - int iconHeight = 12; - int iconX = _display->width() - iconWidth - 5; // Position the icon near the top-right corner - int iconY = 0; - _display->setColor(DisplayDriver::GREEN); - - // battery outline - _display->drawRect(iconX, iconY, iconWidth, iconHeight); - - // battery "cap" - _display->fillRect(iconX + iconWidth, iconY + (iconHeight / 4), 3, iconHeight / 2); - - // fill the battery based on the percentage - int fillWidth = (batteryPercentage * (iconWidth - 4)) / 100; - _display->fillRect(iconX + 2, iconY + 2, fillWidth, iconHeight - 4); -} - -void UITask::renderCurrScreen() { - if (_display == NULL) return; // assert() ?? - - char tmp[80]; - if (_alert[0]) { - _display->setTextSize(1.4); - uint16_t textWidth = _display->getTextWidth(_alert); - _display->setCursor((_display->width() - textWidth) / 2, 22); - _display->setColor(DisplayDriver::GREEN); - _display->print(_alert); - _alert[0] = 0; - _need_refresh = true; - return; - } else if (_origin[0] && _msg[0]) { // message preview - // render message preview - _display->setCursor(0, 0); - _display->setTextSize(1); - _display->setColor(DisplayDriver::GREEN); - _display->print(_node_prefs->node_name); - - _display->setCursor(0, 12); - _display->setColor(DisplayDriver::YELLOW); - _display->print(_origin); - _display->setCursor(0, 24); - _display->setColor(DisplayDriver::LIGHT); - _display->print(_msg); - - _display->setCursor(_display->width() - 28, 9); - _display->setTextSize(2); - _display->setColor(DisplayDriver::ORANGE); - sprintf(tmp, "%d", _msgcount); - _display->print(tmp); - _display->setColor(DisplayDriver::YELLOW); // last color will be kept on T114 - } else if ((millis() - ui_started_at) < BOOT_SCREEN_MILLIS) { // boot screen - // meshcore logo - _display->setColor(DisplayDriver::BLUE); - int logoWidth = 128; - _display->drawXbm((_display->width() - logoWidth) / 2, 3, meshcore_logo, logoWidth, 13); - - // version info - _display->setColor(DisplayDriver::LIGHT); - _display->setTextSize(1); - uint16_t textWidth = _display->getTextWidth(_version_info); - _display->setCursor((_display->width() - textWidth) / 2, 22); - _display->print(_version_info); - } else { // home screen - // node name - _display->setCursor(0, 0); - _display->setTextSize(1); - _display->setColor(DisplayDriver::GREEN); - _display->print(_node_prefs->node_name); - - // battery voltage - renderBatteryIndicator(_board->getBattMilliVolts()); - - // freq / sf - _display->setCursor(0, 20); - _display->setColor(DisplayDriver::YELLOW); - sprintf(tmp, "FREQ: %06.3f SF%d", _node_prefs->freq, _node_prefs->sf); - _display->print(tmp); - - // bw / cr - _display->setCursor(0, 30); - sprintf(tmp, "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr); - _display->print(tmp); - - // BT pin - if (!_connected && the_mesh.getBLEPin() != 0) { - _display->setColor(DisplayDriver::RED); - _display->setTextSize(2); - _display->setCursor(0, 43); - sprintf(tmp, "Pin:%d", the_mesh.getBLEPin()); - _display->print(tmp); - _display->setColor(DisplayDriver::GREEN); - } else { - _display->setColor(DisplayDriver::LIGHT); - } - } - _need_refresh = false; -} - void UITask::userLedHandler() { #ifdef PIN_STATUS_LED static int state = 0; @@ -275,6 +442,11 @@ void UITask::userLedHandler() { #endif } +void UITask::setCurrScreen(UIScreen* c) { + curr = c; + _next_refresh = 0; +} + /* hardware-agnostic pre-shutdown activity should be done here */ @@ -293,96 +465,103 @@ void UITask::shutdown(bool restart){ #endif // PIN_BUZZER - if (restart) + if (restart) { _board->reboot(); - else + } else { + _display->turnOff(); _board->powerOff(); + } +} + +bool UITask::isButtonPressed() const { +#ifdef PIN_USER_BTN + return user_btn.isPressed(); +#else + return false; +#endif } void UITask::loop() { - #ifdef PIN_USER_BTN - if (_userButton) { - _userButton->update(); - } - #endif - #ifdef PIN_USER_BTN_ANA - if (_userButtonAnalog) { - _userButtonAnalog->update(); - } - #endif + char c = 0; +#if defined(PIN_USER_BTN) + int ev = user_btn.check(); + if (ev == BUTTON_EVENT_CLICK) { + c = checkDisplayOn(KEY_SELECT); + } else if (ev == BUTTON_EVENT_LONG_PRESS) { + c = handleLongPress(KEY_ENTER); + } +#endif + + if (c != 0 && curr) { + curr->handleInput(c); + _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer + _next_refresh = 0; // trigger refresh + } + userLedHandler(); #ifdef PIN_BUZZER if (buzzer.isPlaying()) buzzer.loop(); #endif - if (_display != NULL && _display->isOn()) { - static bool _firstBoot = true; - if(_firstBoot && (millis() - ui_started_at) >= BOOT_SCREEN_MILLIS) { - _need_refresh = true; - _firstBoot = false; - } - if (millis() >= _next_refresh && _need_refresh) { - _display->startFrame(); - renderCurrScreen(); - _display->endFrame(); + if (curr) curr->poll(); - _next_refresh = millis() + 1000; // refresh every second + if (_display != NULL && _display->isOn()) { + if (millis() >= _next_refresh && curr) { + _display->startFrame(); + int delay_millis = curr->render(*_display); + if (millis() < _alert_expiry) { // render alert popup + _display->setTextSize(1); + int y = _display->height() / 3; + int p = _display->height() / 32; + _display->setColor(DisplayDriver::DARK); + _display->fillRect(p, y, _display->width() - p*2, y); + _display->setColor(DisplayDriver::LIGHT); // draw box border + _display->drawRect(p, y, _display->width() - p*2, y); + _display->drawTextCentered(_display->width() / 2, y + p*3, _alert); + _next_refresh = _alert_expiry; // will need refresh when alert is dismissed + } else { + _next_refresh = millis() + delay_millis; + } + _display->endFrame(); } if (millis() > _auto_off) { _display->turnOff(); } } + +#ifdef AUTO_SHUTDOWN_MILLIVOLTS + if (millis() > next_batt_chck) { + uint16_t milliVolts = getBattMilliVolts(); + if (milliVolts > 0 && milliVolts < AUTO_SHUTDOWN_MILLIVOLTS) { + shutdown(); + } + next_batt_chck = millis() + 8000; + } +#endif } -void UITask::handleButtonAnyPress() { - MESH_DEBUG_PRINTLN("UITask: any press triggered"); - // called on any button press before other events, to wake up the display quickly - // do not refresh the display here, as it may block the button handler +char UITask::checkDisplayOn(char c) { if (_display != NULL) { - _displayWasOn = _display->isOn(); // Track display state before any action - if (!_displayWasOn) { - _display->turnOn(); + if (!_display->isOn()) { + _display->turnOn(); // turn display on and consume event + c = 0; } _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer + _next_refresh = 0; // trigger refresh } + return c; } -void UITask::handleButtonShortPress() { - MESH_DEBUG_PRINTLN("UITask: short press triggered"); - if (_display != NULL) { - // Only clear message preview if display was already on before button press - if (_displayWasOn) { - // If display was on and showing message preview, clear it - if (_origin[0] && _msg[0]) { - clearMsgPreview(); - } else { - // Otherwise, refresh the display - _need_refresh = true; - } - } else { - _need_refresh = true; // display just turned on, so we need to refresh - } - // Note: Display turn-on and auto-off timer extension are handled by handleButtonAnyPress +char UITask::handleLongPress(char c) { + if (millis() - ui_started_at < 8000) { // long press in first 8 seconds since startup -> CLI/rescue + the_mesh.enterCLIRescue(); + c = 0; // consume event } + return c; } -void UITask::handleButtonDoublePress() { - MESH_DEBUG_PRINTLN("UITask: double press triggered, sending advert"); - // ADVERT - #ifdef PIN_BUZZER - soundBuzzer(UIEventType::ack); - #endif - if (the_mesh.advert()) { - MESH_DEBUG_PRINTLN("Advert sent!"); - sprintf(_alert, "Advert sent!"); - } else { - MESH_DEBUG_PRINTLN("Advert failed!"); - sprintf(_alert, "Advert failed.."); - } - _need_refresh = true; -} - +/* void UITask::handleButtonTriplePress() { MESH_DEBUG_PRINTLN("UITask: triple press triggered"); // Toggle buzzer quiet mode @@ -390,43 +569,12 @@ void UITask::handleButtonTriplePress() { if (buzzer.isQuiet()) { buzzer.quiet(false); soundBuzzer(UIEventType::ack); - sprintf(_alert, "Buzzer: ON"); + showAlert("Buzzer: ON", 600); } else { buzzer.quiet(true); - sprintf(_alert, "Buzzer: OFF"); + showAlert("Buzzer: OFF", 600); } - _need_refresh = true; + _next_refresh = 0; // trigger refresh #endif } - -void UITask::handleButtonQuadruplePress() { - MESH_DEBUG_PRINTLN("UITask: quad press triggered"); - if (_sensors != NULL) { - // toggle GPS onn/off - int num = _sensors->getNumSettings(); - for (int i = 0; i < num; i++) { - if (strcmp(_sensors->getSettingName(i), "gps") == 0) { - if (strcmp(_sensors->getSettingValue(i), "1") == 0) { - _sensors->setSettingValue("gps", "0"); - soundBuzzer(UIEventType::ack); - sprintf(_alert, "GPS: Disabled"); - } else { - _sensors->setSettingValue("gps", "1"); - soundBuzzer(UIEventType::ack); - sprintf(_alert, "GPS: Enabled"); - } - break; - } - } - } - _need_refresh = true; -} - -void UITask::handleButtonLongPress() { - MESH_DEBUG_PRINTLN("UITask: long press triggered"); - if (millis() - ui_started_at < 8000) { // long press in first 8 seconds since startup -> CLI/rescue - the_mesh.enterCLIRescue(); - } else { - shutdown(); - } -} \ No newline at end of file +*/ diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 77ef875f..818779e5 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -2,18 +2,18 @@ #include #include +#include #include -#include +#include +#include #ifdef PIN_BUZZER #include #endif #include "NodePrefs.h" -#include "Button.h" - enum class UIEventType -{ +enum class UIEventType { none, contactMessage, channelMessage, @@ -22,9 +22,12 @@ ack }; +#define MAX_TOP_LEVEL 8 + class UITask { DisplayDriver* _display; mesh::MainBoard* _board; + BaseSerialInterface* _serial; SensorManager* _sensors; #ifdef PIN_BUZZER genericBuzzer buzzer; @@ -32,48 +35,45 @@ class UITask { unsigned long _next_refresh, _auto_off; bool _connected; NodePrefs* _node_prefs; - char _version_info[32]; - char _origin[62]; - char _msg[80]; char _alert[80]; + unsigned long _alert_expiry; int _msgcount; - bool _need_refresh = true; - bool _displayWasOn = false; // Track display state before button press - unsigned long ui_started_at; + unsigned long ui_started_at, next_batt_chck; - // Button handlers -#ifdef PIN_USER_BTN - Button* _userButton = nullptr; -#endif -#ifdef PIN_USER_BTN_ANA - Button* _userButtonAnalog = nullptr; -#endif + UIScreen* splash; + UIScreen* home; + UIScreen* msg_preview; + UIScreen* curr; - void renderCurrScreen(); void userLedHandler(); - void renderBatteryIndicator(uint16_t batteryMilliVolts); // Button action handlers - void handleButtonAnyPress(); - void handleButtonShortPress(); - void handleButtonDoublePress(); - void handleButtonTriplePress(); - void handleButtonQuadruplePress(); - void handleButtonLongPress(); + char checkDisplayOn(char c); + char handleLongPress(char c); + + void setCurrScreen(UIScreen* c); - public: - UITask(mesh::MainBoard* board) : _board(board), _display(NULL), _sensors(NULL) { - _next_refresh = 0; - ui_started_at = 0; - _connected = false; + UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : _board(board), _serial(serial), _display(NULL), _sensors(NULL) { + next_batt_chck = _next_refresh = 0; + ui_started_at = 0; + _connected = false; + curr = NULL; } void begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs); + void gotoHomeScreen() { setCurrScreen(home); } + void showAlert(const char* text, int duration_millis); void setHasConnection(bool connected) { _connected = connected; } + bool hasConnection() const { return _connected; } + uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); } + bool isSerialEnabled() const { return _serial->isEnabled(); } + void enableSerial() { _serial->enable(); } + void disableSerial() { _serial->disable(); } + int getMsgCount() const { return _msgcount; } bool hasDisplay() const { return _display != NULL; } - void clearMsgPreview(); + bool isButtonPressed() const; void msgRead(int msgcount); void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); void soundBuzzer(UIEventType bet = UIEventType::none); diff --git a/examples/companion_radio/icons.h b/examples/companion_radio/icons.h new file mode 100644 index 00000000..5220f409 --- /dev/null +++ b/examples/companion_radio/icons.h @@ -0,0 +1,118 @@ +#pragma once + +#include + +// 'meshcore', 128x13px +static const uint8_t meshcore_logo [] = { + 0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, + 0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe, + 0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc, + 0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00, + 0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00, + 0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8, + 0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8, + 0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0, + 0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00, + 0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00, + 0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8, + 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8, + 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8, +}; + +static const uint8_t bluetooth_on[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, + 0x00, 0x3E, 0x00, 0x00, + 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x3F, 0xC0, 0x00, + 0x00, 0x3B, 0xE0, 0x00, + 0x30, 0x38, 0xF8, 0x00, + 0x3C, 0x38, 0x7C, 0x00, + 0x3E, 0x38, 0x7C, 0x00, + 0x1F, 0xB8, 0xF8, 0x70, + 0x07, 0xF9, 0xF0, 0x78, + 0x03, 0xFF, 0xC0, 0x78, + 0x00, 0xFF, 0x80, 0x3C, + 0x00, 0x7F, 0x07, 0x1C, + 0x00, 0x7E, 0x07, 0x1C, + 0x03, 0xFF, 0x82, 0x1C, + 0x03, 0xFF, 0xC0, 0x78, + 0x07, 0xFB, 0xE0, 0x78, + 0x0F, 0xB8, 0xF8, 0x70, + 0x3E, 0x38, 0x7C, 0x00, + 0x3C, 0x38, 0x7C, 0x00, + 0x38, 0x38, 0xF8, 0x00, + 0x00, 0x39, 0xF0, 0x00, + 0x00, 0x3F, 0xC0, 0x00, + 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x3E, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t bluetooth_off[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x80, 0x00, + 0x00, 0x03, 0xC0, 0x00, + 0x00, 0x03, 0xE0, 0x00, + 0x38, 0x03, 0xF8, 0x00, + 0x3C, 0x03, 0xFC, 0x00, + 0x3E, 0x03, 0xBF, 0x00, + 0x0F, 0x83, 0x8F, 0x80, + 0x07, 0xC3, 0x87, 0xC0, + 0x03, 0xF0, 0x03, 0xC0, + 0x00, 0xF8, 0x0F, 0x80, + 0x00, 0x7C, 0x0F, 0x00, + 0x00, 0x1F, 0x0E, 0x00, + 0x00, 0x0F, 0x80, 0x00, + 0x00, 0x07, 0xE0, 0x00, + 0x00, 0x07, 0xF0, 0x00, + 0x00, 0x0F, 0xF8, 0x00, + 0x00, 0x3F, 0xBE, 0x00, + 0x00, 0x7F, 0x9F, 0x00, + 0x00, 0xFB, 0x8F, 0xC0, + 0x03, 0xE3, 0x83, 0xE0, + 0x03, 0xC3, 0x87, 0xF0, + 0x03, 0x83, 0x8F, 0xFC, + 0x00, 0x03, 0xBF, 0x3C, + 0x00, 0x03, 0xFC, 0x1C, + 0x00, 0x03, 0xF8, 0x00, + 0x00, 0x03, 0xE0, 0x00, + 0x00, 0x03, 0xC0, 0x00, + 0x00, 0x03, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t power_icon[] = { + 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, + 0x00, 0x33, 0xCC, 0x00, 0x00, 0xF3, 0xCF, 0x00, 0x01, 0xF3, 0xCF, 0x80, + 0x03, 0xF3, 0xCF, 0xC0, 0x07, 0xF3, 0xCF, 0xE0, 0x0F, 0xE3, 0xC7, 0xF0, + 0x1F, 0xC3, 0xC3, 0xF8, 0x1F, 0x83, 0xC1, 0xF8, 0x3F, 0x03, 0xC0, 0xFC, + 0x3E, 0x03, 0xC0, 0x7C, 0x3E, 0x03, 0xC0, 0x7C, 0x7E, 0x01, 0x80, 0x7E, + 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, + 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0x7C, + 0x3E, 0x00, 0x00, 0x7C, 0x3F, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0x01, 0xF8, + 0x1F, 0xC0, 0x03, 0xF8, 0x0F, 0xE0, 0x07, 0xF0, 0x0F, 0xF8, 0x1F, 0xF0, + 0x07, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, +}; + +static const uint8_t advert_icon[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, +0x1C, 0x00, 0x00, 0x38, 0x18, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x0C, +0x30, 0x60, 0x06, 0x0C, 0x60, 0xE0, 0x07, 0x06, 0x61, 0xC0, 0x03, 0x86, +0xE1, 0x81, 0x81, 0x87, 0xC3, 0x07, 0xE0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, +0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, +0xC3, 0x07, 0xE0, 0xC3, 0xC1, 0x83, 0xC1, 0x83, 0x61, 0x80, 0x01, 0x86, +0x60, 0xC0, 0x03, 0x06, 0x70, 0xE0, 0x07, 0x0E, 0x30, 0x40, 0x02, 0x0C, +0x38, 0x00, 0x00, 0x1C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x30, +0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; \ No newline at end of file diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 34c30498..d9b3f68b 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -81,7 +81,7 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store); #ifdef DISPLAY_CLASS #include "UITask.h" - UITask ui_task(&board); + UITask ui_task(&board, &serial_interface); #endif /* END GLOBAL OBJECTS */ @@ -99,7 +99,10 @@ void setup() { if (display.begin()) { disp = &display; disp->startFrame(); - disp->print("Please wait..."); + #ifdef ST7789 + disp->setTextSize(2); + #endif + disp->drawTextCentered(disp->width() / 2, 28, "Loading..."); disp->endFrame(); } #endif diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index 8a8710a7..1be703a8 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -83,6 +83,7 @@ void SerialBLEInterface::onConnect(BLEServer* pServer) { void SerialBLEInterface::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) { BLE_DEBUG_PRINTLN("onConnect(), conn_id=%d, mtu=%d", param->connect.conn_id, pServer->getPeerMTU(param->connect.conn_id)); + last_conn_id = param->connect.conn_id; } void SerialBLEInterface::onMtuChanged(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) { @@ -143,6 +144,7 @@ void SerialBLEInterface::disable() { BLE_DEBUG_PRINTLN("SerialBLEInterface::disable"); pServer->getAdvertising()->stop(); + pServer->disconnect(last_conn_id); pService->stop(); oldDeviceConnected = deviceConnected = false; adv_restart_time = 0; diff --git a/src/helpers/esp32/SerialBLEInterface.h b/src/helpers/esp32/SerialBLEInterface.h index bf1eee09..29ad897a 100644 --- a/src/helpers/esp32/SerialBLEInterface.h +++ b/src/helpers/esp32/SerialBLEInterface.h @@ -13,6 +13,7 @@ class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLE bool deviceConnected; bool oldDeviceConnected; bool _isEnabled; + uint16_t last_conn_id; uint32_t _pin_code; unsigned long _last_write; unsigned long adv_restart_time; @@ -56,6 +57,7 @@ public: adv_restart_time = 0; _isEnabled = false; _last_write = 0; + last_conn_id = 0; send_queue_len = recv_queue_len = 0; } diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index a8c11d97..8049f5c0 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -115,6 +115,16 @@ void SerialBLEInterface::enable() { void SerialBLEInterface::disable() { _isEnabled = false; BLE_DEBUG_PRINTLN("SerialBLEInterface::disable"); + + uint16_t conn_id; + if (Bluefruit.getConnectedHandles(&conn_id, 1) > 0) { + Bluefruit.disconnect(conn_id); + } + + Bluefruit.Advertising.restartOnDisconnect(false); + Bluefruit.Advertising.stop(); + Bluefruit.Advertising.clearData(); + stopAdv(); } diff --git a/src/helpers/nrf52/T114Board.h b/src/helpers/nrf52/T114Board.h index 154ccb22..cd58134d 100644 --- a/src/helpers/nrf52/T114Board.h +++ b/src/helpers/nrf52/T114Board.h @@ -60,5 +60,9 @@ public: NVIC_SystemReset(); } + void powerOff() override { + sd_power_system_off(); + } + bool startOTAUpdate(const char* id, char reply[]) override; }; diff --git a/src/helpers/nrf52/ThinkNodeM1Board.h b/src/helpers/nrf52/ThinkNodeM1Board.h index 97334bd3..c1ffcbbf 100644 --- a/src/helpers/nrf52/ThinkNodeM1Board.h +++ b/src/helpers/nrf52/ThinkNodeM1Board.h @@ -55,4 +55,8 @@ public: void reboot() override { NVIC_SystemReset(); } + + void powerOff() override { + sd_power_system_off(); + } }; diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index 2d8b69c1..d81d99fb 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -21,9 +21,15 @@ public: virtual void setColor(Color c) = 0; virtual void setCursor(int x, int y) = 0; virtual void print(const char* str) = 0; + virtual void printWordWrap(const char* str, int max_width) { print(str); } // fallback to basic print() if no override virtual void fillRect(int x, int y, int w, int h) = 0; virtual void drawRect(int x, int y, int w, int h) = 0; virtual void drawXbm(int x, int y, const uint8_t* bits, int w, int h) = 0; virtual uint16_t getTextWidth(const char* str) = 0; + virtual void drawTextCentered(int mid_x, int y, const char* str) { // helper method (override to optimise) + int w = getTextWidth(str); + setCursor(mid_x - w/2, y); + print(str); + } virtual void endFrame() = 0; }; diff --git a/src/helpers/ui/GxEPDDisplay.cpp b/src/helpers/ui/GxEPDDisplay.cpp index 875e29ac..50df9d10 100644 --- a/src/helpers/ui/GxEPDDisplay.cpp +++ b/src/helpers/ui/GxEPDDisplay.cpp @@ -47,6 +47,7 @@ void GxEPDDisplay::clear() { void GxEPDDisplay::startFrame(Color bkg) { display.fillScreen(GxEPD_WHITE); + display.setTextColor(_curr_color = GxEPD_BLACK); } void GxEPDDisplay::setTextSize(int sz) { @@ -67,7 +68,11 @@ void GxEPDDisplay::setTextSize(int sz) { } void GxEPDDisplay::setColor(Color c) { - display.setTextColor(GxEPD_BLACK); + if (c == DARK) { + display.setTextColor(_curr_color = GxEPD_BLACK); + } else { + display.setTextColor(_curr_color = GxEPD_WHITE); + } } void GxEPDDisplay::setCursor(int x, int y) { @@ -79,11 +84,11 @@ void GxEPDDisplay::print(const char* str) { } void GxEPDDisplay::fillRect(int x, int y, int w, int h) { - display.fillRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, GxEPD_BLACK); + display.fillRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color); } void GxEPDDisplay::drawRect(int x, int y, int w, int h) { - display.drawRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, GxEPD_BLACK); + display.drawRect(x*SCALE_X, y*SCALE_Y, w*SCALE_X, h*SCALE_Y, _curr_color); } void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { @@ -116,7 +121,7 @@ void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { // If the bit is set, draw a block of pixels if (bitSet) { // Draw the block as a filled rectangle - display.fillRect(x1, y1, block_w, block_h, GxEPD_BLACK); + display.fillRect(x1, y1, block_w, block_h, _curr_color); } } } diff --git a/src/helpers/ui/GxEPDDisplay.h b/src/helpers/ui/GxEPDDisplay.h index ec2bcec0..49746dee 100644 --- a/src/helpers/ui/GxEPDDisplay.h +++ b/src/helpers/ui/GxEPDDisplay.h @@ -28,6 +28,7 @@ class GxEPDDisplay : public DisplayDriver { GxEPD2_BW display; bool _init = false; bool _isOn = false; + uint16_t _curr_color; public: // there is a margin in y... diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp new file mode 100644 index 00000000..783f7ba7 --- /dev/null +++ b/src/helpers/ui/MomentaryButton.cpp @@ -0,0 +1,74 @@ +#include "MomentaryButton.h" + +MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse) { + _pin = pin; + _reverse = reverse; + down_at = 0; + prev = _reverse ? HIGH : LOW; + cancel = 0; + _long_millis = long_press_millis; +} + +void MomentaryButton::begin(bool pulldownup) { + if (_pin >= 0) { + pinMode(_pin, pulldownup ? (_reverse ? INPUT_PULLUP : INPUT_PULLDOWN) : INPUT); + } +} + +bool MomentaryButton::isPressed() const { + return isPressed(digitalRead(_pin)); +} + +void MomentaryButton::cancelClick() { + cancel = 1; +} + +bool MomentaryButton::isPressed(int level) const { + if (_reverse) { + return level == LOW; + } else { + return level != LOW; + } +} + +int MomentaryButton::check(bool repeat_click) { + if (_pin < 0) return BUTTON_EVENT_NONE; + + int event = BUTTON_EVENT_NONE; + int btn = digitalRead(_pin); + if (btn != prev) { + if (isPressed(btn)) { + down_at = millis(); + } else { + // button UP + if (_long_millis > 0) { + if (down_at > 0 && (unsigned long)(millis() - down_at) < _long_millis) { // only a CLICK if still within the long_press millis + event = BUTTON_EVENT_CLICK; + } + } else { + event = BUTTON_EVENT_CLICK; // any UP results in CLICK event when NOT using long_press feature + } + if (event == BUTTON_EVENT_CLICK && cancel) { + event = BUTTON_EVENT_NONE; + } + down_at = 0; + } + prev = btn; + } + if (!isPressed(btn) && cancel) { // always clear the pending 'cancel' once button is back in UP state + cancel = 0; + } + + if (_long_millis > 0 && down_at > 0 && (unsigned long)(millis() - down_at) >= _long_millis) { + event = BUTTON_EVENT_LONG_PRESS; + down_at = 0; + } + if (down_at > 0 && repeat_click) { + unsigned long diff = (unsigned long)(millis() - down_at); + if (diff >= 700) { + event = BUTTON_EVENT_CLICK; // wait 700 millis before repeating the click events + } + } + + return event; +} \ No newline at end of file diff --git a/src/helpers/ui/MomentaryButton.h b/src/helpers/ui/MomentaryButton.h new file mode 100644 index 00000000..c46561e3 --- /dev/null +++ b/src/helpers/ui/MomentaryButton.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#define BUTTON_EVENT_NONE 0 +#define BUTTON_EVENT_CLICK 1 +#define BUTTON_EVENT_LONG_PRESS 2 + +class MomentaryButton { + int8_t _pin; + int8_t prev, cancel; + bool _reverse; + int _long_millis; + unsigned long down_at; + + bool isPressed(int level) const; + +public: + MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false); + void begin(bool pulldownup=false); + int check(bool repeat_click=false); // returns one of BUTTON_EVENT_* + void cancelClick(); // suppress next BUTTON_EVENT_CLICK (if already in DOWN state) + uint8_t getPin() { return _pin; } + bool isPressed() const; +}; diff --git a/src/helpers/ui/ST7789Display.cpp b/src/helpers/ui/ST7789Display.cpp index 9e71e6bd..185ecc0e 100644 --- a/src/helpers/ui/ST7789Display.cpp +++ b/src/helpers/ui/ST7789Display.cpp @@ -62,6 +62,9 @@ void ST7789Display::clear() { void ST7789Display::startFrame(Color bkg) { display.clear(); + _color = ST77XX_WHITE; + display.setRGB(_color); + display.setFont(ArialMT_Plain_16); } void ST7789Display::setTextSize(int sz) { @@ -81,7 +84,9 @@ void ST7789Display::setColor(Color c) { switch (c) { case DisplayDriver::DARK : _color = ST77XX_BLACK; + display.setColor(OLEDDISPLAY_COLOR::BLACK); break; +#if 0 case DisplayDriver::LIGHT : _color = ST77XX_WHITE; break; @@ -100,8 +105,10 @@ void ST7789Display::setColor(Color c) { case DisplayDriver::ORANGE : _color = ST77XX_ORANGE; break; +#endif default: _color = ST77XX_WHITE; + display.setColor(OLEDDISPLAY_COLOR::WHITE); break; } display.setRGB(_color); @@ -116,6 +123,10 @@ void ST7789Display::print(const char* str) { display.drawString(_x, _y, str); } +void ST7789Display::printWordWrap(const char* str, int max_width) { + display.drawStringMaxWidth(_x, _y, max_width*SCALE_X, str); +} + void ST7789Display::fillRect(int x, int y, int w, int h) { display.fillRect(x*SCALE_X + X_OFFSET, y*SCALE_Y + Y_OFFSET, w*SCALE_X, h*SCALE_Y); } diff --git a/src/helpers/ui/ST7789Display.h b/src/helpers/ui/ST7789Display.h index b267a2cb..8056de81 100644 --- a/src/helpers/ui/ST7789Display.h +++ b/src/helpers/ui/ST7789Display.h @@ -27,6 +27,7 @@ public: void setColor(Color c) override; void setCursor(int x, int y) override; void print(const char* str) override; + void printWordWrap(const char* str, int max_width) override; void fillRect(int x, int y, int w, int h) override; void drawRect(int x, int y, int w, int h) override; void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; diff --git a/src/helpers/ui/UIScreen.h b/src/helpers/ui/UIScreen.h new file mode 100644 index 00000000..6faa591a --- /dev/null +++ b/src/helpers/ui/UIScreen.h @@ -0,0 +1,21 @@ +#pragma once + +#include "DisplayDriver.h" + +#define KEY_LEFT 0xB4 +#define KEY_UP 0xB5 +#define KEY_DOWN 0xB6 +#define KEY_RIGHT 0xB7 +#define KEY_SELECT 10 +#define KEY_ENTER 13 +#define KEY_BACK 27 // Esc + +class UIScreen { +protected: + UIScreen() { } +public: + virtual int render(DisplayDriver& display) =0; // return value is number of millis until next render + virtual bool handleInput(char c) { return false; } + virtual void poll() { } +}; + diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 08eb500e..ec0956d8 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -6,6 +6,7 @@ build_flags = ${sensor_base.build_flags} -I variants/heltec_v3 -D HELTEC_LORA_V3 + -D ESP32_CPU_FREQ=80 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 @@ -46,6 +47,7 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 [env:Heltec_v3_room_server] extends = Heltec_lora32_v3 @@ -91,6 +93,7 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/companion_radio> lib_deps = ${Heltec_lora32_v3.lib_deps} @@ -100,16 +103,18 @@ lib_deps = extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} - -D MAX_CONTACTS=100 + -D MAX_CONTACTS=160 -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + +<../examples/companion_radio> lib_deps = @@ -130,6 +135,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + +<../examples/companion_radio> lib_deps = @@ -172,6 +178,7 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 [env:Heltec_WSL3_room_server] extends = Heltec_lora32_v3 @@ -237,3 +244,49 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} + +[env:Heltec_WSL3_espnow_bridge] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} +; -D LORA_FREQ=915.8 + -D MESH_PACKET_LOGGING=1 + -D ENV_INCLUDE_AHTX0=0 + -D ENV_INCLUDE_BME280=0 + -D ENV_INCLUDE_BMP280=0 + -D ENV_INCLUDE_INA3221=0 + -D ENV_INCLUDE_INA219=0 + -D ENV_INCLUDE_MLX90614=0 + -D ENV_INCLUDE_VL53L0X=0 + -D ENV_INCLUDE_GPS=0 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_bridge/main.cpp> + + +lib_deps = + ${Heltec_lora32_v3.lib_deps} + bakercp/CRC32 @ ^2.0.0 + +[env:Heltec_WSL3_serial_bridge] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} +; -D LORA_FREQ=915.8 + -D MESH_PACKET_LOGGING=1 + -D SERIAL_BRIDGE_RX=47 + -D SERIAL_BRIDGE_TX=48 + -D ENV_INCLUDE_AHTX0=0 + -D ENV_INCLUDE_BME280=0 + -D ENV_INCLUDE_BMP280=0 + -D ENV_INCLUDE_INA3221=0 + -D ENV_INCLUDE_INA219=0 + -D ENV_INCLUDE_MLX90614=0 + -D ENV_INCLUDE_VL53L0X=0 + -D ENV_INCLUDE_GPS=0 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_bridge/main.cpp> + +<../examples/simple_bridge/SerialBridgeRadio.cpp> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + bakercp/CRC32 @ ^2.0.0 diff --git a/variants/heltec_v3/target.cpp b/variants/heltec_v3/target.cpp index 4cbc78fb..78b88197 100644 --- a/variants/heltec_v3/target.cpp +++ b/variants/heltec_v3/target.cpp @@ -25,6 +25,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/heltec_v3/target.h b/variants/heltec_v3/target.h index 992a3d2c..b2125664 100644 --- a/variants/heltec_v3/target.h +++ b/variants/heltec_v3/target.h @@ -10,6 +10,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif extern HeltecV3Board board; @@ -19,6 +20,7 @@ extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); diff --git a/variants/t114/platformio.ini b/variants/t114/platformio.ini index dac12da9..815ef173 100644 --- a/variants/t114/platformio.ini +++ b/variants/t114/platformio.ini @@ -30,6 +30,7 @@ build_src_filter = ${nrf52840_t114.build_src_filter} + +<../variants/t114> + + + + + lib_deps = diff --git a/variants/t114/target.cpp b/variants/t114/target.cpp index d97c03f6..d2fa6c4c 100644 --- a/variants/t114/target.cpp +++ b/variants/t114/target.cpp @@ -16,6 +16,7 @@ T114SensorManager sensors = T114SensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/t114/target.h b/variants/t114/target.h index 8831d9f7..35e86f60 100644 --- a/variants/t114/target.h +++ b/variants/t114/target.h @@ -10,6 +10,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif class T114SensorManager : public SensorManager { @@ -37,6 +38,7 @@ extern T114SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index 3e4bfdb4..59f198e0 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -61,6 +61,7 @@ build_flags = lib_deps = ${Xiao_esp32_C3.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 [env:Xiao_C3_companion_radio_ble] extends = Xiao_esp32_C3 @@ -127,6 +128,7 @@ build_flags = lib_deps = ${Xiao_esp32_C3_custom.lib_deps} ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 [env:Xiao_C3_Repeater_sx1268_custom] extends = Xiao_esp32_C3_custom @@ -146,4 +148,5 @@ build_flags = ; -D MESH_DEBUG=1 lib_deps = ${Xiao_esp32_C3_custom.lib_deps} - ${esp32_ota.lib_deps} \ No newline at end of file + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 From 8d7a49867f485e7f06c752019b14d75b633fa064 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 9 Aug 2025 00:21:10 +1200 Subject: [PATCH 02/46] add support for new companion ui on thinknode m1 --- examples/companion_radio/UITask.cpp | 2 +- src/helpers/ui/GxEPDDisplay.cpp | 5 +++-- variants/thinknode_m1/platformio.ini | 1 + variants/thinknode_m1/target.cpp | 1 + variants/thinknode_m1/target.h | 2 ++ 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 9f5fe5b1..73bd00fc 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -305,7 +305,7 @@ public: } else { sprintf(tmp, "%dh", secs / (60*60)); } - display.setCursor(display.width() - display.getTextWidth(tmp), 0); + display.setCursor(display.width() - display.getTextWidth(tmp) - 2, 0); display.print(tmp); display.drawRect(0, 11, display.width(), 1); // horiz line diff --git a/src/helpers/ui/GxEPDDisplay.cpp b/src/helpers/ui/GxEPDDisplay.cpp index 50df9d10..c3d75bbd 100644 --- a/src/helpers/ui/GxEPDDisplay.cpp +++ b/src/helpers/ui/GxEPDDisplay.cpp @@ -68,10 +68,11 @@ void GxEPDDisplay::setTextSize(int sz) { } void GxEPDDisplay::setColor(Color c) { + // colours need to be inverted for epaper displays if (c == DARK) { - display.setTextColor(_curr_color = GxEPD_BLACK); - } else { display.setTextColor(_curr_color = GxEPD_WHITE); + } else { + display.setTextColor(_curr_color = GxEPD_BLACK); } } diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index 2104a080..972444ba 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -83,6 +83,7 @@ build_src_filter = ${ThinkNode_M1.build_src_filter} + + + + + +<../examples/companion_radio> lib_deps = ${ThinkNode_M1.lib_deps} diff --git a/variants/thinknode_m1/target.cpp b/variants/thinknode_m1/target.cpp index 19230232..2b04d7c6 100644 --- a/variants/thinknode_m1/target.cpp +++ b/variants/thinknode_m1/target.cpp @@ -16,6 +16,7 @@ ThinkNodeM1SensorManager sensors = ThinkNodeM1SensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/thinknode_m1/target.h b/variants/thinknode_m1/target.h index c938d422..eac221c3 100644 --- a/variants/thinknode_m1/target.h +++ b/variants/thinknode_m1/target.h @@ -10,6 +10,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif class ThinkNodeM1SensorManager : public SensorManager { @@ -37,6 +38,7 @@ extern ThinkNodeM1SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); From 1e711f57f49f8daaedc3c041f37411d4bf07528c Mon Sep 17 00:00:00 2001 From: Florent Date: Fri, 8 Aug 2025 15:34:58 +0200 Subject: [PATCH 03/46] techo: initial support of new companion ui --- src/helpers/ui/GxEPDDisplay.cpp | 6 +++--- variants/techo/platformio.ini | 1 + variants/techo/target.cpp | 1 + variants/techo/target.h | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/helpers/ui/GxEPDDisplay.cpp b/src/helpers/ui/GxEPDDisplay.cpp index 50df9d10..8a138c6c 100644 --- a/src/helpers/ui/GxEPDDisplay.cpp +++ b/src/helpers/ui/GxEPDDisplay.cpp @@ -68,10 +68,10 @@ void GxEPDDisplay::setTextSize(int sz) { } void GxEPDDisplay::setColor(Color c) { - if (c == DARK) { - display.setTextColor(_curr_color = GxEPD_BLACK); - } else { + if (c == DARK) { // invert colors for epds display.setTextColor(_curr_color = GxEPD_WHITE); + } else { + display.setTextColor(_curr_color = GxEPD_BLACK); } } diff --git a/variants/techo/platformio.ini b/variants/techo/platformio.ini index 8ad5ca03..45eba9fc 100644 --- a/variants/techo/platformio.ini +++ b/variants/techo/platformio.ini @@ -73,6 +73,7 @@ build_src_filter = ${LilyGo_Techo.build_src_filter} + + + + + +<../examples/companion_radio> lib_deps = ${LilyGo_Techo.lib_deps} diff --git a/variants/techo/target.cpp b/variants/techo/target.cpp index 1e413531..9a10491d 100644 --- a/variants/techo/target.cpp +++ b/variants/techo/target.cpp @@ -16,6 +16,7 @@ TechoSensorManager sensors = TechoSensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/techo/target.h b/variants/techo/target.h index 7c05e742..58fba687 100644 --- a/variants/techo/target.h +++ b/variants/techo/target.h @@ -10,6 +10,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif class TechoSensorManager : public SensorManager { @@ -36,6 +37,7 @@ extern TechoSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); From 153051ab824a23eceba3ff12ae5a160a4224f63d Mon Sep 17 00:00:00 2001 From: 446564 Date: Sat, 9 Aug 2025 20:47:35 -0700 Subject: [PATCH 04/46] add new UI to nano g2 --- variants/nano_g2_ultra/platformio.ini | 1 + variants/nano_g2_ultra/target.cpp | 53 +++++++++++---------------- variants/nano_g2_ultra/target.h | 9 +++-- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/variants/nano_g2_ultra/platformio.ini b/variants/nano_g2_ultra/platformio.ini index 511c0ae7..7cbac320 100644 --- a/variants/nano_g2_ultra/platformio.ini +++ b/variants/nano_g2_ultra/platformio.ini @@ -47,6 +47,7 @@ build_src_filter = ${Nano_G2_Ultra.build_src_filter} + + + + + +<../examples/companion_radio> lib_deps = ${Nano_G2_Ultra.lib_deps} diff --git a/variants/nano_g2_ultra/target.cpp b/variants/nano_g2_ultra/target.cpp index b6084236..81e7744f 100644 --- a/variants/nano_g2_ultra/target.cpp +++ b/variants/nano_g2_ultra/target.cpp @@ -1,5 +1,6 @@ -#include #include "target.h" + +#include #include #include @@ -16,29 +17,26 @@ NanoG2UltraSensorManager sensors = NanoG2UltraSensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; +MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif -bool radio_init() -{ +bool radio_init() { rtc_clock.begin(Wire); return radio.std_init(&SPI); } -uint32_t radio_get_rng_seed() -{ +uint32_t radio_get_rng_seed() { return radio.random(0x7FFFFFFF); } -void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) -{ +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { radio.setFrequency(freq); radio.setSpreadingFactor(sf); radio.setBandwidth(bw); radio.setCodingRate(cr); } -void radio_set_tx_power(uint8_t dbm) -{ +void radio_set_tx_power(uint8_t dbm) { radio.setOutputPower(dbm); } @@ -64,8 +62,7 @@ void NanoG2UltraSensorManager::stop_gps() { _location->stop(); } -bool NanoG2UltraSensorManager::begin() -{ +bool NanoG2UltraSensorManager::begin() { digitalWrite(PIN_GPS_STANDBY, HIGH); // Wake GPS from standby Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX); Serial1.begin(9600); @@ -83,29 +80,26 @@ bool NanoG2UltraSensorManager::begin() return true; } -bool NanoG2UltraSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP &telemetry) -{ - if (requester_permissions & TELEM_PERM_LOCATION) - { // does requester have permission? +bool NanoG2UltraSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP &telemetry) { + if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } return true; } -void NanoG2UltraSensorManager::loop() -{ +void NanoG2UltraSensorManager::loop() { static long next_gps_update = 0; if (!gps_active) { - return; // GPS is not active, skip further processing + return; // GPS is not active, skip further processing } _location->loop(); if (millis() > next_gps_update) { if (_location->isValid()) { - node_lat = ((double)_location->getLatitude())/1000000.; - node_lon = ((double)_location->getLongitude())/1000000.; + node_lat = ((double)_location->getLatitude()) / 1000000.; + node_lon = ((double)_location->getLongitude()) / 1000000.; node_altitude = ((double)_location->getAltitude()) / 1000.0; MESH_DEBUG_PRINTLN("VALID location: lat %f lon %f", node_lat, node_lon); } else { @@ -116,24 +110,22 @@ void NanoG2UltraSensorManager::loop() } } -int NanoG2UltraSensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) +int NanoG2UltraSensorManager::getNumSettings() const { + return 1; +} // just one supported: "gps" (power switch) -const char *NanoG2UltraSensorManager::getSettingName(int i) const -{ +const char *NanoG2UltraSensorManager::getSettingName(int i) const { return i == 0 ? "gps" : NULL; } -const char *NanoG2UltraSensorManager::getSettingValue(int i) const -{ - if (i == 0) - { +const char *NanoG2UltraSensorManager::getSettingValue(int i) const { + if (i == 0) { return gps_active ? "1" : "0"; } return NULL; } -bool NanoG2UltraSensorManager::setSettingValue(const char *name, const char *value) -{ +bool NanoG2UltraSensorManager::setSettingValue(const char *name, const char *value) { if (strcmp(name, "gps") == 0) { if (strcmp(value, "0") == 0) { stop_gps(); @@ -145,8 +137,7 @@ bool NanoG2UltraSensorManager::setSettingValue(const char *name, const char *val return false; // not supported } -mesh::LocalIdentity radio_new_identity() -{ +mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity } diff --git a/variants/nano_g2_ultra/target.h b/variants/nano_g2_ultra/target.h index 5cde6405..3e58b900 100644 --- a/variants/nano_g2_ultra/target.h +++ b/variants/nano_g2_ultra/target.h @@ -1,13 +1,15 @@ #pragma once #define RADIOLIB_STATIC_ONLY 1 -#include #include "nano-g2.h" -#include -#include + +#include #include #include +#include +#include #ifdef DISPLAY_CLASS +#include #include #endif #include @@ -37,6 +39,7 @@ extern NanoG2UltraSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; +extern MomentaryButton user_btn; #endif bool radio_init(); From e84e3066ff0e54a0eaaf19116dc229796afdf865 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 12 Aug 2025 10:01:35 +1000 Subject: [PATCH 05/46] * MomentaryButton: pullupdown param moved to constructor --- src/helpers/ui/MomentaryButton.cpp | 7 ++++--- src/helpers/ui/MomentaryButton.h | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/helpers/ui/MomentaryButton.cpp b/src/helpers/ui/MomentaryButton.cpp index 783f7ba7..9ddf1327 100644 --- a/src/helpers/ui/MomentaryButton.cpp +++ b/src/helpers/ui/MomentaryButton.cpp @@ -1,17 +1,18 @@ #include "MomentaryButton.h" -MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse) { +MomentaryButton::MomentaryButton(int8_t pin, int long_press_millis, bool reverse, bool pulldownup) { _pin = pin; _reverse = reverse; + _pull = pulldownup; down_at = 0; prev = _reverse ? HIGH : LOW; cancel = 0; _long_millis = long_press_millis; } -void MomentaryButton::begin(bool pulldownup) { +void MomentaryButton::begin() { if (_pin >= 0) { - pinMode(_pin, pulldownup ? (_reverse ? INPUT_PULLUP : INPUT_PULLDOWN) : INPUT); + pinMode(_pin, _pull ? (_reverse ? INPUT_PULLUP : INPUT_PULLDOWN) : INPUT); } } diff --git a/src/helpers/ui/MomentaryButton.h b/src/helpers/ui/MomentaryButton.h index c46561e3..0bcc776c 100644 --- a/src/helpers/ui/MomentaryButton.h +++ b/src/helpers/ui/MomentaryButton.h @@ -9,15 +9,15 @@ class MomentaryButton { int8_t _pin; int8_t prev, cancel; - bool _reverse; + bool _reverse, _pull; int _long_millis; unsigned long down_at; bool isPressed(int level) const; public: - MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false); - void begin(bool pulldownup=false); + MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false, bool pulldownup=false); + void begin(); int check(bool repeat_click=false); // returns one of BUTTON_EVENT_* void cancelClick(); // suppress next BUTTON_EVENT_CLICK (if already in DOWN state) uint8_t getPin() { return _pin; } From d7c2293cb839d189564c9e3b464f267a2c3d6ddd Mon Sep 17 00:00:00 2001 From: MikesAllotment Date: Thu, 14 Aug 2025 14:17:26 +0100 Subject: [PATCH 06/46] Added support for new EnhancedUI MomentaryButton for Heltec V2 and Faketec ProMicro variants --- variants/heltec_v2/platformio.ini | 4 ++++ variants/heltec_v2/target.cpp | 1 + variants/heltec_v2/target.h | 2 ++ variants/promicro/platformio.ini | 5 +++++ variants/promicro/target.cpp | 1 + variants/promicro/target.h | 2 ++ 6 files changed, 15 insertions(+) diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index 43bc43ad..04768df4 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -35,6 +35,7 @@ build_flags = build_src_filter = ${Heltec_lora32_v2.build_src_filter} +<../examples/simple_repeater> + + + lib_deps = ${Heltec_lora32_v2.lib_deps} ${esp32_ota.lib_deps} @@ -53,6 +54,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + +<../examples/simple_room_server> lib_deps = ${Heltec_lora32_v2.lib_deps} @@ -84,6 +86,7 @@ build_flags = build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + + +<../examples/companion_radio> lib_deps = ${Heltec_lora32_v2.lib_deps} @@ -104,6 +107,7 @@ build_flags = build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + + +<../examples/companion_radio> lib_deps = ${Heltec_lora32_v2.lib_deps} diff --git a/variants/heltec_v2/target.cpp b/variants/heltec_v2/target.cpp index 418f1f7f..df71d3f4 100644 --- a/variants/heltec_v2/target.cpp +++ b/variants/heltec_v2/target.cpp @@ -18,6 +18,7 @@ SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/heltec_v2/target.h b/variants/heltec_v2/target.h index 0c330316..2e5b17de 100644 --- a/variants/heltec_v2/target.h +++ b/variants/heltec_v2/target.h @@ -9,6 +9,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif extern HeltecV2Board board; @@ -18,6 +19,7 @@ extern SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 78b25aa7..0ec88070 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -39,6 +39,7 @@ extends = Faketec build_src_filter = ${Faketec.build_src_filter} +<../examples/simple_repeater> + + + build_flags = ${Faketec.build_flags} -D ADVERT_NAME='"Faketec Repeater"' @@ -57,6 +58,7 @@ extends = Faketec build_src_filter = ${Faketec.build_src_filter} +<../examples/simple_room_server> + + + build_flags = ${Faketec.build_flags} -D ADVERT_NAME='"Faketec Room"' -D ADVERT_LAT=0.0 @@ -93,6 +95,7 @@ build_flags = ${Faketec.build_flags} build_src_filter = ${Faketec.build_src_filter} +<../examples/companion_radio> + + + lib_deps = ${Faketec.lib_deps} adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 @@ -112,6 +115,7 @@ build_src_filter = ${Faketec.build_src_filter} + +<../examples/companion_radio> + + + lib_deps = ${Faketec.lib_deps} adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 @@ -129,6 +133,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Faketec.build_src_filter} + + + +<../examples/simple_sensor> lib_deps = ${Faketec.lib_deps} diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp index 03a5a16a..b26320e4 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -20,6 +20,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); #endif bool radio_init() { diff --git a/variants/promicro/target.h b/variants/promicro/target.h index de2719e6..38c4b4e8 100644 --- a/variants/promicro/target.h +++ b/variants/promicro/target.h @@ -8,6 +8,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif #include @@ -19,6 +20,7 @@ extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); From 86671c0ff80a38833cc6a550168c33ae7f17b2de Mon Sep 17 00:00:00 2001 From: taco Date: Sat, 16 Aug 2025 09:29:48 +1000 Subject: [PATCH 07/46] Support NewUI on WioTrackerL1 --- variants/wio-tracker-l1/platformio.ini | 1 + variants/wio-tracker-l1/target.cpp | 1 + variants/wio-tracker-l1/target.h | 2 ++ 3 files changed, 4 insertions(+) diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index 380ff90f..cefe9649 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -85,6 +85,7 @@ build_flags = ${WioTrackerL1.build_flags} build_src_filter = ${WioTrackerL1.build_src_filter} + +<../examples/companion_radio> + + + lib_deps = ${WioTrackerL1.lib_deps} adafruit/RTClib @ ^2.1.3 diff --git a/variants/wio-tracker-l1/target.cpp b/variants/wio-tracker-l1/target.cpp index 0809e19e..dc484546 100644 --- a/variants/wio-tracker-l1/target.cpp +++ b/variants/wio-tracker-l1/target.cpp @@ -16,6 +16,7 @@ WioTrackerL1SensorManager sensors = WioTrackerL1SensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/wio-tracker-l1/target.h b/variants/wio-tracker-l1/target.h index ab42b7b5..d5fe4d70 100644 --- a/variants/wio-tracker-l1/target.h +++ b/variants/wio-tracker-l1/target.h @@ -9,6 +9,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif #include @@ -38,6 +39,7 @@ extern AutoDiscoverRTCClock rtc_clock; extern WioTrackerL1SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); From e9ffc3ea93d7912460c38cbaafb726f5455ad8cf Mon Sep 17 00:00:00 2001 From: kelsey hudson Date: Fri, 15 Aug 2025 19:29:22 -0700 Subject: [PATCH 08/46] Ikoka Stick: repeater/room server functionality Make Repeater and Room Server work as build targets. Remove esp32-related alternate pinout cruft from the Ikoka Stick NRF build tree. --- variants/ikoka_stick_nrf/platformio.ini | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/variants/ikoka_stick_nrf/platformio.ini b/variants/ikoka_stick_nrf/platformio.ini index 3e62b5e7..b883e8f7 100644 --- a/variants/ikoka_stick_nrf/platformio.ini +++ b/variants/ikoka_stick_nrf/platformio.ini @@ -93,12 +93,6 @@ lib_deps = ${ikoka_stick_nrf.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:ikoka_stick_nrf_alt_pinout_companion_radio_ble] -extends = env:ikoka_stick_nrf_companion_radio_ble -build_flags = - ${env:ikoka_stick_nrf_companion_radio_ble.build_flags} - -D SX1262_XIAO_S3_VARIANT - [env:ikoka_stick_nrf_repeater] extends = ikoka_stick_nrf build_flags = @@ -111,13 +105,8 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ikoka_stick_nrf.build_src_filter} - +<../examples/simple_repeater/main.cpp> - -[env:ikoka_stick_nrf_alt_pinout_repeater] -extends = env:ikoka_stick_nrf_repeater -build_flags = - ${env:ikoka_stick_nrf_repeater.build_flags} - -D SX1262_XIAO_S3_VARIANT + + + +<../examples/simple_repeater> [env:ikoka_stick_nrf_room_server] extends = ikoka_stick_nrf @@ -130,4 +119,4 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ikoka_stick_nrf.build_src_filter} - +<../examples/simple_room_server/main.cpp> + +<../examples/simple_room_server> From acde9921b53e6a0f95caeb25bec1c698a46fb5f8 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 16 Aug 2025 20:04:54 +1000 Subject: [PATCH 09/46] * Refactor of UITask, moved to /ui-new --- examples/companion_radio/AbstractUITask.h | 46 ++++++++++++++++++ examples/companion_radio/MyMesh.cpp | 24 ++++------ examples/companion_radio/MyMesh.h | 10 ++-- examples/companion_radio/main.cpp | 13 +++-- .../companion_radio/{ => ui-new}/UITask.cpp | 3 +- .../companion_radio/{ => ui-new}/UITask.h | 39 +++++---------- examples/companion_radio/{ => ui-new}/icons.h | 0 variants/heltec_v3/platformio.ini | 48 +------------------ variants/t114/platformio.ini | 4 ++ 9 files changed, 86 insertions(+), 101 deletions(-) create mode 100644 examples/companion_radio/AbstractUITask.h rename examples/companion_radio/{ => ui-new}/UITask.cpp (99%) rename examples/companion_radio/{ => ui-new}/UITask.h (62%) rename examples/companion_radio/{ => ui-new}/icons.h (100%) diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h new file mode 100644 index 00000000..706254af --- /dev/null +++ b/examples/companion_radio/AbstractUITask.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef PIN_BUZZER + #include +#endif + +#include "NodePrefs.h" + +enum class UIEventType { + none, + contactMessage, + channelMessage, + roomMessage, + newContactMessage, + ack +}; + +class AbstractUITask { +protected: + mesh::MainBoard* _board; + BaseSerialInterface* _serial; + bool _connected; + + AbstractUITask(mesh::MainBoard* board, BaseSerialInterface* serial) : _board(board), _serial(serial) { + _connected = false; + } + +public: + void setHasConnection(bool connected) { _connected = connected; } + bool hasConnection() const { return _connected; } + uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); } + bool isSerialEnabled() const { return _serial->isEnabled(); } + void enableSerial() { _serial->enable(); } + void disableSerial() { _serial->disable(); } + virtual void msgRead(int msgcount) = 0; + virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0; + virtual void soundBuzzer(UIEventType bet = UIEventType::none) = 0; + virtual void loop(); +}; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 75828d5e..1fa5478b 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -109,10 +109,6 @@ #define MAX_SIGN_DATA_LEN (8 * 1024) // 8K -#ifdef DISPLAY_CLASS -#include "UITask.h" -#endif - void MyMesh::writeOKFrame() { uint8_t buf[1]; buf[0] = RESP_CODE_OK; @@ -247,7 +243,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path } } else { #ifdef DISPLAY_CLASS - ui_task.soundBuzzer(UIEventType::newContactMessage); + if (_ui) _ui->soundBuzzer(UIEventType::newContactMessage); #endif } @@ -354,10 +350,10 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe #ifdef DISPLAY_CLASS // we only want to show text messages on display, not cli data bool should_display = txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN; - if (should_display) { - ui_task.newMsg(path_len, from.name, text, offline_queue_len); + if (should_display && _ui) { + _ui->newMsg(path_len, from.name, text, offline_queue_len); if (!_serial->isConnected()) { - ui_task.soundBuzzer(UIEventType::contactMessage); + _ui->soundBuzzer(UIEventType::contactMessage); } } #endif @@ -416,7 +412,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe _serial->writeFrame(frame, 1); } else { #ifdef DISPLAY_CLASS - ui_task.soundBuzzer(UIEventType::channelMessage); + if (_ui) _ui->soundBuzzer(UIEventType::channelMessage); #endif } #ifdef DISPLAY_CLASS @@ -426,7 +422,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe if (getChannel(channel_idx, channel_details)) { channel_name = channel_details.name; } - ui_task.newMsg(path_len, channel_name, text, offline_queue_len); + if (_ui) _ui->newMsg(path_len, channel_name, text, offline_queue_len); #endif } @@ -635,9 +631,9 @@ uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t void MyMesh::onSendTimeout() {} -MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store) +MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui) : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), - _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store) { + _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _ui(ui) { _iter_started = false; _cli_rescue = false; offline_queue_len = 0; @@ -1041,7 +1037,7 @@ void MyMesh::handleCmdFrame(size_t len) { if ((out_len = getFromOfflineQueue(out_frame)) > 0) { _serial->writeFrame(out_frame, out_len); #ifdef DISPLAY_CLASS - ui_task.msgRead(offline_queue_len); + if (_ui) _ui->msgRead(offline_queue_len); #endif } else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; @@ -1643,7 +1639,7 @@ void MyMesh::loop() { } #ifdef DISPLAY_CLASS - ui_task.setHasConnection(_serial->isConnected()); + if (_ui) _ui->setHasConnection(_serial->isConnected()); #endif } diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 42819813..0a5057ea 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -2,9 +2,7 @@ #include #include -#ifdef DISPLAY_CLASS -#include "UITask.h" -#endif +#include "AbstractUITask.h" /*------------ Frame Protocol --------------*/ #define FIRMWARE_VER_CODE 7 @@ -87,7 +85,7 @@ struct AdvertPath { class MyMesh : public BaseChatMesh, public DataStoreHost { public: - MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store); + MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui=NULL); void begin(bool has_display); void startInterface(BaseSerialInterface &serial); @@ -179,6 +177,7 @@ private: uint32_t pending_telemetry, pending_discovery; // pending _TELEMETRY_REQ uint32_t pending_req; // pending _BINARY_REQ BaseSerialInterface *_serial; + AbstractUITask* _ui; ContactsIterator _iter; uint32_t _iter_filter_since; @@ -216,6 +215,3 @@ private: }; extern MyMesh the_mesh; -#ifdef DISPLAY_CLASS -extern UITask ui_task; -#endif \ No newline at end of file diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index d9b3f68b..1d5ec564 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -75,14 +75,19 @@ static uint32_t _atoi(const char* sp) { #endif /* GLOBAL OBJECTS */ -StdRNG fast_rng; -SimpleMeshTables tables; -MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store); - #ifdef DISPLAY_CLASS #include "UITask.h" UITask ui_task(&board, &serial_interface); #endif + +StdRNG fast_rng; +SimpleMeshTables tables; +MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store + #ifdef DISPLAY_CLASS + , &ui_task + #endif +); + /* END GLOBAL OBJECTS */ void halt() { diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp similarity index 99% rename from examples/companion_radio/UITask.cpp rename to examples/companion_radio/ui-new/UITask.cpp index 73bd00fc..64b788b2 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1,7 +1,6 @@ #include "UITask.h" #include -#include "NodePrefs.h" -#include "MyMesh.h" +#include "../MyMesh.h" #include "target.h" #define AUTO_OFF_MILLIS 15000 // 15 seconds diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/ui-new/UITask.h similarity index 62% rename from examples/companion_radio/UITask.h rename to examples/companion_radio/ui-new/UITask.h index 818779e5..c8b211b6 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -11,29 +11,16 @@ #include #endif -#include "NodePrefs.h" +#include "../AbstractUITask.h" +#include "../NodePrefs.h" -enum class UIEventType { - none, - contactMessage, - channelMessage, - roomMessage, - newContactMessage, - ack -}; - -#define MAX_TOP_LEVEL 8 - -class UITask { +class UITask : public AbstractUITask { DisplayDriver* _display; - mesh::MainBoard* _board; - BaseSerialInterface* _serial; SensorManager* _sensors; #ifdef PIN_BUZZER genericBuzzer buzzer; #endif unsigned long _next_refresh, _auto_off; - bool _connected; NodePrefs* _node_prefs; char _alert[80]; unsigned long _alert_expiry; @@ -55,28 +42,24 @@ class UITask { public: - UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : _board(board), _serial(serial), _display(NULL), _sensors(NULL) { + UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) { next_batt_chck = _next_refresh = 0; ui_started_at = 0; - _connected = false; curr = NULL; } void begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs); void gotoHomeScreen() { setCurrScreen(home); } void showAlert(const char* text, int duration_millis); - void setHasConnection(bool connected) { _connected = connected; } - bool hasConnection() const { return _connected; } - uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); } - bool isSerialEnabled() const { return _serial->isEnabled(); } - void enableSerial() { _serial->enable(); } - void disableSerial() { _serial->disable(); } int getMsgCount() const { return _msgcount; } bool hasDisplay() const { return _display != NULL; } bool isButtonPressed() const; - void msgRead(int msgcount); - void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); - void soundBuzzer(UIEventType bet = UIEventType::none); + + // from AbsractUITask + void msgRead(int msgcount) override; + void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; + void soundBuzzer(UIEventType bet = UIEventType::none) override; + void loop() override; + void shutdown(bool restart = false); - void loop(); }; diff --git a/examples/companion_radio/icons.h b/examples/companion_radio/ui-new/icons.h similarity index 100% rename from examples/companion_radio/icons.h rename to examples/companion_radio/ui-new/icons.h diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index ec0956d8..c65bbec5 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -103,6 +103,7 @@ lib_deps = extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=160 -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display @@ -117,6 +118,7 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + +<../examples/companion_radio> + +<../examples/companion_radio/ui-new> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -244,49 +246,3 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} - -[env:Heltec_WSL3_espnow_bridge] -extends = Heltec_lora32_v3 -build_flags = - ${Heltec_lora32_v3.build_flags} -; -D LORA_FREQ=915.8 - -D MESH_PACKET_LOGGING=1 - -D ENV_INCLUDE_AHTX0=0 - -D ENV_INCLUDE_BME280=0 - -D ENV_INCLUDE_BMP280=0 - -D ENV_INCLUDE_INA3221=0 - -D ENV_INCLUDE_INA219=0 - -D ENV_INCLUDE_MLX90614=0 - -D ENV_INCLUDE_VL53L0X=0 - -D ENV_INCLUDE_GPS=0 -; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} - +<../examples/simple_bridge/main.cpp> - + -lib_deps = - ${Heltec_lora32_v3.lib_deps} - bakercp/CRC32 @ ^2.0.0 - -[env:Heltec_WSL3_serial_bridge] -extends = Heltec_lora32_v3 -build_flags = - ${Heltec_lora32_v3.build_flags} -; -D LORA_FREQ=915.8 - -D MESH_PACKET_LOGGING=1 - -D SERIAL_BRIDGE_RX=47 - -D SERIAL_BRIDGE_TX=48 - -D ENV_INCLUDE_AHTX0=0 - -D ENV_INCLUDE_BME280=0 - -D ENV_INCLUDE_BMP280=0 - -D ENV_INCLUDE_INA3221=0 - -D ENV_INCLUDE_INA219=0 - -D ENV_INCLUDE_MLX90614=0 - -D ENV_INCLUDE_VL53L0X=0 - -D ENV_INCLUDE_GPS=0 -; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} - +<../examples/simple_bridge/main.cpp> - +<../examples/simple_bridge/SerialBridgeRadio.cpp> -lib_deps = - ${Heltec_lora32_v3.lib_deps} - bakercp/CRC32 @ ^2.0.0 diff --git a/variants/t114/platformio.ini b/variants/t114/platformio.ini index 815ef173..e08da3ac 100644 --- a/variants/t114/platformio.ini +++ b/variants/t114/platformio.ini @@ -73,6 +73,7 @@ build_flags = extends = Heltec_t114 build_flags = ${Heltec_t114.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -83,6 +84,7 @@ build_flags = build_src_filter = ${Heltec_t114.build_src_filter} + +<../examples/companion_radio> + +<../examples/companion_radio/ui-new> lib_deps = ${Heltec_t114.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -91,6 +93,7 @@ lib_deps = extends = Heltec_t114 build_flags = ${Heltec_t114.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 ; -D BLE_PIN_CODE=123456 @@ -100,6 +103,7 @@ build_flags = build_src_filter = ${Heltec_t114.build_src_filter} + +<../examples/companion_radio> + +<../examples/companion_radio/ui-new> lib_deps = ${Heltec_t114.lib_deps} densaugeo/base64 @ ~1.4.0 \ No newline at end of file From e14b022a7cbd51923e1187c46290a3853f6ba0ae Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 16 Aug 2025 21:09:35 +1000 Subject: [PATCH 10/46] * original UITask now in /ui-orig folder --- examples/companion_radio/ui-new/UITask.h | 2 +- .../companion_radio/{ => ui-orig}/Button.cpp | 0 .../companion_radio/{ => ui-orig}/Button.h | 0 examples/companion_radio/ui-orig/UITask.cpp | 431 ++++++++++++++++++ examples/companion_radio/ui-orig/UITask.h | 73 +++ variants/heltec_v3/platformio.ini | 8 +- variants/nano_g2_ultra/platformio.ini | 9 +- variants/promicro/platformio.ini | 8 +- variants/t1000-e/platformio.ini | 2 + variants/t114/platformio.ini | 8 +- variants/techo/platformio.ini | 4 +- variants/thinknode_m1/platformio.ini | 4 +- variants/wio-tracker-l1/platformio.ini | 4 +- 13 files changed, 538 insertions(+), 15 deletions(-) rename examples/companion_radio/{ => ui-orig}/Button.cpp (100%) rename examples/companion_radio/{ => ui-orig}/Button.h (100%) create mode 100644 examples/companion_radio/ui-orig/UITask.cpp create mode 100644 examples/companion_radio/ui-orig/UITask.h diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index c8b211b6..f9e01550 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -55,7 +55,7 @@ public: bool hasDisplay() const { return _display != NULL; } bool isButtonPressed() const; - // from AbsractUITask + // from AbstractUITask void msgRead(int msgcount) override; void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; void soundBuzzer(UIEventType bet = UIEventType::none) override; diff --git a/examples/companion_radio/Button.cpp b/examples/companion_radio/ui-orig/Button.cpp similarity index 100% rename from examples/companion_radio/Button.cpp rename to examples/companion_radio/ui-orig/Button.cpp diff --git a/examples/companion_radio/Button.h b/examples/companion_radio/ui-orig/Button.h similarity index 100% rename from examples/companion_radio/Button.h rename to examples/companion_radio/ui-orig/Button.h diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp new file mode 100644 index 00000000..29d995a7 --- /dev/null +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -0,0 +1,431 @@ +#include "UITask.h" +#include +#include +#include "../MyMesh.h" + +#define AUTO_OFF_MILLIS 15000 // 15 seconds +#define BOOT_SCREEN_MILLIS 3000 // 3 seconds + +#ifdef PIN_STATUS_LED +#define LED_ON_MILLIS 20 +#define LED_ON_MSG_MILLIS 200 +#define LED_CYCLE_MILLIS 4000 +#endif + +#ifndef USER_BTN_PRESSED +#define USER_BTN_PRESSED LOW +#endif + +// 'meshcore', 128x13px +static const uint8_t meshcore_logo [] PROGMEM = { + 0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, + 0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe, + 0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc, + 0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00, + 0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00, + 0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8, + 0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8, + 0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0, + 0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00, + 0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00, + 0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8, + 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8, + 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8, +}; + +void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs) { + _display = display; + _sensors = sensors; + _auto_off = millis() + AUTO_OFF_MILLIS; + clearMsgPreview(); + _node_prefs = node_prefs; + if (_display != NULL) { + _display->turnOn(); + } + + // strip off dash and commit hash by changing dash to null terminator + // e.g: v1.2.3-abcdef -> v1.2.3 + char *version = strdup(FIRMWARE_VERSION); + char *dash = strchr(version, '-'); + if (dash) { + *dash = 0; + } + + // v1.2.3 (1 Jan 2025) + sprintf(_version_info, "%s (%s)", version, FIRMWARE_BUILD_DATE); + +#ifdef PIN_BUZZER + buzzer.begin(); +#endif + + // Initialize digital button if available +#ifdef PIN_USER_BTN + _userButton = new Button(PIN_USER_BTN, USER_BTN_PRESSED); + _userButton->begin(); + + // Set up digital button callbacks + _userButton->onShortPress([this]() { handleButtonShortPress(); }); + _userButton->onDoublePress([this]() { handleButtonDoublePress(); }); + _userButton->onTriplePress([this]() { handleButtonTriplePress(); }); + _userButton->onQuadruplePress([this]() { handleButtonQuadruplePress(); }); + _userButton->onLongPress([this]() { handleButtonLongPress(); }); + _userButton->onAnyPress([this]() { handleButtonAnyPress(); }); +#endif + + // Initialize analog button if available +#ifdef PIN_USER_BTN_ANA + _userButtonAnalog = new Button(PIN_USER_BTN_ANA, USER_BTN_PRESSED, true, 20); + _userButtonAnalog->begin(); + + // Set up analog button callbacks + _userButtonAnalog->onShortPress([this]() { handleButtonShortPress(); }); + _userButtonAnalog->onDoublePress([this]() { handleButtonDoublePress(); }); + _userButtonAnalog->onTriplePress([this]() { handleButtonTriplePress(); }); + _userButtonAnalog->onQuadruplePress([this]() { handleButtonQuadruplePress(); }); + _userButtonAnalog->onLongPress([this]() { handleButtonLongPress(); }); + _userButtonAnalog->onAnyPress([this]() { handleButtonAnyPress(); }); +#endif + ui_started_at = millis(); +} + +void UITask::soundBuzzer(UIEventType bet) { +#if defined(PIN_BUZZER) +switch(bet){ + case UIEventType::contactMessage: + // gemini's pick + buzzer.play("MsgRcv3:d=4,o=6,b=200:32e,32g,32b,16c7"); + break; + case UIEventType::channelMessage: + buzzer.play("kerplop:d=16,o=6,b=120:32g#,32c#"); + break; + case UIEventType::ack: + buzzer.play("ack:d=32,o=8,b=120:c"); + break; + case UIEventType::roomMessage: + case UIEventType::newContactMessage: + case UIEventType::none: + default: + break; +} +#endif +// Serial.print("DBG: Buzzzzzz -> "); +// Serial.println((int) bet); +} + +void UITask::msgRead(int msgcount) { + _msgcount = msgcount; + if (msgcount == 0) { + clearMsgPreview(); + } +} + +void UITask::clearMsgPreview() { + _origin[0] = 0; + _msg[0] = 0; + _need_refresh = true; +} + +void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) { + _msgcount = msgcount; + + if (path_len == 0xFF) { + sprintf(_origin, "(F) %s", from_name); + } else { + sprintf(_origin, "(%d) %s", (uint32_t) path_len, from_name); + } + StrHelper::strncpy(_msg, text, sizeof(_msg)); + + if (_display != NULL) { + if (!_display->isOn()) _display->turnOn(); + _auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer + _need_refresh = true; + } +} + +void UITask::renderBatteryIndicator(uint16_t batteryMilliVolts) { + // Convert millivolts to percentage + const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V) + const int maxMilliVolts = 4200; // Maximum voltage (e.g., 4.2V) + int batteryPercentage = ((batteryMilliVolts - minMilliVolts) * 100) / (maxMilliVolts - minMilliVolts); + if (batteryPercentage < 0) batteryPercentage = 0; // Clamp to 0% + if (batteryPercentage > 100) batteryPercentage = 100; // Clamp to 100% + + // battery icon + int iconWidth = 24; + int iconHeight = 12; + int iconX = _display->width() - iconWidth - 5; // Position the icon near the top-right corner + int iconY = 0; + _display->setColor(DisplayDriver::GREEN); + + // battery outline + _display->drawRect(iconX, iconY, iconWidth, iconHeight); + + // battery "cap" + _display->fillRect(iconX + iconWidth, iconY + (iconHeight / 4), 3, iconHeight / 2); + + // fill the battery based on the percentage + int fillWidth = (batteryPercentage * (iconWidth - 4)) / 100; + _display->fillRect(iconX + 2, iconY + 2, fillWidth, iconHeight - 4); +} + +void UITask::renderCurrScreen() { + if (_display == NULL) return; // assert() ?? + + char tmp[80]; + if (_alert[0]) { + _display->setTextSize(1.4); + uint16_t textWidth = _display->getTextWidth(_alert); + _display->setCursor((_display->width() - textWidth) / 2, 22); + _display->setColor(DisplayDriver::GREEN); + _display->print(_alert); + _alert[0] = 0; + _need_refresh = true; + return; + } else if (_origin[0] && _msg[0]) { // message preview + // render message preview + _display->setCursor(0, 0); + _display->setTextSize(1); + _display->setColor(DisplayDriver::GREEN); + _display->print(_node_prefs->node_name); + + _display->setCursor(0, 12); + _display->setColor(DisplayDriver::YELLOW); + _display->print(_origin); + _display->setCursor(0, 24); + _display->setColor(DisplayDriver::LIGHT); + _display->print(_msg); + + _display->setCursor(_display->width() - 28, 9); + _display->setTextSize(2); + _display->setColor(DisplayDriver::ORANGE); + sprintf(tmp, "%d", _msgcount); + _display->print(tmp); + _display->setColor(DisplayDriver::YELLOW); // last color will be kept on T114 + } else if ((millis() - ui_started_at) < BOOT_SCREEN_MILLIS) { // boot screen + // meshcore logo + _display->setColor(DisplayDriver::BLUE); + int logoWidth = 128; + _display->drawXbm((_display->width() - logoWidth) / 2, 3, meshcore_logo, logoWidth, 13); + + // version info + _display->setColor(DisplayDriver::LIGHT); + _display->setTextSize(1); + uint16_t textWidth = _display->getTextWidth(_version_info); + _display->setCursor((_display->width() - textWidth) / 2, 22); + _display->print(_version_info); + } else { // home screen + // node name + _display->setCursor(0, 0); + _display->setTextSize(1); + _display->setColor(DisplayDriver::GREEN); + _display->print(_node_prefs->node_name); + + // battery voltage + renderBatteryIndicator(_board->getBattMilliVolts()); + + // freq / sf + _display->setCursor(0, 20); + _display->setColor(DisplayDriver::YELLOW); + sprintf(tmp, "FREQ: %06.3f SF%d", _node_prefs->freq, _node_prefs->sf); + _display->print(tmp); + + // bw / cr + _display->setCursor(0, 30); + sprintf(tmp, "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr); + _display->print(tmp); + + // BT pin + if (!_connected && the_mesh.getBLEPin() != 0) { + _display->setColor(DisplayDriver::RED); + _display->setTextSize(2); + _display->setCursor(0, 43); + sprintf(tmp, "Pin:%d", the_mesh.getBLEPin()); + _display->print(tmp); + _display->setColor(DisplayDriver::GREEN); + } else { + _display->setColor(DisplayDriver::LIGHT); + } + } + _need_refresh = false; +} + +void UITask::userLedHandler() { +#ifdef PIN_STATUS_LED + static int state = 0; + static int next_change = 0; + static int last_increment = 0; + + int cur_time = millis(); + if (cur_time > next_change) { + if (state == 0) { + state = 1; + if (_msgcount > 0) { + last_increment = LED_ON_MSG_MILLIS; + } else { + last_increment = LED_ON_MILLIS; + } + next_change = cur_time + last_increment; + } else { + state = 0; + next_change = cur_time + LED_CYCLE_MILLIS - last_increment; + } + digitalWrite(PIN_STATUS_LED, state); + } +#endif +} + +/* + hardware-agnostic pre-shutdown activity should be done here +*/ +void UITask::shutdown(bool restart){ + + #ifdef PIN_BUZZER + /* note: we have a choice here - + we can do a blocking buzzer.loop() with non-deterministic consequences + or we can set a flag and delay the shutdown for a couple of seconds + while a non-blocking buzzer.loop() plays out in UITask::loop() + */ + buzzer.shutdown(); + uint32_t buzzer_timer = millis(); // fail-safe shutdown + while (buzzer.isPlaying() && (millis() - 2500) < buzzer_timer) + buzzer.loop(); + + #endif // PIN_BUZZER + + if (restart) + _board->reboot(); + else + _board->powerOff(); +} + +void UITask::loop() { + #ifdef PIN_USER_BTN + if (_userButton) { + _userButton->update(); + } + #endif + #ifdef PIN_USER_BTN_ANA + if (_userButtonAnalog) { + _userButtonAnalog->update(); + } + #endif + userLedHandler(); + +#ifdef PIN_BUZZER + if (buzzer.isPlaying()) buzzer.loop(); +#endif + + if (_display != NULL && _display->isOn()) { + static bool _firstBoot = true; + if(_firstBoot && (millis() - ui_started_at) >= BOOT_SCREEN_MILLIS) { + _need_refresh = true; + _firstBoot = false; + } + if (millis() >= _next_refresh && _need_refresh) { + _display->startFrame(); + renderCurrScreen(); + _display->endFrame(); + + _next_refresh = millis() + 1000; // refresh every second + } + if (millis() > _auto_off) { + _display->turnOff(); + } + } +} + +void UITask::handleButtonAnyPress() { + MESH_DEBUG_PRINTLN("UITask: any press triggered"); + // called on any button press before other events, to wake up the display quickly + // do not refresh the display here, as it may block the button handler + if (_display != NULL) { + _displayWasOn = _display->isOn(); // Track display state before any action + if (!_displayWasOn) { + _display->turnOn(); + } + _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer + } +} + +void UITask::handleButtonShortPress() { + MESH_DEBUG_PRINTLN("UITask: short press triggered"); + if (_display != NULL) { + // Only clear message preview if display was already on before button press + if (_displayWasOn) { + // If display was on and showing message preview, clear it + if (_origin[0] && _msg[0]) { + clearMsgPreview(); + } else { + // Otherwise, refresh the display + _need_refresh = true; + } + } else { + _need_refresh = true; // display just turned on, so we need to refresh + } + // Note: Display turn-on and auto-off timer extension are handled by handleButtonAnyPress + } +} + +void UITask::handleButtonDoublePress() { + MESH_DEBUG_PRINTLN("UITask: double press triggered, sending advert"); + // ADVERT + #ifdef PIN_BUZZER + soundBuzzer(UIEventType::ack); + #endif + if (the_mesh.advert()) { + MESH_DEBUG_PRINTLN("Advert sent!"); + sprintf(_alert, "Advert sent!"); + } else { + MESH_DEBUG_PRINTLN("Advert failed!"); + sprintf(_alert, "Advert failed.."); + } + _need_refresh = true; +} + +void UITask::handleButtonTriplePress() { + MESH_DEBUG_PRINTLN("UITask: triple press triggered"); + // Toggle buzzer quiet mode + #ifdef PIN_BUZZER + if (buzzer.isQuiet()) { + buzzer.quiet(false); + soundBuzzer(UIEventType::ack); + sprintf(_alert, "Buzzer: ON"); + } else { + buzzer.quiet(true); + sprintf(_alert, "Buzzer: OFF"); + } + _need_refresh = true; + #endif +} + +void UITask::handleButtonQuadruplePress() { + MESH_DEBUG_PRINTLN("UITask: quad press triggered"); + if (_sensors != NULL) { + // toggle GPS onn/off + int num = _sensors->getNumSettings(); + for (int i = 0; i < num; i++) { + if (strcmp(_sensors->getSettingName(i), "gps") == 0) { + if (strcmp(_sensors->getSettingValue(i), "1") == 0) { + _sensors->setSettingValue("gps", "0"); + soundBuzzer(UIEventType::ack); + sprintf(_alert, "GPS: Disabled"); + } else { + _sensors->setSettingValue("gps", "1"); + soundBuzzer(UIEventType::ack); + sprintf(_alert, "GPS: Enabled"); + } + break; + } + } + } + _need_refresh = true; +} + +void UITask::handleButtonLongPress() { + MESH_DEBUG_PRINTLN("UITask: long press triggered"); + if (millis() - ui_started_at < 8000) { // long press in first 8 seconds since startup -> CLI/rescue + the_mesh.enterCLIRescue(); + } else { + shutdown(); + } +} \ No newline at end of file diff --git a/examples/companion_radio/ui-orig/UITask.h b/examples/companion_radio/ui-orig/UITask.h new file mode 100644 index 00000000..a59ddc41 --- /dev/null +++ b/examples/companion_radio/ui-orig/UITask.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef PIN_BUZZER + #include +#endif + +#include "../AbstractUITask.h" +#include "../NodePrefs.h" + +#include "Button.h" + +class UITask : public AbstractUITask { + DisplayDriver* _display; + SensorManager* _sensors; +#ifdef PIN_BUZZER + genericBuzzer buzzer; +#endif + unsigned long _next_refresh, _auto_off; + NodePrefs* _node_prefs; + char _version_info[32]; + char _origin[62]; + char _msg[80]; + char _alert[80]; + int _msgcount; + bool _need_refresh = true; + bool _displayWasOn = false; // Track display state before button press + unsigned long ui_started_at; + + // Button handlers +#ifdef PIN_USER_BTN + Button* _userButton = nullptr; +#endif +#ifdef PIN_USER_BTN_ANA + Button* _userButtonAnalog = nullptr; +#endif + + void renderCurrScreen(); + void userLedHandler(); + void renderBatteryIndicator(uint16_t batteryMilliVolts); + + // Button action handlers + void handleButtonAnyPress(); + void handleButtonShortPress(); + void handleButtonDoublePress(); + void handleButtonTriplePress(); + void handleButtonQuadruplePress(); + void handleButtonLongPress(); + + +public: + + UITask(mesh::MainBoard* board, BaseSerialInterface* serial) : AbstractUITask(board, serial), _display(NULL), _sensors(NULL) { + _next_refresh = 0; + ui_started_at = 0; + } + void begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* node_prefs); + + bool hasDisplay() const { return _display != NULL; } + void clearMsgPreview(); + + // from AbstractUITask + void msgRead(int msgcount) override; + void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; + void soundBuzzer(UIEventType bet = UIEventType::none) override; + void loop() override; + + void shutdown(bool restart = false); +}; diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index c65bbec5..4968d357 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -86,6 +86,7 @@ lib_deps = extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display @@ -94,7 +95,8 @@ build_flags = build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -117,8 +119,8 @@ build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + - +<../examples/companion_radio> - +<../examples/companion_radio/ui-new> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/nano_g2_ultra/platformio.ini b/variants/nano_g2_ultra/platformio.ini index 511813a8..7fb2de28 100644 --- a/variants/nano_g2_ultra/platformio.ini +++ b/variants/nano_g2_ultra/platformio.ini @@ -34,6 +34,7 @@ extends = Nano_G2_Ultra build_flags = ${Nano_G2_Ultra.build_flags} -I src/helpers/ui + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -48,7 +49,8 @@ build_src_filter = ${Nano_G2_Ultra.build_src_filter} + + + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Nano_G2_Ultra.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -62,6 +64,7 @@ extends = Nano_G2_Ultra build_flags = ${Nano_G2_Ultra.build_flags} -I src/helpers/ui + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D OFFLINE_QUEUE_SIZE=256 @@ -72,7 +75,9 @@ build_flags = build_src_filter = ${Nano_G2_Ultra.build_src_filter} + + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Nano_G2_Ultra.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 0ec88070..6b77bdcf 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -87,15 +87,17 @@ lib_deps = ${Faketec.lib_deps} [env:Faketec_companion_radio_usb] extends = Faketec build_flags = ${Faketec.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SSD1306Display ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${Faketec.build_src_filter} - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Faketec.lib_deps} adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 @@ -103,6 +105,7 @@ lib_deps = ${Faketec.lib_deps} [env:Faketec_companion_radio_ble] extends = Faketec build_flags = ${Faketec.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -113,9 +116,10 @@ build_flags = ${Faketec.build_flags} -D MESH_DEBUG=1 build_src_filter = ${Faketec.build_src_filter} + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Faketec.lib_deps} adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index 00974208..2811e243 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -37,6 +37,7 @@ upload_protocol = nrfutil [env:t1000e_companion_radio_ble] extends = t1000-e build_flags = ${t1000-e.build_flags} + -I examples/companion_radio/ui-orig -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -53,6 +54,7 @@ build_src_filter = ${t1000-e.build_src_filter} + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> lib_deps = ${t1000-e.lib_deps} densaugeo/base64 @ ~1.4.0 stevemarple/MicroNMEA @ ^2.0.6 diff --git a/variants/t114/platformio.ini b/variants/t114/platformio.ini index e08da3ac..e48ee121 100644 --- a/variants/t114/platformio.ini +++ b/variants/t114/platformio.ini @@ -83,8 +83,8 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_t114.build_src_filter} + - +<../examples/companion_radio> - +<../examples/companion_radio/ui-new> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Heltec_t114.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -102,8 +102,8 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_t114.build_src_filter} + - +<../examples/companion_radio> - +<../examples/companion_radio/ui-new> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Heltec_t114.lib_deps} densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/techo/platformio.ini b/variants/techo/platformio.ini index 45eba9fc..2c82f38a 100644 --- a/variants/techo/platformio.ini +++ b/variants/techo/platformio.ini @@ -61,6 +61,7 @@ extends = LilyGo_Techo build_flags = ${LilyGo_Techo.build_flags} -I src/helpers/ui + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -74,7 +75,8 @@ build_src_filter = ${LilyGo_Techo.build_src_filter} + + + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_Techo.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/thinknode_m1/platformio.ini b/variants/thinknode_m1/platformio.ini index 972444ba..fa8cd55b 100644 --- a/variants/thinknode_m1/platformio.ini +++ b/variants/thinknode_m1/platformio.ini @@ -68,6 +68,7 @@ extends = ThinkNode_M1 build_flags = ${ThinkNode_M1.build_flags} -I src/helpers/ui + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -84,7 +85,8 @@ build_src_filter = ${ThinkNode_M1.build_src_filter} + + + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${ThinkNode_M1.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index cefe9649..0cb5a1ad 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -73,6 +73,7 @@ lib_deps = ${WioTrackerL1.lib_deps} [env:WioTrackerL1_companion_radio_ble] extends = WioTrackerL1 build_flags = ${WioTrackerL1.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -84,9 +85,10 @@ build_flags = ${WioTrackerL1.build_flags} -D PIN_BUZZER=12 build_src_filter = ${WioTrackerL1.build_src_filter} + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${WioTrackerL1.lib_deps} adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 From 37d7257f04c21f51044b68b62c5ec6fb9bfae4b4 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 16 Aug 2025 21:53:36 +1000 Subject: [PATCH 11/46] * Heltec tracker, now using /ui-new * variants with no DISPLAY_CLASS .ini fixes --- variants/heltec_ct62/platformio.ini | 4 ++-- variants/heltec_mesh_solar/platformio.ini | 4 ++-- variants/heltec_tracker/platformio.ini | 5 ++++- variants/heltec_tracker/target.cpp | 1 + variants/heltec_tracker/target.h | 2 ++ variants/lilygo_tbeam_SX1262/platformio.ini | 2 +- variants/lilygo_tbeam_SX1276/platformio.ini | 2 +- variants/lilygo_tbeam_supreme_SX1262/platformio.ini | 2 +- variants/lilygo_tlora_c6/platformio.ini | 2 +- variants/lilygo_tlora_v2_1/platformio.ini | 4 ++-- variants/meshadventurer/platformio.ini | 8 ++++---- variants/picow/platformio.ini | 6 +++--- variants/sensecap_solar/platformio.ini | 4 ++-- variants/waveshare_rp2040_lora/platformio.ini | 6 +++--- variants/xiao_c3/platformio.ini | 4 ++-- variants/xiao_c6/platformio.ini | 4 ++-- 16 files changed, 33 insertions(+), 27 deletions(-) diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index ec24cdcd..9721d037 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -61,7 +61,7 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_ct62.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Heltec_ct62.lib_deps} ${esp32_ota.lib_deps} @@ -80,7 +80,7 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_ct62.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + lib_deps = ${Heltec_ct62.lib_deps} diff --git a/variants/heltec_mesh_solar/platformio.ini b/variants/heltec_mesh_solar/platformio.ini index 5cd8af86..9fd3edd5 100644 --- a/variants/heltec_mesh_solar/platformio.ini +++ b/variants/heltec_mesh_solar/platformio.ini @@ -68,7 +68,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_mesh_solar.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Heltec_mesh_solar.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -85,7 +85,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_mesh_solar.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Heltec_mesh_solar.lib_deps} densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index 8d9013a5..357ab854 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -39,6 +39,7 @@ extends = Heltec_tracker_base build_flags = ${Heltec_tracker_base.build_flags} -I src/helpers/ui + -I examples/companion_radio/ui-new -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for Serial -D DISPLAY_ROTATION=1 -D DISPLAY_CLASS=ST7735Display @@ -51,7 +52,9 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_tracker_base.build_src_filter} + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + lib_deps = ${Heltec_tracker_base.lib_deps} diff --git a/variants/heltec_tracker/target.cpp b/variants/heltec_tracker/target.cpp index f41702c5..5ba9a8fb 100644 --- a/variants/heltec_tracker/target.cpp +++ b/variants/heltec_tracker/target.cpp @@ -21,6 +21,7 @@ HWTSensorManager sensors = HWTSensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display(&board.periph_power); // peripheral power pin is shared + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/heltec_tracker/target.h b/variants/heltec_tracker/target.h index c08be80a..8ac5eb72 100644 --- a/variants/heltec_tracker/target.h +++ b/variants/heltec_tracker/target.h @@ -10,6 +10,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif class HWTSensorManager : public SensorManager { @@ -36,6 +37,7 @@ extern HWTSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index eac899f0..a0432b00 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -49,7 +49,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${LilyGo_TBeam_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index 8febe7b3..21c164dd 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -46,7 +46,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${LilyGo_TBeam_SX1276.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 5c2d6e86..1e8bacd8 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -82,7 +82,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${T_Beam_S3_Supreme_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tlora_c6/platformio.ini b/variants/lilygo_tlora_c6/platformio.ini index 7f89c3da..5aab2213 100644 --- a/variants/lilygo_tlora_c6/platformio.ini +++ b/variants/lilygo_tlora_c6/platformio.ini @@ -79,7 +79,7 @@ build_flags = ${tlora_c6.build_flags} build_src_filter = ${tlora_c6.build_src_filter} + - - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${tlora_c6.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 71b49aab..dfd83b5e 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -89,7 +89,7 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -108,7 +108,7 @@ build_flags = build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/meshadventurer/platformio.ini b/variants/meshadventurer/platformio.ini index f8995e18..60cc55eb 100644 --- a/variants/meshadventurer/platformio.ini +++ b/variants/meshadventurer/platformio.ini @@ -78,7 +78,7 @@ lib_deps = [env:Meshadventurer_sx1262_companion_radio_usb] extends = Meshadventurer build_src_filter = ${Meshadventurer.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + build_flags = ${Meshadventurer.build_flags} @@ -96,7 +96,7 @@ lib_deps = [env:Meshadventurer_sx1262_companion_radio_ble] extends = Meshadventurer build_src_filter = ${Meshadventurer.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + + build_flags = @@ -157,7 +157,7 @@ lib_deps = [env:Meshadventurer_sx1268_companion_radio_usb] extends = Meshadventurer build_src_filter = ${Meshadventurer.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + build_flags = ${Meshadventurer.build_flags} @@ -175,7 +175,7 @@ lib_deps = [env:Meshadventurer_sx1268_companion_radio_ble] extends = Meshadventurer build_src_filter = ${Meshadventurer.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + + build_flags = diff --git a/variants/picow/platformio.ini b/variants/picow/platformio.ini index 8b6c2506..7b75c224 100644 --- a/variants/picow/platformio.ini +++ b/variants/picow/platformio.ini @@ -52,7 +52,7 @@ build_flags = ${picow.build_flags} ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${picow.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${picow.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -66,7 +66,7 @@ lib_deps = ${picow.lib_deps} ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${picow.build_src_filter} -; +<../examples/companion_radio> +; +<../examples/companion_radio/*.cpp> ; lib_deps = ${picow.lib_deps} ; densaugeo/base64 @ ~1.4.0 @@ -81,7 +81,7 @@ lib_deps = ${picow.lib_deps} ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${picow.build_src_filter} -; +<../examples/companion_radio> +; +<../examples/companion_radio/*.cpp> ; lib_deps = ${picow.lib_deps} ; densaugeo/base64 @ ~1.4.0 diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index 9626e9cd..bbac5d99 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -85,7 +85,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${SenseCap_Solar.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${SenseCap_Solar.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -100,7 +100,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${SenseCap_Solar.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${SenseCap_Solar.lib_deps} densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/waveshare_rp2040_lora/platformio.ini b/variants/waveshare_rp2040_lora/platformio.ini index 2730734d..933c7661 100644 --- a/variants/waveshare_rp2040_lora/platformio.ini +++ b/variants/waveshare_rp2040_lora/platformio.ini @@ -61,7 +61,7 @@ build_flags = ${waveshare_rp2040_lora.build_flags} ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${waveshare_rp2040_lora.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${waveshare_rp2040_lora.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -75,7 +75,7 @@ lib_deps = ${waveshare_rp2040_lora.lib_deps} ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${waveshare_rp2040_lora.build_src_filter} -; +<../examples/companion_radio> +; +<../examples/companion_radio/*.cpp> ; lib_deps = ${waveshare_rp2040_lora.lib_deps} ; densaugeo/base64 @ ~1.4.0 @@ -90,7 +90,7 @@ lib_deps = ${waveshare_rp2040_lora.lib_deps} ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${waveshare_rp2040_lora.build_src_filter} -; +<../examples/companion_radio> +; +<../examples/companion_radio/*.cpp> ; lib_deps = ${waveshare_rp2040_lora.lib_deps} ; densaugeo/base64 @ ~1.4.0 diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index 59f198e0..659313db 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -66,7 +66,7 @@ lib_deps = [env:Xiao_C3_companion_radio_ble] extends = Xiao_esp32_C3 build_src_filter = ${Xiao_esp32_C3.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + build_flags = ${Xiao_esp32_C3.build_flags} @@ -89,7 +89,7 @@ lib_deps = [env:Xiao_C3_companion_radio_usb] extends = Xiao_esp32_C3 build_src_filter = ${Xiao_esp32_C3.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + build_flags = ${Xiao_esp32_C3.build_flags} diff --git a/variants/xiao_c6/platformio.ini b/variants/xiao_c6/platformio.ini index 95dede08..fdf0f337 100644 --- a/variants/xiao_c6/platformio.ini +++ b/variants/xiao_c6/platformio.ini @@ -62,7 +62,7 @@ build_flags = ${Xiao_C6.build_flags} build_src_filter = ${Xiao_C6.build_src_filter} + - - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Xiao_C6.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -117,7 +117,7 @@ build_flags = ${Meshimi.build_flags} build_src_filter = ${Meshimi.build_src_filter} + - - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Meshimi.lib_deps} densaugeo/base64 @ ~1.4.0 From 022bfc4f4bd9ea3807889ee4e092d4a89828ae82 Mon Sep 17 00:00:00 2001 From: kelsey hudson Date: Sat, 16 Aug 2025 15:09:42 -0700 Subject: [PATCH 12/46] Ikoka Stick: Use new UI, make repeater & room server work * Enable the new UI on the BLE and USB Companion roles. * Fix compilation issues with the repeater and room server roles. * Remove ESP32-related alternate pinout cruft from the NRF build tree. * build.sh: add build-matching-firmwares command to allow e.g. building all roles for a given variant by passing the variant name. --- build.sh | 11 +++++++++++ variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h | 4 ++-- variants/ikoka_stick_nrf/platformio.ini | 13 +++++++++---- variants/ikoka_stick_nrf/target.cpp | 1 + variants/ikoka_stick_nrf/target.h | 2 ++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/build.sh b/build.sh index 095a1633..47fec4a3 100755 --- a/build.sh +++ b/build.sh @@ -3,6 +3,7 @@ # usage # sh build.sh build-firmware RAK_4631_Repeater # sh build.sh build-firmwares +# sh build.sh build-matching-firmwares RAK_4631 # sh build.sh build-companion-firmwares # sh build.sh build-repeater-firmwares # sh build.sh build-room-server-firmwares @@ -144,6 +145,16 @@ mkdir -p out if [[ $1 == "build-firmware" ]]; then if [ "$2" ]; then build_firmware $2 + else + echo "usage: $0 build-firmware " + exit 1 + fi +elif [[ $1 == "build-matching-firmwares" ]]; then + if [ "$2" ]; then + build_all_firmwares_matching $2 + else + echo "usage: $0 build-matching-firmwares " + exit 1 fi elif [[ $1 == "build-firmwares" ]]; then build_firmwares diff --git a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h b/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h index a5f33b01..1bd8b31d 100644 --- a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h +++ b/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h @@ -29,10 +29,10 @@ public: #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { - digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { - digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif diff --git a/variants/ikoka_stick_nrf/platformio.ini b/variants/ikoka_stick_nrf/platformio.ini index b883e8f7..e78bc058 100644 --- a/variants/ikoka_stick_nrf/platformio.ini +++ b/variants/ikoka_stick_nrf/platformio.ini @@ -55,6 +55,7 @@ build_flags = ${nrf52840_xiao.build_flags} build_src_filter = ${nrf52840_xiao.build_src_filter} + + + + + +<../variants/ikoka_stick_nrf> debug_tool = jlink @@ -68,12 +69,14 @@ build_flags = -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 + -I examples/companion_radio/ui-new ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ikoka_stick_nrf.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${ikoka_stick_nrf.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -84,11 +87,13 @@ build_flags = ${ikoka_stick_nrf.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 + -I examples/companion_radio/ui-new ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ikoka_stick_nrf.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${ikoka_stick_nrf.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -106,7 +111,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${ikoka_stick_nrf.build_src_filter} + - +<../examples/simple_repeater> + +<../examples/simple_repeater/*.cpp> [env:ikoka_stick_nrf_room_server] extends = ikoka_stick_nrf @@ -119,4 +124,4 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ikoka_stick_nrf.build_src_filter} - +<../examples/simple_room_server> + +<../examples/simple_room_server/*.cpp> diff --git a/variants/ikoka_stick_nrf/target.cpp b/variants/ikoka_stick_nrf/target.cpp index e50150eb..c2712761 100644 --- a/variants/ikoka_stick_nrf/target.cpp +++ b/variants/ikoka_stick_nrf/target.cpp @@ -6,6 +6,7 @@ ikoka_stick_nrf_board board; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); diff --git a/variants/ikoka_stick_nrf/target.h b/variants/ikoka_stick_nrf/target.h index 758cd019..8311503a 100644 --- a/variants/ikoka_stick_nrf/target.h +++ b/variants/ikoka_stick_nrf/target.h @@ -11,7 +11,9 @@ #ifdef DISPLAY_CLASS #include + #include extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif extern ikoka_stick_nrf_board board; From c30a103bafcd43163d99a060c1178645e83a352b Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 15:50:25 +1000 Subject: [PATCH 13/46] * WSL3 fixes. Heltec V2 ui-new --- variants/heltec_v2/platformio.ini | 8 ++++++-- variants/heltec_v3/platformio.ini | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index 04768df4..ea41f845 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -78,6 +78,7 @@ lib_deps = extends = Heltec_lora32_v2 build_flags = ${Heltec_lora32_v2.build_flags} + -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -87,7 +88,8 @@ build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -96,6 +98,7 @@ lib_deps = extends = Heltec_lora32_v2 build_flags = ${Heltec_lora32_v2.build_flags} + -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -108,7 +111,8 @@ build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 4968d357..b9193431 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -214,7 +214,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -228,7 +228,7 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 From 8f8830047b2651bef38d1e7ecf0983e38d733693 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 16:06:26 +1000 Subject: [PATCH 14/46] * T3S3 variants, ui-new --- variants/generic_espnow/platformio.ini | 2 +- variants/lilygo_t3s3/platformio.ini | 10 ++++++++-- variants/lilygo_t3s3/target.cpp | 1 + variants/lilygo_t3s3/target.h | 2 ++ variants/lilygo_t3s3_sx1276/platformio.ini | 10 ++++++++-- variants/lilygo_t3s3_sx1276/target.cpp | 1 + variants/lilygo_t3s3_sx1276/target.h | 2 ++ 7 files changed, 23 insertions(+), 5 deletions(-) diff --git a/variants/generic_espnow/platformio.ini b/variants/generic_espnow/platformio.ini index dbc902f0..cf3e4c94 100644 --- a/variants/generic_espnow/platformio.ini +++ b/variants/generic_espnow/platformio.ini @@ -60,7 +60,7 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 ; NOTE: DO NOT ENABLE --> -D ESPNOW_DEBUG_LOGGING=1 build_src_filter = ${Generic_ESPNOW.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Generic_ESPNOW.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index f3a95e96..722a3243 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -89,6 +89,7 @@ lib_deps = extends = LilyGo_T3S3_sx1262 build_flags = ${LilyGo_T3S3_sx1262.build_flags} + -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -96,7 +97,9 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -105,6 +108,7 @@ lib_deps = extends = LilyGo_T3S3_sx1262 build_flags = ${LilyGo_T3S3_sx1262.build_flags} + -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -116,7 +120,9 @@ build_flags = build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_t3s3/target.cpp b/variants/lilygo_t3s3/target.cpp index b7c4542c..1c7b3b09 100644 --- a/variants/lilygo_t3s3/target.cpp +++ b/variants/lilygo_t3s3/target.cpp @@ -13,6 +13,7 @@ SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif #ifndef LORA_CR diff --git a/variants/lilygo_t3s3/target.h b/variants/lilygo_t3s3/target.h index b768b2b0..f184c757 100644 --- a/variants/lilygo_t3s3/target.h +++ b/variants/lilygo_t3s3/target.h @@ -9,6 +9,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif extern ESP32Board board; @@ -18,6 +19,7 @@ extern SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); diff --git a/variants/lilygo_t3s3_sx1276/platformio.ini b/variants/lilygo_t3s3_sx1276/platformio.ini index 74eced9c..8f1c00e4 100644 --- a/variants/lilygo_t3s3_sx1276/platformio.ini +++ b/variants/lilygo_t3s3_sx1276/platformio.ini @@ -88,6 +88,7 @@ extends = LilyGo_T3S3_sx1276 upload_speed = 115200 build_flags = ${LilyGo_T3S3_sx1276.build_flags} + -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -95,7 +96,9 @@ build_flags = -D MESH_DEBUG=1 build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_T3S3_sx1276.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -104,6 +107,7 @@ lib_deps = extends = LilyGo_T3S3_sx1276 build_flags = ${LilyGo_T3S3_sx1276.build_flags} + -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SSD1306Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -115,7 +119,9 @@ build_flags = build_src_filter = ${LilyGo_T3S3_sx1276.build_src_filter} + + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_T3S3_sx1276.lib_deps} densaugeo/base64 @ ~1.4.0 \ No newline at end of file diff --git a/variants/lilygo_t3s3_sx1276/target.cpp b/variants/lilygo_t3s3_sx1276/target.cpp index b2ee4455..042ff206 100644 --- a/variants/lilygo_t3s3_sx1276/target.cpp +++ b/variants/lilygo_t3s3_sx1276/target.cpp @@ -18,6 +18,7 @@ SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/lilygo_t3s3_sx1276/target.h b/variants/lilygo_t3s3_sx1276/target.h index 52ecf867..98a0fe35 100644 --- a/variants/lilygo_t3s3_sx1276/target.h +++ b/variants/lilygo_t3s3_sx1276/target.h @@ -9,6 +9,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif extern ESP32Board board; @@ -18,6 +19,7 @@ extern SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); From 2477d60fae3414db51e27c53f784a4101af529fe Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 16:18:39 +1000 Subject: [PATCH 15/46] * TBeam variants: ui-new --- variants/lilygo_tbeam_SX1262/platformio.ini | 3 +++ variants/lilygo_tbeam_SX1262/target.cpp | 1 + variants/lilygo_tbeam_SX1262/target.h | 2 ++ variants/lilygo_tbeam_SX1276/platformio.ini | 3 +++ variants/lilygo_tbeam_SX1276/target.cpp | 1 + variants/lilygo_tbeam_SX1276/target.h | 2 ++ variants/lilygo_tbeam_supreme_SX1262/platformio.ini | 3 +++ variants/lilygo_tbeam_supreme_SX1262/target.cpp | 1 + variants/lilygo_tbeam_supreme_SX1262/target.h | 2 ++ 9 files changed, 18 insertions(+) diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index a0432b00..7bb008ac 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -37,6 +37,7 @@ extends = LilyGo_TBeam_SX1262 board_build.upload.maximum_ram_size=2000000 build_flags = ${LilyGo_TBeam_SX1262.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -49,7 +50,9 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_TBeam_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tbeam_SX1262/target.cpp b/variants/lilygo_tbeam_SX1262/target.cpp index fb05958b..a8caecb3 100644 --- a/variants/lilygo_tbeam_SX1262/target.cpp +++ b/variants/lilygo_tbeam_SX1262/target.cpp @@ -25,6 +25,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/lilygo_tbeam_SX1262/target.h b/variants/lilygo_tbeam_SX1262/target.h index cb9b28df..5f33abb8 100644 --- a/variants/lilygo_tbeam_SX1262/target.h +++ b/variants/lilygo_tbeam_SX1262/target.h @@ -9,6 +9,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif extern TBeamBoard board; @@ -18,6 +19,7 @@ extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index 21c164dd..e0391f1d 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -36,6 +36,7 @@ extends = LilyGo_TBeam_SX1276 board_build.upload.maximum_ram_size=2000000 build_flags = ${LilyGo_TBeam_SX1276.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -46,7 +47,9 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_TBeam_SX1276.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tbeam_SX1276/target.cpp b/variants/lilygo_tbeam_SX1276/target.cpp index 7e2537bb..0a7517a2 100644 --- a/variants/lilygo_tbeam_SX1276/target.cpp +++ b/variants/lilygo_tbeam_SX1276/target.cpp @@ -25,6 +25,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/lilygo_tbeam_SX1276/target.h b/variants/lilygo_tbeam_SX1276/target.h index bcd8cb0b..b382b652 100644 --- a/variants/lilygo_tbeam_SX1276/target.h +++ b/variants/lilygo_tbeam_SX1276/target.h @@ -9,6 +9,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif extern TBeamBoard board; @@ -18,6 +19,7 @@ extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 1e8bacd8..9b10f459 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -73,6 +73,7 @@ lib_deps = extends = T_Beam_S3_Supreme_SX1262 build_flags = ${T_Beam_S3_Supreme_SX1262.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -82,7 +83,9 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${T_Beam_S3_Supreme_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index 68d54396..8ad306f1 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -5,6 +5,7 @@ TBeamBoard board; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif static SPIClass spi; diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.h b/variants/lilygo_tbeam_supreme_SX1262/target.h index 62a92329..c6ffa0a6 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.h +++ b/variants/lilygo_tbeam_supreme_SX1262/target.h @@ -11,6 +11,8 @@ #ifdef DISPLAY_CLASS #include extern DISPLAY_CLASS display; + #include + extern MomentaryButton user_btn; #endif extern TBeamBoard board; From 276a0576934f2bf0359fb5ef4cb9a329404da073 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 16:31:50 +1000 Subject: [PATCH 16/46] * lilygo_tlora_v2_1: ui-new --- examples/companion_radio/AbstractUITask.h | 2 +- variants/lilygo_tlora_v2_1/platformio.ini | 6 ++++++ variants/lilygo_tlora_v2_1/target.cpp | 1 + variants/lilygo_tlora_v2_1/target.h | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h index 706254af..1277bba9 100644 --- a/examples/companion_radio/AbstractUITask.h +++ b/examples/companion_radio/AbstractUITask.h @@ -42,5 +42,5 @@ public: virtual void msgRead(int msgcount) = 0; virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0; virtual void soundBuzzer(UIEventType bet = UIEventType::none) = 0; - virtual void loop(); + virtual void loop() = 0; }; diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index dfd83b5e..0ed06856 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -83,13 +83,16 @@ lib_deps = extends = LilyGo_TLora_V2_1_1_6 build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -98,6 +101,7 @@ lib_deps = extends = LilyGo_TLora_V2_1_1_6 build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -108,7 +112,9 @@ build_flags = build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_tlora_v2_1/target.cpp b/variants/lilygo_tlora_v2_1/target.cpp index 5e8f15b2..65a78c19 100644 --- a/variants/lilygo_tlora_v2_1/target.cpp +++ b/variants/lilygo_tlora_v2_1/target.cpp @@ -14,6 +14,7 @@ EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/lilygo_tlora_v2_1/target.h b/variants/lilygo_tlora_v2_1/target.h index f05b8055..380d733b 100644 --- a/variants/lilygo_tlora_v2_1/target.h +++ b/variants/lilygo_tlora_v2_1/target.h @@ -10,6 +10,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif extern LilyGoTLoraBoard board; @@ -19,6 +20,7 @@ extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); From f5ad1df1034c7353caa251b24f1ae2968837e9bc Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 16:35:39 +1000 Subject: [PATCH 17/46] * Minewsemi: ui-orig --- variants/minewsemi_me25ls01/platformio.ini | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/variants/minewsemi_me25ls01/platformio.ini b/variants/minewsemi_me25ls01/platformio.ini index f7265af4..3436062f 100644 --- a/variants/minewsemi_me25ls01/platformio.ini +++ b/variants/minewsemi_me25ls01/platformio.ini @@ -51,6 +51,7 @@ lib_deps = ${nrf52840_me25ls01.lib_deps} [env:Minewsemi_me25ls01_companion_radio_ble] extends = me25ls01 build_flags = ${me25ls01.build_flags} + -I examples/companion_radio/ui-orig -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -66,6 +67,7 @@ build_flags = ${me25ls01.build_flags} build_src_filter = ${me25ls01.build_src_filter} + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> lib_deps = ${me25ls01.lib_deps} adafruit/RTClib @ ^2.1.3 @@ -146,6 +148,7 @@ lib_deps = ${me25ls01.lib_deps} [env:Minewsemi_me25ls01_companion_radio_usb] extends = me25ls01 build_flags = ${me25ls01.build_flags} + -I examples/companion_radio/ui-orig -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 ;-D BLE_PIN_CODE=123456 @@ -158,7 +161,8 @@ build_flags = ${me25ls01.build_flags} -D DISPLAY_CLASS=NullDisplayDriver build_src_filter = ${me25ls01.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> lib_deps = ${me25ls01.lib_deps} adafruit/RTClib @ ^2.1.3 From fe376e8c35fd8ff19667f36190ee5b6d791426a3 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 16:49:08 +1000 Subject: [PATCH 18/46] * RAK_4631: ui-new --- src/helpers/nrf52/SerialBLEInterface.cpp | 4 ++++ variants/rak4631/platformio.ini | 10 ++++++++-- variants/rak4631/target.cpp | 1 + variants/rak4631/target.h | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index 8049f5c0..170a7331 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -116,10 +116,14 @@ void SerialBLEInterface::disable() { _isEnabled = false; BLE_DEBUG_PRINTLN("SerialBLEInterface::disable"); +#ifdef RAK_BOARD + Bluefruit.disconnect(Bluefruit.connHandle()); +#else uint16_t conn_id; if (Bluefruit.getConnectedHandles(&conn_id, 1) > 0) { Bluefruit.disconnect(conn_id); } +#endif Bluefruit.Advertising.restartOnDisconnect(false); Bluefruit.Advertising.stop(); diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 8fb4d1bb..f0dea837 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -64,6 +64,7 @@ build_src_filter = ${rak4631.build_src_filter} extends = rak4631 build_flags = ${rak4631.build_flags} + -I examples/companion_radio/ui-new -D PIN_USER_BTN=9 -D PIN_USER_BTN_ANA=31 -D DISPLAY_CLASS=SSD1306Display @@ -73,7 +74,9 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${rak4631.build_src_filter} + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -82,6 +85,7 @@ lib_deps = extends = rak4631 build_flags = ${rak4631.build_flags} + -I examples/companion_radio/ui-new -D PIN_USER_BTN=9 -D PIN_USER_BTN_ANA=31 -D DISPLAY_CLASS=SSD1306Display @@ -95,7 +99,9 @@ build_flags = build_src_filter = ${rak4631.build_src_filter} + + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${rak4631.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index 4e9d3cce..71d4e88c 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -6,6 +6,7 @@ RAK4631Board board; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index c4c88183..5e93b7fc 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -11,6 +11,8 @@ #ifdef DISPLAY_CLASS #include extern DISPLAY_CLASS display; + #include + extern MomentaryButton user_btn; #endif extern RAK4631Board board; From b332b06304787c2048c65a2aeeb9b7d965ebdec0 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 16 Aug 2025 18:13:50 +0200 Subject: [PATCH 19/46] techo_ui: some tweaks --- examples/companion_radio/ui-new/UITask.cpp | 10 +++++++--- src/helpers/ui/GxEPDDisplay.cpp | 4 ++-- variants/techo/platformio.ini | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 64b788b2..9f34d913 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -14,6 +14,10 @@ #define LONG_PRESS_MILLIS 1200 +#ifndef UI_RECENT_LIST_SIZE + #define UI_RECENT_LIST_SIZE 4 +#endif + #define PRESS_LABEL "long press" #include "icons.h" @@ -79,7 +83,7 @@ class HomeScreen : public UIScreen { NodePrefs* _node_prefs; uint8_t _page; bool _shutdown_init; - AdvertPath recent[4]; + AdvertPath recent[UI_RECENT_LIST_SIZE]; void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) { // Convert millivolts to percentage @@ -156,10 +160,10 @@ public: display.drawTextCentered(display.width() / 2, 43, tmp); } } else if (_page == HomePage::RECENT) { - the_mesh.getRecentlyHeard(recent, 4); + the_mesh.getRecentlyHeard(recent, UI_RECENT_LIST_SIZE); display.setColor(DisplayDriver::GREEN); int y = 20; - for (int i = 0; i < 4; i++, y += 11) { + for (int i = 0; i < UI_RECENT_LIST_SIZE; i++, y += 11) { auto a = &recent[i]; if (a->name[0] == 0) continue; // empty slot display.setCursor(0, y); diff --git a/src/helpers/ui/GxEPDDisplay.cpp b/src/helpers/ui/GxEPDDisplay.cpp index c3d75bbd..ace25460 100644 --- a/src/helpers/ui/GxEPDDisplay.cpp +++ b/src/helpers/ui/GxEPDDisplay.cpp @@ -29,8 +29,8 @@ void GxEPDDisplay::turnOn() { if (!_init) begin(); #if DISP_BACKLIGHT digitalWrite(DISP_BACKLIGHT, HIGH); - _isOn = true; #endif + _isOn = true; } void GxEPDDisplay::turnOff() { @@ -132,7 +132,7 @@ uint16_t GxEPDDisplay::getTextWidth(const char* str) { int16_t x1, y1; uint16_t w, h; display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h); - return w / SCALE_X; + return ceil((w + 1) / SCALE_X); } void GxEPDDisplay::endFrame() { diff --git a/variants/techo/platformio.ini b/variants/techo/platformio.ini index 2c82f38a..2e6ad3b0 100644 --- a/variants/techo/platformio.ini +++ b/variants/techo/platformio.ini @@ -68,6 +68,7 @@ build_flags = -D BLE_DEBUG_LOGGING=1 -D DISPLAY_CLASS=GxEPDDisplay -D OFFLINE_QUEUE_SIZE=256 + -D UI_RECENT_LIST_SIZE=9 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_Techo.build_src_filter} From 76a53bf84d034927455d369699ea189dd6d9705f Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 17:23:28 +1000 Subject: [PATCH 20/46] * Wio-e5-mini: ui-orig * WioTrackerL1: ui-new --- variants/wio-e5-mini/platformio.ini | 2 ++ variants/wio-tracker-l1/platformio.ini | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/variants/wio-e5-mini/platformio.ini b/variants/wio-e5-mini/platformio.ini index 93508d8e..3d98d93e 100644 --- a/variants/wio-e5-mini/platformio.ini +++ b/variants/wio-e5-mini/platformio.ini @@ -37,11 +37,13 @@ build_src_filter = ${lora_e5_mini.build_src_filter} [env:wio-e5-mini_companion_radio_usb] extends = lora_e5_mini build_flags = ${lora_e5_mini.build_flags} + -I examples/companion_radio/ui-orig -D LORA_TX_POWER=22 -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=NullDisplayDriver build_src_filter = ${lora_e5_mini.build_src_filter} +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> lib_deps = ${lora_e5_mini.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index 0cb5a1ad..ddf7cf47 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -56,13 +56,16 @@ lib_deps = ${WioTrackerL1.lib_deps} [env:WioTrackerL1_companion_radio_usb] extends = WioTrackerL1 build_flags = ${WioTrackerL1.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D DISPLAY_CLASS=SH1106Display ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${WioTrackerL1.build_src_filter} - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + + lib_deps = ${WioTrackerL1.lib_deps} From 7613b9455d93176a575efe1cc4f9e62f5c42aa3b Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 17:36:35 +1000 Subject: [PATCH 21/46] * Xiao_nRF and Xiao_rp2040 build fixes --- variants/xiao_nrf52/platformio.ini | 4 ++-- variants/xiao_rp2040/platformio.ini | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index bba3e632..fd4c362b 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -68,7 +68,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_nrf52.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Xiao_nrf52.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -83,7 +83,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_nrf52.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Xiao_nrf52.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/xiao_rp2040/platformio.ini b/variants/xiao_rp2040/platformio.ini index 960fdbba..619350ec 100644 --- a/variants/xiao_rp2040/platformio.ini +++ b/variants/xiao_rp2040/platformio.ini @@ -58,7 +58,7 @@ build_flags = ${Xiao_rp2040.build_flags} ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${Xiao_rp2040.build_src_filter} - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Xiao_rp2040.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -72,7 +72,7 @@ lib_deps = ${Xiao_rp2040.lib_deps} ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Xiao_rp2040.build_src_filter} -; +<../examples/companion_radio> +; +<../examples/companion_radio/*.cpp> ; lib_deps = ${Xiao_rp2040.lib_deps} ; densaugeo/base64 @ ~1.4.0 @@ -87,7 +87,7 @@ lib_deps = ${Xiao_rp2040.lib_deps} ; ; -D MESH_PACKET_LOGGING=1 ; ; -D MESH_DEBUG=1 ; build_src_filter = ${Xiao_rp2040.build_src_filter} -; +<../examples/companion_radio> +; +<../examples/companion_radio/*.cpp> ; lib_deps = ${Xiao_rp2040.lib_deps} ; densaugeo/base64 @ ~1.4.0 From e6152f9d6c4fdd30ba0d50ec083f5d72f3beee7e Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 17 Aug 2025 17:52:05 +1000 Subject: [PATCH 22/46] * Xiao_s3_wio: ui-new --- variants/xiao_s3_wio/platformio.ini | 7 +++++-- variants/xiao_s3_wio/target.cpp | 1 + variants/xiao_s3_wio/target.h | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index 4d6fed88..fe4670b5 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -79,6 +79,7 @@ lib_deps = extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} + -I examples/companion_radio/ui-new -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -90,7 +91,9 @@ build_flags = build_src_filter = ${Xiao_S3_WIO.build_src_filter} + + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Xiao_S3_WIO.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -108,7 +111,7 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Xiao_S3_WIO.build_src_filter} + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> lib_deps = ${Xiao_S3_WIO.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/xiao_s3_wio/target.cpp b/variants/xiao_s3_wio/target.cpp index 4b2b059f..ed8584ff 100644 --- a/variants/xiao_s3_wio/target.cpp +++ b/variants/xiao_s3_wio/target.cpp @@ -18,6 +18,7 @@ SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/xiao_s3_wio/target.h b/variants/xiao_s3_wio/target.h index b768b2b0..f184c757 100644 --- a/variants/xiao_s3_wio/target.h +++ b/variants/xiao_s3_wio/target.h @@ -9,6 +9,7 @@ #include #ifdef DISPLAY_CLASS #include + #include #endif extern ESP32Board board; @@ -18,6 +19,7 @@ extern SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); From 8f1ccb65aeadf62b6f9fddd2eb29edf64e1a73c9 Mon Sep 17 00:00:00 2001 From: taco Date: Sun, 17 Aug 2025 18:10:58 +1000 Subject: [PATCH 23/46] WioTrackerL1: add poweroff support --- variants/wio-tracker-l1/WioTrackerL1Board.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/variants/wio-tracker-l1/WioTrackerL1Board.h b/variants/wio-tracker-l1/WioTrackerL1Board.h index 03aef79c..f04b673f 100644 --- a/variants/wio-tracker-l1/WioTrackerL1Board.h +++ b/variants/wio-tracker-l1/WioTrackerL1Board.h @@ -38,5 +38,9 @@ public: NVIC_SystemReset(); } + void powerOff() override { + sd_power_system_off(); + } + bool startOTAUpdate(const char* id, char reply[]) override; }; From cb8ca91d27ece5bbfc9116ca3d6cf26e7896b8d8 Mon Sep 17 00:00:00 2001 From: taco Date: Sun, 17 Aug 2025 19:01:28 +1000 Subject: [PATCH 24/46] WioTrackerL1: add joystick left and right for new UI --- examples/companion_radio/ui-new/UITask.cpp | 14 ++++++++++++++ variants/wio-tracker-l1/target.cpp | 2 ++ variants/wio-tracker-l1/target.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 9f34d913..f3ad28c6 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -494,6 +494,20 @@ void UITask::loop() { c = handleLongPress(KEY_ENTER); } #endif +#if defined(WIO_TRACKER_L1) + ev = joystick_left.check(); + if (ev == BUTTON_EVENT_CLICK) { + c = checkDisplayOn(KEY_LEFT); + } else if (ev == BUTTON_EVENT_LONG_PRESS) { + c = handleLongPress(KEY_LEFT); + } + ev = joystick_right.check(); + if (ev == BUTTON_EVENT_CLICK) { + c = checkDisplayOn(KEY_RIGHT); + } else if (ev == BUTTON_EVENT_LONG_PRESS) { + c = handleLongPress(KEY_RIGHT); + } +#endif if (c != 0 && curr) { curr->handleInput(c); diff --git a/variants/wio-tracker-l1/target.cpp b/variants/wio-tracker-l1/target.cpp index dc484546..349d73b4 100644 --- a/variants/wio-tracker-l1/target.cpp +++ b/variants/wio-tracker-l1/target.cpp @@ -17,6 +17,8 @@ WioTrackerL1SensorManager sensors = WioTrackerL1SensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); + MomentaryButton joystick_left(JOYSTICK_LEFT, 1000, true); + MomentaryButton joystick_right(JOYSTICK_RIGHT, 1000, true); #endif bool radio_init() { diff --git a/variants/wio-tracker-l1/target.h b/variants/wio-tracker-l1/target.h index d5fe4d70..6f5da7c6 100644 --- a/variants/wio-tracker-l1/target.h +++ b/variants/wio-tracker-l1/target.h @@ -40,6 +40,8 @@ extern WioTrackerL1SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; extern MomentaryButton user_btn; + extern MomentaryButton joystick_left; + extern MomentaryButton joystick_right; #endif bool radio_init(); From a4916f81eb9c241964d3bb31a601786f605a0b59 Mon Sep 17 00:00:00 2001 From: recrof Date: Sun, 17 Aug 2025 11:57:38 +0200 Subject: [PATCH 25/46] station g2: switch to new_ui --- variants/station_g2/platformio.ini | 12 ++++++++---- variants/station_g2/target.cpp | 5 +---- variants/station_g2/target.h | 2 ++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index 5d9a6823..3ed73f43 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -22,6 +22,7 @@ build_flags = build_src_filter = ${esp32_base.build_src_filter} +<../variants/station_g2> + + + lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit SH110X @ ~2.1.13 @@ -65,14 +66,16 @@ lib_deps = extends = Station_G2 build_flags = ${Station_G2.build_flags} + -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SH1106Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${Station_G2.build_src_filter} - + - +<../examples/companion_radio> + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Station_G2.lib_deps} densaugeo/base64 @ ~1.4.0 @@ -81,6 +84,7 @@ lib_deps = extends = Station_G2 build_flags = ${Station_G2.build_flags} + -I examples/companion_radio/ui-new -D DISPLAY_CLASS=SH1106Display -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 @@ -91,8 +95,8 @@ build_flags = ; -D MESH_DEBUG=1 build_src_filter = ${Station_G2.build_src_filter} + - + - +<../examples/companion_radio> + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${Station_G2.lib_deps} densaugeo/base64 @ ~1.4.0 diff --git a/variants/station_g2/target.cpp b/variants/station_g2/target.cpp index 2b19f5f0..5423af68 100644 --- a/variants/station_g2/target.cpp +++ b/variants/station_g2/target.cpp @@ -18,10 +18,7 @@ SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; -#endif - -#ifndef LORA_CR - #define LORA_CR 5 + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif bool radio_init() { diff --git a/variants/station_g2/target.h b/variants/station_g2/target.h index 6d80f098..3f67af3a 100644 --- a/variants/station_g2/target.h +++ b/variants/station_g2/target.h @@ -10,6 +10,7 @@ #ifdef DISPLAY_CLASS #include + #include #endif extern StationG2Board board; @@ -19,6 +20,7 @@ extern SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; #endif bool radio_init(); From b8f80afee95137b76251e2f4e225d3cf60eb8b7f Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 17 Aug 2025 16:28:02 +0200 Subject: [PATCH 26/46] t1000: wait for button release before powering off --- variants/t1000-e/T1000eBoard.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/variants/t1000-e/T1000eBoard.h b/variants/t1000-e/T1000eBoard.h index 24584757..f87d71e0 100644 --- a/variants/t1000-e/T1000eBoard.h +++ b/variants/t1000-e/T1000eBoard.h @@ -84,12 +84,21 @@ public: digitalWrite(PIN_3V3_EN, LOW); #endif + // set led on and wait for button release before poweroff + #ifdef LED_PIN + digitalWrite(LED_PIN, HIGH); + #endif + #ifdef BUTTON_PIN + while(digitalRead(BUTTON_PIN)); + #endif #ifdef LED_PIN digitalWrite(LED_PIN, LOW); #endif + #ifdef BUTTON_PIN nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH); #endif + sd_power_system_off(); } From d145d5936dcfd19fda2b72de51daaa69cf6463bd Mon Sep 17 00:00:00 2001 From: recrof Date: Mon, 18 Aug 2025 12:53:44 +0200 Subject: [PATCH 27/46] fix RAK build errors --- variants/rak4631/platformio.ini | 6 ++---- variants/rak4631/target.cpp | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index f0dea837..c9091878 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -22,6 +22,8 @@ build_flags = ${nrf52_base.build_flags} build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> + + + + + lib_deps = ${nrf52_base.lib_deps} ${sensor_base.lib_deps} @@ -73,8 +75,6 @@ build_flags = ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${rak4631.build_src_filter} - + - + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> lib_deps = @@ -97,9 +97,7 @@ build_flags = ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${rak4631.build_src_filter} - + + - + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> lib_deps = diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index 71d4e88c..618c9fc5 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -4,6 +4,10 @@ RAK4631Board board; +#ifndef PIN_USER_BTN + #define PIN_USER_BTN (-1) +#endif + #ifdef DISPLAY_CLASS DISPLAY_CLASS display; MomentaryButton user_btn(PIN_USER_BTN, 1000, true); From ff03b041d0f0dafe3e976152812151d43e11d73f Mon Sep 17 00:00:00 2001 From: Florent Date: Tue, 19 Aug 2025 09:03:34 +0200 Subject: [PATCH 28/46] techo_ui: implement poweroff + led fixes --- src/helpers/nrf52/TechoBoard.h | 19 +++++++++++++++++++ variants/techo/platformio.ini | 1 + variants/techo/variant.cpp | 2 ++ variants/techo/variant.h | 12 ++++-------- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/helpers/nrf52/TechoBoard.h b/src/helpers/nrf52/TechoBoard.h index c8ef7006..2c05c4ed 100644 --- a/src/helpers/nrf52/TechoBoard.h +++ b/src/helpers/nrf52/TechoBoard.h @@ -43,6 +43,25 @@ public: return "LilyGo T-Echo"; } + void powerOff() override { + #ifdef LED_RED + digitalWrite(LED_RED, LOW); + #endif + #ifdef LED_GREEN + digitalWrite(LED_GREEN, LOW); + #endif + #ifdef LED_BLUE + digitalWrite(LED_BLUE, LOW); + #endif + #ifdef DISP_BACKLIGHT + digitalWrite(DISP_BACKLIGHT, LOW); + #endif + #ifdef PIN_PWR_EN + digitalWrite(PIN_PWR_EN, LOW); + #endif + sd_power_system_off(); + } + void reboot() override { NVIC_SystemReset(); } diff --git a/variants/techo/platformio.ini b/variants/techo/platformio.ini index 2e6ad3b0..76712178 100644 --- a/variants/techo/platformio.ini +++ b/variants/techo/platformio.ini @@ -21,6 +21,7 @@ build_flags = ${nrf52840_techo.build_flags} -D LORA_TX_POWER=22 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 + -D P_LORA_TX_LED=LED_GREEN build_src_filter = ${nrf52840_techo.build_src_filter} + + diff --git a/variants/techo/variant.cpp b/variants/techo/variant.cpp index ad1fd560..0bad7829 100644 --- a/variants/techo/variant.cpp +++ b/variants/techo/variant.cpp @@ -24,6 +24,8 @@ void initVariant() { pinMode(LED_GREEN, OUTPUT); pinMode(LED_BLUE, OUTPUT); digitalWrite(LED_BLUE, HIGH); + digitalWrite(LED_GREEN, HIGH); + digitalWrite(LED_RED, HIGH); pinMode(PIN_TXCO, OUTPUT); digitalWrite(PIN_TXCO, HIGH); diff --git a/variants/techo/variant.h b/variants/techo/variant.h index 6aebf82f..da8d81d4 100644 --- a/variants/techo/variant.h +++ b/variants/techo/variant.h @@ -61,19 +61,15 @@ //////////////////////////////////////////////////////////////////////////////// // Builtin LEDs -#define LED_RED (34) -#define LED_GREEN (33) +#define LED_RED (13) #define LED_BLUE (14) +#define LED_GREEN (15) -#define PIN_STATUS_LED LED_GREEN -#define LED_BUILTIN LED_GREEN -#define PIN_LED LED_BUILTIN +//#define PIN_STATUS_LED LED_BLUE +#define LED_BUILTIN (-1) #define LED_PIN LED_BUILTIN #define LED_STATE_ON LOW -#define PIN_NEOPIXEL (14) -#define NEOPIXEL_NUM (2) - //////////////////////////////////////////////////////////////////////////////// // Builtin buttons From a9d4cf1d21784fb1d63b134be739e287ebc432da Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 19 Aug 2025 23:14:11 +1000 Subject: [PATCH 29/46] * various repeaters: fix for missing MomentaryButton module --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 02190102..2e350f81 100644 --- a/platformio.ini +++ b/platformio.ini @@ -47,6 +47,7 @@ build_src_filter = +<*.cpp> + + + + ; ----------------- ESP32 --------------------- From 5dc930410cb6517f5e8b8d55a99fc1e3845977ed Mon Sep 17 00:00:00 2001 From: recrof Date: Wed, 20 Aug 2025 13:44:41 +0200 Subject: [PATCH 30/46] set default flood advert interval to 12 hours --- examples/simple_repeater/main.cpp | 8 ++++---- examples/simple_room_server/main.cpp | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 7ad0e64a..622a0554 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -159,7 +159,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { } void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr) { - #if MAX_NEIGHBOURS // check if neighbours enabled + #if MAX_NEIGHBOURS // check if neighbours enabled // find existing neighbour, else use least recently updated uint32_t oldest_timestamp = 0xFFFFFFFF; NeighbourInfo* neighbour = &neighbours[0]; @@ -589,7 +589,7 @@ public: _prefs.cr = LORA_CR; _prefs.tx_power_dbm = LORA_TX_POWER; _prefs.advert_interval = 1; // default to 2 minutes for NEW installs - _prefs.flood_advert_interval = 3; // 3 hours + _prefs.flood_advert_interval = 12; // 12 hours _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled } @@ -611,8 +611,8 @@ public: const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; } const char* getRole() override { return FIRMWARE_ROLE; } const char* getNodeName() { return _prefs.node_name; } - NodePrefs* getNodePrefs() { - return &_prefs; + NodePrefs* getNodePrefs() { + return &_prefs; } void savePrefs() override { diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 34b94ad2..712d02a5 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -298,7 +298,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { // uint32_t now = getRTCClock()->getCurrentTimeUnique(); // memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp memcpy(reply_data, &sender_timestamp, 4); // reflect sender_timestamp back in response packet (kind of like a 'tag') - + switch (payload[0]) { case REQ_TYPE_GET_STATUS: { ServerStats stats; @@ -746,9 +746,9 @@ public: _prefs.tx_power_dbm = LORA_TX_POWER; _prefs.disable_fwd = 1; _prefs.advert_interval = 1; // default to 2 minutes for NEW installs - _prefs.flood_advert_interval = 3; // 3 hours + _prefs.flood_advert_interval = 12; // 12 hours _prefs.flood_max = 64; - _prefs.interference_threshold = 0; // disabled + _prefs.interference_threshold = 0; // disabled #ifdef ROOM_PASSWORD StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif @@ -778,8 +778,8 @@ public: const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; } const char* getRole() override { return FIRMWARE_ROLE; } const char* getNodeName() { return _prefs.node_name; } - NodePrefs* getNodePrefs() { - return &_prefs; + NodePrefs* getNodePrefs() { + return &_prefs; } void savePrefs() override { From c63653659922a0f27b02e5cbd08218546ffb8e84 Mon Sep 17 00:00:00 2001 From: Alex Wolden Date: Wed, 20 Aug 2025 22:01:38 -0700 Subject: [PATCH 31/46] Add INA226 to rak --- platformio.ini | 2 ++ .../sensors/EnvironmentSensorManager.cpp | 26 +++++++++++++++++++ .../sensors/EnvironmentSensorManager.h | 1 + 3 files changed, 29 insertions(+) diff --git a/platformio.ini b/platformio.ini index 2e350f81..e935d77e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -115,12 +115,14 @@ build_flags = -D ENV_INCLUDE_LPS22HB=1 -D ENV_INCLUDE_INA3221=1 -D ENV_INCLUDE_INA219=1 + -D ENV_INCLUDE_INA226=1 -D ENV_INCLUDE_INA260=1 -D ENV_INCLUDE_MLX90614=1 -D ENV_INCLUDE_VL53L0X=1 lib_deps = adafruit/Adafruit INA3221 Library @ ^1.0.1 adafruit/Adafruit INA219 @ ^1.2.3 + robtillaart/INA226 @ ^0.6.4 adafruit/Adafruit INA260 Library @ ^1.5.3 adafruit/Adafruit AHTX0 @ ^2.0.5 adafruit/Adafruit BME280 Library @ ^2.3.0 diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 0f3289b8..1a14349d 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -65,6 +65,12 @@ static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS); static Adafruit_INA260 INA260; #endif +#if ENV_INCLUDE_INA226 +#define TELEM_INA226_ADDRESS 0x44 +#include +static INA226 INA226(TELEM_INA226_ADDRESS); +#endif + #if ENV_INCLUDE_MLX90614 #define TELEM_MLX90614_ADDRESS 0x5A // MLX90614 IR temperature sensor I2C address #include @@ -202,6 +208,17 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_INA226 + if (INA226.begin()) { + MESH_DEBUG_PRINTLN("Found INA226 at address: %02X", TELEM_INA226_ADDRESS); + INA226.setMaxCurrentShunt(1.0, 0.002); // 1A max, 2 milliohm shunt + INA226_initialized = true; + } else { + INA226_initialized = false; + MESH_DEBUG_PRINTLN("INA226 was not found at I2C address %02X", TELEM_INA226_ADDRESS); + } + #endif + #if ENV_INCLUDE_MLX90614 if (MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found MLX90614 at address: %02X", TELEM_MLX90614_ADDRESS); @@ -323,6 +340,15 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif + #if ENV_INCLUDE_INA226 + if (INA226_initialized) { + telemetry.addVoltage(next_available_channel, INA226.getBusVoltage()); + telemetry.addCurrent(next_available_channel, INA226.getCurrent_mA() / 1000.0); + telemetry.addPower(next_available_channel, INA226.getPower_mW() / 1000.0); + next_available_channel++; + } + #endif + #if ENV_INCLUDE_MLX90614 if (MLX90614_initialized) { telemetry.addTemperature(TELEM_CHANNEL_SELF, MLX90614.readObjectTempC()); diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index bb0fd2b9..3302d6f6 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -14,6 +14,7 @@ protected: bool INA3221_initialized = false; bool INA219_initialized = false; bool INA260_initialized = false; + bool INA226_initialized = false; bool SHTC3_initialized = false; bool LPS22HB_initialized = false; bool MLX90614_initialized = false; From 0c37eafd01e7041ae0cc8b32d6d5fcbdc39243d4 Mon Sep 17 00:00:00 2001 From: Alex Wolden Date: Thu, 21 Aug 2025 13:21:37 -0700 Subject: [PATCH 32/46] Fixed shunt value for ina226 --- src/helpers/sensors/EnvironmentSensorManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 1a14349d..f444b67b 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -67,6 +67,8 @@ static Adafruit_INA260 INA260; #if ENV_INCLUDE_INA226 #define TELEM_INA226_ADDRESS 0x44 +#define TELEM_INA226_SHUNT_VALUE 0.100 +#define TELEM_INA226_MAX_AMP 0.8 #include static INA226 INA226(TELEM_INA226_ADDRESS); #endif @@ -211,7 +213,7 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_INA226 if (INA226.begin()) { MESH_DEBUG_PRINTLN("Found INA226 at address: %02X", TELEM_INA226_ADDRESS); - INA226.setMaxCurrentShunt(1.0, 0.002); // 1A max, 2 milliohm shunt + INA226.setMaxCurrentShunt(TELEM_INA226_MAX_AMP, TELEM_INA226_SHUNT_VALUE); INA226_initialized = true; } else { INA226_initialized = false; From cb96503b92e31e88617d8e7dd58c5b4f3e6f5c9e Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 23 Aug 2025 14:45:18 +1000 Subject: [PATCH 33/46] * Companion: new-ui, power off now labelled "hibernate" --- examples/companion_radio/ui-new/UITask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index f3ad28c6..1920dd26 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -213,10 +213,10 @@ public: display.setColor(DisplayDriver::GREEN); display.setTextSize(1); if (_shutdown_init) { - display.drawTextCentered(display.width() / 2, 34, "shutting down..."); + display.drawTextCentered(display.width() / 2, 34, "hibernating..."); } else { display.drawXbm((display.width() - 32) / 2, 18, power_icon, 32, 32); - display.drawTextCentered(display.width() / 2, 64 - 11, "off: " PRESS_LABEL); + display.drawTextCentered(display.width() / 2, 64 - 11, "hibernate: " PRESS_LABEL); } } return 5000; // next render after 5000 ms From dedef4931581b58bb3cf70971476d18eac4750eb Mon Sep 17 00:00:00 2001 From: kelsey hudson Date: Fri, 22 Aug 2025 23:29:28 -0700 Subject: [PATCH 34/46] Ikoka Stick: Adding variant support for Ebyte S22 modules The Ebyte S22 module comes in three variants, 22dBm, 30dBm, and 33dBm. Each requires a different TX power max setting to avoid frying the PA frontend. Breaks out variants and roles by module type. --- .../ikoka_stick_nrf/ikoka_stick_nrf_board.h | 18 +- variants/ikoka_stick_nrf/platformio.ini | 228 ++++++++++++++++-- 2 files changed, 206 insertions(+), 40 deletions(-) diff --git a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h b/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h index 1bd8b31d..c66f4827 100644 --- a/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h +++ b/variants/ikoka_stick_nrf/ikoka_stick_nrf_board.h @@ -5,20 +5,6 @@ #ifdef XIAO_NRF52 -// redefine lora pins if using the S3 variant of SX1262 board -#ifdef SX1262_XIAO_S3_VARIANT - #undef P_LORA_DIO_1 - #undef P_LORA_BUSY - #undef P_LORA_RESET - #undef P_LORA_NSS - #undef SX126X_RXEN - #define P_LORA_DIO_1 D0 - #define P_LORA_BUSY D1 - #define P_LORA_RESET D2 - #define P_LORA_NSS D3 - #define SX126X_RXEN D4 -#endif - class ikoka_stick_nrf_board : public mesh::MainBoard { protected: uint8_t startup_reason; @@ -29,10 +15,10 @@ public: #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { - digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on + digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on } void onAfterTransmit() override { - digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off + digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off } #endif diff --git a/variants/ikoka_stick_nrf/platformio.ini b/variants/ikoka_stick_nrf/platformio.ini index e78bc058..6e6ae101 100644 --- a/variants/ikoka_stick_nrf/platformio.ini +++ b/variants/ikoka_stick_nrf/platformio.ini @@ -19,11 +19,10 @@ lib_deps = adafruit/Adafruit INA3221 Library @ ^1.0.1 adafruit/Adafruit INA219 @ ^1.2.3 adafruit/Adafruit AHTX0 @ ^2.0.5 - adafruit/Adafruit BME280 Library @ ^2.3.0 + adafruit/Adafruit BME280 Library @ ^2.3.0 adafruit/Adafruit SSD1306 @ ^2.5.13 - -[ikoka_stick_nrf] +[ikoka_stick_nrf_baseboard] extends = nrf52840_xiao ;board_build.ldscript = boards/nrf52840_s140_v7.ld build_flags = ${nrf52840_xiao.build_flags} @@ -34,7 +33,6 @@ build_flags = ${nrf52840_xiao.build_flags} -D DISPLAY_ROTATION=2 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper - -D LORA_TX_POWER=9 -D P_LORA_DIO_1=D1 -D P_LORA_RESET=D2 -D P_LORA_BUSY=D3 @@ -52,19 +50,59 @@ build_flags = ${nrf52840_xiao.build_flags} -D ENV_INCLUDE_BME280=1 -D ENV_INCLUDE_INA3221=1 -D ENV_INCLUDE_INA219=1 +debug_tool = jlink +upload_protocol = nrfutil + + +;;; abstracted hardware variants + +[ikoka_stick_nrf_e22_22dbm] +extends = ikoka_stick_nrf_baseboard +; No PA in this model, full 22dBm +build_flags = + ${ikoka_stick_nrf_baseboard.build_flags} + -D LORA_TX_POWER=22 build_src_filter = ${nrf52840_xiao.build_src_filter} + + + + +<../variants/ikoka_stick_nrf> -debug_tool = jlink -upload_protocol = nrfutil -[env:ikoka_stick_nrf_companion_radio_ble] -extends = ikoka_stick_nrf +[ikoka_stick_nrf_e22_30dbm] +extends = ikoka_stick_nrf_baseboard +; limit txpower to 20dBm on E22-900M30S. Anything higher will +; cause distortion in the PA output. 20dBm in -> 30dBm out build_flags = - ${ikoka_stick_nrf.build_flags} + ${ikoka_stick_nrf_baseboard.build_flags} + -D LORA_TX_POWER=20 +build_src_filter = ${nrf52840_xiao.build_src_filter} + + + + + + + + + +<../variants/ikoka_stick_nrf> + +[ikoka_stick_nrf_e22_33dbm] +extends = ikoka_stick_nrf_baseboard +; limit txpower to 9dBm on E22-900M33S to avoid hardware damage +; to the rf amplifier frontend. 9dBm in -> 33dBm out +build_flags = + ${ikoka_stick_nrf_baseboard.build_flags} + -D LORA_TX_POWER=9 +build_src_filter = ${nrf52840_xiao.build_src_filter} + + + + + + + + + +<../variants/ikoka_stick_nrf> + +;;; abstracted firmware roles + +[ikoka_stick_nrf_companion_radio_ble] +extends = ikoka_stick_nrf_baseboard +build_flags = + ${ikoka_stick_nrf_baseboard.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 @@ -73,35 +111,35 @@ build_flags = ; -D BLE_DEBUG_LOGGING=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${ikoka_stick_nrf.build_src_filter} +build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter} + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> lib_deps = - ${ikoka_stick_nrf.lib_deps} + ${ikoka_stick_nrf_baseboard.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:ikoka_stick_nrf_companion_radio_usb] -extends = ikoka_stick_nrf +[ikoka_stick_nrf_companion_radio_usb] +extends = ikoka_stick_nrf_baseboard build_flags = - ${ikoka_stick_nrf.build_flags} + ${ikoka_stick_nrf_baseboard.build_flags} -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -I examples/companion_radio/ui-new ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${ikoka_stick_nrf.build_src_filter} +build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter} + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-new/*.cpp> lib_deps = - ${ikoka_stick_nrf.lib_deps} + ${ikoka_stick_nrf_baseboard.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:ikoka_stick_nrf_repeater] -extends = ikoka_stick_nrf +[ikoka_stick_nrf_repeater] +extends = ikoka_stick_nrf_baseboard build_flags = - ${ikoka_stick_nrf.build_flags} + ${ikoka_stick_nrf_baseboard.build_flags} -D ADVERT_NAME='"Ikoka Stick Repeater"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 @@ -109,19 +147,161 @@ build_flags = -D MAX_NEIGHBOURS=8 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${ikoka_stick_nrf.build_src_filter} +build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter} + +<../examples/simple_repeater/*.cpp> -[env:ikoka_stick_nrf_room_server] -extends = ikoka_stick_nrf +[ikoka_stick_nrf_room_server] +extends = ikoka_stick_nrf_baseboard build_flags = - ${ikoka_stick_nrf.build_flags} + ${ikoka_stick_nrf_baseboard.build_flags} -D ADVERT_NAME='"Ikoka Stick Room"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 -D ADMIN_PASSWORD='"password"' ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${ikoka_stick_nrf.build_src_filter} +build_src_filter = ${ikoka_stick_nrf_baseboard.build_src_filter} +<../examples/simple_room_server/*.cpp> + +;;; hardware + firmware variants + +;;; 22dBm EBYTE E22-900M22 variants + +[env:ikoka_stick_nrf_22dbm_companion_radio_usb] +extends = + ikoka_stick_nrf_e22_22dbm + ikoka_stick_nrf_companion_radio_usb +build_flags = + ${ikoka_stick_nrf_companion_radio_usb.build_flags} + ${ikoka_stick_nrf_e22_22dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_companion_radio_usb.build_src_filter} + ${ikoka_stick_nrf_e22_22dbm.build_src_filter} + +[env:ikoka_stick_nrf_22dbm_companion_radio_ble] +extends = + ikoka_stick_nrf_e22_22dbm + ikoka_stick_nrf_companion_radio_ble +build_flags = + ${ikoka_stick_nrf_companion_radio_ble.build_flags} + ${ikoka_stick_nrf_e22_22dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_companion_radio_ble.build_src_filter} + ${ikoka_stick_nrf_e22_22dbm.build_src_filter} + +[env:ikoka_stick_nrf_22dbm_repeater] +extends = + ikoka_stick_nrf_e22_22dbm + ikoka_stick_nrf_repeater +build_flags = + ${ikoka_stick_nrf_repeater.build_flags} + ${ikoka_stick_nrf_e22_22dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_repeater.build_src_filter} + ${ikoka_stick_nrf_e22_22dbm.build_src_filter} + +[env:ikoka_stick_nrf_22dbm_room_server] +extends = + ikoka_stick_nrf_e22_22dbm + ikoka_stick_nrf_room_server +build_flags = + ${ikoka_stick_nrf_room_server.build_flags} + ${ikoka_stick_nrf_e22_22dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_room_server.build_src_filter} + ${ikoka_stick_nrf_e22_22dbm.build_src_filter} + + +;;; 30dBm EBYTE E22-900M30 variants + +[env:ikoka_stick_nrf_30dbm_companion_radio_usb] +extends = + ikoka_stick_nrf_e22_30dbm + ikoka_stick_nrf_companion_radio_usb +build_flags = + ${ikoka_stick_nrf_companion_radio_usb.build_flags} + ${ikoka_stick_nrf_e22_30dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_companion_radio_usb.build_src_filter} + ${ikoka_stick_nrf_e22_30dbm.build_src_filter} + +[env:ikoka_stick_nrf_30dbm_companion_radio_ble] +extends = + ikoka_stick_nrf_e22_30dbm + ikoka_stick_nrf_companion_radio_ble +build_flags = + ${ikoka_stick_nrf_companion_radio_ble.build_flags} + ${ikoka_stick_nrf_e22_30dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_companion_radio_ble.build_src_filter} + ${ikoka_stick_nrf_e22_30dbm.build_src_filter} + +[env:ikoka_stick_nrf_30dbm_repeater] +extends = + ikoka_stick_nrf_e22_30dbm + ikoka_stick_nrf_repeater +build_flags = + ${ikoka_stick_nrf_repeater.build_flags} + ${ikoka_stick_nrf_e22_30dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_repeater.build_src_filter} + ${ikoka_stick_nrf_e22_30dbm.build_src_filter} + +[env:ikoka_stick_nrf_30dbm_room_server] +extends = + ikoka_stick_nrf_e22_30dbm + ikoka_stick_nrf_room_server +build_flags = + ${ikoka_stick_nrf_room_server.build_flags} + ${ikoka_stick_nrf_e22_30dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_room_server.build_src_filter} + ${ikoka_stick_nrf_e22_30dbm.build_src_filter} + + +;;; 33dBm EBYTE E22-900M33 variants + +[env:ikoka_stick_nrf_33dbm_companion_radio_usb] +extends = + ikoka_stick_nrf_e22_33dbm + ikoka_stick_nrf_companion_radio_usb +build_flags = + ${ikoka_stick_nrf_companion_radio_usb.build_flags} + ${ikoka_stick_nrf_e22_33dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_companion_radio_usb.build_src_filter} + ${ikoka_stick_nrf_e22_33dbm.build_src_filter} + +[env:ikoka_stick_nrf_33dbm_companion_radio_ble] +extends = + ikoka_stick_nrf_e22_33dbm + ikoka_stick_nrf_companion_radio_ble +build_flags = + ${ikoka_stick_nrf_companion_radio_ble.build_flags} + ${ikoka_stick_nrf_e22_33dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_companion_radio_ble.build_src_filter} + ${ikoka_stick_nrf_e22_33dbm.build_src_filter} + +[env:ikoka_stick_nrf_33dbm_repeater] +extends = + ikoka_stick_nrf_e22_33dbm + ikoka_stick_nrf_repeater +build_flags = + ${ikoka_stick_nrf_repeater.build_flags} + ${ikoka_stick_nrf_e22_33dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_repeater.build_src_filter} + ${ikoka_stick_nrf_e22_33dbm.build_src_filter} + +[env:ikoka_stick_nrf_33dbm_room_server] +extends = + ikoka_stick_nrf_e22_33dbm + ikoka_stick_nrf_room_server +build_flags = + ${ikoka_stick_nrf_room_server.build_flags} + ${ikoka_stick_nrf_e22_33dbm.build_flags} +build_src_filter = + ${ikoka_stick_nrf_room_server.build_src_filter} + ${ikoka_stick_nrf_e22_33dbm.build_src_filter} From 685f75234b3669c6d07ae6ff5f659e36b11edc14 Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 24 Aug 2025 13:06:19 +0200 Subject: [PATCH 35/46] let users compile their projects against meshcore as a lib --- build_as_lib.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ library.json | 16 +++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 build_as_lib.py create mode 100644 library.json diff --git a/build_as_lib.py b/build_as_lib.py new file mode 100644 index 00000000..93593cdb --- /dev/null +++ b/build_as_lib.py @@ -0,0 +1,53 @@ +from os.path import join, realpath, exists +import shutil, os + +Import("env") + +platform_stm32=False +platform_esp32=False +platform_nrf52=False +platform_rp2040=False + +src_filter = [] +src_filter.append("+<*>") +src_filter.append("-") # don't build with ui for now ... + +for item in env.get("CPPDEFINES", []): + if isinstance(item, str) and item == "STM32_PLATFORM": + # add STM32 specific sources + env.Append(CPPPATH=[realpath("src/helpers/stm32")]) + platform_stm32=True + env.Append(BUILD_FLAGS=["-I src/helpers/stm32"]) + elif isinstance(item, str) and item == "ESP32": + platform_esp32=True + env.Append(CPPPATH=[realpath("src/helpers/esp32")]) + env.Append(BUILD_FLAGS=["-I src/helpers/esp32"]) + elif isinstance(item, str) and item == "WIO_E5_DEV_VARIANT": + env.Append(BUILD_FLAGS=["-I variants/wio-e5-dev"]) + src_filter.append("+<../variants/wio-e5-dev>") + elif isinstance(item, str) and item == "RAK_3X72_VARIANT": + env.Append(BUILD_FLAGS=["-I variants/rak3x72"]) + src_filter.append("+<../variants/rak3x72>") + elif isinstance(item, str) and item == "XIAO_S3_WIO_VARIANT": + env.Append(BUILD_FLAGS=["-I variants/xiao_s3_wio"]) + src_filter.append("+<../variants/xiao_s3_wio>") + elif isinstance(item, str) and item == "XIAO_C6_VARIANT": + env.Append(BUILD_FLAGS=["-I variants/xiao_c6"]) + src_filter.append("+<../variants/xiao_c6>") + elif isinstance(item, str) and item == "GENERIC_ESPNOW_VARIANT": + env.Append(BUILD_FLAGS=["-I variants/generic_espnow"]) + src_filter.append("+<../variants/generic_espnow>") + src_filter.append("+") + +if not platform_stm32: + src_filter.append("-") +if not platform_esp32: + src_filter.append("-") +if not platform_nrf52: + src_filter.append("-") +if not platform_rp2040: + src_filter.append("-") + +env.Replace(SRC_FILTER=src_filter) + +print (env.Dump()) diff --git a/library.json b/library.json new file mode 100644 index 00000000..982983a3 --- /dev/null +++ b/library.json @@ -0,0 +1,16 @@ +{ + "name": "MeshCore", + "version" : "1.7.4", + "dependencies": { + "SPI": "*", + "Wire": "*", + "jgromes/RadioLib": "^7.1.2", + "rweather/Crypto": "^0.4.0", + "adafruit/RTClib": "^2.1.3", + "melopero/Melopero RV3028": "^1.1.0", + "electroniccats/CayenneLPP": "1.4.0" + }, + "build": { + "extraScript": "build_as_lib.py" + } +} \ No newline at end of file From 228bac0add4f3cf175fee950a7e9b76ff006a7b6 Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 24 Aug 2025 13:09:55 +0200 Subject: [PATCH 36/46] simplify src_filter creation --- build_as_lib.py | 67 ++++++++++++++++--------------------------------- 1 file changed, 22 insertions(+), 45 deletions(-) diff --git a/build_as_lib.py b/build_as_lib.py index 93593cdb..f3410625 100644 --- a/build_as_lib.py +++ b/build_as_lib.py @@ -1,53 +1,30 @@ -from os.path import join, realpath, exists -import shutil, os +from os.path import realpath -Import("env") +Import("env") # type: ignore +menv=env # type: ignore -platform_stm32=False -platform_esp32=False -platform_nrf52=False -platform_rp2040=False +src_filter = [ + '+<*.cpp>', + '+', + '+', + '+', +] -src_filter = [] -src_filter.append("+<*>") -src_filter.append("-") # don't build with ui for now ... - -for item in env.get("CPPDEFINES", []): +for item in menv.get("CPPDEFINES", []): if isinstance(item, str) and item == "STM32_PLATFORM": # add STM32 specific sources - env.Append(CPPPATH=[realpath("src/helpers/stm32")]) - platform_stm32=True - env.Append(BUILD_FLAGS=["-I src/helpers/stm32"]) + menv.Append(CPPPATH=[realpath("src/helpers/stm32")]) + menv.Append(BUILD_FLAGS=["-I src/helpers/stm32"]) + src_filter.append("+") elif isinstance(item, str) and item == "ESP32": - platform_esp32=True - env.Append(CPPPATH=[realpath("src/helpers/esp32")]) - env.Append(BUILD_FLAGS=["-I src/helpers/esp32"]) - elif isinstance(item, str) and item == "WIO_E5_DEV_VARIANT": - env.Append(BUILD_FLAGS=["-I variants/wio-e5-dev"]) - src_filter.append("+<../variants/wio-e5-dev>") - elif isinstance(item, str) and item == "RAK_3X72_VARIANT": - env.Append(BUILD_FLAGS=["-I variants/rak3x72"]) - src_filter.append("+<../variants/rak3x72>") - elif isinstance(item, str) and item == "XIAO_S3_WIO_VARIANT": - env.Append(BUILD_FLAGS=["-I variants/xiao_s3_wio"]) - src_filter.append("+<../variants/xiao_s3_wio>") - elif isinstance(item, str) and item == "XIAO_C6_VARIANT": - env.Append(BUILD_FLAGS=["-I variants/xiao_c6"]) - src_filter.append("+<../variants/xiao_c6>") - elif isinstance(item, str) and item == "GENERIC_ESPNOW_VARIANT": - env.Append(BUILD_FLAGS=["-I variants/generic_espnow"]) - src_filter.append("+<../variants/generic_espnow>") - src_filter.append("+") + menv.Append(CPPPATH=[realpath("src/helpers/esp32")]) + menv.Append(BUILD_FLAGS=["-I src/helpers/esp32"]) + src_filter.append("+") + elif isinstance(item, tuple) and item[0] == "MC_VARIANT": + variant_name = item[1] + menv.Append(BUILD_FLAGS=[f"-I variants/{variant_name}"]) + src_filter.append(f"+<../variants/{variant_name}>") -if not platform_stm32: - src_filter.append("-") -if not platform_esp32: - src_filter.append("-") -if not platform_nrf52: - src_filter.append("-") -if not platform_rp2040: - src_filter.append("-") +menv.Replace(SRC_FILTER=src_filter) -env.Replace(SRC_FILTER=src_filter) - -print (env.Dump()) +#print (menv.Dump()) From b88a360adab85851cfab4bdcc732ee82f7b521be Mon Sep 17 00:00:00 2001 From: Florent Date: Sun, 24 Aug 2025 14:47:03 +0200 Subject: [PATCH 37/46] lib_build: add nrf52 and RP2040 --- build_as_lib.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/build_as_lib.py b/build_as_lib.py index f3410625..da4337c0 100644 --- a/build_as_lib.py +++ b/build_as_lib.py @@ -6,20 +6,39 @@ menv=env # type: ignore src_filter = [ '+<*.cpp>', '+', + '+' '+', '+', ] +# add build and include dirs according to CPPDEFINES for item in menv.get("CPPDEFINES", []): + + # STM32 if isinstance(item, str) and item == "STM32_PLATFORM": - # add STM32 specific sources menv.Append(CPPPATH=[realpath("src/helpers/stm32")]) menv.Append(BUILD_FLAGS=["-I src/helpers/stm32"]) src_filter.append("+") + + # ESP32 elif isinstance(item, str) and item == "ESP32": menv.Append(CPPPATH=[realpath("src/helpers/esp32")]) menv.Append(BUILD_FLAGS=["-I src/helpers/esp32"]) src_filter.append("+") + + # NRF52 + elif isinstance(item, str) and item == "NRF52_PLATFORM": + menv.Append(CPPPATH=[realpath("src/helpers/nrf52")]) + menv.Append(BUILD_FLAGS=["-I src/helpers/nrf52"]) + src_filter.append("+") + + # RP2040 + elif isinstance(item, str) and item == "RP2040_PLATFORM": + menv.Append(CPPPATH=[realpath("src/helpers/rp2040")]) + menv.Append(BUILD_FLAGS=["-I src/helpers/rp2040"]) + src_filter.append("+") + + # VARIANTS HANDLING elif isinstance(item, tuple) and item[0] == "MC_VARIANT": variant_name = item[1] menv.Append(BUILD_FLAGS=[f"-I variants/{variant_name}"]) From bbee057b03ac74dfbbd8f77a6c7f98504efd4a77 Mon Sep 17 00:00:00 2001 From: kelsey hudson Date: Sun, 24 Aug 2025 15:54:05 -0700 Subject: [PATCH 38/46] Station G2: Adds repeater variant with packet logging on Adds a variant to Station G2 which turns on mesh packet logging to serial. Useful for telemetry logging. Sites such as map.w0z.is depend on this functionality. --- variants/station_g2/platformio.ini | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index 3ed73f43..756ff5f3 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -45,6 +45,25 @@ lib_deps = ${Station_G2.lib_deps} ${esp32_ota.lib_deps} +[env:Station_G2_logging_repeater] +extends = Station_G2 +build_flags = + ${Station_G2.build_flags} + -D ADVERT_NAME='"Station G2 Logging Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 + -D MESH_PACKET_LOGGING=1 + -D SX126X_RX_BOOSTED_GAIN=1 +; https://wiki.uniteng.com/en/meshtastic/station-g2#impact-of-lora-node-dense-areashigh-noise-environments-on-rf-performance +; -D MESH_DEBUG=1 +build_src_filter = ${Station_G2.build_src_filter} + +<../examples/simple_repeater> +lib_deps = + ${Station_G2.lib_deps} + ${esp32_ota.lib_deps} + [env:Station_G2_room_server] extends = Station_G2 build_src_filter = ${Station_G2.build_src_filter} From d0fb8d2f30c43ce415c68094d454a5d3f0ef0201 Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 25 Aug 2025 12:11:39 +0200 Subject: [PATCH 39/46] lib_build: support display --- build_as_lib.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/build_as_lib.py b/build_as_lib.py index da4337c0..43e06fdd 100644 --- a/build_as_lib.py +++ b/build_as_lib.py @@ -6,11 +6,13 @@ menv=env # type: ignore src_filter = [ '+<*.cpp>', '+', - '+' + '+', '+', '+', ] +use_display = False + # add build and include dirs according to CPPDEFINES for item in menv.get("CPPDEFINES", []): @@ -38,12 +40,25 @@ for item in menv.get("CPPDEFINES", []): menv.Append(BUILD_FLAGS=["-I src/helpers/rp2040"]) src_filter.append("+") + # DISPLAY HANDLING + elif isinstance(item, tuple) and item[0] == "DISPLAY_CLASS": + display_class = item[1] + use_display = True + src_filter.append(f"+") + if (display_class == "ST7789Display") : + src_filter.append(f"+") + src_filter.append(f"+") + # VARIANTS HANDLING elif isinstance(item, tuple) and item[0] == "MC_VARIANT": variant_name = item[1] menv.Append(BUILD_FLAGS=[f"-I variants/{variant_name}"]) src_filter.append(f"+<../variants/{variant_name}>") +if use_display : + menv.Append(CPPPATH=[realpath("src/helpers/ui")]) + menv.Append(BUILD_FLAGS=["-I src/helpers/ui"]) + menv.Replace(SRC_FILTER=src_filter) #print (menv.Dump()) From 033706adcfbb89e138d35e1fb9fbdb4011eecdcd Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 25 Aug 2025 16:21:23 +0200 Subject: [PATCH 40/46] lib_build: fix ST7789 so we don't have to add helpers/ui in INC_DIRS --- build_as_lib.py | 8 +------- src/helpers/ui/ST7789Display.h | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/build_as_lib.py b/build_as_lib.py index 43e06fdd..59091556 100644 --- a/build_as_lib.py +++ b/build_as_lib.py @@ -9,10 +9,9 @@ src_filter = [ '+', '+', '+', + '+', ] -use_display = False - # add build and include dirs according to CPPDEFINES for item in menv.get("CPPDEFINES", []): @@ -43,7 +42,6 @@ for item in menv.get("CPPDEFINES", []): # DISPLAY HANDLING elif isinstance(item, tuple) and item[0] == "DISPLAY_CLASS": display_class = item[1] - use_display = True src_filter.append(f"+") if (display_class == "ST7789Display") : src_filter.append(f"+") @@ -55,10 +53,6 @@ for item in menv.get("CPPDEFINES", []): menv.Append(BUILD_FLAGS=[f"-I variants/{variant_name}"]) src_filter.append(f"+<../variants/{variant_name}>") -if use_display : - menv.Append(CPPPATH=[realpath("src/helpers/ui")]) - menv.Append(BUILD_FLAGS=["-I src/helpers/ui"]) - menv.Replace(SRC_FILTER=src_filter) #print (menv.Dump()) diff --git a/src/helpers/ui/ST7789Display.h b/src/helpers/ui/ST7789Display.h index 8056de81..0f06da4c 100644 --- a/src/helpers/ui/ST7789Display.h +++ b/src/helpers/ui/ST7789Display.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include "ST7789Spi.h" class ST7789Display : public DisplayDriver { ST7789Spi display; From 15249bb8d5edde15ada5710cb4b6bd34711e3cc6 Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 25 Aug 2025 17:50:48 +0200 Subject: [PATCH 41/46] lib_build: include example code in build --- build_as_lib.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build_as_lib.py b/build_as_lib.py index 59091556..910c58fe 100644 --- a/build_as_lib.py +++ b/build_as_lib.py @@ -52,6 +52,11 @@ for item in menv.get("CPPDEFINES", []): variant_name = item[1] menv.Append(BUILD_FLAGS=[f"-I variants/{variant_name}"]) src_filter.append(f"+<../variants/{variant_name}>") + + # INCLUDE EXAMPLE CODE IN BUILD (to provide your own support files without touching the tree) + elif isinstance(item, tuple) and item[0] == "BUILD_EXAMPLE": + example_name = item[1] + src_filter.append(f"+<../examples/{example_name}>") menv.Replace(SRC_FILTER=src_filter) From df18dfb48105fd71efd4ee26525aec6af46f5cc5 Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 25 Aug 2025 18:34:50 +0200 Subject: [PATCH 42/46] lib_build: exclude file from example --- build_as_lib.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build_as_lib.py b/build_as_lib.py index 910c58fe..4e8eea57 100644 --- a/build_as_lib.py +++ b/build_as_lib.py @@ -58,6 +58,11 @@ for item in menv.get("CPPDEFINES", []): example_name = item[1] src_filter.append(f"+<../examples/{example_name}>") + # EXCLUDE A SOURCE FILE FROM AN EXAMPLE (must be placed after example name or boom) + elif isinstance(item, tuple) and item[0] == "EXCLUDE_FROM_EXAMPLE": + exclude_name = item[1] + src_filter.append(f"-<../examples/{example_name}/{exclude_name}>") + menv.Replace(SRC_FILTER=src_filter) #print (menv.Dump()) From 78fcb704bc30f43046af0ecfb31dfaf2dbafa8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= Date: Mon, 25 Aug 2025 17:44:14 +0100 Subject: [PATCH 43/46] Increase power limit for Heltec v3 433 MHz --- variants/heltec_v3/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index b9193431..24bc0bf0 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -17,7 +17,7 @@ build_flags = -D PIN_VEXT_EN=36 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 - -D SX126X_CURRENT_LIMIT=140 + -D SX126X_CURRENT_LIMIT=160 -D SX126X_RX_BOOSTED_GAIN=1 -D PIN_GPS_RX=47 -D PIN_GPS_TX=48 From 0959e64d1133ee99e863c789a5cc15a43ea126d2 Mon Sep 17 00:00:00 2001 From: Florent Date: Tue, 26 Aug 2025 13:28:42 +0200 Subject: [PATCH 44/46] lib_build: add UI_FLAVOR and some cleanup --- build_as_lib.py | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/build_as_lib.py b/build_as_lib.py index 4e8eea57..d8e95378 100644 --- a/build_as_lib.py +++ b/build_as_lib.py @@ -15,28 +15,14 @@ src_filter = [ # add build and include dirs according to CPPDEFINES for item in menv.get("CPPDEFINES", []): - # STM32 - if isinstance(item, str) and item == "STM32_PLATFORM": - menv.Append(CPPPATH=[realpath("src/helpers/stm32")]) - menv.Append(BUILD_FLAGS=["-I src/helpers/stm32"]) + # PLATFORM HANDLING + if item == "STM32_PLATFORM": src_filter.append("+") - - # ESP32 - elif isinstance(item, str) and item == "ESP32": - menv.Append(CPPPATH=[realpath("src/helpers/esp32")]) - menv.Append(BUILD_FLAGS=["-I src/helpers/esp32"]) + elif item == "ESP32": src_filter.append("+") - - # NRF52 - elif isinstance(item, str) and item == "NRF52_PLATFORM": - menv.Append(CPPPATH=[realpath("src/helpers/nrf52")]) - menv.Append(BUILD_FLAGS=["-I src/helpers/nrf52"]) + elif item == "NRF52_PLATFORM": src_filter.append("+") - - # RP2040 - elif isinstance(item, str) and item == "RP2040_PLATFORM": - menv.Append(CPPPATH=[realpath("src/helpers/rp2040")]) - menv.Append(BUILD_FLAGS=["-I src/helpers/rp2040"]) + elif item == "RP2040_PLATFORM": src_filter.append("+") # DISPLAY HANDLING @@ -50,19 +36,29 @@ for item in menv.get("CPPDEFINES", []): # VARIANTS HANDLING elif isinstance(item, tuple) and item[0] == "MC_VARIANT": variant_name = item[1] - menv.Append(BUILD_FLAGS=[f"-I variants/{variant_name}"]) src_filter.append(f"+<../variants/{variant_name}>") # INCLUDE EXAMPLE CODE IN BUILD (to provide your own support files without touching the tree) elif isinstance(item, tuple) and item[0] == "BUILD_EXAMPLE": example_name = item[1] - src_filter.append(f"+<../examples/{example_name}>") + src_filter.append(f"+<../examples/{example_name}/*.cpp>") # EXCLUDE A SOURCE FILE FROM AN EXAMPLE (must be placed after example name or boom) elif isinstance(item, tuple) and item[0] == "EXCLUDE_FROM_EXAMPLE": exclude_name = item[1] + if example_name is None: + print("***** PLEASE DEFINE EXAMPLE FIRST *****") + break src_filter.append(f"-<../examples/{example_name}/{exclude_name}>") + # DEAL WITH UI VARIANT FOR AN EXAMPLE + elif isinstance(item, tuple) and item[0] == "MC_UI_FLAVOR": + ui_flavor = item[1] + if example_name is None: + print("***** PLEASE DEFINE EXAMPLE FIRST *****") + break + src_filter.append(f"+<../examples/{example_name}/{ui_flavor}/*.cpp>") + menv.Replace(SRC_FILTER=src_filter) #print (menv.Dump()) From 6536e9931d82faaf129bfd716777c951a45b9a6e Mon Sep 17 00:00:00 2001 From: ave Date: Tue, 26 Aug 2025 21:30:56 +0200 Subject: [PATCH 45/46] add why2025 badge as a target --- variants/xiao_c6/platformio.ini | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/variants/xiao_c6/platformio.ini b/variants/xiao_c6/platformio.ini index fdf0f337..602bd6ae 100644 --- a/variants/xiao_c6/platformio.ini +++ b/variants/xiao_c6/platformio.ini @@ -121,3 +121,65 @@ build_src_filter = ${Meshimi.build_src_filter} lib_deps = ${Meshimi.lib_deps} densaugeo/base64 @ ~1.4.0 + +; WHY2025 badge variant +; requires soldering 2 pins between the esp32-C6 and the lora chip as shown here: https://wiki.why2025.org/Project:Meshtastic_on_the_WHY2025_badge +; also requires wiping the esp32-P4 +[WHY2025_badge] +extends = Xiao_C6 +board_build.partitions = default_8MB.csv +board_upload.flash_size = 8MB +board_upload.maximum_size = 8388608 +build_flags = + ${Xiao_C6.build_flags} + -D P_LORA_SCLK=6 + -D P_LORA_MISO=2 + -D P_LORA_MOSI=7 + -D P_LORA_NSS=4 + -D P_LORA_DIO_1=5 + -D P_LORA_BUSY=11 + -D P_LORA_RESET=1 + -D SX126X_TXEN=3 + -UPIN_BOARD_SDA + -UPIN_BOARD_SCL + -UP_LORA_TX_LED + -USX126X_RXEN + -USX126X_DIO2_AS_RF_SWITCH + -USX126X_DIO3_TCXO_VOLTAGE + +[env:WHY2025_badge_Repeater] +extends = WHY2025_badge +build_src_filter = ${WHY2025_badge.build_src_filter} + +<../examples/simple_repeater/main.cpp> +build_flags = + ${WHY2025_badge.build_flags} + -D ADVERT_NAME='"WHY2025 Badge Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=8 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${WHY2025_badge.lib_deps} +; ${esp32_ota.lib_deps} + +[env:WHY2025_badge_companion_radio_ble] +extends = WHY2025_badge +build_flags = ${WHY2025_badge.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + -D ENABLE_PRIVATE_KEY_IMPORT=1 + -D ENABLE_PRIVATE_KEY_EXPORT=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${WHY2025_badge.build_src_filter} + + + - + +<../examples/companion_radio/*.cpp> +lib_deps = + ${WHY2025_badge.lib_deps} + densaugeo/base64 @ ~1.4.0 From 136f733df52c8e3e801fec5534261fa42357f17d Mon Sep 17 00:00:00 2001 From: Florent Date: Wed, 27 Aug 2025 21:19:12 +0200 Subject: [PATCH 46/46] SensorMesh: add the possibility to receive msgs from admin --- examples/simple_sensor/SensorMesh.cpp | 99 +++++++++++++++++++-------- examples/simple_sensor/SensorMesh.h | 3 +- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index ce36e0c2..ad295501 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -613,6 +613,23 @@ void SensorMesh::getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) { } } +void SensorMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) { + if (dest.out_path_len < 0) { + mesh::Packet* ack = createAck(ack_hash); + if (ack) sendFlood(ack, TXT_ACK_DELAY); + } else { + uint32_t d = TXT_ACK_DELAY; + if (getExtraAckTransmitCount() > 0) { + mesh::Packet* a1 = createMultiAck(ack_hash, 1); + if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, d); + d += 300; + } + + mesh::Packet* a2 = createAck(ack_hash); + if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, d); + } +} + void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) { int i = matching_peer_indexes[sender_idx]; if (i < 0 || i >= num_contacts) { @@ -656,38 +673,55 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i memcpy(&sender_timestamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong) uint flags = (data[4] >> 2); // message attempt number, and other flags - if (!(flags == TXT_TYPE_CLI_DATA)) { - MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported text type received: flags=%02x", (uint32_t)flags); - } else if (sender_timestamp > from.last_timestamp) { // prevent replay attacks - from.last_timestamp = sender_timestamp; - from.last_activity = getRTCClock()->getCurrentTime(); + if (sender_timestamp > from.last_timestamp) { // prevent replay attacks + if (flags == TXT_TYPE_PLAIN) { + bool handled = handleIncomingMsg(from, sender_timestamp, &data[5], flags, len - 5); + if (handled) { // if msg was handled then send an ack + uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it + mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), from.id.pub_key, PUB_KEY_SIZE); - // len can be > original length, but 'text' will be padded with zeroes - data[len] = 0; // need to make a C string again, with null terminator - - uint8_t temp[166]; - char *command = (char *) &data[5]; - char *reply = (char *) &temp[5]; - handleCommand(sender_timestamp, command, reply); - - int text_len = strlen(reply); - if (text_len > 0) { - uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); - if (timestamp == sender_timestamp) { - // WORKAROUND: the two timestamps need to be different, in the CLI view - timestamp++; - } - memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique - temp[4] = (TXT_TYPE_CLI_DATA << 2); - - auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, from.id, secret, temp, 5 + text_len); - if (reply) { - if (from.out_path_len < 0) { - sendFlood(reply, CLI_REPLY_DELAY_MILLIS); + if (packet->isRouteFlood()) { + // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK + mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, + PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4); + if (path) sendFlood(path, TXT_ACK_DELAY); } else { - sendDirect(reply, from.out_path, from.out_path_len, CLI_REPLY_DELAY_MILLIS); + sendAckTo(from, ack_hash); + } + } + } else if (flags == TXT_TYPE_CLI_DATA) { + from.last_timestamp = sender_timestamp; + from.last_activity = getRTCClock()->getCurrentTime(); + + // len can be > original length, but 'text' will be padded with zeroes + data[len] = 0; // need to make a C string again, with null terminator + + uint8_t temp[166]; + char *command = (char *) &data[5]; + char *reply = (char *) &temp[5]; + handleCommand(sender_timestamp, command, reply); + + int text_len = strlen(reply); + if (text_len > 0) { + uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); + if (timestamp == sender_timestamp) { + // WORKAROUND: the two timestamps need to be different, in the CLI view + timestamp++; + } + memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique + temp[4] = (TXT_TYPE_CLI_DATA << 2); + + auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, from.id, secret, temp, 5 + text_len); + if (reply) { + if (from.out_path_len < 0) { + sendFlood(reply, CLI_REPLY_DELAY_MILLIS); + } else { + sendDirect(reply, from.out_path, from.out_path_len, CLI_REPLY_DELAY_MILLIS); + } } } + } else { + MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported text type received: flags=%02x", (uint32_t)flags); } } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected"); @@ -695,6 +729,15 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i } } +bool SensorMesh::handleIncomingMsg(ContactInfo& from, uint32_t timestamp, uint8_t* data, uint flags, size_t len) { + MESH_DEBUG_PRINT("handleIncomingMsg: unhandled msg from "); + #ifdef MESH_DEBUG + mesh::Utils::printHex(Serial, from.id.pub_key, PUB_KEY_SIZE); + Serial.printf(": %s\n", data); + #endif + return false; +} + bool SensorMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) { int i = matching_peer_indexes[sender_idx]; if (i < 0 || i >= num_contacts) { diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index 0d87617b..62c1867e 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -140,7 +140,8 @@ protected: void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override; bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override; void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override; - + virtual bool handleIncomingMsg(ContactInfo& from, uint32_t timestamp, uint8_t* data, uint flags, size_t len); + void sendAckTo(const ContactInfo& dest, uint32_t ack_hash); private: FILESYSTEM* _fs; unsigned long next_local_advert, next_flood_advert;