Merge branch 'system' into extended

This commit is contained in:
Thomas Hooge 2025-08-04 12:39:54 +02:00
commit 7de226b447
10 changed files with 799 additions and 290 deletions

View File

@ -0,0 +1,204 @@
/*
Menu system for online configuration
*/
#include "ConfigMenu.h"
ConfigMenuItem::ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemval, String itemunit) {
if (! (itemtype == "int" or itemtype == "bool")) {
valtype = "int";
} else {
valtype = itemtype;
}
label = itemlabel;
min = 0;
max = std::numeric_limits<uint16_t>::max();
value = itemval;
unit = itemunit;
}
void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> valsteps) {
min = valmin;
max = valmax;
steps = valsteps;
};
bool ConfigMenuItem::checkRange(uint16_t checkval) {
return (checkval >= min) and (checkval <= max);
}
String ConfigMenuItem::getLabel() {
return label;
};
uint16_t ConfigMenuItem::getValue() {
return value;
}
bool ConfigMenuItem::setValue(uint16_t newval) {
if (valtype == "int") {
if (newval >= min and newval <= max) {
value = newval;
return true;
}
return false; // out of range
} else if (valtype == "bool") {
value = (newval != 0) ? 1 : 0;
return true;
}
return false; // invalid type
};
void ConfigMenuItem::incValue() {
// increase value by step
if (valtype == "int") {
if (value + step < max) {
value += step;
} else {
value = max;
}
} else if (valtype == "bool") {
value = !value;
}
};
void ConfigMenuItem::decValue() {
// decrease value by step
if (valtype == "int") {
if (value - step > min) {
value -= step;
} else {
value = min;
}
} else if (valtype == "bool") {
value = !value;
}
};
String ConfigMenuItem::getUnit() {
return unit;
}
uint16_t ConfigMenuItem::getStep() {
return step;
}
void ConfigMenuItem::setStep(uint16_t newstep) {
if (std::find(steps.begin(), steps.end(), newstep) == steps.end()) {
return; // invalid step: not in list of possible steps
}
step = newstep;
}
int8_t ConfigMenuItem::getPos() {
return position;
};
void ConfigMenuItem::setPos(int8_t newpos) {
position = newpos;
};
String ConfigMenuItem::getType() {
return valtype;
}
ConfigMenu::ConfigMenu(String menutitle, uint16_t menu_x, uint16_t menu_y) {
title = menutitle;
x = menu_x;
y = menu_y;
};
ConfigMenuItem* ConfigMenu::addItem(String key, String label, String valtype, uint16_t val, String valunit) {
if (items.find(key) != items.end()) {
// duplicate keys not allowed
return nullptr;
}
ConfigMenuItem *itm = new ConfigMenuItem(valtype, label, val, valunit);
items.insert(std::pair<String, ConfigMenuItem*>(key, itm));
// Append key to index, index starting with 0
int8_t ix = items.size() - 1;
index[ix] = key;
itm->setPos(ix);
return itm;
};
void ConfigMenu::setItemDimension(uint16_t itemwidth, uint16_t itemheight) {
w = itemwidth;
h = itemheight;
};
void ConfigMenu::setItemActive(String key) {
if (items.find(key) != items.end()) {
activeitem = items[key]->getPos();
} else {
activeitem = -1;
}
};
int8_t ConfigMenu::getActiveIndex() {
return activeitem;
}
ConfigMenuItem* ConfigMenu::getActiveItem() {
if (activeitem < 0) {
return nullptr;
}
return items[index[activeitem]];
};
ConfigMenuItem* ConfigMenu::getItemByIndex(uint8_t ix) {
if (ix > index.size() - 1) {
return nullptr;
}
return items[index[ix]];
};
ConfigMenuItem* ConfigMenu::getItemByKey(String key) {
if (items.find(key) == items.end()) {
return nullptr;
}
return items[key];
};
uint8_t ConfigMenu::getItemCount() {
return items.size();
};
void ConfigMenu::goPrev() {
if (activeitem == 0) {
activeitem = items.size() - 1;
} else {
activeitem--;
}
}
void ConfigMenu::goNext() {
if (activeitem == items.size() - 1) {
activeitem = 0;
} else {
activeitem++;
}
}
Point ConfigMenu::getXY() {
return {static_cast<double>(x), static_cast<double>(y)};
}
Rect ConfigMenu::getRect() {
return {static_cast<double>(x), static_cast<double>(y),
static_cast<double>(w), static_cast<double>(h)};
}
Rect ConfigMenu::getItemRect(int8_t index) {
return {static_cast<double>(x), static_cast<double>(y + index * h),
static_cast<double>(w), static_cast<double>(h)};
}
void ConfigMenu::setCallback(void (*callback)()) {
fptrCallback = callback;
}
void ConfigMenu::storeValues() {
if (fptrCallback) {
fptrCallback();
}
}

View File

@ -0,0 +1,66 @@
#pragma once
#include <Arduino.h>
#include <vector>
#include <map>
#include "Graphics.h" // for Point and Rect
class ConfigMenuItem {
private:
String label;
uint16_t value;
String unit;
String valtype; // "int" | "bool"
uint16_t min;
uint16_t max;
std::vector<uint16_t> steps;
uint16_t step;
int8_t position; // counted fom 0
public:
ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemval, String itemunit);
void setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> steps);
bool checkRange(uint16_t checkval);
String getLabel();
uint16_t getValue();
bool setValue(uint16_t newval);
void incValue();
void decValue();
String getUnit();
uint16_t getStep();
void setStep(uint16_t newstep);
int8_t getPos();
void setPos(int8_t newpos);
String getType();
};
class ConfigMenu {
private:
String title;
std::map <String,ConfigMenuItem*> items;
std::map <uint8_t,String> index;
int8_t activeitem = -1; // refers to position of item
uint16_t x;
uint16_t y;
uint16_t w;
uint16_t h;
void (*fptrCallback)();
public:
ConfigMenu(String title, uint16_t menu_x, uint16_t menu_y);
ConfigMenuItem* addItem(String key, String label, String valtype, uint16_t val, String valunit);
void setItemDimension(uint16_t itemwidth, uint16_t itemheight);
int8_t getActiveIndex();
void setItemActive(String key);
ConfigMenuItem* getActiveItem();
ConfigMenuItem* getItemByIndex(uint8_t index);
ConfigMenuItem* getItemByKey(String key);
uint8_t getItemCount();
void goPrev();
void goNext();
Point getXY();
Rect getRect();
Rect getItemRect(int8_t index);
void setCallback(void (*callback)());
void storeValues();
};

View File

@ -0,0 +1,25 @@
/*
Generic graphics functions
*/
#include <math.h>
#include "Graphics.h"
Point rotatePoint(const Point& origin, const Point& p, double angle) {
// rotate poind around origin by degrees
Point rotated;
double phi = angle * M_PI / 180.0;
double dx = p.x - origin.x;
double dy = p.y - origin.y;
rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy;
rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy;
return rotated;
}
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}

17
lib/obp60task/Graphics.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <vector>
struct Point {
double x;
double y;
};
struct Rect {
double x;
double y;
double w;
double h;
};
Point rotatePoint(const Point& origin, const Point& p, double angle);
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle);

View File

@ -14,6 +14,30 @@ https://controllerstech.com/ws2812-leds-using-spi/
*/
String Color::toHex() {
char hexColor[8];
sprintf(hexColor, "#%02X%02X%02X", r, g, b);
return String(hexColor);
}
String Color::toName() {
static std::map<int, String> const names = {
{0xff0000, "Red"},
{0x00ff00, "Green"},
{0x0000ff, "Blue",},
{0xff9900, "Orange"},
{0xffff00, "Yellow"},
{0x3366ff, "Aqua"},
{0xff0066, "Violet"},
{0xffffff, "White"}
};
int color = (r << 16) + (g << 8) + b;
auto it = names.find(color);
if (it == names.end()) {
return toHex();
}
return it->second;
}
static uint8_t mulcolor(uint8_t f1, uint8_t f2){
uint16_t rt=f1;

View File

@ -10,7 +10,7 @@ class Color{
uint8_t g;
uint8_t b;
Color():r(0),g(0),b(0){}
Color(uint8_t cr, uint8_t cg,uint8_t cb):
Color(uint8_t cr, uint8_t cg, uint8_t cb):
b(cb),g(cg),r(cr){}
Color(const Color &o):b(o.b),g(o.g),r(o.r){}
bool equal(const Color &o) const{
@ -22,6 +22,8 @@ class Color{
bool operator != (const Color &other) const{
return ! equal(other);
}
String toHex();
String toName();
};
static Color COLOR_GREEN=Color(0,255,0);

View File

@ -109,6 +109,27 @@ void hardwareInit(GwApi *api)
}
}
void powerInit(String powermode) {
// Max Power | Only 5.0V | Min Power
if (powermode == "Max Power" || powermode == "Only 5.0V") {
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, true);// Power on ePaper display
setPortPin(OBP_POWER_SD, true); // Power on SD card
#endif
} else { // Min Power
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, false);// Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
#endif
}
}
void setPortPin(uint pin, bool value){
pinMode(pin, OUTPUT);
digitalWrite(pin, value);
@ -278,30 +299,20 @@ String xdrDelete(String input){
return input;
}
Point rotatePoint(const Point& origin, const Point& p, double angle) {
// rotate poind around origin by degrees
Point rotated;
double phi = angle * M_PI / 180.0;
double dx = p.x - origin.x;
double dy = p.y - origin.y;
rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy;
rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy;
return rotated;
}
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}
void fillPoly4(const std::vector<Point>& p4, uint16_t color) {
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color);
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color);
}
void drawPoly(const std::vector<Point>& points, uint16_t color) {
size_t polysize = points.size();
for (size_t i = 0; i < polysize - 1; i++) {
getdisplay().drawLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y, color);
}
// close path
getdisplay().drawLine(points[polysize-1].x, points[polysize-1].y, points[0].x, points[0].y, color);
}
// Split string into words, whitespace separated
std::vector<String> split(const String &s) {
std::vector<String> words;
@ -363,6 +374,24 @@ void drawTextRalign(int16_t x, int16_t y, String text) {
getdisplay().print(text);
}
// Draw text inside box, normal or inverted
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border) {
if (inverted) {
getdisplay().fillRect(box.x, box.y, box.w, box.h, fg);
getdisplay().setTextColor(bg);
} else {
if (border) {
getdisplay().fillRect(box.x + 1, box.y + 1, box.w - 2, box.h - 2, bg);
getdisplay().drawRect(box.x, box.y, box.w, box.h, fg);
}
getdisplay().setTextColor(fg);
}
uint16_t border_offset = box.h / 4; // 25% of box height
getdisplay().setCursor(box.x + border_offset, box.y + box.h - border_offset);
getdisplay().print(text);
getdisplay().setTextColor(fg);
}
// Show a triangle for trend direction high (x, y is the left edge)
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){
getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color);

View File

@ -5,6 +5,7 @@
#include <Arduino.h>
#include "OBP60Hardware.h"
#include "LedSpiTask.h"
#include "Graphics.h"
#include <GxEPD2_BW.h> // E-paper lib V2
#include <Adafruit_FRAM_I2C.h> // I2C FRAM
@ -65,19 +66,15 @@ GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
#define PAGE_UPDATE 1 // page wants display to update
#define PAGE_HIBERNATE 2 // page wants displey to hibernate
struct Point {
double x;
double y;
};
Point rotatePoint(const Point& origin, const Point& p, double angle);
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle);
void fillPoly4(const std::vector<Point>& p4, uint16_t color);
void drawPoly(const std::vector<Point>& points, uint16_t color);
void deepSleep(CommonData &common);
uint8_t getLastPage();
void hardwareInit(GwApi *api);
void powerInit(String powermode);
void setPortPin(uint pin, bool value); // Set port pin for extension port
@ -99,6 +96,7 @@ String xdrDelete(String input); // Delete xdr prefix from string
void drawTextCenter(int16_t cx, int16_t cy, String text);
void drawTextRalign(int16_t x, int16_t y, String text);
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border);
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color);
void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color);

View File

@ -2,6 +2,7 @@
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "ConfigMenu.h"
#include "images/logo64.xbm"
#include <esp32/clk.h>
#include "qrcode.h"
@ -25,37 +26,377 @@
* Consists of some sub-pages with following content:
* 1. Hard and software information
* 2. System settings
* 3. NMEA2000 device list
* 3. System configuration: running and NVRAM
* 4. NMEA2000 device list
* 5. SD Card information if available
*
* TODO
* - setCpuFrequencyMhz(80|160|240);
* - Accesspoint / ! Änderung im Gatewaycode erforderlich?
* if (! isApActive()) {
* wifiSSID = config->getString(config->wifiSSID);
* wifiPass = config->getString(config->wifiPass);
* wifiSoftAP(wifiSSID, wifiPass);
* }
* - Power mode
* powerInit(powermode);
*/
class PageSystem : public Page
{
uint64_t chipid;
bool simulation;
bool sdcard;
String buzzer_mode;
uint8_t buzzer_power;
String cpuspeed;
String rtc_module;
String gps_module;
String env_module;
private:
GwConfigHandler *config;
GwLog *logger;
String batt_sensor;
String solar_sensor;
String gen_sensor;
String rot_sensor;
double homelat;
double homelon;
// NVRAM config options
String flashLED;
char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard
// Generic data access
uint64_t chipid;
bool simulation;
bool sdcard;
String buzzer_mode;
uint8_t buzzer_power;
String cpuspeed;
String rtc_module;
String gps_module;
String env_module;
String batt_sensor;
String solar_sensor;
String gen_sensor;
String rot_sensor;
double homelat;
double homelon;
char mode = 'N'; // (N)ormal, (S)ettings, (C)onfiguration, (D)evice list, c(A)rd
int8_t editmode = -1; // marker for menu/edit/set function
ConfigMenu *menu;
void incMode() {
if (mode == 'N') { // Normal
mode = 'S';
} else if (mode == 'S') { // Settings
mode = 'C';
} else if (mode == 'C') { // Config
mode = 'D';
} else if (mode == 'D') { // Device list
if (sdcard) {
mode = 'A'; // SD-Card
} else {
mode = 'N';
}
} else {
mode = 'N';
}
}
void decMode() {
if (mode == 'N') {
if (sdcard) {
mode = 'A';
} else {
mode = 'D';
}
} else if (mode == 'S') { // Settings
mode = 'N';
} else if (mode == 'C') { // Config
mode = 'S';
} else if (mode == 'D') { // Device list
mode = 'C';
} else {
mode = 'D';
}
}
void displayModeNormal() {
// Default system page view
uint16_t y0 = 155;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System information");
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
displayBarcode(String(ssid), 320, 200, 2);
getdisplay().setCursor(8, 70);
getdisplay().print(String("MCUDEVICE-") + String(ssid));
getdisplay().setCursor(8, 95);
getdisplay().print("Firmware version: ");
getdisplay().setCursor(150, 95);
getdisplay().print(VERSINFO);
getdisplay().setCursor(8, 113);
getdisplay().print("Board version: ");
getdisplay().setCursor(150, 113);
getdisplay().print(BOARDINFO);
getdisplay().print(String(" HW ") + String(PCBINFO));
getdisplay().setCursor(8, 131);
getdisplay().print("Display version: ");
getdisplay().setCursor(150, 131);
getdisplay().print(DISPLAYINFO);
getdisplay().print("; GxEPD2 v");
getdisplay().print(GXEPD2INFO);
getdisplay().setCursor(8, 265);
#ifdef BOARD_OBP60S3
getdisplay().print("Press STBY to enter deep sleep mode");
#endif
#ifdef BOARD_OBP40S3
getdisplay().print("Press wheel to enter deep sleep mode");
#endif
// Flash memory size
uint32_t flash_size = ESP.getFlashChipSize();
getdisplay().setCursor(8, y0);
getdisplay().print("FLASH:");
getdisplay().setCursor(90, y0);
getdisplay().print(String(flash_size / 1024) + String(" kB"));
// PSRAM memory size
uint32_t psram_size = ESP.getPsramSize();
getdisplay().setCursor(8, y0 + 16);
getdisplay().print("PSRAM:");
getdisplay().setCursor(90, y0 + 16);
getdisplay().print(String(psram_size / 1024) + String(" kB"));
// FRAM available / status
getdisplay().setCursor(8, y0 + 32);
getdisplay().print("FRAM:");
getdisplay().setCursor(90, y0 + 32);
getdisplay().print(hasFRAM ? "available" : "not found");
#ifdef BOARD_OBP40S3
// SD-Card
getdisplay().setCursor(8, y0 + 48);
getdisplay().print("SD-Card:");
getdisplay().setCursor(90, y0 + 48);
if (sdcard) {
uint64_t cardsize = SD.cardSize() / (1024 * 1024);
getdisplay().print(String(cardsize) + String(" MB"));
} else {
getdisplay().print("off");
}
#endif
// CPU speed config / active
getdisplay().setCursor(202, y0);
getdisplay().print("CPU speed:");
getdisplay().setCursor(300, y0);
getdisplay().print(cpuspeed);
getdisplay().print(" / ");
int cpu_freq = esp_clk_cpu_freq() / 1000000;
getdisplay().print(String(cpu_freq));
// total RAM free
int Heap_free = esp_get_free_heap_size();
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Total free:");
getdisplay().setCursor(300, y0 + 16);
getdisplay().print(String(Heap_free));
// RAM free for task
int RAM_free = uxTaskGetStackHighWaterMark(NULL);
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Task free:");
getdisplay().setCursor(300, y0 + 32);
getdisplay().print(String(RAM_free));
}
void displayModeConfig() {
// Configuration interface
uint16_t x0 = 16;
uint16_t y0 = 80;
uint16_t dy = 20;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System configuration");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
/*getdisplay().setCursor(x0, y0);
getdisplay().print("CPU speed: 80 | 160 | 240");
getdisplay().setCursor(x0, y0 + 1 * dy);
getdisplay().print("Power mode: Max | 5V | Min");
getdisplay().setCursor(x0, y0 + 2 * dy);
getdisplay().print("Accesspoint: On | Off");
// TODO Change NVRAM-preferences settings here
getdisplay().setCursor(x0, y0 + 4 * dy);
getdisplay().print("Simulation: On | Off"); */
getdisplay().setFont(&Ubuntu_Bold8pt8b);
for (int i = 0 ; i < menu->getItemCount(); i++) {
ConfigMenuItem *itm = menu->getItemByIndex(i);
if (!itm) {
LOG_DEBUG(GwLog::ERROR, "Menu item not found: %d", i);
} else {
Rect r = menu->getItemRect(i);
bool inverted = (i == menu->getActiveIndex());
drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false);
if (inverted and editmode > 0) {
// triangle as edit marker
getdisplay().fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor);
}
getdisplay().setCursor(r.x + r.w + 40, r.y + r.h - 4);
if (itm->getType() == "int") {
getdisplay().print(itm->getValue());
getdisplay().print(itm->getUnit());
} else {
getdisplay().print(itm->getValue() == 0 ? "No" : "Yes");
}
}
}
}
void displayModeSettings() {
// View some of the current settings
const uint16_t x0 = 8;
const uint16_t y0 = 72;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, 48);
getdisplay().print("System settings");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
// left column
getdisplay().setCursor(x0, y0);
getdisplay().print("Simulation:");
getdisplay().setCursor(120, y0);
getdisplay().print(simulation ? "on" : "off");
getdisplay().setCursor(x0, y0 + 16);
getdisplay().print("Environment:");
getdisplay().setCursor(120, y0 + 16);
getdisplay().print(env_module);
getdisplay().setCursor(x0, y0 + 32);
getdisplay().print("Buzzer:");
getdisplay().setCursor(120, y0 + 32);
getdisplay().print(buzzer_mode);
getdisplay().setCursor(x0, y0 + 64);
getdisplay().print("GPS:");
getdisplay().setCursor(120, y0 + 64);
getdisplay().print(gps_module);
getdisplay().setCursor(x0, y0 + 80);
getdisplay().print("RTC:");
getdisplay().setCursor(120, y0 + 80);
getdisplay().print(rtc_module);
getdisplay().setCursor(x0, y0 + 96);
getdisplay().print("Wifi:");
getdisplay().setCursor(120, y0 + 96);
getdisplay().print(commonData->status.wifiApOn ? "on" : "off");
// Home location
getdisplay().setCursor(x0, y0 + 128);
getdisplay().print("Home Lat.:");
drawTextRalign(230, y0 + 128, formatLatitude(homelat));
getdisplay().setCursor(x0, y0 + 144);
getdisplay().print("Home Lon.:");
drawTextRalign(230, y0 + 144, formatLongitude(homelon));
// right column
getdisplay().setCursor(202, y0);
getdisplay().print("Batt. sensor:");
getdisplay().setCursor(320, y0);
getdisplay().print(batt_sensor);
// Solar sensor
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Solar sensor:");
getdisplay().setCursor(320, y0 + 16);
getdisplay().print(solar_sensor);
// Generator sensor
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Gen. sensor:");
getdisplay().setCursor(320, y0 + 32);
getdisplay().print(gen_sensor);
#ifdef BOARD_OBP60S3
// Backlight infos
getdisplay().setCursor(202, y0 + 64);
getdisplay().print("Backlight:");
getdisplay().setCursor(320, y0 + 64);
getdisplay().printf("%d%%", commonData->backlight.brightness);
// TODO test function with OBP60 device
getdisplay().setCursor(202, y0 + 80);
getdisplay().print("Bl color:");
getdisplay().setCursor(320, y0 + 80);
getdisplay().print(commonData->backlight.color.toName());
getdisplay().setCursor(202, y0 + 96);
getdisplay().print("Bl mode:");
getdisplay().setCursor(320, y0 + 96);
getdisplay().print(commonData->backlight.mode);
// TODO Buzzer mode and power
#endif
// Gyro sensor
// WIP / FEATURE
}
void displayModeSDCard() {
// SD card info
uint16_t x0 = 20;
uint16_t y0 = 72;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("SD Card info");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(x0, y0);
getdisplay().print("Work in progress...");
}
void displayModeDevicelist() {
// NMEA2000 device list
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("NMEA2000 device list");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 80);
getdisplay().print("RxD: ");
getdisplay().print(String(commonData->status.n2kRx));
getdisplay().setCursor(20, 100);
getdisplay().print("TxD: ");
getdisplay().print(String(commonData->status.n2kTx));
}
void storeConfig() {
menu->storeValues();
}
public:
PageSystem(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSystem");
config = commonData->config;
logger = commonData->logger;
logger->logDebug(GwLog::LOG,"Instantiate PageSystem");
if (hasFRAM) {
mode = fram.read(FRAM_SYSTEM_MODE);
}
flashLED = common.config->getString(common.config->flashLED);
chipid = ESP.getEfuseMac();
simulation = common.config->getBool(common.config->useSimuData);
#ifdef BOARD_OBP40S3
@ -74,6 +415,24 @@ public:
rot_sensor = common.config->getString(common.config->useRotSensor);
homelat = common.config->getString(common.config->homeLAT).toDouble();
homelon = common.config->getString(common.config->homeLON).toDouble();
// CPU speed: 80 | 160 | 240
// Power mode: Max | 5V | Min
// Accesspoint: On | Off
// TODO Change NVRAM-preferences settings here
// getdisplay().setCursor(x0, y0 + 4 * dy);
// getdisplay().print("Simulation: On | Off");
// Initialize config menu
menu = new ConfigMenu("Options", 40, 80);
menu->setItemDimension(150, 20);
ConfigMenuItem *newitem;
newitem = menu->addItem("accesspoint", "Accesspoint", "bool", 0, "");
newitem = menu->addItem("simulation", "Simulation", "on/off", 0, "");
menu->setItemActive("accesspoint");
}
virtual void setupKeys(){
@ -90,19 +449,7 @@ public:
// Switch display mode
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler");
if (key == 2) {
if (mode == 'N') {
mode = 'S';
} else if (mode == 'S') {
mode = 'D';
} else if (mode == 'D') {
if (sdcard) {
mode = 'C';
} else {
mode = 'N';
}
} else {
mode = 'N';
}
incMode();
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
return 0;
}
@ -126,8 +473,13 @@ public:
}
#endif
#ifdef BOARD_OBP40S3
// grab cursor keys to disable page navigation
if (key == 9 or key == 10) {
// use cursor keys for local mode navigation
if (key == 9) {
incMode();
return 0;
}
if (key == 10) {
decMode();
return 0;
}
// standby / deep sleep
@ -168,225 +520,35 @@ public:
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
String flashLED = config->getString(config->flashLED);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
if(flashLED == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem");
// Draw page
//***********************************************************
uint16_t x0 = 8; // left column
uint16_t y0 = 48; // data table starts here
// Logging page information
LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem, Mode=%c", mode);
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height());
if (mode == 'N') {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System Information");
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
y0 = 155;
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
displayBarcode(String(ssid), 320, 200, 2);
getdisplay().setCursor(8, 70);
getdisplay().print(String("MCUDEVICE-") + String(ssid));
getdisplay().setCursor(8, 95);
getdisplay().print("Firmware version: ");
getdisplay().setCursor(150, 95);
getdisplay().print(VERSINFO);
getdisplay().setCursor(8, 113);
getdisplay().print("Board version: ");
getdisplay().setCursor(150, 113);
getdisplay().print(BOARDINFO);
getdisplay().print(String(" HW ") + String(PCBINFO));
getdisplay().setCursor(8, 131);
getdisplay().print("Display version: ");
getdisplay().setCursor(150, 131);
getdisplay().print(DISPLAYINFO);
getdisplay().print("; GxEPD2 v");
getdisplay().print(GXEPD2INFO);
getdisplay().setCursor(8, 265);
#ifdef BOARD_OBP60S3
getdisplay().print("Press STBY to enter deep sleep mode");
#endif
#ifdef BOARD_OBP40S3
getdisplay().print("Press wheel to enter deep sleep mode");
#endif
// Flash memory size
uint32_t flash_size = ESP.getFlashChipSize();
getdisplay().setCursor(8, y0);
getdisplay().print("FLASH:");
getdisplay().setCursor(90, y0);
getdisplay().print(String(flash_size / 1024) + String(" kB"));
// PSRAM memory size
uint32_t psram_size = ESP.getPsramSize();
getdisplay().setCursor(8, y0 + 16);
getdisplay().print("PSRAM:");
getdisplay().setCursor(90, y0 + 16);
getdisplay().print(String(psram_size / 1024) + String(" kB"));
// FRAM available / status
getdisplay().setCursor(8, y0 + 32);
getdisplay().print("FRAM:");
getdisplay().setCursor(90, y0 + 32);
getdisplay().print(hasFRAM ? "available" : "not found");
#ifdef BOARD_OBP40S3
// SD-Card
getdisplay().setCursor(8, y0 + 48);
getdisplay().print("SD-Card:");
getdisplay().setCursor(90, y0 + 48);
if (sdcard) {
uint64_t cardsize = SD.cardSize() / (1024 * 1024);
getdisplay().print(String(cardsize) + String(" MB"));
} else {
getdisplay().print("off");
}
#endif
// CPU speed config / active
getdisplay().setCursor(202, y0);
getdisplay().print("CPU speed:");
getdisplay().setCursor(300, y0);
getdisplay().print(cpuspeed);
getdisplay().print(" / ");
int cpu_freq = esp_clk_cpu_freq() / 1000000;
getdisplay().print(String(cpu_freq));
// total RAM free
int Heap_free = esp_get_free_heap_size();
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Total free:");
getdisplay().setCursor(300, y0 + 16);
getdisplay().print(String(Heap_free));
// RAM free for task
int RAM_free = uxTaskGetStackHighWaterMark(NULL);
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Task free:");
getdisplay().setCursor(300, y0 + 32);
getdisplay().print(String(RAM_free));
} else if (mode == 'S') {
// Settings
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, 48);
getdisplay().print("System settings");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
x0 = 8;
y0 = 72;
// left column
getdisplay().setCursor(x0, y0);
getdisplay().print("Simulation:");
getdisplay().setCursor(120, y0);
getdisplay().print(simulation ? "on" : "off");
getdisplay().setCursor(x0, y0 + 16);
getdisplay().print("Environment:");
getdisplay().setCursor(120, y0 + 16);
getdisplay().print(env_module);
getdisplay().setCursor(x0, y0 + 32);
getdisplay().print("Buzzer:");
getdisplay().setCursor(120, y0 + 32);
getdisplay().print(buzzer_mode);
getdisplay().setCursor(x0, y0 + 64);
getdisplay().print("GPS:");
getdisplay().setCursor(120, y0 + 64);
getdisplay().print(gps_module);
getdisplay().setCursor(x0, y0 + 80);
getdisplay().print("RTC:");
getdisplay().setCursor(120, y0 + 80);
getdisplay().print(rtc_module);
getdisplay().setCursor(x0, y0 + 96);
getdisplay().print("Wifi:");
getdisplay().setCursor(120, y0 + 96);
getdisplay().print(commonData->status.wifiApOn ? "on" : "off");
// Home location
getdisplay().setCursor(x0, y0 + 128);
getdisplay().print("Home Lat.:");
getdisplay().setCursor(120, y0 + 128);
getdisplay().print(formatLatitude(homelat));
getdisplay().setCursor(x0, y0 + 144);
getdisplay().print("Home Lon.:");
getdisplay().setCursor(120, y0 + 144);
getdisplay().print(formatLongitude(homelon));
// right column
getdisplay().setCursor(202, y0);
getdisplay().print("Batt. sensor:");
getdisplay().setCursor(320, y0);
getdisplay().print(batt_sensor);
// Solar sensor
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Solar sensor:");
getdisplay().setCursor(320, y0 + 16);
getdisplay().print(solar_sensor);
// Generator sensor
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Gen. sensor:");
getdisplay().setCursor(320, y0 + 32);
getdisplay().print(gen_sensor);
// Gyro sensor
} else if (mode == 'C') {
// Card info
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("SD Card info");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
x0 = 20;
y0 = 72;
getdisplay().setCursor(x0, y0);
getdisplay().print("Work in progress...");
} else {
// NMEA2000 device list
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("NMEA2000 device list");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 80);
getdisplay().print("RxD: ");
getdisplay().print(String(commonData->status.n2kRx));
getdisplay().setCursor(20, 100);
getdisplay().print("TxD: ");
getdisplay().print(String(commonData->status.n2kTx));
// call current system page
switch (mode) {
case 'N':
displayModeNormal();
break;
case 'S':
displayModeSettings();
break;
case 'C':
displayModeConfig();
break;
case 'A':
displayModeSDCard();
break;
case 'D':
displayModeDevicelist();
break;
}
// Update display

View File

@ -55,31 +55,13 @@ void OBP60Init(GwApi *api){
// Check I2C devices
// Init hardware
hardwareInit(api);
// Init power rail 5.0V
// Init power
String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str());
if(powermode == "Max Power" || powermode == "Only 5.0V"){
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, true);// Power on ePaper display
setPortPin(OBP_POWER_SD, true); // Power on SD card
#endif
}
else{
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, false);// Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
#endif
}
powerInit(powermode);
#ifdef BOARD_OBP40S3
bool sdcard = config->getBool(config->useSDCard);