diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp new file mode 100644 index 00000000..e8a204e3 --- /dev/null +++ b/examples/simple_repeater/UITask.cpp @@ -0,0 +1,79 @@ +#include "UITask.h" +#include + +#define AUTO_OFF_MILLIS 20000 // 20 seconds + +// '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(const char* node_name, const char* build_date) { + _prevBtnState = HIGH; + _auto_off = millis() + AUTO_OFF_MILLIS; + _node_name = node_name; + _build_date = build_date; + _display->turnOn(); +} + +void UITask::renderCurrScreen() { + char tmp[80]; + // 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); + _display->setCursor(0, 43); + _display->print("< Repeater >"); + //_display->printf("freq : %03.2f sf %d\n", _prefs.freq, _prefs.sf); + //_display->printf("bw : %03.2f cr %d\n", _prefs.bw, _prefs.cr); +} + +void UITask::loop() { +#ifdef PIN_USER_BTN + if (millis() >= _next_read) { + int btnState = digitalRead(PIN_USER_BTN); + if (btnState != _prevBtnState) { + if (btnState == LOW) { // pressed? + if (_display->isOn()) { + // TODO: any action ? + } else { + _display->turnOn(); + } + _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer + } + _prevBtnState = btnState; + } + _next_read = millis() + 200; // 5 reads per second + } +#endif + + if (_display->isOn()) { + if (millis() >= _next_refresh) { + _display->startFrame(); + renderCurrScreen(); + _display->endFrame(); + + _next_refresh = millis() + 1000; // refresh every second + } + if (millis() > _auto_off) { + _display->turnOff(); + } + } +} diff --git a/examples/simple_repeater/UITask.h b/examples/simple_repeater/UITask.h new file mode 100644 index 00000000..e58d6111 --- /dev/null +++ b/examples/simple_repeater/UITask.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +class UITask { + DisplayDriver* _display; + unsigned long _next_read, _next_refresh, _auto_off; + int _prevBtnState; + const char* _node_name; + const char* _build_date; + + void renderCurrScreen(); +public: + UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; } + void begin(const char* node_name, const char* build_date); + + void loop(); +}; \ No newline at end of file diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index d2a9626f..10776e3e 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -84,6 +84,15 @@ #error "need to provide a 'board' object" #endif +#ifdef DISPLAY_CLASS + #include + + static DISPLAY_CLASS display; + + #include "UITask.h" + static UITask ui_task(display); +#endif + #define PACKET_LOG_FILE "/packet_log" /* ------------------------------ Code -------------------------------- */ @@ -529,6 +538,7 @@ public: const char* getFirmwareVer() override { return FIRMWARE_VERSION; } const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; } + const char* getNodeName() { return _prefs.node_name; } void savePrefs() override { #if defined(NRF52_PLATFORM) @@ -604,6 +614,9 @@ public: updateAdvertTimer(); // schedule next local advert } + #ifdef DISPLAY_CLASS + ui_task.loop(); + #endif } }; @@ -649,6 +662,10 @@ void setup() { float tcxo = 1.6f; #endif +#ifdef DISPLAY_CLASS + display.begin(); +#endif + #if defined(NRF52_PLATFORM) SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); SPI.begin(); @@ -701,6 +718,10 @@ void setup() { the_mesh.begin(fs); +#ifdef DISPLAY_CLASS + ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE); +#endif + // send out initial Advertisement to the mesh the_mesh.sendSelfAdvertisement(2000); } diff --git a/examples/simple_room_server/UITask.cpp b/examples/simple_room_server/UITask.cpp new file mode 100644 index 00000000..2f1d8ae5 --- /dev/null +++ b/examples/simple_room_server/UITask.cpp @@ -0,0 +1,79 @@ +#include "UITask.h" +#include + +#define AUTO_OFF_MILLIS 20000 // 20 seconds + +// '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(const char* node_name, const char* build_date) { + _prevBtnState = HIGH; + _auto_off = millis() + AUTO_OFF_MILLIS; + _node_name = node_name; + _build_date = build_date; + _display->turnOn(); +} + +void UITask::renderCurrScreen() { + char tmp[80]; + // 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); + _display->setCursor(0, 43); + _display->print("< Room Server >"); + //_display->printf("freq : %03.2f sf %d\n", _prefs.freq, _prefs.sf); + //_display->printf("bw : %03.2f cr %d\n", _prefs.bw, _prefs.cr); +} + +void UITask::loop() { +#ifdef PIN_USER_BTN + if (millis() >= _next_read) { + int btnState = digitalRead(PIN_USER_BTN); + if (btnState != _prevBtnState) { + if (btnState == LOW) { // pressed? + if (_display->isOn()) { + // TODO: any action ? + } else { + _display->turnOn(); + } + _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer + } + _prevBtnState = btnState; + } + _next_read = millis() + 200; // 5 reads per second + } +#endif + + if (_display->isOn()) { + if (millis() >= _next_refresh) { + _display->startFrame(); + renderCurrScreen(); + _display->endFrame(); + + _next_refresh = millis() + 1000; // refresh every second + } + if (millis() > _auto_off) { + _display->turnOff(); + } + } +} diff --git a/examples/simple_room_server/UITask.h b/examples/simple_room_server/UITask.h new file mode 100644 index 00000000..e58d6111 --- /dev/null +++ b/examples/simple_room_server/UITask.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +class UITask { + DisplayDriver* _display; + unsigned long _next_read, _next_refresh, _auto_off; + int _prevBtnState; + const char* _node_name; + const char* _build_date; + + void renderCurrScreen(); +public: + UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; } + void begin(const char* node_name, const char* build_date); + + void loop(); +}; \ No newline at end of file diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index cce7e3d4..ed1ab345 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -88,6 +88,15 @@ #error "need to provide a 'board' object" #endif +#ifdef DISPLAY_CLASS + #include + + static DISPLAY_CLASS display; + + #include "UITask.h" + static UITask ui_task(display); +#endif + /* ------------------------------ Code -------------------------------- */ struct ClientInfo { @@ -570,6 +579,7 @@ public: const char* getFirmwareVer() override { return FIRMWARE_VERSION; } const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; } + const char* getNodeName() { return _prefs.node_name; } void savePrefs() override { #if defined(NRF52_PLATFORM) @@ -664,6 +674,10 @@ public: updateAdvertTimer(); // schedule next local advert } + #ifdef DISPLAY_CLASS + ui_task.loop(); + #endif + // TODO: periodically check for OLD/inactive entries in known_clients[], and evict } }; @@ -710,6 +724,10 @@ void setup() { float tcxo = 1.6f; #endif +#ifdef DISPLAY_CLASS + display.begin(); +#endif + #if defined(NRF52_PLATFORM) SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); SPI.begin(); @@ -761,6 +779,10 @@ void setup() { the_mesh.begin(fs); +#ifdef DISPLAY_CLASS + ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE); +#endif + // send out initial Advertisement to the mesh the_mesh.sendSelfAdvertisement(2000); } diff --git a/platformio.ini b/platformio.ini index 58d1e172..2457a306 100644 --- a/platformio.ini +++ b/platformio.ini @@ -126,19 +126,21 @@ lib_deps = extends = Heltec_lora32_v3 build_flags = ${Heltec_lora32_v3.build_flags} + -D DISPLAY_CLASS=SSD1306Display -D ADVERT_NAME="\"Heltec Repeater\"" -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 -D ADMIN_PASSWORD="\"password\"" -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_repeater/main.cpp> +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_repeater> [env:Heltec_v3_room_server] extends = Heltec_lora32_v3 -build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_room_server/main.cpp> +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + +<../examples/simple_room_server> build_flags = ${Heltec_lora32_v3.build_flags} + -D DISPLAY_CLASS=SSD1306Display -D ADVERT_NAME="\"Heltec Room\"" -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 @@ -526,6 +528,7 @@ board_build.ldscript = boards/nrf52840_s140_v7.ld build_flags = ${nrf52840_t1000e.build_flags} -Ivariants/t1000-e -DT1000_E + -D PIN_USER_BTN=6 -D RADIO_CLASS=CustomLR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper build_src_filter = ${nrf52840_t1000e.build_src_filter}