Files
HaloKeymind/examples/simple_repeater/UITask.cpp
T

291 lines
9.7 KiB
C++

#include "UITask.h"
#include "target.h"
#include <Arduino.h>
#include <helpers/CommonCLI.h>
#ifdef DISPLAY_REDRAW_ON_CHANGE
#include <helpers/ui/DisplayFrameSignature.h>
#endif
#ifndef USER_BTN_PRESSED
#define USER_BTN_PRESSED LOW
#endif
#ifdef WITH_MQTT_BRIDGE
#include <WiFi.h>
#include <helpers/esp32/WebConfigServer.h> // defines WITH_WEBCONFIG on ESP32
#endif
#define AUTO_OFF_MILLIS 20000 // 20 seconds
#define BOOT_SCREEN_MILLIS 4000 // 4 seconds
#define POWEROFF_DELAY 3000
// '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(NodePrefs* node_prefs, const char* build_date, const char* firmware_version) {
_prevBtnState = HIGH;
_auto_off = millis() + AUTO_OFF_MILLIS;
_started_at = millis();
_node_prefs = node_prefs;
_display->turnOn();
#ifdef DISPLAY_REDRAW_ON_CHANGE
_frame_valid = false;
#endif
#if defined(PIN_USER_BTN) && defined(DISPLAY_CLASS)
user_btn.begin();
#endif
// 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)
snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date);
free(version);
}
void UITask::renderCurrScreen() {
char tmp[80];
if (millis() < _started_at + BOOT_SCREEN_MILLIS) { // boot screen
// meshcore logo
_display->setColor(UIColor::corp_blue);
int logoWidth = 128;
_display->drawXbm((_display->width() - logoWidth) / 2, 3, meshcore_logo, logoWidth, 13);
// meshcore website
const char* website = "https://meshcore.io";
_display->setColor(UIColor::primary_txt);
_display->setTextSize(1);
_display->drawTextCentered(_display->width() / 2, 22, website);
// version info
_display->setTextSize(1);
_display->drawTextCentered(_display->width() / 2, 35, _version_info);
// node type
const char* node_type = "< Repeater >";
_display->drawTextCentered(_display->width() / 2, 48, node_type);
} else if (_powering_off_at > 0) {
// meshcore logo
_display->setColor(UIColor::corp_blue);
int logoWidth = 128;
_display->drawXbm((_display->width() - logoWidth) / 2, 3, meshcore_logo, logoWidth, 13);
// meshcore website
const char* website = "https://meshcore.io";
_display->setColor(UIColor::primary_txt);
_display->setTextSize(1);
_display->drawTextCentered(_display->width()/ 2, 22, website);
// Powering off
const char* poweroff_string = "Turning OFF";
uint16_t poffWidth = _display->getTextWidth(poweroff_string);
_display->setCursor((_display->width() - poffWidth) / 2, 48);
_display->drawTextCentered(_display->width()/2, 48, poweroff_string);
} else { // home screen
#ifdef WITH_WEBCONFIG
if (WebConfigServer::isRebootPending()) {
// save confirmed on-device: show ground truth even if the browser
// lost its connection before the confirmation reached it
_display->setTextSize(1);
_display->setColor(UIColor::corp_blue);
_display->setCursor(0, 14);
_display->print("Config saved!");
_display->setColor(UIColor::primary_txt);
_display->setCursor(0, 30);
_display->print("Rebooting...");
return;
}
char wc_ssid[33], wc_ip[16];
if (WebConfigServer::getSetupInfo(wc_ssid, sizeof(wc_ssid), wc_ip, sizeof(wc_ip))) {
// setup portal active: show join instructions instead of the home screen
_display->setTextSize(1);
_display->setColor(UIColor::corp_blue);
_display->setCursor(0, 0);
_display->print("Observer WiFi Setup");
_display->setColor(UIColor::primary_txt);
_display->setCursor(0, 14);
_display->print("Join WiFi:");
_display->setColor(UIColor::warning_txt);
_display->setCursor(6, 24);
_display->print(wc_ssid);
_display->setColor(UIColor::primary_txt);
_display->setCursor(0, 40);
_display->print("Then browse to:");
_display->setColor(UIColor::warning_txt);
_display->setCursor(6, 50);
_display->print(wc_ip);
return;
}
#endif
// node name
_display->setCursor(0, 0);
_display->setTextSize(1);
_display->setColor(UIColor::primary_txt);
_display->print(_node_prefs->node_name);
// freq / sf
_display->setCursor(0, 20);
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);
#ifdef WITH_MQTT_BRIDGE
// Display IP address for MQTT bridge devices
if (WiFi.status() == WL_CONNECTED) {
IPAddress ip = WiFi.localIP();
_display->setCursor(0, 40);
_display->setColor(UIColor::primary_txt);
snprintf(tmp, sizeof(tmp), "IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
_display->print(tmp);
}
#endif
}
}
#ifdef DISPLAY_REDRAW_ON_CHANGE
uint32_t UITask::getFrameSignature() {
uint32_t signature = DisplayFrameSignature::INITIAL;
char tmp[80];
if (millis() < _started_at + BOOT_SCREEN_MILLIS) {
signature = DisplayFrameSignature::append(signature, "boot");
return DisplayFrameSignature::append(signature, _version_info);
}
if (_powering_off_at > 0) {
return DisplayFrameSignature::append(signature, "powering-off");
}
#ifdef WITH_WEBCONFIG
if (WebConfigServer::isRebootPending()) {
return DisplayFrameSignature::append(signature, "rebooting");
}
char wc_ssid[33], wc_ip[16];
if (WebConfigServer::getSetupInfo(wc_ssid, sizeof(wc_ssid), wc_ip, sizeof(wc_ip))) {
signature = DisplayFrameSignature::append(signature, "setup");
signature = DisplayFrameSignature::append(signature, wc_ssid);
return DisplayFrameSignature::append(signature, wc_ip);
}
#endif
signature = DisplayFrameSignature::append(signature, "home");
signature = DisplayFrameSignature::append(signature, _node_prefs->node_name);
snprintf(tmp, sizeof(tmp), "FREQ: %06.3f SF%d", _node_prefs->freq, _node_prefs->sf);
signature = DisplayFrameSignature::append(signature, tmp);
snprintf(tmp, sizeof(tmp), "BW: %03.2f CR: %d", _node_prefs->bw, _node_prefs->cr);
signature = DisplayFrameSignature::append(signature, tmp);
#ifdef WITH_MQTT_BRIDGE
if (WiFi.status() == WL_CONNECTED) {
IPAddress ip = WiFi.localIP();
snprintf(tmp, sizeof(tmp), "IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
signature = DisplayFrameSignature::append(signature, tmp);
} else {
signature = DisplayFrameSignature::append(signature, "wifi-disconnected");
}
#endif
return signature;
}
#endif
void UITask::loop() {
#if defined(PIN_USER_BTN) && defined(DISPLAY_CLASS)
int ev = user_btn.check();
if (ev == BUTTON_EVENT_CLICK) {
if (_display->isOn()) {
// TODO: any action ?
} else {
_display->turnOn();
#ifdef DISPLAY_REDRAW_ON_CHANGE
_frame_valid = false;
#endif
}
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
_display->turnOn();
Serial.println("Powering Off");
_powering_off_at = millis() + POWEROFF_DELAY;
}
#endif
#ifdef WITH_WEBCONFIG
// While the setup portal is up there's no user button to wake the screen
// reliably - keep it on so the join instructions stay visible.
if (WebConfigServer::getSetupInfo(NULL, 0, NULL, 0)) {
if (!_display->isOn()) {
_display->turnOn();
#ifdef DISPLAY_REDRAW_ON_CHANGE
_frame_valid = false;
#endif
}
_auto_off = millis() + AUTO_OFF_MILLIS;
}
#endif
if (_display->isOn()) {
if (millis() >= _next_refresh) {
bool redraw = true;
#ifdef DISPLAY_REDRAW_ON_CHANGE
uint32_t frame_signature = getFrameSignature();
redraw = !_frame_valid || frame_signature != _last_frame_signature;
#endif
if (redraw) {
_display->startFrame();
renderCurrScreen();
_display->endFrame();
#ifdef DISPLAY_REDRAW_ON_CHANGE
_last_frame_signature = frame_signature;
_frame_valid = true;
#endif
}
_next_refresh = millis() + 1000; // check for visible changes every second
}
if (millis() > _auto_off) {
_display->turnOff();
#ifdef DISPLAY_REDRAW_ON_CHANGE
_frame_valid = false;
#endif
}
}
if (_powering_off_at > 0) { // power off timer armed
#ifdef LED_PIN
digitalWrite(LED_PIN, LED_STATE_ON); // switch on the led until poweroff
#endif
if (millis() > _powering_off_at) {
_board->powerOff(); // should not return
}
}
}