From 7bb16cd7f3c91397e3757a8449dc6fce4b3ffcad Mon Sep 17 00:00:00 2001 From: Florent de Lamotte Date: Mon, 10 Mar 2025 17:11:55 +0100 Subject: [PATCH] enhancement on the UI and support for power off and status_led for t1000e --- examples/companion_radio/UITask.cpp | 161 +++++++++++++++++++--------- examples/companion_radio/UITask.h | 19 +++- examples/companion_radio/main.cpp | 23 ++-- platformio.ini | 8 +- src/MeshCore.h | 1 + src/helpers/HeltecV3Board.h | 4 + src/helpers/nrf52/T1000eBoard.h | 4 +- 7 files changed, 157 insertions(+), 63 deletions(-) diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 8ffb2c1f..f4981984 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -28,7 +28,16 @@ void UITask::begin(const char* node_name, const char* build_date, uint32_t pin_c _node_name = node_name; _build_date = build_date; _pin_code = pin_code; - _display->turnOn(); + if (_display != NULL) { + _display->turnOn(); + } +} + +void UITask::msgRead(int msgcount) { + _msgcount = msgcount; + if (msgcount == 0) { + clearMsgPreview(); + } } void UITask::clearMsgPreview() { @@ -36,7 +45,9 @@ void UITask::clearMsgPreview() { _msg[0] = 0; } -void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* text) { +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 { @@ -44,67 +55,121 @@ void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* } StrHelper::strncpy(_msg, text, sizeof(_msg)); - if (!_display->isOn()) _display->turnOn(); - _auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer + if (_display != NULL) { + if (!_display->isOn()) _display->turnOn(); + _auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer + } } void UITask::renderCurrScreen() { - char tmp[80]; - if (_origin[0] && _msg[0]) { - // render message preview - _display->setCursor(0, 0); - _display->setTextSize(1); - _display->print(_node_name); + if (_display != NULL) { + char tmp[80]; + if (_origin[0] && _msg[0]) { + // render message preview + _display->setCursor(0, 0); + _display->setTextSize(1); + _display->print(_node_name); - _display->setCursor(0, 12); - _display->print(_origin); - _display->setCursor(0, 24); - _display->print(_msg); + _display->setCursor(0, 12); + _display->print(_origin); + _display->setCursor(0, 24); + _display->print(_msg); - //_display->setCursor(100, 9); TODO - //_display->setTextSize(2); - //_display->printf("%d", msgs); - } else { - // render 'home' screen - _display->drawXbm(0, 0, meshcore_logo, 128, 13); - _display->setCursor(0, 20); - _display->setTextSize(1); - _display->print(_node_name); - - sprintf(tmp, "Build: %s", _build_date); - _display->setCursor(0, 32); - _display->print(tmp); - - if (_connected) { - //_display->printf("freq : %03.2f sf %d\n", _prefs.freq, _prefs.sf); - //_display->printf("bw : %03.2f cr %d\n", _prefs.bw, _prefs.cr); - } else if (_pin_code != 0) { + _display->setCursor(100, 9); _display->setTextSize(2); - _display->setCursor(0, 43); - sprintf(tmp, "Pin:%d", _pin_code); + sprintf(tmp, "%d", _msgcount); _display->print(tmp); + } else { + // render 'home' screen + _display->drawXbm(0, 0, meshcore_logo, 128, 13); + _display->setCursor(0, 20); + _display->setTextSize(1); + _display->print(_node_name); + + sprintf(tmp, "Build: %s", _build_date); + _display->setCursor(0, 32); + _display->print(tmp); + + if (_connected) { + //_display->printf("freq : %03.2f sf %d\n", _prefs.freq, _prefs.sf); + //_display->printf("bw : %03.2f cr %d\n", _prefs.bw, _prefs.cr); + } else if (_pin_code != 0) { + _display->setTextSize(2); + _display->setCursor(0, 43); + sprintf(tmp, "Pin:%d", _pin_code); + _display->print(tmp); + } } } } -void UITask::loop() { - if (millis() >= _next_read) { - int btnState = digitalRead(PIN_USER_BTN); - if (btnState != _prevBtnState) { - if (btnState == LOW) { // pressed? - if (_display->isOn()) { - clearMsgPreview(); - } else { - _display->turnOn(); - } - _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer +void UITask::userLedHandler() { +#ifdef PIN_STATUS_LED + static int state = 0; + static int next_change = 0; + int cur_time = millis(); + if (cur_time > next_change) { + if (state == 0) { + state = 1; // led on, short = unread msg + if (_msgcount > 0) { + next_change = cur_time + 500; + } else { + next_change = cur_time + 2000; + } + } else { + state = 0; + if (_board->getBattMilliVolts() > 3800) { + next_change = cur_time + 2000; + } else { + next_change = cur_time + 4000; // 4s blank if bat level low } - _prevBtnState = btnState; } - _next_read = millis() + 100; // 10 reads per second + digitalWrite(PIN_STATUS_LED, state); } +#endif +} - if (_display->isOn()) { +void UITask::buttonHandler() { +#ifdef PIN_USER_BTN + static int prev_btn_state = HIGH; + static unsigned long btn_state_change_time = 0; + static unsigned long next_read = 0; + int cur_time = millis(); + if (cur_time >= next_read) { + int btn_state = digitalRead(PIN_USER_BTN); + if (btn_state != prev_btn_state) { + if (btn_state == USER_BTN_PRESSED) { // pressed? + if (_display != NULL) { + if (_display->isOn()) { + clearMsgPreview(); + } else { + _display->turnOn(); + } + _auto_off = cur_time + AUTO_OFF_MILLIS; // extend auto-off timer + } + } else { // unpressed ? check pressed time ... + if ((cur_time - btn_state_change_time) > 5000) { + Serial.println("power off"); + #ifdef PIN_STATUS_LED + digitalWrite(PIN_STATUS_LED, LOW); + delay(10); + #endif + _board->powerOff(); + } + } + btn_state_change_time = millis(); + prev_btn_state = btn_state; + } + next_read = millis() + 100; // 10 reads per second + } +#endif +} + +void UITask::loop() { + buttonHandler(); + userLedHandler(); + + if (_display != NULL && _display->isOn()) { if (millis() >= _next_refresh) { _display->startFrame(); renderCurrScreen(); diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index f1a63225..fa645324 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -1,10 +1,12 @@ #pragma once +#include #include class UITask { DisplayDriver* _display; - unsigned long _next_read, _next_refresh, _auto_off; + mesh::MainBoard* _board; + unsigned long _next_refresh, _auto_off; int _prevBtnState; bool _connected; uint32_t _pin_code; @@ -12,14 +14,23 @@ class UITask { const char* _build_date; char _origin[62]; char _msg[80]; + int _msgcount; void renderCurrScreen(); + void buttonHandler(); + void userLedHandler(); + public: - UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; _connected = false; } + + UITask(mesh::MainBoard* board, DisplayDriver* display) : _board(board), _display(display){ + _next_refresh = 0; + _connected = false; + } void begin(const char* node_name, const char* build_date, uint32_t pin_code); void setHasConnection(bool connected) { _connected = connected; } void clearMsgPreview(); - void showMsgPreview(uint8_t path_len, const char* from_name, const char* text); + void msgRead(int msgcount); + void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); void loop(); -}; \ No newline at end of file +}; diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 833e41d8..a4009779 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -97,12 +97,16 @@ #endif #ifdef DISPLAY_CLASS + #include "UITask.h" #include static DISPLAY_CLASS display; - + static UITask ui_task(&board, &display); + #define HAS_UI +#elif defined(HAS_UI) #include "UITask.h" - static UITask ui_task(display); + + static UITask ui_task(&board, NULL); #endif // Believe it or not, this std C function is busted on some platforms! @@ -508,8 +512,8 @@ protected: } else { soundBuzzer(); } - #ifdef DISPLAY_CLASS - ui_task.showMsgPreview(path_len, from.name, text); + #ifdef HAS_UI + ui_task.newMsg(path_len, from.name, text, offline_queue_len); #endif } @@ -550,8 +554,8 @@ protected: } else { soundBuzzer(); } - #ifdef DISPLAY_CLASS - ui_task.showMsgPreview(in_path_len < 0 ? 0xFF : in_path_len, "Public", text); + #ifdef HAS_UI + ui_task.newMsg(in_path_len < 0 ? 0xFF : in_path_len, "Public", text, offline_queue_len); #endif } @@ -1010,6 +1014,9 @@ public: int out_len; if ((out_len = getFromOfflineQueue(out_frame)) > 0) { _serial->writeFrame(out_frame, out_len); + #ifdef HAS_UI + ui_task.msgRead(offline_queue_len); + #endif } else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; _serial->writeFrame(out_frame, 1); @@ -1190,7 +1197,7 @@ public: checkConnections(); } - #ifdef DISPLAY_CLASS + #ifdef HAS_UI ui_task.setHasConnection(_serial->isConnected()); ui_task.loop(); #endif @@ -1315,6 +1322,8 @@ void setup() { #ifdef DISPLAY_CLASS display.begin(); +#endif +#ifdef HAS_UI ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE, the_mesh.getBLEPin()); #endif } diff --git a/platformio.ini b/platformio.ini index 02ce189c..a8bdf219 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,6 +22,7 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 -D LORA_FREQ=867.5 -D LORA_BW=250 -D LORA_SF=10 + -D USER_BTN_PRESSED=LOW build_src_filter = +<*.cpp> + @@ -774,10 +775,14 @@ board = tracker-t1000-e board_build.ldscript = boards/nrf52840_s140_v7.ld build_flags = ${nrf52840_t1000e.build_flags} -Ivariants/t1000-e - -DT1000_E + -D T1000_E -D PIN_USER_BTN=6 + -D USER_BTN_PRESSED=HIGH + -D PIN_STATUS_LED=24 -D RADIO_CLASS=CustomLR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper + -D MAX_LORA_TX_POWER=22 + -D LORA_TX_POWER=22 build_src_filter = ${nrf52840_t1000e.build_src_filter} + + @@ -794,6 +799,7 @@ build_flags = ${t1000-e.build_flags} -D BLE_DEBUG_LOGGING=1 -D MESH_PACKET_LOGGING=1 -D MESH_DEBUG=1 + -D HAS_UI build_src_filter = ${t1000-e.build_src_filter} + +<../examples/companion_radio/*.cpp> diff --git a/src/MeshCore.h b/src/MeshCore.h index 6bb11989..5f9956a3 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -40,6 +40,7 @@ public: virtual void onBeforeTransmit() { } virtual void onAfterTransmit() { } virtual void reboot() = 0; + virtual void powerOff() { while (1) { }}; // hope it's overriden or never called ;) virtual uint8_t getStartupReason() const = 0; virtual bool startOTAUpdate() { return false; } // not supported }; diff --git a/src/helpers/HeltecV3Board.h b/src/helpers/HeltecV3Board.h index 927d537e..c61189f2 100644 --- a/src/helpers/HeltecV3Board.h +++ b/src/helpers/HeltecV3Board.h @@ -67,6 +67,10 @@ public: esp_deep_sleep_start(); // CPU halts here and never returns! } + void powerOff() override { + enterDeepSleep(0); + } + uint16_t getBattMilliVolts() override { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, PIN_ADC_CTRL_ACTIVE); diff --git a/src/helpers/nrf52/T1000eBoard.h b/src/helpers/nrf52/T1000eBoard.h index 3c0f30b4..ce2206e8 100644 --- a/src/helpers/nrf52/T1000eBoard.h +++ b/src/helpers/nrf52/T1000eBoard.h @@ -3,8 +3,6 @@ #include #include -#define HAS_T1000e_POWEROFF - // LoRa and SPI pins #define P_LORA_DIO_1 (32 + 1) // P1.1 #define P_LORA_NSS (0 + 12) // P0.12 @@ -61,7 +59,7 @@ public: return 0; } - void powerOff() { + void powerOff() override { #ifdef HAS_GPS digitalWrite(GPS_VRTC_EN, LOW); digitalWrite(GPS_RESET, LOW);