mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-10-07 07:25:14 +02:00
Compare commits
186
Commits
anchor
..
8287242bf9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8287242bf9 | ||
|
|
6ee158519d | ||
|
|
933b1677f6 | ||
|
|
1ea63d7612 | ||
|
|
9186491b5e | ||
|
|
c1e69dd81b | ||
|
|
3dd13ae899 | ||
|
|
6c318d5a69 | ||
|
|
0202c5d82a | ||
|
|
d44dc94ec7 | ||
|
|
0f98331eb4 | ||
|
|
33768440b1 | ||
|
|
9eb0983ee4 | ||
|
|
93084c980a | ||
|
|
18c1da5991 | ||
|
|
99f88cca3f | ||
|
|
fecb9a8043 | ||
|
|
985d4ef3e6 | ||
|
|
50dc5955a0 | ||
|
|
aebc18a75d | ||
|
|
e60ead0455 | ||
|
|
eaa1f793ff | ||
|
|
88dfa84cc8 | ||
|
|
6c6557f54e | ||
|
|
58a0bbf0d4 | ||
|
|
f68e988736 | ||
|
|
807e4cec30 | ||
|
|
e129b18057 | ||
|
|
e757ac4016 | ||
|
|
71e3ba79f6 | ||
|
|
da4c66bec2 | ||
|
|
fb181c1b01 | ||
|
|
53cf497a70 | ||
|
|
92ee3a0c4f | ||
|
|
4e07e1d22e | ||
|
|
2a8558589b | ||
|
|
2da5ffc4ea | ||
|
|
4e8953ed2a | ||
|
|
83aae1ea81 | ||
|
|
82c0ce91a5 | ||
|
|
2e7b0435b2 | ||
|
|
b8e9e02617 | ||
|
|
fe05b734de | ||
|
|
a93f951f18 | ||
|
|
8ebde00676 | ||
|
|
2a98f49e40 | ||
|
|
d1b729a554 | ||
|
|
dac7459355 | ||
|
|
775d4456a2 | ||
|
|
04ea564bdb | ||
|
|
751f4e2296 | ||
|
|
3dd7f4751a | ||
|
|
a2822451c1 | ||
|
|
63af2807fb | ||
|
|
c17f8238a4 | ||
|
|
8f50c6a09c | ||
|
|
e906ad6d00 | ||
|
|
89482016db | ||
|
|
abfe68f963 | ||
|
|
01ff478c29 | ||
|
|
d0fe7a2d9b | ||
|
|
80800c32a3 | ||
|
|
a218da8948 | ||
|
|
1425297ef4 | ||
|
|
92a6266d71 | ||
|
|
7be6796a84 | ||
|
|
15c5c450a1 | ||
|
|
c0aa6b4010 | ||
|
|
9c9d435390 | ||
|
|
2d5838ebc8 | ||
|
|
2f30e858c8 | ||
|
|
1098afa8b2 | ||
|
|
cfac5b0a05 | ||
|
|
f84ef25a30 | ||
|
|
f99656f3d7 | ||
|
|
ae1f402b49 | ||
|
|
3b2f73e22a | ||
|
|
89dfdb35c3 | ||
|
|
3f1d2d4ee2 | ||
|
|
856e19cd5e | ||
|
|
530c101421 | ||
|
|
3deb80b2c6 | ||
|
|
1f0e3cb6eb | ||
|
|
a445726757 | ||
|
|
aecc3c775f | ||
|
|
0dfb1f8535 | ||
|
|
ba851d7e1b | ||
|
|
f7b1b56715 | ||
|
|
08153cc585 | ||
|
|
e309ec2123 | ||
|
|
7be72a8cc4 | ||
|
|
5fcca363f8 | ||
|
|
7c6005958e | ||
|
|
522759a26c | ||
|
|
ea63386cfc | ||
|
|
f362e96749 | ||
|
|
27d0e5d1b7 | ||
|
|
53f7a1c5bb | ||
|
|
588c88dfff | ||
|
|
73bc57fe9a | ||
|
|
d13c4af9cb | ||
|
|
a7cdfe700b | ||
|
|
f165c628ae | ||
|
|
54dcb5fd5a | ||
|
|
604e2a15bd | ||
|
|
4e0bc88419 | ||
|
|
c1a49fac4b | ||
|
|
019f7a11ef | ||
|
|
12fc962107 | ||
|
|
a0afd5deca | ||
|
|
57272f3a44 | ||
|
|
64f9058f85 | ||
|
|
cd84e427a3 | ||
|
|
86cd1ed97c | ||
|
|
ec667c18f5 | ||
|
|
af9bca4d66 | ||
|
|
bc1440c428 | ||
|
|
5f161ce839 | ||
|
|
a17db389fb | ||
|
|
c5533ab0ef | ||
|
|
44e99cda50 | ||
|
|
2c2318adcc | ||
|
|
373878825c | ||
|
|
8cd193f2fc | ||
|
|
44b318ea0c | ||
|
|
9cfa9ccf67 | ||
|
|
762004ac9f | ||
|
|
907a817573 | ||
|
|
53bdc491f1 | ||
|
|
6cd0965e46 | ||
|
|
f3f20f40c7 | ||
|
|
ab0bac7577 | ||
|
|
a4b99fb619 | ||
|
|
b7fab08306 | ||
|
|
b10792b243 | ||
|
|
2b2d2e3fd6 | ||
|
|
75724b29eb | ||
|
|
abcbf50422 | ||
|
|
03b9294a1e | ||
|
|
2e47263008 | ||
|
|
ffa9c8ac83 | ||
|
|
39c4e7fb32 | ||
|
|
5a30d7d022 | ||
|
|
b29d89525a | ||
|
|
270642ad7c | ||
|
|
253aaa5b2e | ||
|
|
1aac8fdcd9 | ||
|
|
7e35c250cd | ||
|
|
0b3b5f387b | ||
|
|
42c147ac57 | ||
|
|
7434601382 | ||
|
|
2d6127912b | ||
|
|
415c155949 | ||
|
|
9ac0b4a2bb | ||
|
|
8a9f87a8f4 | ||
|
|
ea6920f696 | ||
|
|
ce4b581f7f | ||
|
|
aea401589d | ||
|
|
e33f908187 | ||
|
|
065a9807d2 | ||
|
|
98c318f055 | ||
|
|
97fcebdcb7 | ||
|
|
a6fd3ef599 | ||
|
|
79aeeed65c | ||
|
|
386b023677 | ||
|
|
501b9f7023 | ||
|
|
94f29eed06 | ||
|
|
6a0811f42f | ||
|
|
bcb618fc7b | ||
|
|
2de3a1b084 | ||
|
|
c647c2fe44 | ||
|
|
25422b77d6 | ||
|
|
66e71acac3 | ||
|
|
b85504bf50 | ||
|
|
f21c8da2d2 | ||
|
|
3043be8e1d | ||
|
|
02b2c888ee | ||
|
|
1b4c952d2b | ||
|
|
00b06f458b | ||
|
|
34af16092b | ||
|
|
6c7997e369 | ||
|
|
7f747e9b35 | ||
|
|
71512e7262 | ||
|
|
4468c0555b | ||
|
|
99404991a3 | ||
|
|
ee5077e0a5 |
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino":{
|
||||||
|
"ldscript": "esp32s3_out.ld",
|
||||||
|
"partitions": "default_16MB.csv",
|
||||||
|
"memory_type": "qio_opi"
|
||||||
|
},
|
||||||
|
"core": "esp32",
|
||||||
|
"extra_flags": [
|
||||||
|
"-DBOARD_HAS_PSRAM",
|
||||||
|
"-DARDUINO_ESP32S3_DEV",
|
||||||
|
"-DARDUINO_USB_MODE=1",
|
||||||
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||||
|
],
|
||||||
|
"f_cpu": "240000000L",
|
||||||
|
"f_flash": "80000000L",
|
||||||
|
"flash_mode": "qio",
|
||||||
|
"hwids": [
|
||||||
|
[
|
||||||
|
"0x303A",
|
||||||
|
"0x1001"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"mcu": "esp32s3",
|
||||||
|
"variant": "obp70s3"
|
||||||
|
},
|
||||||
|
"connectivity": [
|
||||||
|
"bluetooth",
|
||||||
|
"wifi"
|
||||||
|
],
|
||||||
|
"debug": {
|
||||||
|
"default_tool": "esp-builtin",
|
||||||
|
"onboard_tools": [
|
||||||
|
"esp-builtin"
|
||||||
|
],
|
||||||
|
"openocd_target": "esp32s3.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": [
|
||||||
|
"arduino",
|
||||||
|
"espidf"
|
||||||
|
],
|
||||||
|
"name": "OBP70 ESP32-S3-N16R8 16 MB QD, 8 MB PSRAM)",
|
||||||
|
"upload": {
|
||||||
|
"flash_size": "16MB",
|
||||||
|
"maximum_ram_size": 327680,
|
||||||
|
"maximum_size": 16777216,
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"wait_for_upload_port": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 460800
|
||||||
|
},
|
||||||
|
"url": "https://open-boat-projects.org/en/diy-multifunktionsdisplay-obp-60/",
|
||||||
|
"vendor": "Open Boat Projects"
|
||||||
|
}
|
||||||
+10
-1
@@ -2,6 +2,9 @@
|
|||||||
#define _GWWIFI_H
|
#define _GWWIFI_H
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <GWConfig.h>
|
#include <GWConfig.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
class GwWifi{
|
class GwWifi{
|
||||||
private:
|
private:
|
||||||
const GwConfigHandler *config;
|
const GwConfigHandler *config;
|
||||||
@@ -16,13 +19,19 @@ class GwWifi{
|
|||||||
bool apActive=false;
|
bool apActive=false;
|
||||||
bool fixedApPass=true;
|
bool fixedApPass=true;
|
||||||
bool clientIsConnected=false;
|
bool clientIsConnected=false;
|
||||||
|
SemaphoreHandle_t wifiMutex=nullptr;
|
||||||
|
static const TickType_t WIFI_MUTEX_TIMEOUT=pdMS_TO_TICKS(1000);
|
||||||
|
bool acquireMutex();
|
||||||
|
void releaseMutex();
|
||||||
public:
|
public:
|
||||||
const char *AP_password = "esp32nmea2k";
|
const char *AP_password = "esp32nmea2k";
|
||||||
GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true);
|
GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true);
|
||||||
|
~GwWifi();
|
||||||
void setup();
|
void setup();
|
||||||
void loop();
|
void loop();
|
||||||
bool clientConnected();
|
bool clientConnected();
|
||||||
bool connectClient();
|
bool connectClient(); // Blocking version
|
||||||
|
bool connectClientAsync(); // Non-blocking version for other tasks
|
||||||
String apIP();
|
String apIP();
|
||||||
bool isApActive(){return apActive;}
|
bool isApActive(){return apActive;}
|
||||||
bool isClientActive(){return wifiClient->asBoolean();}
|
bool isClientActive(){return wifiClient->asBoolean();}
|
||||||
|
|||||||
+99
-3
@@ -1,7 +1,6 @@
|
|||||||
#include <esp_wifi.h>
|
#include <esp_wifi.h>
|
||||||
#include "GWWifi.h"
|
#include "GWWifi.h"
|
||||||
|
|
||||||
|
|
||||||
GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
|
GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
|
||||||
this->config=config;
|
this->config=config;
|
||||||
this->logger=log;
|
this->logger=log;
|
||||||
@@ -9,6 +8,28 @@ GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
|
|||||||
wifiSSID=config->getConfigItem(config->wifiSSID,true);
|
wifiSSID=config->getConfigItem(config->wifiSSID,true);
|
||||||
wifiPass=config->getConfigItem(config->wifiPass,true);
|
wifiPass=config->getConfigItem(config->wifiPass,true);
|
||||||
this->fixedApPass=fixedApPass;
|
this->fixedApPass=fixedApPass;
|
||||||
|
wifiMutex=xSemaphoreCreateMutex();
|
||||||
|
if (wifiMutex==nullptr){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: unable to create mutex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GwWifi::~GwWifi(){
|
||||||
|
if (wifiMutex!=nullptr){
|
||||||
|
vSemaphoreDelete(wifiMutex);
|
||||||
|
wifiMutex=nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GwWifi::acquireMutex(){
|
||||||
|
if (wifiMutex==nullptr) return false;
|
||||||
|
return xSemaphoreTake(wifiMutex,WIFI_MUTEX_TIMEOUT)==pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GwWifi::releaseMutex(){
|
||||||
|
if (wifiMutex!=nullptr){
|
||||||
|
xSemaphoreGive(wifiMutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void GwWifi::setup(){
|
void GwWifi::setup(){
|
||||||
LOG_DEBUG(GwLog::LOG,"Wifi setup");
|
LOG_DEBUG(GwLog::LOG,"Wifi setup");
|
||||||
@@ -85,8 +106,14 @@ bool GwWifi::connectInternal(){
|
|||||||
if (wifiClient->asBoolean()){
|
if (wifiClient->asBoolean()){
|
||||||
clientIsConnected=false;
|
clientIsConnected=false;
|
||||||
LOG_DEBUG(GwLog::LOG,"creating wifiClient ssid=%s",wifiSSID->asString().c_str());
|
LOG_DEBUG(GwLog::LOG,"creating wifiClient ssid=%s",wifiSSID->asString().c_str());
|
||||||
|
// CRITICAL SECTION: WiFi-Operationen müssen serialisiert werden
|
||||||
|
if (!acquireMutex()){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in connectInternal");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
WiFi.setAutoReconnect(false); //#102
|
WiFi.setAutoReconnect(false); //#102
|
||||||
wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString());
|
wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString());
|
||||||
|
releaseMutex();
|
||||||
LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt);
|
LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt);
|
||||||
lastConnectStart=millis();
|
lastConnectStart=millis();
|
||||||
return true;
|
return true;
|
||||||
@@ -104,9 +131,47 @@ void GwWifi::loop(){
|
|||||||
if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now)
|
if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now)
|
||||||
{
|
{
|
||||||
LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString());
|
LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString());
|
||||||
WiFi.disconnect();
|
|
||||||
|
// Keep locked sections short to avoid cross-core stalls/WDT.
|
||||||
|
if (acquireMutex()){
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
releaseMutex();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop (disconnect)");
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(300);
|
||||||
|
|
||||||
|
if (!apActive) {
|
||||||
|
if (acquireMutex()){
|
||||||
|
esp_err_t stopErr=esp_wifi_stop();
|
||||||
|
releaseMutex();
|
||||||
|
if (stopErr != ESP_OK){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: esp_wifi_stop failed: %d",(int)stopErr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop (stop)");
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
if (acquireMutex()){
|
||||||
|
esp_err_t startErr=esp_wifi_start();
|
||||||
|
releaseMutex();
|
||||||
|
if (startErr != ESP_OK){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: esp_wifi_start failed: %d",(int)startErr);
|
||||||
|
}
|
||||||
connectInternal();
|
connectInternal();
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop (start)");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connectInternal();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if (! clientIsConnected){
|
if (! clientIsConnected){
|
||||||
@@ -126,11 +191,42 @@ void GwWifi::loop(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GwWifi::clientConnected(){
|
bool GwWifi::clientConnected(){
|
||||||
return WiFi.status() == WL_CONNECTED;
|
// CRITICAL SECTION: WiFi.status() muss geschützt werden
|
||||||
|
if (!acquireMutex()){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in clientConnected");
|
||||||
|
return false; // Conservative: nehme an, nicht verbunden
|
||||||
|
}
|
||||||
|
bool result = WiFi.status() == WL_CONNECTED;
|
||||||
|
releaseMutex();
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool GwWifi::connectClient(){
|
bool GwWifi::connectClient(){
|
||||||
|
// CRITICAL SECTION: Disconnect und Connect müssen atomar sein
|
||||||
|
if (!acquireMutex()){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in connectClient");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
|
releaseMutex();
|
||||||
|
return connectInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GwWifi::connectClientAsync(){
|
||||||
|
// Non-blocking version: Versuche Mutex zu nehmen, gib aber sofort auf
|
||||||
|
// Ideal für Tasks, die nicht blockieren dürfen
|
||||||
|
if (wifiMutex==nullptr){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex not initialized in connectClientAsync");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (xSemaphoreTake(wifiMutex, 0)!=pdTRUE){
|
||||||
|
LOG_DEBUG(GwLog::LOG,"GwWifi: connectClientAsync skipped - WiFi busy");
|
||||||
|
return false; // WiFi ist aktuell busy, versuche es später nochmal
|
||||||
|
}
|
||||||
|
WiFi.disconnect();
|
||||||
|
xSemaphoreGive(wifiMutex);
|
||||||
return connectInternal();
|
return connectInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
Changes to original project (wellenvogel)
|
||||||
|
|
||||||
|
* esp32-nmea2000-obp60/gwwifi/GwWifi.cpp
|
||||||
|
- any fixes for reconnect handling
|
||||||
|
* GWStatisticsw.h
|
||||||
|
- changed time source for log messages
|
||||||
|
* esp32-nmea2000-obp60/platformio.ini
|
||||||
|
- change to newer versions for AsyncTCP and AsyncWebServer (better handling for bad WiFi connections)
|
||||||
|
|
||||||
|
From AsyncTCP-esphome @ 2.0.1
|
||||||
|
ottowinter/ESPAsyncWebServer-esphome@2.0.1
|
||||||
|
|
||||||
|
To AsyncTCP-esphome @ 2.1.1
|
||||||
|
ottowinter/ESPAsyncWebServer-esphome@3.4.0
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
dir="$1"
|
||||||
|
|
||||||
|
total=0
|
||||||
|
|
||||||
|
while IFS= read -r -d '' file; do
|
||||||
|
lines=$(wc -l < "$file")
|
||||||
|
echo "$file : $lines"
|
||||||
|
total=$((total + lines))
|
||||||
|
done < <(find "$dir" \( -name "*.c" -o -name "*.cpp" -o -name "*.h" \) -type f -print0)
|
||||||
|
|
||||||
|
echo "-----------------------"
|
||||||
|
echo "Over all files: $total"
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
/*
|
|
||||||
Menu system for online configuration
|
|
||||||
|
|
||||||
A menu consists of a list of menuitems.
|
|
||||||
|
|
||||||
Graphical representation is stored:
|
|
||||||
upper left corner: x, y
|
|
||||||
bounding box:
|
|
||||||
A menu consists of three columns
|
|
||||||
- menu text, if selected highlighted
|
|
||||||
- menu value with optional unit
|
|
||||||
- menu description or additional data for value
|
|
||||||
|
|
||||||
*/
|
|
||||||
#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;
|
|
||||||
step = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> valsteps) {
|
|
||||||
min = valmin;
|
|
||||||
max = valmax;
|
|
||||||
steps = valsteps;
|
|
||||||
step = steps[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
#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 desc; // optional data to display
|
|
||||||
String valtype; // "int" | "bool" -> TODO "list"
|
|
||||||
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();
|
|
||||||
};
|
|
||||||
@@ -2,13 +2,22 @@
|
|||||||
#include <mbedtls/base64.h>
|
#include <mbedtls/base64.h>
|
||||||
|
|
||||||
// Decoder for Base64 content
|
// Decoder for Base64 content
|
||||||
bool ImageDecoder::decodeBase64(const String& base64, uint8_t* outBuffer, size_t outSize, size_t& decodedSize) {
|
bool ImageDecoder::decodeBase64(const char* base64, size_t base64Len, uint8_t* outBuffer, size_t outSize, size_t& decodedSize) {
|
||||||
|
if (base64 == nullptr) {
|
||||||
|
decodedSize = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int ret = mbedtls_base64_decode(
|
int ret = mbedtls_base64_decode(
|
||||||
outBuffer,
|
outBuffer,
|
||||||
outSize,
|
outSize,
|
||||||
&decodedSize,
|
&decodedSize,
|
||||||
(const unsigned char*)base64.c_str(),
|
(const unsigned char*)base64,
|
||||||
base64.length()
|
base64Len
|
||||||
);
|
);
|
||||||
return (ret == 0);
|
return (ret == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decoder for Base64 content
|
||||||
|
bool ImageDecoder::decodeBase64(const String& base64, uint8_t* outBuffer, size_t outSize, size_t& decodedSize) {
|
||||||
|
return decodeBase64(base64.c_str(), base64.length(), outBuffer, outSize, decodedSize);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,5 +5,6 @@
|
|||||||
|
|
||||||
class ImageDecoder {
|
class ImageDecoder {
|
||||||
public:
|
public:
|
||||||
|
bool decodeBase64(const char* base64, size_t base64Len, uint8_t* outBuffer, size_t outSize, size_t& decodedSize);
|
||||||
bool decodeBase64(const String& base64, uint8_t* outBuffer, size_t outSize, size_t& decodedSize);
|
bool decodeBase64(const String& base64, uint8_t* outBuffer, size_t outSize, size_t& decodedSize);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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){
|
static uint8_t mulcolor(uint8_t f1, uint8_t f2){
|
||||||
uint16_t rt=f1;
|
uint16_t rt=f1;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class Color{
|
|||||||
uint8_t g;
|
uint8_t g;
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
Color():r(0),g(0),b(0){}
|
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){}
|
b(cb),g(cg),r(cr){}
|
||||||
Color(const Color &o):b(o.b),g(o.g),r(o.r){}
|
Color(const Color &o):b(o.b),g(o.g),r(o.r){}
|
||||||
bool equal(const Color &o) const{
|
bool equal(const Color &o) const{
|
||||||
@@ -22,6 +22,8 @@ class Color{
|
|||||||
bool operator != (const Color &other) const{
|
bool operator != (const Color &other) const{
|
||||||
return ! equal(other);
|
return ! equal(other);
|
||||||
}
|
}
|
||||||
|
String toHex();
|
||||||
|
String toName();
|
||||||
};
|
};
|
||||||
|
|
||||||
static Color COLOR_GREEN=Color(0,255,0);
|
static Color COLOR_GREEN=Color(0,255,0);
|
||||||
|
|||||||
+442
-30
@@ -1,16 +1,140 @@
|
|||||||
#include "NetworkClient.h"
|
#include "NetworkClient.h"
|
||||||
|
#include "GWWifi.h" // WiFi management (thread-safe)
|
||||||
|
|
||||||
|
extern GwWifi gwWifi; // Extern declaration of global WiFi instance
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "puff.h"
|
#include "puff.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t crc32_update(uint32_t crc, const uint8_t* data, size_t len) {
|
||||||
|
crc = ~crc;
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
crc ^= data[i];
|
||||||
|
for (int bit = 0; bit < 8; ++bit) {
|
||||||
|
uint32_t mask = -(int32_t)(crc & 1U);
|
||||||
|
crc = (crc >> 1) ^ (0xEDB88320U & mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ~crc;
|
||||||
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
NetworkClient::NetworkClient(size_t reserveSize)
|
NetworkClient::NetworkClient(size_t reserveSize)
|
||||||
: _doc(reserveSize),
|
: _doc(reserveSize),
|
||||||
_valid(false)
|
_valid(false),
|
||||||
|
_jsonRaw(nullptr),
|
||||||
|
_jsonRawLen(0),
|
||||||
|
_imageWidth(0),
|
||||||
|
_imageHeight(0),
|
||||||
|
_numberPixels(0),
|
||||||
|
_pictureBase64(nullptr),
|
||||||
|
_pictureBase64Len(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkClient::~NetworkClient() {
|
||||||
|
if (_jsonRaw != nullptr) {
|
||||||
|
free(_jsonRaw);
|
||||||
|
_jsonRaw = nullptr;
|
||||||
|
_jsonRawLen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkClient::findJsonIntField(const char* json, size_t len, const char* key, int& outValue) {
|
||||||
|
if (json == nullptr || key == nullptr || len == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pattern[64];
|
||||||
|
int plen = snprintf(pattern, sizeof(pattern), "\"%s\"", key);
|
||||||
|
if (plen <= 0 || (size_t)plen >= sizeof(pattern)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* keyPos = strstr(json, pattern);
|
||||||
|
if (keyPos == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* end = json + len;
|
||||||
|
const char* colon = strchr(keyPos + plen, ':');
|
||||||
|
if (colon == nullptr || colon >= end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* p = colon + 1;
|
||||||
|
while (p < end && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
if (p >= end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* parseEnd = nullptr;
|
||||||
|
long value = strtol(p, &parseEnd, 10);
|
||||||
|
if (parseEnd == p) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outValue = (int)value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkClient::extractJsonStringInPlace(char* json, size_t len, const char* key, char*& outValue, size_t& outLen) {
|
||||||
|
outValue = nullptr;
|
||||||
|
outLen = 0;
|
||||||
|
|
||||||
|
if (json == nullptr || key == nullptr || len == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pattern[64];
|
||||||
|
int plen = snprintf(pattern, sizeof(pattern), "\"%s\"", key);
|
||||||
|
if (plen <= 0 || (size_t)plen >= sizeof(pattern)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* keyPos = strstr(json, pattern);
|
||||||
|
if (keyPos == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* end = json + len;
|
||||||
|
char* colon = strchr(keyPos + plen, ':');
|
||||||
|
if (colon == nullptr || colon >= end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* p = colon + 1;
|
||||||
|
while (p < end && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
if (p >= end || *p != '"') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* valueStart = p + 1;
|
||||||
|
char* cur = valueStart;
|
||||||
|
while (cur < end) {
|
||||||
|
if (*cur == '\\') {
|
||||||
|
++cur;
|
||||||
|
if (cur < end) {
|
||||||
|
++cur;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*cur == '"') {
|
||||||
|
*cur = '\0';
|
||||||
|
outValue = valueStart;
|
||||||
|
outLen = (size_t)(cur - valueStart);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
++cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Skip GZIP Header an goto DEFLATE content
|
// Skip GZIP Header an goto DEFLATE content
|
||||||
int NetworkClient::skipGzipHeader(const uint8_t* data, size_t len) {
|
int NetworkClient::skipGzipHeader(const uint8_t* data, size_t len) {
|
||||||
if (len < 10) return -1;
|
if (len < 10) return -1;
|
||||||
@@ -51,8 +175,15 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
const size_t capacity = READLIMIT; // Read limit for data (can be adjusted in NetworkClient.h)
|
const size_t capacity = READLIMIT; // Read limit for data (can be adjusted in NetworkClient.h)
|
||||||
uint8_t* buffer = (uint8_t*)malloc(capacity);
|
uint8_t* buffer = (uint8_t*)malloc(capacity);
|
||||||
|
|
||||||
|
// If not with WiFi connectetd then return without any activities
|
||||||
|
if (!gwWifi.clientConnected()) {
|
||||||
|
if (DEBUGING) {Serial.println("No WiFi connection");}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If frame buffer not correct allocated then return without any activities
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
if (DEBUG) {Serial.println("Malloc failed (buffer");}
|
if (DEBUGING) {Serial.println("Malloc failed buffer");}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,20 +194,50 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
http.setTimeout(TCPREADTIMEOUT); // Read timeout in ms (can be adjusted in NetworkClient.h)
|
http.setTimeout(TCPREADTIMEOUT); // Read timeout in ms (can be adjusted in NetworkClient.h)
|
||||||
|
|
||||||
http.begin(url);
|
http.begin(url);
|
||||||
|
|
||||||
|
// NEW: force server to close the connection after the response (prevents "stuck" keep-alive reads)
|
||||||
|
http.addHeader("Connection", "close");
|
||||||
|
|
||||||
|
// NEW: request gzip, but we will only decompress if the server actually answers with gzip
|
||||||
http.addHeader("Accept-Encoding", "gzip");
|
http.addHeader("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
|
// NEW: register headers BEFORE GET() (more reliable with Arduino HTTPClient)
|
||||||
|
if (DEBUGING) {
|
||||||
|
// We need follow key words
|
||||||
|
const char* keys[] = {
|
||||||
|
"Content-Encoding",
|
||||||
|
"Transfer-Encoding",
|
||||||
|
"Content-Length"
|
||||||
|
};
|
||||||
|
// Read header
|
||||||
|
http.collectHeaders(keys, 3);
|
||||||
|
}
|
||||||
|
|
||||||
int code = http.GET();
|
int code = http.GET();
|
||||||
if (code != HTTP_CODE_OK) {
|
if (code != HTTP_CODE_OK) {
|
||||||
Serial.printf("HTTP ERROR: %d\n", code);
|
Serial.printf("HTTP Client ERROR: %d (%s)\n", code, http.errorToString(code).c_str());
|
||||||
|
|
||||||
// Hard reset HTTP + socket
|
// Hard reset HTTP + socket
|
||||||
WiFiClient* tmp = http.getStreamPtr();
|
WiFiClient* tmp = http.getStreamPtr();
|
||||||
if (tmp) tmp->stop(); // Force close TCP socket
|
if (tmp) tmp->stop(); // Force close TCP socket
|
||||||
http.end();
|
|
||||||
|
|
||||||
|
http.end();
|
||||||
free(buffer);
|
free(buffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
if (DEBUGING) {
|
||||||
|
String ce = http.header("Content-Encoding");
|
||||||
|
String te = http.header("Transfer-Encoding");
|
||||||
|
String cl = http.header("Content-Length");
|
||||||
|
|
||||||
|
// Print header informations
|
||||||
|
Serial.printf("Content-Encoding=%s Transfer-Encoding=%s Content-Length=%s\n",
|
||||||
|
ce.c_str(),
|
||||||
|
te.c_str(),
|
||||||
|
cl.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WiFiClient* stream = http.getStreamPtr();
|
WiFiClient* stream = http.getStreamPtr();
|
||||||
|
|
||||||
@@ -85,55 +246,251 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
const uint32_t READ_TIMEOUT = READDATATIMEOUT; // Timeout for reading data (can be adjusted in NetworkClient.h)
|
const uint32_t READ_TIMEOUT = READDATATIMEOUT; // Timeout for reading data (can be adjusted in NetworkClient.h)
|
||||||
|
|
||||||
bool complete = false;
|
bool complete = false;
|
||||||
|
bool aborting = false; // NEW: remember if we must force-close socket
|
||||||
|
|
||||||
while (http.connected() && !complete) {
|
// NEW: detect if server really sent gzip
|
||||||
|
String ce = http.header("Content-Encoding");
|
||||||
|
bool isGzip = ce.equalsIgnoreCase("gzip");
|
||||||
|
|
||||||
size_t avail = stream->available();
|
// NEW: read expected body size if provided by server (prevents waiting forever for missing bytes)
|
||||||
|
int total = http.getSize(); // returns Content-Length, or -1 if unknown/chunked
|
||||||
|
|
||||||
|
// NEW: fail fast if server claims something larger than our buffer
|
||||||
|
if (total > 0 && (size_t)total > capacity) {
|
||||||
|
Serial.println("Response exceeds READLIMIT.");
|
||||||
|
aborting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: if not gzip, we will not try to decompress (prevents false "Decompress OK" / random success)
|
||||||
|
// You can either handle plain JSON here or just fail-fast.
|
||||||
|
if (!isGzip && !aborting) {
|
||||||
|
if (DEBUGING) {
|
||||||
|
Serial.println("Server response is NOT gzip (Content-Encoding != gzip).");
|
||||||
|
Serial.println("Either disable Accept-Encoding: gzip or add plain-body handling here.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Plain-body handling (recommended): read full body into outData as-is ---
|
||||||
|
// NEW: try to read Content-Length bytes if available (more robust)
|
||||||
|
if (total > 0 && (size_t)total > capacity) {
|
||||||
|
Serial.println("Plain response exceeds READLIMIT.");
|
||||||
|
aborting = true;
|
||||||
|
} else {
|
||||||
|
// Read until we have all bytes (Content-Length) or until connection closes + buffer drains
|
||||||
|
while ((http.connected() || (stream && stream->available())) && !aborting) {
|
||||||
|
size_t avail = stream ? stream->available() : 0;
|
||||||
if (avail == 0) {
|
if (avail == 0) {
|
||||||
if (millis() - lastData > READ_TIMEOUT) {
|
if (millis() - lastData > READ_TIMEOUT) {
|
||||||
Serial.println("TIMEOUT waiting for data!");
|
Serial.println("TIMEOUT waiting for data (plain)!");
|
||||||
|
aborting = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay(1);
|
delay(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (len >= capacity) {
|
||||||
|
Serial.println("READLIMIT reached, aborting (plain).");
|
||||||
|
aborting = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (len + avail > capacity)
|
if (len + avail > capacity)
|
||||||
avail = capacity - len;
|
avail = capacity - len;
|
||||||
|
|
||||||
int read = stream->readBytes(buffer + len, avail);
|
int read = stream->readBytes(buffer + len, avail);
|
||||||
len += read;
|
if (read > 0) {
|
||||||
|
len += (size_t)read;
|
||||||
lastData = millis();
|
lastData = millis();
|
||||||
|
}
|
||||||
|
|
||||||
if (DEBUG) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
// NEW: stop reading as soon as we have the full response
|
||||||
|
if (total > 0 && (int)len >= total) {
|
||||||
|
break; // we got full body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (len < 20) continue; // Not enough data for header
|
if (aborting) {
|
||||||
|
// --- Added: Force-close connection only if aborted to avoid TCP RST storms ---
|
||||||
|
if (stream) stream->stop(); // Force close TCP socket
|
||||||
|
http.end();
|
||||||
|
free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int headerOffset = skipGzipHeader(buffer, len);
|
if (total > 0 && (int)len != total) {
|
||||||
if (headerOffset < 0) continue;
|
Serial.printf("Plain response incomplete: got=%d expected=%d\n", (int)len, total);
|
||||||
|
if (stream) stream->stop();
|
||||||
|
http.end();
|
||||||
|
free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long testLen = len * 8; // Dynamic expansion
|
// Return plain body to caller
|
||||||
uint8_t* test = (uint8_t*)malloc(testLen);
|
outData = (uint8_t*)malloc(len + 1);
|
||||||
|
if (!outData) {
|
||||||
|
Serial.println("Malloc failed outData (plain).");
|
||||||
|
// --- Added: Force-close connection only if aborted to avoid TCP RST storms ---
|
||||||
|
if (stream) stream->stop(); // Force close TCP socket
|
||||||
|
http.end();
|
||||||
|
free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(outData, buffer, len);
|
||||||
|
outData[len] = 0;
|
||||||
|
outLen = len;
|
||||||
|
|
||||||
if (!test) continue;
|
http.end();
|
||||||
|
free(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long srcLen = len - headerOffset;
|
// --- GZIP path (only if Content-Encoding is gzip) ---
|
||||||
|
if (!aborting) {
|
||||||
|
|
||||||
int res = puff(test, &testLen, buffer + headerOffset, &srcLen);
|
// NEW: read exactly Content-Length bytes when available (prevents partial-body timeout loops)
|
||||||
if (res == 0) {
|
while ((http.connected() || (stream && stream->available())) && !complete && !aborting) {
|
||||||
if (DEBUG) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
|
||||||
outData = test;
|
size_t avail = stream ? stream->available() : 0;
|
||||||
outLen = testLen;
|
|
||||||
complete = true;
|
if (avail == 0) {
|
||||||
|
// NEW: if Content-Length is known and we already read it all, stop immediately
|
||||||
|
if (total > 0 && (int)len >= total) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(test);
|
if (millis() - lastData > READ_TIMEOUT) {
|
||||||
|
Serial.println("TIMEOUT waiting for data!");
|
||||||
|
aborting = true; // NEW: mark abnormal exit
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Added: Force-close connection in all cases to avoid stuck TCP sockets ---
|
// NEW: safety check if buffer limit is reached
|
||||||
if (stream) stream->stop();
|
if (len >= capacity) {
|
||||||
|
Serial.println("READLIMIT reached, aborting.");
|
||||||
|
aborting = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: if Content-Length is known, do not read beyond it
|
||||||
|
if (total > 0) {
|
||||||
|
size_t remaining = (size_t)total - len;
|
||||||
|
if (avail > remaining) avail = remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len + avail > capacity)
|
||||||
|
avail = capacity - len;
|
||||||
|
|
||||||
|
int read = stream->readBytes(buffer + len, avail);
|
||||||
|
if (read <= 0) {
|
||||||
|
// NEW: avoid tight loop if read returns zero
|
||||||
|
delay(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += (size_t)read;
|
||||||
|
lastData = millis();
|
||||||
|
|
||||||
|
if (DEBUGING) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
||||||
|
|
||||||
|
// NEW: if Content-Length is known and fully received, we can stop reading
|
||||||
|
if (total > 0 && (int)len >= total) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: only attempt gzip parse/decompress after we have a complete body (when Content-Length is known)
|
||||||
|
// This avoids wasting heap with repeated malloc/free and reduces fragmentation over long runtimes.
|
||||||
|
if (!aborting) {
|
||||||
|
if (total > 0 && (int)len != total) {
|
||||||
|
Serial.printf("GZIP response incomplete: got=%d expected=%d\n", (int)len, total);
|
||||||
|
aborting = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aborting) {
|
||||||
|
if (len < 20) {
|
||||||
|
aborting = true;
|
||||||
|
} else {
|
||||||
|
int headerOffset = skipGzipHeader(buffer, len);
|
||||||
|
if (headerOffset < 0) {
|
||||||
|
aborting = true;
|
||||||
|
} else {
|
||||||
|
size_t deflateLen = len - (size_t)headerOffset;
|
||||||
|
// GZIP trailer (CRC32 + ISIZE) is 8 bytes and not part of deflate stream.
|
||||||
|
if (deflateLen >= 8) {
|
||||||
|
deflateLen -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long srcLenForSize = (unsigned long)deflateLen;
|
||||||
|
unsigned long outNeeded = 0;
|
||||||
|
int sizeRes = puff(NIL, &outNeeded, buffer + headerOffset, &srcLenForSize);
|
||||||
|
|
||||||
|
if (sizeRes != 0) {
|
||||||
|
if (DEBUGING) {
|
||||||
|
Serial.printf("Decompress size probe failed: res=%d src=%lu\n", sizeRes, srcLenForSize);
|
||||||
|
}
|
||||||
|
aborting = true;
|
||||||
|
} else {
|
||||||
|
uint8_t* test = (uint8_t*)malloc((size_t)outNeeded + 1);
|
||||||
|
if (!test) {
|
||||||
|
Serial.println("Malloc failed test buffer, aborting.");
|
||||||
|
aborting = true;
|
||||||
|
} else {
|
||||||
|
unsigned long srcLen = (unsigned long)deflateLen;
|
||||||
|
unsigned long testLen = outNeeded;
|
||||||
|
int res = puff(test, &testLen, buffer + headerOffset, &srcLen);
|
||||||
|
|
||||||
|
if (res == 0) {
|
||||||
|
uint32_t trailerCrc =
|
||||||
|
(uint32_t)buffer[len - 8] |
|
||||||
|
((uint32_t)buffer[len - 7] << 8) |
|
||||||
|
((uint32_t)buffer[len - 6] << 16) |
|
||||||
|
((uint32_t)buffer[len - 5] << 24);
|
||||||
|
uint32_t trailerIsize =
|
||||||
|
(uint32_t)buffer[len - 4] |
|
||||||
|
((uint32_t)buffer[len - 3] << 8) |
|
||||||
|
((uint32_t)buffer[len - 2] << 16) |
|
||||||
|
((uint32_t)buffer[len - 1] << 24);
|
||||||
|
uint32_t calcCrc = crc32_update(0, test, (size_t)testLen);
|
||||||
|
uint32_t calcIsize = (uint32_t)testLen;
|
||||||
|
|
||||||
|
if (calcCrc != trailerCrc || calcIsize != trailerIsize) {
|
||||||
|
Serial.printf(
|
||||||
|
"GZIP CRC/ISIZE mismatch crc=%08lx/%08lx isize=%lu/%lu\n",
|
||||||
|
(unsigned long)calcCrc,
|
||||||
|
(unsigned long)trailerCrc,
|
||||||
|
(unsigned long)calcIsize,
|
||||||
|
(unsigned long)trailerIsize
|
||||||
|
);
|
||||||
|
free(test);
|
||||||
|
aborting = true;
|
||||||
|
} else {
|
||||||
|
test[testLen] = 0;
|
||||||
|
if (DEBUGING) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
||||||
|
outData = test;
|
||||||
|
outLen = (size_t)testLen;
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (DEBUGING) {
|
||||||
|
Serial.printf("Decompress failed: res=%d out=%lu src=%lu\n", res, testLen, srcLen);
|
||||||
|
}
|
||||||
|
free(test);
|
||||||
|
aborting = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Added: Force-close connection only if aborted to avoid TCP RST storms ---
|
||||||
|
if (aborting && stream) stream->stop(); // NEW: stop() only on abnormal termination
|
||||||
|
|
||||||
http.end();
|
http.end();
|
||||||
free(buffer);
|
free(buffer);
|
||||||
@@ -150,6 +507,18 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
bool NetworkClient::fetchAndDecompressJson(const String& url) {
|
bool NetworkClient::fetchAndDecompressJson(const String& url) {
|
||||||
|
|
||||||
_valid = false;
|
_valid = false;
|
||||||
|
_doc.clear();
|
||||||
|
_imageWidth = 0;
|
||||||
|
_imageHeight = 0;
|
||||||
|
_numberPixels = 0;
|
||||||
|
_pictureBase64 = nullptr;
|
||||||
|
_pictureBase64Len = 0;
|
||||||
|
|
||||||
|
if (_jsonRaw != nullptr) {
|
||||||
|
free(_jsonRaw);
|
||||||
|
_jsonRaw = nullptr;
|
||||||
|
_jsonRawLen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* raw = nullptr;
|
uint8_t* raw = nullptr;
|
||||||
size_t rawLen = 0;
|
size_t rawLen = 0;
|
||||||
@@ -159,15 +528,38 @@ bool NetworkClient::fetchAndDecompressJson(const String& url) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeserializationError err = deserializeJson(_doc, raw, rawLen);
|
char* json = reinterpret_cast<char*>(raw);
|
||||||
free(raw);
|
bool ok = true;
|
||||||
|
ok = findJsonIntField(json, rawLen, "number_pixels", _numberPixels) && ok;
|
||||||
|
ok = findJsonIntField(json, rawLen, "width", _imageWidth) && ok;
|
||||||
|
ok = findJsonIntField(json, rawLen, "height", _imageHeight) && ok;
|
||||||
|
ok = extractJsonStringInPlace(json, rawLen, "picture_base64", _pictureBase64, _pictureBase64Len) && ok;
|
||||||
|
|
||||||
if (err) {
|
if (!ok) {
|
||||||
Serial.printf("JSON ERROR: %s\n", err.c_str());
|
Serial.println("JSON field extraction failed.");
|
||||||
|
free(raw);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) {Serial.println("JSON OK!");}
|
if (_imageWidth <= 0 || _imageHeight <= 0 || _pictureBase64Len == 0) {
|
||||||
|
Serial.printf("JSON invalid geometry/data w=%d h=%d b64=%u\n",
|
||||||
|
_imageWidth,
|
||||||
|
_imageHeight,
|
||||||
|
(unsigned int)_pictureBase64Len);
|
||||||
|
free(raw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_jsonRaw = raw;
|
||||||
|
_jsonRawLen = rawLen;
|
||||||
|
|
||||||
|
if (DEBUGING) {
|
||||||
|
Serial.printf("JSON fields OK: num=%d w=%d h=%d b64=%u\n",
|
||||||
|
_numberPixels,
|
||||||
|
_imageWidth,
|
||||||
|
_imageHeight,
|
||||||
|
(unsigned int)_pictureBase64Len);
|
||||||
|
}
|
||||||
_valid = true;
|
_valid = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -176,6 +568,26 @@ JsonDocument& NetworkClient::json() {
|
|||||||
return _doc;
|
return _doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int NetworkClient::imageWidth() const {
|
||||||
|
return _imageWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NetworkClient::imageHeight() const {
|
||||||
|
return _imageHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NetworkClient::numberPixels() const {
|
||||||
|
return _numberPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* NetworkClient::pictureBase64() const {
|
||||||
|
return _pictureBase64;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NetworkClient::pictureBase64Len() const {
|
||||||
|
return _pictureBase64Len;
|
||||||
|
}
|
||||||
|
|
||||||
bool NetworkClient::isValid() const {
|
bool NetworkClient::isValid() const {
|
||||||
return _valid;
|
return _valid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
#define DEBUG false // Debug flag for NetworkClient for more live information
|
#define DEBUGING true // Debug flag for NetworkClient for more live information
|
||||||
#define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted)
|
#define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted)
|
||||||
#define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection
|
#define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection
|
||||||
#define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack
|
#define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack
|
||||||
@@ -12,16 +12,31 @@
|
|||||||
class NetworkClient {
|
class NetworkClient {
|
||||||
public:
|
public:
|
||||||
NetworkClient(size_t reserveSize = 0);
|
NetworkClient(size_t reserveSize = 0);
|
||||||
|
~NetworkClient();
|
||||||
|
|
||||||
bool fetchAndDecompressJson(const String& url);
|
bool fetchAndDecompressJson(const String& url);
|
||||||
JsonDocument& json();
|
JsonDocument& json();
|
||||||
|
int imageWidth() const;
|
||||||
|
int imageHeight() const;
|
||||||
|
int numberPixels() const;
|
||||||
|
const char* pictureBase64() const;
|
||||||
|
size_t pictureBase64Len() const;
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DynamicJsonDocument _doc;
|
DynamicJsonDocument _doc;
|
||||||
bool _valid;
|
bool _valid;
|
||||||
|
uint8_t* _jsonRaw;
|
||||||
|
size_t _jsonRawLen;
|
||||||
|
int _imageWidth;
|
||||||
|
int _imageHeight;
|
||||||
|
int _numberPixels;
|
||||||
|
char* _pictureBase64;
|
||||||
|
size_t _pictureBase64Len;
|
||||||
|
|
||||||
int skipGzipHeader(const uint8_t* data, size_t len);
|
int skipGzipHeader(const uint8_t* data, size_t len);
|
||||||
bool httpGetGzip(const String& url, uint8_t*& outData, size_t& outLen);
|
bool httpGetGzip(const String& url, uint8_t*& outData, size_t& outLen);
|
||||||
|
static bool findJsonIntField(const char* json, size_t len, const char* key, int& outValue);
|
||||||
|
static bool extractJsonStringInPlace(char* json, size_t len, const char* key, char*& outValue, size_t& outLen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include "imglib.h"
|
#include "imglib.h"
|
||||||
|
|
||||||
// Character sets
|
// Character sets
|
||||||
|
#include "fonts/DSEG7Classic-BoldItalic10pt7b.h"
|
||||||
|
#include "fonts/DSEG7Classic-BoldItalic12pt7b.h"
|
||||||
#include "fonts/DSEG7Classic-BoldItalic16pt7b.h"
|
#include "fonts/DSEG7Classic-BoldItalic16pt7b.h"
|
||||||
#include "fonts/DSEG7Classic-BoldItalic20pt7b.h"
|
#include "fonts/DSEG7Classic-BoldItalic20pt7b.h"
|
||||||
#include "fonts/DSEG7Classic-BoldItalic26pt7b.h"
|
#include "fonts/DSEG7Classic-BoldItalic26pt7b.h"
|
||||||
@@ -56,6 +58,61 @@ GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> display(GxEPD2_4
|
|||||||
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){return display;}
|
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){return display;}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// panel device + offscreen shadow framebuffer
|
||||||
|
static LGFX panelDisplay;
|
||||||
|
static LGFXCanvas shadowDisplay(&panelDisplay);
|
||||||
|
static LGFXCanvas scaleDisplay(&panelDisplay);
|
||||||
|
static bool shadowDisplayInitialized = false;
|
||||||
|
static bool scaleDisplayInitialized = false;
|
||||||
|
static uint16_t scaleDisplayWidth = 0;
|
||||||
|
static uint16_t scaleDisplayHeight = 0;
|
||||||
|
|
||||||
|
LGFXCanvas & getdisplay(){return shadowDisplay;}
|
||||||
|
LGFX & getpaneldisplay(){return panelDisplay;}
|
||||||
|
LGFXCanvas & getscaleddisplay(){return scaleDisplay;}
|
||||||
|
|
||||||
|
bool initDisplayShadowBuffer(){
|
||||||
|
if (shadowDisplayInitialized) return true;
|
||||||
|
|
||||||
|
shadowDisplay.setPsram(true);
|
||||||
|
shadowDisplay.setColorDepth(16);
|
||||||
|
shadowDisplay.setTextDatum(textdatum_t::baseline_left);
|
||||||
|
|
||||||
|
if (shadowDisplay.createSprite(GxEPD_WIDTH, GxEPD_HEIGHT) == nullptr) {
|
||||||
|
shadowDisplayInitialized = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowDisplay.fillScreen(GxEPD_BLACK);
|
||||||
|
shadowDisplayInitialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initDisplayScaleBuffer(uint16_t width, uint16_t height){
|
||||||
|
if (scaleDisplayInitialized && scaleDisplayWidth == width && scaleDisplayHeight == height) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleDisplay.deleteSprite();
|
||||||
|
scaleDisplay.setPsram(true);
|
||||||
|
scaleDisplay.setColorDepth(16);
|
||||||
|
scaleDisplay.setTextDatum(textdatum_t::baseline_left);
|
||||||
|
|
||||||
|
if (scaleDisplay.createSprite(width, height) == nullptr) {
|
||||||
|
scaleDisplayInitialized = false;
|
||||||
|
scaleDisplayWidth = 0;
|
||||||
|
scaleDisplayHeight = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleDisplayWidth = width;
|
||||||
|
scaleDisplayHeight = height;
|
||||||
|
scaleDisplayInitialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Horter I2C moduls
|
// Horter I2C moduls
|
||||||
PCF8574 pcf8574_Modul1(PCF8574_I2C_ADDR1); // First digital IO modul PCF8574 from Horter
|
PCF8574 pcf8574_Modul1(PCF8574_I2C_ADDR1); // First digital IO modul PCF8574 from Horter
|
||||||
|
|
||||||
@@ -86,7 +143,7 @@ void hardwareInit(GwApi *api)
|
|||||||
GwLog *logger = api->getLogger();
|
GwLog *logger = api->getLogger();
|
||||||
GwConfigHandler *config = api->getConfig();
|
GwConfigHandler *config = api->getConfig();
|
||||||
|
|
||||||
Wire.begin();
|
Wire.begin(OBP_I2C_SDA, OBP_I2C_SCL);
|
||||||
// Init PCF8574 digital outputs
|
// Init PCF8574 digital outputs
|
||||||
Wire.setClock(I2C_SPEED_LOW); // Set I2C clock on 10 kHz
|
Wire.setClock(I2C_SPEED_LOW); // Set I2C clock on 10 kHz
|
||||||
if(pcf8574_Modul1.begin()){ // Initialize PCF8574
|
if(pcf8574_Modul1.begin()){ // Initialize PCF8574
|
||||||
@@ -175,7 +232,7 @@ void hardwareInit(GwApi *api)
|
|||||||
void powerInit(String powermode) {
|
void powerInit(String powermode) {
|
||||||
// Max Power | Only 5.0V | Min Power
|
// Max Power | Only 5.0V | Min Power
|
||||||
if (powermode == "Max Power" || powermode == "Only 5.0V") {
|
if (powermode == "Max Power" || powermode == "Only 5.0V") {
|
||||||
#ifdef HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
|
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
|
||||||
#endif
|
#endif
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
@@ -183,7 +240,7 @@ void powerInit(String powermode) {
|
|||||||
setPortPin(OBP_POWER_SD, true); // Power on SD card
|
setPortPin(OBP_POWER_SD, true); // Power on SD card
|
||||||
#endif
|
#endif
|
||||||
} else { // Min Power
|
} else { // Min Power
|
||||||
#ifdef HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
|
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
|
||||||
#endif
|
#endif
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
@@ -251,8 +308,12 @@ void deepSleep(CommonData &common){
|
|||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(65, 175);
|
getdisplay().setCursor(65, 175);
|
||||||
getdisplay().print("To wake up press key and wait 5s");
|
getdisplay().print("To wake up press key and wait 5s");
|
||||||
getdisplay().nextPage(); // Update display contents
|
displayNextPage(); // Update display contents
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
getpaneldisplay().powerSave(true); // Display power save
|
||||||
|
#else
|
||||||
getdisplay().powerOff(); // Display power off
|
getdisplay().powerOff(); // Display power off
|
||||||
|
#endif
|
||||||
setPortPin(OBP_POWER_50, false); // Power off ePaper display
|
setPortPin(OBP_POWER_50, false); // Power off ePaper display
|
||||||
// Stop system
|
// Stop system
|
||||||
esp_deep_sleep_start(); // Deep Sleep with weakup via touch pin
|
esp_deep_sleep_start(); // Deep Sleep with weakup via touch pin
|
||||||
@@ -276,8 +337,12 @@ void deepSleep(CommonData &common){
|
|||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(65, 175);
|
getdisplay().setCursor(65, 175);
|
||||||
getdisplay().print("To wake up press wheel and wait 5s");
|
getdisplay().print("To wake up press wheel and wait 5s");
|
||||||
getdisplay().nextPage(); // Partial update
|
displayNextPage(); // Partial update
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
getpaneldisplay().powerSave(true); // Display power save
|
||||||
|
#else
|
||||||
getdisplay().powerOff(); // Display power off
|
getdisplay().powerOff(); // Display power off
|
||||||
|
#endif
|
||||||
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
|
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
|
||||||
setPortPin(OBP_POWER_SD, false); // Power off SD card
|
setPortPin(OBP_POWER_SD, false); // Power off SD card
|
||||||
// Stop system
|
// Stop system
|
||||||
@@ -475,67 +540,193 @@ std::vector<String> wordwrap(String &line, uint16_t maxwidth) {
|
|||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to just get the exact width of a string
|
|
||||||
uint16_t getStringPixelWidth(const char* str, const GFXfont* font) {
|
|
||||||
int16_t minx = INT16_MAX;
|
|
||||||
int16_t maxx = INT16_MIN;
|
|
||||||
int16_t cursor_x = 0;
|
|
||||||
while (*str) {
|
|
||||||
char c = *str++;
|
|
||||||
if (c < font->first || c > font->last) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
GFXglyph* glyph = &font->glyph[c - font->first];
|
|
||||||
if (glyph->width > 0) {
|
|
||||||
int16_t glyphStart = cursor_x + glyph->xOffset;
|
|
||||||
int16_t glyphEnd = glyphStart + glyph->width;
|
|
||||||
if (glyphStart < minx) minx = glyphStart;
|
|
||||||
if (glyphEnd > maxx) maxx = glyphEnd;
|
|
||||||
}
|
|
||||||
cursor_x += glyph->xAdvance;
|
|
||||||
}
|
|
||||||
if (minx > maxx)
|
|
||||||
return 0;
|
|
||||||
return maxx - minx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw centered text
|
// Draw centered text
|
||||||
void drawTextCenter(int16_t cx, int16_t cy, String text) {
|
// bool dsegAdjust = false - optional, for adjustment of DSEG italic font
|
||||||
|
// returns left <x> edge of printed string
|
||||||
|
int16_t drawTextCenter(int16_t cx, int16_t cy, String text, bool dsegAdjust) {
|
||||||
int16_t x1, y1;
|
int16_t x1, y1;
|
||||||
uint16_t w, h;
|
uint16_t w, h;
|
||||||
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
|
displayGetTextBounds(text, 0, 0, &x1, &y1, &w, &h, dsegAdjust);
|
||||||
getdisplay().setCursor(cx - w / 2, cy + h / 2);
|
int16_t cursorX = cx - (x1 + static_cast<int16_t>(w / 2));
|
||||||
|
int16_t cursorY = cy - (y1 + static_cast<int16_t>(h / 2));
|
||||||
|
getdisplay().setCursor(cursorX, cursorY);
|
||||||
getdisplay().print(text);
|
getdisplay().print(text);
|
||||||
|
|
||||||
|
return cursorX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw centered botton with centered text
|
// Draw centered button with centered text
|
||||||
void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted) {
|
void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted) {
|
||||||
int16_t x1, y1;
|
int16_t x1, y1;
|
||||||
uint16_t w, h;
|
uint16_t w, h;
|
||||||
uint16_t color;
|
displayGetTextBounds(text, 0, 0, &x1, &y1, &w, &h);
|
||||||
|
int16_t cursorX = cx - (x1 + static_cast<int16_t>(w / 2));
|
||||||
getdisplay().getTextBounds(text, cx, cy, &x1, &y1, &w, &h); // Find text center
|
int16_t cursorY = cy - (y1 + static_cast<int16_t>(h / 2));
|
||||||
getdisplay().setCursor(cx - w/2, cy + h/2); // Set cursor to center
|
|
||||||
//getdisplay().drawPixel(cx, cy, fg); // Debug pixel for center position
|
//getdisplay().drawPixel(cx, cy, fg); // Debug pixel for center position
|
||||||
if (inverted) {
|
if (inverted) {
|
||||||
getdisplay().fillRoundRect(cx - sx / 2, cy - sy / 2, sx, sy, 5, fg); // Draw button
|
getdisplay().fillRoundRect(cx - sx / 2, cy - sy / 2, sx, sy, 5, fg); // Draw button
|
||||||
getdisplay().setTextColor(bg);
|
getdisplay().setTextColor(bg);
|
||||||
|
getdisplay().setCursor(cursorX, cursorY); // Set cursor to center
|
||||||
getdisplay().print(text); // Draw text
|
getdisplay().print(text); // Draw text
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().drawRoundRect(cx - sx / 2, cy - sy / 2, sx, sy, 5, fg); // Draw button
|
getdisplay().drawRoundRect(cx - sx / 2, cy - sy / 2, sx, sy, 5, fg); // Draw button
|
||||||
getdisplay().setTextColor(fg);
|
getdisplay().setTextColor(fg);
|
||||||
|
getdisplay().setCursor(cursorX, cursorY); // Set cursor to center
|
||||||
getdisplay().print(text); // Draw text
|
getdisplay().print(text); // Draw text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw right aligned text
|
// Draw right aligned text
|
||||||
void drawTextRalign(int16_t x, int16_t y, String text) {
|
// int16_t x - upper right x position for text to start printing
|
||||||
int16_t x1, y1;
|
// int16_t y - upper right y position for text to start printing
|
||||||
uint16_t w, h;
|
// const String& text - text to be printed
|
||||||
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
|
// bool dsegAdjust = false - optional, for adjustment of DSEG italic font
|
||||||
getdisplay().setCursor(x - w - 1, y); // '-1' required since some strings wrap around w/o it
|
// returns left <x> edge of printed string
|
||||||
|
int16_t drawTextRalign(int16_t x, int16_t y, const String& text, bool dsegAdjust) {
|
||||||
|
int16_t x1 = 0, y1 = 0;
|
||||||
|
uint16_t w = 0, h = 0;
|
||||||
|
|
||||||
|
displayGetTextBounds(text, 0, y, &x1, &y1, &w, &h, dsegAdjust);
|
||||||
|
int16_t cursorX = x - x1 - w;
|
||||||
|
getdisplay().setCursor(cursorX, y);
|
||||||
getdisplay().print(text);
|
getdisplay().print(text);
|
||||||
|
|
||||||
|
return cursorX; // currently visible left edge
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of <printBoatValue()>; does the actual printing
|
||||||
|
static void printBoatValueImpl(const String &sBoatValue, const int16_t x, const int16_t y,
|
||||||
|
const int8_t align, const int8_t fontSize, bool smallDecs) {
|
||||||
|
const GFXfont *mainFont = nullptr;
|
||||||
|
const GFXfont *decimalFont = nullptr;
|
||||||
|
constexpr bool dsegAdjust = true;
|
||||||
|
|
||||||
|
String integerPart = "";
|
||||||
|
String decimalPart = "";
|
||||||
|
int16_t dot = 0;
|
||||||
|
|
||||||
|
uint16_t intW = 0, decW = 0;
|
||||||
|
int16_t decimalLeftX = 0;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// Font selection
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
if (sBoatValue.indexOf('"') != -1)
|
||||||
|
{
|
||||||
|
// LAT/LON values
|
||||||
|
if (fontSize <= 12)
|
||||||
|
mainFont = &Ubuntu_Bold12pt8b;
|
||||||
|
else if (fontSize <= 16)
|
||||||
|
mainFont = &Ubuntu_Bold16pt8b;
|
||||||
|
else if (fontSize <= 20)
|
||||||
|
mainFont = &Ubuntu_Bold20pt8b;
|
||||||
|
else
|
||||||
|
mainFont = &Ubuntu_Bold32pt8b;
|
||||||
|
|
||||||
|
smallDecs = false; // disable small decimals for LAT/LON in any case
|
||||||
|
}
|
||||||
|
else if (fontSize <= 12) {
|
||||||
|
mainFont = &DSEG7Classic_BoldItalic12pt7b;
|
||||||
|
decimalFont = &DSEG7Classic_BoldItalic10pt7b;
|
||||||
|
} else if (fontSize <= 16) {
|
||||||
|
mainFont = &DSEG7Classic_BoldItalic16pt7b;
|
||||||
|
decimalFont = &DSEG7Classic_BoldItalic12pt7b;
|
||||||
|
} else if (fontSize <= 20) {
|
||||||
|
mainFont = &DSEG7Classic_BoldItalic20pt7b;
|
||||||
|
decimalFont = &DSEG7Classic_BoldItalic16pt7b;
|
||||||
|
} else if (fontSize <= 26) {
|
||||||
|
mainFont = &DSEG7Classic_BoldItalic26pt7b;
|
||||||
|
decimalFont = &DSEG7Classic_BoldItalic20pt7b;
|
||||||
|
} else if (fontSize <= 30) {
|
||||||
|
mainFont = &DSEG7Classic_BoldItalic30pt7b;
|
||||||
|
decimalFont = &DSEG7Classic_BoldItalic26pt7b;
|
||||||
|
} else if (fontSize <= 42) {
|
||||||
|
mainFont = &DSEG7Classic_BoldItalic42pt7b;
|
||||||
|
decimalFont = &DSEG7Classic_BoldItalic30pt7b;
|
||||||
|
} else {
|
||||||
|
mainFont = &DSEG7Classic_BoldItalic60pt7b;
|
||||||
|
decimalFont = &DSEG7Classic_BoldItalic42pt7b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// separate integer and decimal part of the value
|
||||||
|
dot = sBoatValue.indexOf('.');
|
||||||
|
if (dot == -1) { // no decimal point found
|
||||||
|
integerPart = sBoatValue;
|
||||||
|
decimalPart = "";
|
||||||
|
smallDecs = false; // print only integer part
|
||||||
|
} else {
|
||||||
|
integerPart = sBoatValue.substring(0, dot + 1); // keep decimal point
|
||||||
|
decimalPart = sBoatValue.substring(dot + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure integer part
|
||||||
|
int16_t ix1, iy1;
|
||||||
|
uint16_t iw, ih;
|
||||||
|
display.setFont(mainFont);
|
||||||
|
displayGetTextBounds(integerPart, 0, 0, &ix1, &iy1, &iw, &ih, dsegAdjust);
|
||||||
|
intW = ix1 + iw;
|
||||||
|
|
||||||
|
// Measure decimal part only if one actually exists
|
||||||
|
decW = 0;
|
||||||
|
if (dot != -1 && decimalPart.length() > 0) {
|
||||||
|
int16_t dx1, dy1;
|
||||||
|
uint16_t dw, dh;
|
||||||
|
display.setFont(smallDecs ? decimalFont : mainFont);
|
||||||
|
displayGetTextBounds(decimalPart, 0, 0, &dx1, &dy1, &dw, &dh, dsegAdjust);
|
||||||
|
decW = dx1 + dw;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int16_t totalWidth = intW + decW;
|
||||||
|
int16_t startX;
|
||||||
|
switch (align) {
|
||||||
|
case 0: // LEFT
|
||||||
|
startX = x;
|
||||||
|
break;
|
||||||
|
case 1: // CENTER
|
||||||
|
startX = x - totalWidth / 2;
|
||||||
|
break;
|
||||||
|
case 2: // RIGHT
|
||||||
|
default:
|
||||||
|
startX = x - totalWidth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
decimalLeftX = startX + intW;
|
||||||
|
|
||||||
|
// Draw integer part
|
||||||
|
display.setFont(mainFont);
|
||||||
|
getdisplay().setCursor(startX, y);
|
||||||
|
getdisplay().print(integerPart);
|
||||||
|
|
||||||
|
// Draw decimal part only when present
|
||||||
|
if (dot != -1 && decimalPart.length() > 0) {
|
||||||
|
display.setFont(smallDecs ? decimalFont : mainFont);
|
||||||
|
getdisplay().setCursor(decimalLeftX, y);
|
||||||
|
getdisplay().print(decimalPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print boat data value at x,y position with selectable alignment
|
||||||
|
// Version with <GwApi::BoatValue *bValue>; formats the boat data value and then prints the formatted value
|
||||||
|
// GwApi::BoatValue *bValue - boat value to print
|
||||||
|
// CommonData &commondata - <commondata> required for <formatValue()> function
|
||||||
|
// int16_t x - upper left/right x position for text to start printing
|
||||||
|
// int16_t y - upper left/right y position for text to start printing
|
||||||
|
// int8_t align - alignment [0,1,2] of the value: 0 = left, 1 = center, 2 = right
|
||||||
|
// int8_t fontSize - font size for integer part of number or for full value, if <smallDecs> is <false>/not set
|
||||||
|
// bool smallDecs - flag specifies if decimals will be printed in smaller fontsize [default = false]
|
||||||
|
void printBoatValue(GwApi::BoatValue *bValue, CommonData &commondata, const int16_t x, const int16_t y,
|
||||||
|
const int8_t align, const int8_t fontSize, const bool smallDecs)
|
||||||
|
{
|
||||||
|
String sBoatValue = formatValue(bValue, commondata).svalue;
|
||||||
|
printBoatValueImpl(sBoatValue, x, y, align, fontSize, smallDecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print boat data value at x,y position with selectable alignment; oerload with ready-to-print <String>; omits boat data value formatting
|
||||||
|
void printBoatValue(const String &sBoatValue, const int16_t x, const int16_t y,
|
||||||
|
const int8_t align, const int8_t fontSize, const bool smallDecs)
|
||||||
|
{
|
||||||
|
printBoatValueImpl(sBoatValue, x, y, align, fontSize, smallDecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw text inside box, normal or inverted
|
// Draw text inside box, normal or inverted
|
||||||
@@ -619,7 +810,7 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
|
|||||||
usbRxOld = commonData.status.usbRx;
|
usbRxOld = commonData.status.usbRx;
|
||||||
usbTxOld = commonData.status.usbTx;
|
usbTxOld = commonData.status.usbTx;
|
||||||
|
|
||||||
#ifdef HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
// Display key lock status
|
// Display key lock status
|
||||||
if (commonData.keylock) {
|
if (commonData.keylock) {
|
||||||
getdisplay().drawXBitmap(170, 1, lock_bits, icon_width, icon_height, commonData.fgcolor);
|
getdisplay().drawXBitmap(170, 1, lock_bits, icon_width, icon_height, commonData.fgcolor);
|
||||||
@@ -662,19 +853,20 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
|
|||||||
// Date and time
|
// Date and time
|
||||||
String fmttype = commonData.config->getString(commonData.config->dateFormat);
|
String fmttype = commonData.config->getString(commonData.config->dateFormat);
|
||||||
String timesource = commonData.config->getString(commonData.config->timeSource);
|
String timesource = commonData.config->getString(commonData.config->timeSource);
|
||||||
|
double tz = commonData.config->getString(commonData.config->timeZone).toDouble();
|
||||||
getdisplay().setTextColor(commonData.fgcolor);
|
getdisplay().setTextColor(commonData.fgcolor);
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(230, 15);
|
getdisplay().setCursor(230, 15);
|
||||||
if (timesource == "RTC" or timesource == "iRTC") {
|
if (timesource == "RTC" or timesource == "iRTC") {
|
||||||
// TODO take DST into account
|
// TODO take DST into account
|
||||||
if (commonData.data.rtcValid) {
|
if (commonData.data.rtcValid) {
|
||||||
time_t tv = mktime(&commonData.data.rtcTime) + (int)(commonData.tz * 3600);
|
time_t tv = mktime(&commonData.data.rtcTime) + (int)(tz * 3600);
|
||||||
struct tm *local_tm = localtime(&tv);
|
struct tm *local_tm = localtime(&tv);
|
||||||
getdisplay().print(formatTime('m', local_tm->tm_hour, local_tm->tm_min, 0));
|
getdisplay().print(formatTime('m', local_tm->tm_hour, local_tm->tm_min, 0));
|
||||||
getdisplay().print(" ");
|
getdisplay().print(" ");
|
||||||
getdisplay().print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
|
getdisplay().print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
|
||||||
getdisplay().print(" ");
|
getdisplay().print(" ");
|
||||||
getdisplay().print(commonData.tz == 0 ? "UTC" : "LOT");
|
getdisplay().print(tz == 0 ? "UTC" : "LOT");
|
||||||
} else {
|
} else {
|
||||||
drawTextRalign(396, 15, "RTC invalid");
|
drawTextRalign(396, 15, "RTC invalid");
|
||||||
}
|
}
|
||||||
@@ -689,7 +881,7 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
|
|||||||
getdisplay().print(" ");
|
getdisplay().print(" ");
|
||||||
getdisplay().print(actdate);
|
getdisplay().print(actdate);
|
||||||
getdisplay().print(" ");
|
getdisplay().print(" ");
|
||||||
getdisplay().print(commonData.tz == 0 ? "UTC" : "LOT");
|
getdisplay().print(tz == 0 ? "UTC" : "LOT");
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if(commonData.config->getBool(commonData.config->useSimuData) == true){
|
if(commonData.config->getBool(commonData.config->useSimuData) == true){
|
||||||
@@ -711,7 +903,7 @@ void displayFooter(CommonData &commonData) {
|
|||||||
getdisplay().setFont(&Atari16px);
|
getdisplay().setFont(&Atari16px);
|
||||||
getdisplay().setTextColor(commonData.fgcolor);
|
getdisplay().setTextColor(commonData.fgcolor);
|
||||||
|
|
||||||
#ifdef HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
// Frame around key icon area
|
// Frame around key icon area
|
||||||
if (! commonData.keylock) {
|
if (! commonData.keylock) {
|
||||||
// horizontal elements
|
// horizontal elements
|
||||||
@@ -946,7 +1138,7 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor){
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generator graphic with fill level
|
// Generator graphic
|
||||||
void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
||||||
// Show battery
|
// Show battery
|
||||||
int xb = x; // X position
|
int xb = x; // X position
|
||||||
@@ -963,6 +1155,81 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
|||||||
getdisplay().print("G");
|
getdisplay().print("G");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display rudder position as horizontal bargraph with configurable +/- range (degrees)
|
||||||
|
void displayRudderPosition(int rudderPosition, uint8_t rangeDeg, uint16_t cx, uint16_t cy, uint16_t fg, uint16_t bg){
|
||||||
|
const int w = 360;
|
||||||
|
const int h = 20;
|
||||||
|
const int t = 3; // Line thickness
|
||||||
|
const int halfw = w/2;
|
||||||
|
const int halfh = h/2;
|
||||||
|
// Calculate top-left of bar (cx,cy are center of 0°)
|
||||||
|
int left = int(cx) - halfw;
|
||||||
|
int top = int(cy) - halfh;
|
||||||
|
|
||||||
|
// clamp provided range to allowed bounds [10,45]
|
||||||
|
if (rangeDeg < 10) rangeDeg = 10;
|
||||||
|
if (rangeDeg > 45) rangeDeg = 45;
|
||||||
|
|
||||||
|
// Pixels per degree for +/-rangeDeg -> total span = 2*rangeDeg
|
||||||
|
const float pxPerDeg = float(w) / (2.0f * float(rangeDeg));
|
||||||
|
|
||||||
|
// Draw outer border (thickness t)
|
||||||
|
for (int i = 0; i < t; i++) {
|
||||||
|
getdisplay().drawRect(left + i, top + i, w - 2 * i, h - 2 * i, fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill inner area with background
|
||||||
|
getdisplay().fillRect(left + t, top + t, w - 2 * t, h - 2 * t, bg);
|
||||||
|
|
||||||
|
// Draw center line
|
||||||
|
getdisplay().drawRect(cx - 1, top + 1, 3 , h - 2, fg);
|
||||||
|
|
||||||
|
// Clamp rudder position to -rangeDeg..rangeDeg
|
||||||
|
if (rudderPosition > (int)rangeDeg) rudderPosition = (int)rangeDeg;
|
||||||
|
if (rudderPosition < -((int)rangeDeg)) rudderPosition = -((int)rangeDeg);
|
||||||
|
|
||||||
|
// Compute fill width in pixels
|
||||||
|
int fillPx = int(round(rudderPosition * pxPerDeg)); // positive -> right
|
||||||
|
|
||||||
|
// Fill area from center to position (if non-zero)
|
||||||
|
int centerx = cx;
|
||||||
|
int innerTop = top + t;
|
||||||
|
int innerH = h - 2 * t;
|
||||||
|
if (fillPx > 0) {
|
||||||
|
// Right side
|
||||||
|
getdisplay().fillRect(centerx, innerTop, fillPx, innerH, fg);
|
||||||
|
} else if (fillPx < 0) {
|
||||||
|
// Left side
|
||||||
|
getdisplay().fillRect(centerx + fillPx, innerTop, -fillPx, innerH, fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Draw tick marks every 5° and labels outside the bar
|
||||||
|
getdisplay().setTextColor(fg);
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
for (int angle = -((int)rangeDeg); angle <= (int)rangeDeg; angle += 5) {
|
||||||
|
int xpos = int(round(centerx + angle * pxPerDeg));
|
||||||
|
// Vertical tick inside bar
|
||||||
|
getdisplay().drawLine(xpos, top, xpos, top + h + 2, fg);
|
||||||
|
// Label outside: below the bar
|
||||||
|
String lbl = String(angle);
|
||||||
|
int16_t bx, by;
|
||||||
|
uint16_t bw, bh;
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// LovyanGFX: compute width/height manually
|
||||||
|
bw = getdisplay().textWidth(lbl);
|
||||||
|
bh = getdisplay().fontHeight();
|
||||||
|
bx = 0; by = 0;
|
||||||
|
#else
|
||||||
|
getdisplay().getTextBounds(lbl, 0, 0, &bx, &by, &bw, &bh);
|
||||||
|
#endif
|
||||||
|
int16_t tx = xpos - bw/2;
|
||||||
|
int16_t ty = top + h + bh + 5; // A little spacing
|
||||||
|
getdisplay().setCursor(tx, ty);
|
||||||
|
getdisplay().print(lbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Function to handle HTTP image request
|
// Function to handle HTTP image request
|
||||||
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||||
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request) {
|
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request) {
|
||||||
@@ -974,9 +1241,16 @@ void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUM
|
|||||||
|
|
||||||
logger->logDebug(GwLog::LOG,"handle image request [%s]: %s", imgformat, filename);
|
logger->logDebug(GwLog::LOG,"handle image request [%s]: %s", imgformat, filename);
|
||||||
|
|
||||||
uint8_t *fb = getdisplay().getBuffer(); // EPD framebuffer
|
uint8_t *fb = nullptr; // EPD framebuffer
|
||||||
std::vector<uint8_t> imageBuffer; // image in webserver transferbuffer
|
std::vector<uint8_t> imageBuffer; // image in webserver transferbuffer
|
||||||
String mimetype;
|
String mimetype;
|
||||||
|
#ifndef TFT_DISPLAY
|
||||||
|
fb = getdisplay().getBuffer(); // available only for EPD
|
||||||
|
#endif
|
||||||
|
if (!fb) {
|
||||||
|
request->send(500, "text/plain", "screenshot not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (imgformat == "gif") {
|
if (imgformat == "gif") {
|
||||||
// GIF is commpressed with LZW, so small
|
// GIF is commpressed with LZW, so small
|
||||||
|
|||||||
@@ -5,14 +5,28 @@
|
|||||||
#include "OBP60Hardware.h"
|
#include "OBP60Hardware.h"
|
||||||
#include "LedSpiTask.h"
|
#include "LedSpiTask.h"
|
||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
#include <GxEPD2_BW.h> // E-paper lib V2
|
#include <GxEPD2_BW.h> // GxEPD2 lib for b/w E-Ink displays
|
||||||
#include <Adafruit_FRAM_I2C.h> // I2C FRAM
|
#include <Adafruit_FRAM_I2C.h> // I2C FRAM
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
#if !defined(TFT_320x480_ST7796) && !defined(TFT_320x480_ILI9488)
|
||||||
|
#error "TFT_DISPLAY requires one panel type: TFT_320x480_ST7796 or TFT_320x480_ILI9488"
|
||||||
|
#endif
|
||||||
|
#if defined(TFT_320x480_ST7796) && defined(TFT_320x480_ILI9488)
|
||||||
|
#error "Select exactly one TFT panel type: TFT_320x480_ST7796 or TFT_320x480_ILI9488"
|
||||||
|
#endif
|
||||||
|
#include <LovyanGFX.hpp> // TFT LCD lib for 320x480 color displays
|
||||||
|
#undef GxEPD_WHITE
|
||||||
|
#define GxEPD_WHITE TFT_WHITE // Replacement color for white on TFT (OBPHardware.h)
|
||||||
|
#undef GxEPD_BLACK
|
||||||
|
#define GxEPD_BLACK TFT_BLACK // Replacement color for black on TFT (OBPHardware.h)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
#include "esp_vfs_fat.h"
|
#include "esp_vfs_fat.h"
|
||||||
#include "sdmmc_cmd.h"
|
#include "sdmmc_cmd.h"
|
||||||
#define MOUNT_POINT "/sdcard"
|
#define MOUNT_POINT "/sdcard"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// FRAM address reservations 32kB: 0x0000 - 0x7FFF
|
// FRAM address reservations 32kB: 0x0000 - 0x7FFF
|
||||||
@@ -42,6 +56,8 @@ extern sdmmc_card_t *sdcard;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Fonts declarations for display (#includes see OBP60Extensions.cpp)
|
// Fonts declarations for display (#includes see OBP60Extensions.cpp)
|
||||||
|
extern const GFXfont DSEG7Classic_BoldItalic10pt7b;
|
||||||
|
extern const GFXfont DSEG7Classic_BoldItalic12pt7b;
|
||||||
extern const GFXfont DSEG7Classic_BoldItalic16pt7b;
|
extern const GFXfont DSEG7Classic_BoldItalic16pt7b;
|
||||||
extern const GFXfont DSEG7Classic_BoldItalic20pt7b;
|
extern const GFXfont DSEG7Classic_BoldItalic20pt7b;
|
||||||
extern const GFXfont DSEG7Classic_BoldItalic26pt7b;
|
extern const GFXfont DSEG7Classic_BoldItalic26pt7b;
|
||||||
@@ -74,11 +90,627 @@ GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay();
|
|||||||
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
|
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// LovyanGFX based display wrapper for TFT panels
|
||||||
|
class LGFX : public lgfx::LGFX_Device {
|
||||||
|
public:
|
||||||
|
lgfx::Bus_SPI _bus_instance;
|
||||||
|
|
||||||
|
#ifdef TFT_320x480_ST7796
|
||||||
|
LGFX(void) {
|
||||||
|
{
|
||||||
|
auto cfg = _bus_instance.config();
|
||||||
|
cfg.spi_host = SPI2_HOST;
|
||||||
|
cfg.spi_mode = 0;
|
||||||
|
cfg.freq_write = 80000000; // High speed ST7796
|
||||||
|
cfg.freq_read = 16000000;
|
||||||
|
cfg.pin_sclk = OBP_SPI_CLK;
|
||||||
|
cfg.pin_mosi = OBP_SPI_DIN;
|
||||||
|
cfg.pin_miso = -1;
|
||||||
|
cfg.pin_dc = OBP_SPI_DC;
|
||||||
|
_bus_instance.config(cfg);
|
||||||
|
_panel_instance.setBus(&_bus_instance);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto cfg = _panel_instance.config();
|
||||||
|
cfg.pin_cs = OBP_SPI_CS;
|
||||||
|
cfg.pin_rst = OBP_SPI_RST;
|
||||||
|
cfg.pin_busy = -1;
|
||||||
|
cfg.panel_width = 320; // Native width resolution
|
||||||
|
cfg.panel_height = 480; // Native hight resolution
|
||||||
|
cfg.offset_x = 0; // No panel offset: full framebuffer mapping
|
||||||
|
cfg.offset_y = 0; // No panel offset: full framebuffer mapping
|
||||||
|
cfg.offset_rotation = 3; // Rotate display content conter clock wise 90 deg ST7796
|
||||||
|
cfg.dummy_read_pixel = 8;
|
||||||
|
cfg.dummy_read_bits = 1;
|
||||||
|
cfg.memory_width = 320;
|
||||||
|
cfg.memory_height = 480;
|
||||||
|
// cfg.pwm_control not available in this LovyanGFX version
|
||||||
|
cfg.invert = false;
|
||||||
|
cfg.rgb_order = false;
|
||||||
|
cfg.dlen_16bit = false;
|
||||||
|
cfg.bus_shared = true;
|
||||||
|
_panel_instance.config(cfg);
|
||||||
|
}
|
||||||
|
// No dedicated TFT PWM backlight pin configured on this board.
|
||||||
|
// Keep backlight handling outside LovyanGFX to avoid LEDC init on invalid GPIO.
|
||||||
|
setPanel(&_panel_instance);
|
||||||
|
// Match Adafruit GFX cursor semantics: y coordinate is text baseline.
|
||||||
|
setTextDatum(textdatum_t::baseline_left);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TFT_320x480_ILI9488
|
||||||
|
LGFX(void) {
|
||||||
|
{
|
||||||
|
auto cfg = _bus_instance.config();
|
||||||
|
cfg.spi_host = SPI2_HOST;
|
||||||
|
cfg.spi_mode = 0;
|
||||||
|
cfg.freq_write = 40000000; // Slow speed ILI9488
|
||||||
|
cfg.freq_read = 16000000;
|
||||||
|
cfg.pin_sclk = OBP_SPI_CLK;
|
||||||
|
cfg.pin_mosi = OBP_SPI_DIN;
|
||||||
|
cfg.pin_miso = -1;
|
||||||
|
cfg.pin_dc = OBP_SPI_DC;
|
||||||
|
_bus_instance.config(cfg);
|
||||||
|
_panel_instance.setBus(&_bus_instance);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto cfg = _panel_instance.config();
|
||||||
|
cfg.pin_cs = OBP_SPI_CS;
|
||||||
|
cfg.pin_rst = OBP_SPI_RST;
|
||||||
|
cfg.pin_busy = -1;
|
||||||
|
cfg.panel_width = 320; // Native width resolution
|
||||||
|
cfg.panel_height = 480; // Native hight resolution
|
||||||
|
cfg.offset_x = 0; // No panel offset: full framebuffer mapping
|
||||||
|
cfg.offset_y = 0; // No panel offset: full framebuffer mapping
|
||||||
|
cfg.offset_rotation = 1; // Rotate display content clock wise 90 deg ILI9488
|
||||||
|
cfg.dummy_read_pixel = 8;
|
||||||
|
cfg.dummy_read_bits = 1;
|
||||||
|
cfg.memory_width = 320;
|
||||||
|
cfg.memory_height = 480;
|
||||||
|
// cfg.pwm_control not available in this LovyanGFX version
|
||||||
|
cfg.invert = false;
|
||||||
|
cfg.rgb_order = false;
|
||||||
|
cfg.dlen_16bit = false;
|
||||||
|
cfg.bus_shared = true;
|
||||||
|
_panel_instance.config(cfg);
|
||||||
|
}
|
||||||
|
// No dedicated TFT PWM backlight pin configured on this board.
|
||||||
|
// Keep backlight handling outside LovyanGFX to avoid LEDC init on invalid GPIO.
|
||||||
|
setPanel(&_panel_instance);
|
||||||
|
// Match Adafruit GFX cursor semantics: y coordinate is text baseline.
|
||||||
|
setTextDatum(textdatum_t::baseline_left);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// compatibility helpers --------------------------------------------------
|
||||||
|
using lgfx::LGFX_Device::setFont;
|
||||||
|
void setFont(const lgfx::IFont* font) {
|
||||||
|
_currentAdfFont = nullptr;
|
||||||
|
lgfx::LGFX_Device::setFont(font);
|
||||||
|
}
|
||||||
|
// Adafruit GFX fonts support on TFT via LovyanGFX bridge
|
||||||
|
void setFont(const GFXfont *font) {
|
||||||
|
if (font == nullptr) {
|
||||||
|
_currentAdfFont = nullptr;
|
||||||
|
lgfx::LGFX_Device::setFont(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font->glyph == nullptr || font->bitmap == nullptr || font->last < font->first) {
|
||||||
|
_currentAdfFont = nullptr;
|
||||||
|
lgfx::LGFX_Device::setFont(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t glyphCount = static_cast<uint16_t>(font->last - font->first + 1);
|
||||||
|
if (glyphCount == 0) {
|
||||||
|
lgfx::LGFX_Device::setFont(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_adfGlyphCount != glyphCount || _adfGlyphBridge == nullptr) {
|
||||||
|
if (_adfGlyphBridge != nullptr) {
|
||||||
|
free(_adfGlyphBridge);
|
||||||
|
_adfGlyphBridge = nullptr;
|
||||||
|
_adfGlyphCount = 0;
|
||||||
|
}
|
||||||
|
_adfGlyphBridge = static_cast<lgfx::GFXglyph*>(malloc(sizeof(lgfx::GFXglyph) * glyphCount));
|
||||||
|
if (_adfGlyphBridge == nullptr) {
|
||||||
|
lgfx::LGFX_Device::setFont(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_adfGlyphCount = glyphCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t index = 0; index < glyphCount; ++index) {
|
||||||
|
_adfGlyphBridge[index].bitmapOffset = font->glyph[index].bitmapOffset;
|
||||||
|
_adfGlyphBridge[index].width = font->glyph[index].width;
|
||||||
|
_adfGlyphBridge[index].height = font->glyph[index].height;
|
||||||
|
_adfGlyphBridge[index].xAdvance = font->glyph[index].xAdvance;
|
||||||
|
_adfGlyphBridge[index].xOffset = font->glyph[index].xOffset;
|
||||||
|
_adfGlyphBridge[index].yOffset = font->glyph[index].yOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
_adfFontBridge = lgfx::GFXfont(
|
||||||
|
const_cast<uint8_t*>(font->bitmap),
|
||||||
|
_adfGlyphBridge,
|
||||||
|
font->first,
|
||||||
|
font->last,
|
||||||
|
font->yAdvance
|
||||||
|
);
|
||||||
|
_currentAdfFont = font;
|
||||||
|
lgfx::LGFX_Device::setFont(&_adfFontBridge);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getTextBounds(const String &txt, int16_t x, int16_t y,
|
||||||
|
int16_t *x0, int16_t *y0,
|
||||||
|
uint16_t *w, uint16_t *h) {
|
||||||
|
if (w == nullptr || h == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_currentAdfFont == nullptr || txt.length() == 0) {
|
||||||
|
*w = textWidth(txt);
|
||||||
|
*h = fontHeight();
|
||||||
|
if (x0) *x0 = x;
|
||||||
|
if (y0) *y0 = y - static_cast<int16_t>(*h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float sx = getTextSizeX();
|
||||||
|
const float sy = getTextSizeY();
|
||||||
|
|
||||||
|
int32_t cursorX = x;
|
||||||
|
int32_t cursorY = y;
|
||||||
|
int32_t minX = 0;
|
||||||
|
int32_t minY = 0;
|
||||||
|
int32_t maxX = 0;
|
||||||
|
int32_t maxY = 0;
|
||||||
|
bool hasPixel = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < txt.length(); ++i) {
|
||||||
|
char c = txt[i];
|
||||||
|
if (c == '\r') continue;
|
||||||
|
if (c == '\n') {
|
||||||
|
cursorX = x;
|
||||||
|
cursorY += static_cast<int32_t>(_currentAdfFont->yAdvance * sy);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c < _currentAdfFont->first || c > _currentAdfFont->last) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GFXglyph* glyph = &_currentAdfFont->glyph[static_cast<uint16_t>(c) - _currentAdfFont->first];
|
||||||
|
const int32_t gw = static_cast<int32_t>(glyph->width * sx);
|
||||||
|
const int32_t gh = static_cast<int32_t>(glyph->height * sy);
|
||||||
|
const int32_t gx1 = cursorX + static_cast<int32_t>(glyph->xOffset * sx);
|
||||||
|
const int32_t gy1 = cursorY + static_cast<int32_t>(glyph->yOffset * sy);
|
||||||
|
|
||||||
|
if (gw > 0 && gh > 0) {
|
||||||
|
const int32_t gx2 = gx1 + gw - 1;
|
||||||
|
const int32_t gy2 = gy1 + gh - 1;
|
||||||
|
if (!hasPixel) {
|
||||||
|
minX = gx1; minY = gy1; maxX = gx2; maxY = gy2;
|
||||||
|
hasPixel = true;
|
||||||
|
} else {
|
||||||
|
if (gx1 < minX) minX = gx1;
|
||||||
|
if (gy1 < minY) minY = gy1;
|
||||||
|
if (gx2 > maxX) maxX = gx2;
|
||||||
|
if (gy2 > maxY) maxY = gy2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursorX += static_cast<int32_t>(glyph->xAdvance * sx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPixel) {
|
||||||
|
if (x0) *x0 = static_cast<int16_t>(minX);
|
||||||
|
if (y0) *y0 = static_cast<int16_t>(minY);
|
||||||
|
*w = static_cast<uint16_t>(maxX - minX + 1);
|
||||||
|
*h = static_cast<uint16_t>(maxY - minY + 1);
|
||||||
|
} else {
|
||||||
|
if (x0) *x0 = x;
|
||||||
|
if (y0) *y0 = y;
|
||||||
|
*w = 0;
|
||||||
|
*h = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// E-Ink interface compatibility
|
||||||
|
void setFullWindow() { /* no-op on TFT */ }
|
||||||
|
|
||||||
|
// Runtime panel offset control for TFT panels
|
||||||
|
void setPanelOffset(int16_t x, int16_t y) {
|
||||||
|
auto cfg = _panel_instance.config();
|
||||||
|
cfg.offset_x = x;
|
||||||
|
cfg.offset_y = y;
|
||||||
|
_panel_instance.config(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getPanelOffset(int16_t &x, int16_t &y) {
|
||||||
|
auto cfg = _panel_instance.config();
|
||||||
|
x = cfg.offset_x;
|
||||||
|
y = cfg.offset_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
lgfx::GFXfont _adfFontBridge { nullptr, nullptr, 0, 0, 0 };
|
||||||
|
lgfx::GFXglyph* _adfGlyphBridge = nullptr;
|
||||||
|
uint16_t _adfGlyphCount = 0;
|
||||||
|
const GFXfont* _currentAdfFont = nullptr;
|
||||||
|
#if defined(TFT_320x480_ST7796)
|
||||||
|
lgfx::Panel_ST7796 _panel_instance;
|
||||||
|
#elif defined(TFT_320x480_ILI9488)
|
||||||
|
lgfx::Panel_ILI9488 _panel_instance;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class LGFXCanvas : public lgfx::LGFX_Sprite {
|
||||||
|
public:
|
||||||
|
explicit LGFXCanvas(lgfx::LGFX_Device* parent = nullptr) : lgfx::LGFX_Sprite(parent) {}
|
||||||
|
|
||||||
|
using lgfx::LGFX_Sprite::setFont;
|
||||||
|
void setFont(const GFXfont *font) {
|
||||||
|
if (font == nullptr) {
|
||||||
|
_currentAdfFont = nullptr;
|
||||||
|
lgfx::LGFX_Sprite::setFont(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font->glyph == nullptr || font->bitmap == nullptr || font->last < font->first) {
|
||||||
|
_currentAdfFont = nullptr;
|
||||||
|
lgfx::LGFX_Sprite::setFont(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t glyphCount = static_cast<uint16_t>(font->last - font->first + 1);
|
||||||
|
if (glyphCount == 0) {
|
||||||
|
lgfx::LGFX_Sprite::setFont(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_adfGlyphCount != glyphCount || _adfGlyphBridge == nullptr) {
|
||||||
|
if (_adfGlyphBridge != nullptr) {
|
||||||
|
free(_adfGlyphBridge);
|
||||||
|
_adfGlyphBridge = nullptr;
|
||||||
|
_adfGlyphCount = 0;
|
||||||
|
}
|
||||||
|
_adfGlyphBridge = static_cast<lgfx::GFXglyph*>(malloc(sizeof(lgfx::GFXglyph) * glyphCount));
|
||||||
|
if (_adfGlyphBridge == nullptr) {
|
||||||
|
lgfx::LGFX_Sprite::setFont(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_adfGlyphCount = glyphCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t index = 0; index < glyphCount; ++index) {
|
||||||
|
_adfGlyphBridge[index].bitmapOffset = font->glyph[index].bitmapOffset;
|
||||||
|
_adfGlyphBridge[index].width = font->glyph[index].width;
|
||||||
|
_adfGlyphBridge[index].height = font->glyph[index].height;
|
||||||
|
_adfGlyphBridge[index].xAdvance = font->glyph[index].xAdvance;
|
||||||
|
_adfGlyphBridge[index].xOffset = font->glyph[index].xOffset;
|
||||||
|
_adfGlyphBridge[index].yOffset = font->glyph[index].yOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
_adfFontBridge = lgfx::GFXfont(
|
||||||
|
const_cast<uint8_t*>(font->bitmap),
|
||||||
|
_adfGlyphBridge,
|
||||||
|
font->first,
|
||||||
|
font->last,
|
||||||
|
font->yAdvance
|
||||||
|
);
|
||||||
|
_currentAdfFont = font;
|
||||||
|
lgfx::LGFX_Sprite::setFont(&_adfFontBridge);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getTextBounds(const String &txt, int16_t x, int16_t y,
|
||||||
|
int16_t *x0, int16_t *y0,
|
||||||
|
uint16_t *w, uint16_t *h) {
|
||||||
|
if (w == nullptr || h == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_currentAdfFont == nullptr || txt.length() == 0) {
|
||||||
|
*w = textWidth(txt);
|
||||||
|
*h = fontHeight();
|
||||||
|
if (x0) *x0 = x;
|
||||||
|
if (y0) *y0 = y - static_cast<int16_t>(*h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float sx = getTextSizeX();
|
||||||
|
const float sy = getTextSizeY();
|
||||||
|
|
||||||
|
int32_t cursorX = x;
|
||||||
|
int32_t cursorY = y;
|
||||||
|
int32_t minX = 0;
|
||||||
|
int32_t minY = 0;
|
||||||
|
int32_t maxX = 0;
|
||||||
|
int32_t maxY = 0;
|
||||||
|
bool hasPixel = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < txt.length(); ++i) {
|
||||||
|
char c = txt[i];
|
||||||
|
if (c == '\r') continue;
|
||||||
|
if (c == '\n') {
|
||||||
|
cursorX = x;
|
||||||
|
cursorY += static_cast<int32_t>(_currentAdfFont->yAdvance * sy);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c < _currentAdfFont->first || c > _currentAdfFont->last) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GFXglyph* glyph = &_currentAdfFont->glyph[static_cast<uint16_t>(c) - _currentAdfFont->first];
|
||||||
|
const int32_t gw = static_cast<int32_t>(glyph->width * sx);
|
||||||
|
const int32_t gh = static_cast<int32_t>(glyph->height * sy);
|
||||||
|
const int32_t gx1 = cursorX + static_cast<int32_t>(glyph->xOffset * sx);
|
||||||
|
const int32_t gy1 = cursorY + static_cast<int32_t>(glyph->yOffset * sy);
|
||||||
|
|
||||||
|
if (gw > 0 && gh > 0) {
|
||||||
|
const int32_t gx2 = gx1 + gw - 1;
|
||||||
|
const int32_t gy2 = gy1 + gh - 1;
|
||||||
|
if (!hasPixel) {
|
||||||
|
minX = gx1; minY = gy1; maxX = gx2; maxY = gy2;
|
||||||
|
hasPixel = true;
|
||||||
|
} else {
|
||||||
|
if (gx1 < minX) minX = gx1;
|
||||||
|
if (gy1 < minY) minY = gy1;
|
||||||
|
if (gx2 > maxX) maxX = gx2;
|
||||||
|
if (gy2 > maxY) maxY = gy2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursorX += static_cast<int32_t>(glyph->xAdvance * sx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPixel) {
|
||||||
|
if (x0) *x0 = static_cast<int16_t>(minX);
|
||||||
|
if (y0) *y0 = static_cast<int16_t>(minY);
|
||||||
|
*w = static_cast<uint16_t>(maxX - minX + 1);
|
||||||
|
*h = static_cast<uint16_t>(maxY - minY + 1);
|
||||||
|
} else {
|
||||||
|
if (x0) *x0 = x;
|
||||||
|
if (y0) *y0 = y;
|
||||||
|
*w = 0;
|
||||||
|
*h = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFullWindow() { /* no-op on TFT */ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
lgfx::GFXfont _adfFontBridge { nullptr, nullptr, 0, 0, 0 };
|
||||||
|
lgfx::GFXglyph* _adfGlyphBridge = nullptr;
|
||||||
|
uint16_t _adfGlyphCount = 0;
|
||||||
|
const GFXfont* _currentAdfFont = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
LGFXCanvas & getdisplay();
|
||||||
|
LGFX & getpaneldisplay();
|
||||||
|
LGFXCanvas & getscaleddisplay();
|
||||||
|
bool initDisplayShadowBuffer();
|
||||||
|
bool initDisplayScaleBuffer(uint16_t width, uint16_t height);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Page display return values
|
// Page display return values
|
||||||
#define PAGE_OK 0 // all ok, do nothing
|
#define PAGE_OK 0 // all ok, do nothing
|
||||||
#define PAGE_UPDATE 1 // page wants display to update
|
#define PAGE_UPDATE 1 // page wants display to update
|
||||||
#define PAGE_HIBERNATE 2 // page wants displey to hibernate
|
#define PAGE_HIBERNATE 2 // page wants displey to hibernate
|
||||||
|
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
#ifndef OBP_TFT_ENABLE_SCALING
|
||||||
|
#define OBP_TFT_ENABLE_SCALING 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OBP_TFT_SCALE_ANTIALIAS
|
||||||
|
#define OBP_TFT_SCALE_ANTIALIAS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if OBP_TFT_SCALE_ANTIALIAS
|
||||||
|
inline uint16_t lerpRgb565(uint16_t c0, uint16_t c1, uint16_t w8) {
|
||||||
|
const uint16_t r0 = (c0 >> 11) & 0x1F;
|
||||||
|
const uint16_t g0 = (c0 >> 5) & 0x3F;
|
||||||
|
const uint16_t b0 = c0 & 0x1F;
|
||||||
|
|
||||||
|
const uint16_t r1 = (c1 >> 11) & 0x1F;
|
||||||
|
const uint16_t g1 = (c1 >> 5) & 0x3F;
|
||||||
|
const uint16_t b1 = c1 & 0x1F;
|
||||||
|
|
||||||
|
const uint16_t r = static_cast<uint16_t>(r0 + ((static_cast<int32_t>(r1) - r0) * w8 + 128) / 256);
|
||||||
|
const uint16_t g = static_cast<uint16_t>(g0 + ((static_cast<int32_t>(g1) - g0) * w8 + 128) / 256);
|
||||||
|
const uint16_t b = static_cast<uint16_t>(b0 + ((static_cast<int32_t>(b1) - b0) * w8 + 128) / 256);
|
||||||
|
|
||||||
|
return static_cast<uint16_t>((r << 11) | (g << 5) | b);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t sampleBilinearRgb565(LGFXCanvas& src, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t wx, uint16_t wy) {
|
||||||
|
const uint16_t c00 = src.readPixel(x0, y0);
|
||||||
|
const uint16_t c10 = src.readPixel(x1, y0);
|
||||||
|
const uint16_t c01 = src.readPixel(x0, y1);
|
||||||
|
const uint16_t c11 = src.readPixel(x1, y1);
|
||||||
|
|
||||||
|
const uint16_t top = lerpRgb565(c00, c10, wx);
|
||||||
|
const uint16_t bot = lerpRgb565(c01, c11, wx);
|
||||||
|
return lerpRgb565(top, bot, wy);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Draw monochrome bitmap on both E-Ink and TFT displays
|
||||||
|
// supports various packing and bit orders; optional runtime conversion for TFT
|
||||||
|
inline void drawMonochromeBitmap(
|
||||||
|
int16_t x, int16_t y,
|
||||||
|
const uint8_t *bmp,
|
||||||
|
int16_t w, int16_t h,
|
||||||
|
uint16_t color,
|
||||||
|
bool vertical=false, // true: bytes run vertically (each byte 8 pixels down)
|
||||||
|
bool lsbFirst=false, // true: least significant bit = left/top pixel
|
||||||
|
bool mirrorX=false) // true: bytes run right-to-left within each row
|
||||||
|
{
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// TFT converts per‑pixel
|
||||||
|
int bytesPerRow = (w + 7) / 8;
|
||||||
|
for (int yy = 0; yy < h; yy++) {
|
||||||
|
for (int xx = 0; xx < w; xx++) {
|
||||||
|
int byteIdx;
|
||||||
|
int bitIdx;
|
||||||
|
if (vertical) {
|
||||||
|
// vertical packing: column-major bytes
|
||||||
|
int col = mirrorX ? (w - 1 - xx) : xx;
|
||||||
|
byteIdx = col * ((h + 7) / 8) + (yy / 8);
|
||||||
|
bitIdx = yy % 8;
|
||||||
|
} else {
|
||||||
|
// horizontal packing: row-major bytes
|
||||||
|
int col = mirrorX ? (w - 1 - xx) : xx;
|
||||||
|
byteIdx = yy * bytesPerRow + (col / 8);
|
||||||
|
bitIdx = col % 8;
|
||||||
|
}
|
||||||
|
uint8_t b = bmp[byteIdx];
|
||||||
|
bool pix;
|
||||||
|
if (lsbFirst) {
|
||||||
|
pix = b & (1 << bitIdx);
|
||||||
|
} else {
|
||||||
|
pix = b & (1 << (7 - bitIdx));
|
||||||
|
}
|
||||||
|
if (pix) {
|
||||||
|
getdisplay().drawPixel(x + xx, y + yy, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((yy & 0x0F) == 0) {
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// E‑Paper: just hand over to driver (expects MSB‑first horizontal)
|
||||||
|
getdisplay().drawBitmap(x, y, bmp, w, h, color);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Display wrapper functions for E-Ink/TFT compatibility
|
||||||
|
|
||||||
|
// generic bitmap draw that accepts 1‑bit data; TFT version
|
||||||
|
// forwards to drawMonochromeBitmap whereas EPD uses native drawBitmap
|
||||||
|
inline void displayDrawBitmap(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bmp,
|
||||||
|
int16_t w, int16_t h,
|
||||||
|
uint16_t color) {
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
drawMonochromeBitmap(x, y, bmp, w, h, color);
|
||||||
|
#else
|
||||||
|
getdisplay().drawBitmap(x, y, bmp, w, h, color);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void displayFirstPage() {
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
initDisplayShadowBuffer();
|
||||||
|
#else
|
||||||
|
getdisplay().firstPage();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void displayNextPage() {
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
if (initDisplayShadowBuffer()) {
|
||||||
|
LGFXCanvas &src = getdisplay();
|
||||||
|
LGFX &dst = getpaneldisplay();
|
||||||
|
|
||||||
|
const uint16_t srcW = GxEPD_WIDTH;
|
||||||
|
const uint16_t srcH = GxEPD_HEIGHT;
|
||||||
|
const uint16_t dstW = static_cast<uint16_t>(dst.width());
|
||||||
|
const uint16_t dstH = static_cast<uint16_t>(dst.height());
|
||||||
|
|
||||||
|
#if !OBP_TFT_ENABLE_SCALING
|
||||||
|
const uint16_t drawX = static_cast<uint16_t>((dstW > srcW) ? ((dstW - srcW) / 2U) : 0U);
|
||||||
|
const uint16_t drawY = static_cast<uint16_t>((dstH > srcH) ? ((dstH - srcH) / 2U) : 0U);
|
||||||
|
src.pushSprite(drawX, drawY);
|
||||||
|
#else
|
||||||
|
|
||||||
|
const uint16_t targetH = (dstH < 320U) ? dstH : 320U;
|
||||||
|
const uint32_t scaledW32 = (static_cast<uint32_t>(srcW) * targetH + (srcH / 2U)) / srcH;
|
||||||
|
const uint16_t targetW = static_cast<uint16_t>((scaledW32 < dstW) ? scaledW32 : dstW);
|
||||||
|
|
||||||
|
const uint16_t drawX = static_cast<uint16_t>((dstW - targetW) / 2U);
|
||||||
|
const uint16_t drawY = static_cast<uint16_t>((dstH - targetH) / 2U);
|
||||||
|
|
||||||
|
const uint16_t borderColor = src.readPixel(0, 0);
|
||||||
|
if (initDisplayScaleBuffer(dstW, dstH)) {
|
||||||
|
LGFXCanvas &scaled = getscaleddisplay();
|
||||||
|
scaled.fillScreen(borderColor);
|
||||||
|
|
||||||
|
for (uint16_t y = 0; y < targetH; ++y) {
|
||||||
|
const uint32_t syfp = (targetH > 1)
|
||||||
|
? (static_cast<uint32_t>(y) * (srcH - 1) * 256U) / (targetH - 1)
|
||||||
|
: 0;
|
||||||
|
const uint16_t sy0 = static_cast<uint16_t>(syfp >> 8);
|
||||||
|
const uint16_t sy1 = (sy0 + 1 < srcH) ? static_cast<uint16_t>(sy0 + 1) : sy0;
|
||||||
|
const uint16_t wy = static_cast<uint16_t>(syfp & 0xFFU);
|
||||||
|
|
||||||
|
for (uint16_t x = 0; x < targetW; ++x) {
|
||||||
|
const uint32_t sxfp = (targetW > 1)
|
||||||
|
? (static_cast<uint32_t>(x) * (srcW - 1) * 256U) / (targetW - 1)
|
||||||
|
: 0;
|
||||||
|
const uint16_t sx0 = static_cast<uint16_t>(sxfp >> 8);
|
||||||
|
const uint16_t sx1 = (sx0 + 1 < srcW) ? static_cast<uint16_t>(sx0 + 1) : sx0;
|
||||||
|
|
||||||
|
#if OBP_TFT_SCALE_ANTIALIAS
|
||||||
|
const uint16_t wx = static_cast<uint16_t>(sxfp & 0xFFU);
|
||||||
|
const uint16_t color = sampleBilinearRgb565(src, sx0, sy0, sx1, sy1, wx, wy);
|
||||||
|
#else
|
||||||
|
const uint16_t color = src.readPixel(sx0, sy0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
scaled.drawPixel(drawX + x, drawY + y, color);
|
||||||
|
}
|
||||||
|
if ((y & 0x0F) == 0) {
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled.pushSprite(0, 0);
|
||||||
|
} else {
|
||||||
|
src.pushSprite(drawX, drawY);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
getdisplay().nextPage();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void displaySetPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// TFT LCD doesn't use partial windows
|
||||||
|
(void)x; (void)y; (void)w; (void)h;
|
||||||
|
#else
|
||||||
|
getdisplay().setPartialWindow(x, y, w, h);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void displaySetFullWindow() {
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// TFT LCD doesn't need setFullWindow()
|
||||||
|
#else
|
||||||
|
getdisplay().setFullWindow();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// replacement for getTextBounds that works with both EPD and TFT
|
||||||
|
inline void displayGetTextBounds(const String &txt, int16_t x, int16_t y,
|
||||||
|
int16_t *x0, int16_t *y0,
|
||||||
|
uint16_t *w, uint16_t *h, const bool dsegAdjust = false) {
|
||||||
|
String str = txt;
|
||||||
|
// make sure that we always have a char with max size at last position for boundary test
|
||||||
|
// for monotype italic DSEG7 font to avoid text wobbling
|
||||||
|
if (dsegAdjust && str.length() > 0 && isDigit(str[str.length() - 1])) {
|
||||||
|
str.setCharAt(str.length() - 1, '8');
|
||||||
|
}
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
getdisplay().getTextBounds(str, x, y, x0, y0, w, h);
|
||||||
|
#else
|
||||||
|
getdisplay().getTextBounds(str, x, y, x0, y0, w, h);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void fillPoly4(const std::vector<Point>& p4, uint16_t color);
|
void fillPoly4(const std::vector<Point>& p4, uint16_t color);
|
||||||
void drawPoly(const std::vector<Point>& points, uint16_t color);
|
void drawPoly(const std::vector<Point>& points, uint16_t color);
|
||||||
|
|
||||||
@@ -108,10 +740,12 @@ void setBuzzerPower(uint power); // Set buzzer power
|
|||||||
|
|
||||||
String xdrDelete(String input); // Delete xdr prefix from string
|
String xdrDelete(String input); // Delete xdr prefix from string
|
||||||
|
|
||||||
void drawTextCenter(int16_t cx, int16_t cy, String text);
|
int16_t drawTextCenter(int16_t cx, int16_t cy, String text, bool dsegAdjust = false);
|
||||||
void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted);
|
void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted);
|
||||||
void drawTextRalign(int16_t x, int16_t y, String text);
|
int16_t drawTextRalign(int16_t x, int16_t y, const String& text, bool dsegAdjust = false);
|
||||||
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border);
|
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border);
|
||||||
|
void printBoatValue(GwApi::BoatValue *bValue, CommonData &commondata, const int16_t x, const int16_t y, const int8_t align, const int8_t fontSize, const bool smallDecs = false);
|
||||||
|
void printBoatValue(const String &sBoatValue, const int16_t x, const int16_t y, const int8_t align, const int8_t fontSize, const bool smallDecs = false);
|
||||||
|
|
||||||
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color);
|
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);
|
void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color);
|
||||||
@@ -128,6 +762,10 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor); // S
|
|||||||
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic
|
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic
|
||||||
void startLedTask(GwApi *api);
|
void startLedTask(GwApi *api);
|
||||||
|
|
||||||
|
// Display rudder position as horizontal bargraph with configurable +/- range (degrees)
|
||||||
|
// 'rangeDeg' is unsigned and will be clamped to [10,45]
|
||||||
|
void displayRudderPosition(int rudderPosition, uint8_t rangeDeg, uint16_t cx, uint16_t cy, uint16_t fg, uint16_t bg);
|
||||||
|
|
||||||
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
|
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "GwApi.h"
|
#include "GwApi.h"
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
|
#include "OBPDataOperations.h"
|
||||||
|
|
||||||
// ToDo
|
// ToDo
|
||||||
// simulation data
|
// simulation data
|
||||||
@@ -49,15 +50,10 @@ String formatLongitude(double lon) {
|
|||||||
return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lon > 0) ? "E" : "W");
|
return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lon > 0) ? "E" : "W");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert and format boat value from SI to user defined format (definition for compatibility purposes)
|
|
||||||
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata) {
|
|
||||||
|
|
||||||
return formatValue(value, commondata, false); // call <formatValue> with standard handling of user setting for simulation data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert and format boat value from SI to user defined format
|
// Convert and format boat value from SI to user defined format
|
||||||
// generate random simulation data; can be deselected to use conversion+formatting function even in simulation mode
|
// generates random simulation data; can be deselected to use conversion+formatting function even in simulation mode
|
||||||
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting){
|
// String setPrecision - overrides precision setting from config; if not set, use config setting
|
||||||
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting, String setPrecision) {
|
||||||
GwLog *logger = commondata.logger;
|
GwLog *logger = commondata.logger;
|
||||||
FormattedData result;
|
FormattedData result;
|
||||||
static int dayoffset = 0;
|
static int dayoffset = 0;
|
||||||
@@ -75,12 +71,13 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
String tempFormat = commondata.config->getString(commondata.config->tempFormat); // [K|°C|°F]
|
String tempFormat = commondata.config->getString(commondata.config->tempFormat); // [K|°C|°F]
|
||||||
String dateFormat = commondata.config->getString(commondata.config->dateFormat); // [DE|GB|US]
|
String dateFormat = commondata.config->getString(commondata.config->dateFormat); // [DE|GB|US]
|
||||||
String precision = commondata.config->getString(commondata.config->valueprecision); // [1|2]
|
String precision = commondata.config->getString(commondata.config->valueprecision); // [1|2]
|
||||||
|
bool usesimudata = commondata.config->getBool(commondata.config->useSimuData); // [on|off]
|
||||||
|
|
||||||
bool usesimudata;
|
if (setPrecision == "1" || setPrecision == "2") {
|
||||||
|
precision = setPrecision; // override precision setting from config
|
||||||
|
}
|
||||||
if (ignoreSimuDataSetting){
|
if (ignoreSimuDataSetting){
|
||||||
usesimudata = false; // ignore user setting for simulation data; we want to format the boat value passed to this function
|
usesimudata = false; // ignore user setting for simulation data; we want to format the boat value passed to this function
|
||||||
} else {
|
|
||||||
usesimudata = commondata.config->getBool(commondata.config->useSimuData); // [on|off]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If boat value not valid
|
// If boat value not valid
|
||||||
@@ -95,13 +92,12 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
double limit_dec_10;
|
double limit_dec_10;
|
||||||
double limit_dec_100;
|
double limit_dec_100;
|
||||||
if (precision == "1") {
|
if (precision == "1") {
|
||||||
//
|
|
||||||
//All values are displayed using a DSEG7* font. In this font, ' ' is a very short space, and '.' takes up no space at all.
|
//All values are displayed using a DSEG7* font. In this font, ' ' is a very short space, and '.' takes up no space at all.
|
||||||
//For a space that is as long as a number, '!' is used. For details see https://www.keshikan.net/fonts-e.html
|
//For a space that is as long as a number, '!' is used. For details see https://www.keshikan.net/fonts-e.html
|
||||||
//
|
|
||||||
fmt_dec_1 = "!%1.1f"; //insert a blank digit and then display a two-digit number
|
fmt_dec_1 = "!%1.1f"; // blank digit + 2 digit number, incl. 1 decimal
|
||||||
fmt_dec_10 = "!%2.0f"; //insert a blank digit and then display a two-digit number
|
fmt_dec_10 = "%3.1f"; // 3 digit number, incl. 1 decimal
|
||||||
fmt_dec_100 = "%3.0f"; //dispay a three digit number
|
fmt_dec_100 = "%3.0f"; // 3 digit number, no decimal
|
||||||
limit_dec_10=9.95; // use fmt_dec_1 below this number to avoid formatting 9.96 as 10.0 instead of 10
|
limit_dec_10=9.95; // use fmt_dec_1 below this number to avoid formatting 9.96 as 10.0 instead of 10
|
||||||
limit_dec_100=99.5;
|
limit_dec_100=99.5;
|
||||||
} else {
|
} else {
|
||||||
@@ -216,10 +212,10 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
rawvalue = value->value;
|
rawvalue = value->value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
course = M_PI_2 + float(random(-17, 17) / 100.0); // create random course/wind values with 90° +/- 10°
|
course = M_PI_2 + float(random(-26, 26) / 100.0); // create random course/wind values with 90° +/- 15°
|
||||||
rawvalue = course;
|
rawvalue = course;
|
||||||
}
|
}
|
||||||
course = course * RAD_TO_DEG; // Unit conversion form rad to deg
|
course = WindUtils::to360(course * RAD_TO_DEG); // Unit conversion form rad to deg
|
||||||
|
|
||||||
// Format 3 numbers with prefix zero
|
// Format 3 numbers with prefix zero
|
||||||
snprintf(buffer,bsize,"%03.0f",course);
|
snprintf(buffer,bsize,"%03.0f",course);
|
||||||
@@ -227,7 +223,7 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
result.cvalue = course;
|
result.cvalue = course;
|
||||||
}
|
}
|
||||||
//########################################################
|
//########################################################
|
||||||
else if (value->getFormat() == "formatKnots" && (value->getName() == "SOG" || value->getName() == "STW")){
|
else if (value->getFormat() == "formatKnots" && (value->getName() == "SOG" || value->getName() == "STW") || value->getName() == "DFT"){
|
||||||
double speed = 0;
|
double speed = 0;
|
||||||
if (usesimudata == false) {
|
if (usesimudata == false) {
|
||||||
speed = value->value;
|
speed = value->value;
|
||||||
@@ -519,11 +515,11 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
}
|
}
|
||||||
if (String(tempFormat) == "C") {
|
if (String(tempFormat) == "C") {
|
||||||
temp = temp - 273.15;
|
temp = temp - 273.15;
|
||||||
result.unit = "C";
|
result.unit = "Deg C";
|
||||||
}
|
}
|
||||||
else if (String(tempFormat) == "F") {
|
else if (String(tempFormat) == "F") {
|
||||||
temp = (temp - 273.15) * 9 / 5 + 32;
|
temp = (temp - 273.15) * 9 / 5 + 32;
|
||||||
result.unit = "F";
|
result.unit = "Deg F";
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
result.unit = "K";
|
result.unit = "K";
|
||||||
@@ -587,7 +583,11 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
rawvalue = 968 + float(random(0, 10));
|
rawvalue = 968 + float(random(0, 10));
|
||||||
pressure = rawvalue;
|
pressure = rawvalue;
|
||||||
}
|
}
|
||||||
|
if (pressure < 999.5) {
|
||||||
|
snprintf(buffer, bsize, "!%3.0f", pressure);
|
||||||
|
} else {
|
||||||
snprintf(buffer, bsize, "%4.0f", pressure);
|
snprintf(buffer, bsize, "%4.0f", pressure);
|
||||||
|
}
|
||||||
result.unit = "hPa";
|
result.unit = "hPa";
|
||||||
result.cvalue = pressure;
|
result.cvalue = pressure;
|
||||||
}
|
}
|
||||||
@@ -603,7 +603,11 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
rawvalue = value->value;
|
rawvalue = value->value;
|
||||||
pressure = 968 + float(random(0, 10));
|
pressure = 968 + float(random(0, 10));
|
||||||
}
|
}
|
||||||
|
if (pressure < 999.5) {
|
||||||
|
snprintf(buffer, bsize, "!%3.0f", pressure);
|
||||||
|
} else {
|
||||||
snprintf(buffer, bsize, "%4.0f", pressure);
|
snprintf(buffer, bsize, "%4.0f", pressure);
|
||||||
|
}
|
||||||
result.unit = "mBar";
|
result.unit = "mBar";
|
||||||
result.cvalue = pressure;
|
result.cvalue = pressure;
|
||||||
}
|
}
|
||||||
@@ -835,7 +839,7 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
result.cvalue = dplace;
|
result.cvalue = dplace;
|
||||||
}
|
}
|
||||||
//########################################################
|
//########################################################
|
||||||
else if (value->getFormat() == "formatXdr:A:D"){
|
else if ((value->getFormat() == "formatXdr:A:D") || ((value->getFormat() == "formatXdr:A:rd"))){
|
||||||
double angle = 0;
|
double angle = 0;
|
||||||
if (usesimudata == false) {
|
if (usesimudata == false) {
|
||||||
angle = value->value;
|
angle = value->value;
|
||||||
@@ -900,6 +904,40 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert and format boat value from SI to user defined format
|
||||||
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata) {
|
||||||
|
|
||||||
|
return formatValue(value, commondata, false, String("-1")); // call <formatValue> with standard setting for simulation data and precision setting from config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert and format boat value from SI to user defined format with simulation data setting and precision setting from config
|
||||||
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting) {
|
||||||
|
|
||||||
|
return formatValue(value, commondata, ignoreSimuDataSetting, String("-1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert and format boat value from SI to user defined format with standard setting for simulation data and specified precision
|
||||||
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, String setPrecision) {
|
||||||
|
|
||||||
|
return formatValue(value, commondata, false, setPrecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format double value from SI to user defined format and convert to string user defined precision setting
|
||||||
|
String formatValue(const double &value, const String &vFormat, CommonData &commondata)
|
||||||
|
{
|
||||||
|
GwApi::BoatValue tmpBVal("dummy"); // temporary boat value for string formatter
|
||||||
|
String sVal;
|
||||||
|
|
||||||
|
tmpBVal.setFormat(vFormat);
|
||||||
|
tmpBVal.valid = true;
|
||||||
|
tmpBVal.value = value;
|
||||||
|
sVal = formatValue(&tmpBVal, commondata, false, String("-1")).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||||
|
if (sVal.length() > 0 && sVal[0] == '!') {
|
||||||
|
sVal = sVal.substring(1); // cut leading "!" created at OBPFormatter; doesn't work for other fonts than 7SEG
|
||||||
|
}
|
||||||
|
return sVal;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper method for conversion of any data value from SI to user defined format
|
// Helper method for conversion of any data value from SI to user defined format
|
||||||
double convertValue(const double &value, const String &name, const String &format, CommonData &commondata)
|
double convertValue(const double &value, const String &name, const String &format, CommonData &commondata)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// General hardware definitions
|
// General hardware definitions
|
||||||
// CAN and RS485 bus pin definitions see obp60task.h
|
// CAN and RS485 bus pin definitions see obp60task.h
|
||||||
|
|
||||||
#if defined HARDWARE_V20 || HARDWARE_V21
|
#if defined BOARD_OBP60S3 || defined BOARD_OBP70S3
|
||||||
// Direction pin for RS485 NMEA0183
|
// Direction pin for RS485 NMEA0183
|
||||||
#define OBP_DIRECTION_PIN 18
|
#define OBP_DIRECTION_PIN 18
|
||||||
// I2C
|
// I2C
|
||||||
@@ -34,13 +34,17 @@
|
|||||||
#define PCF8574_I2C_ADDR1 0x20 // First digital out module
|
#define PCF8574_I2C_ADDR1 0x20 // First digital out module
|
||||||
// FRAM (e.g. MB85RC256V)
|
// FRAM (e.g. MB85RC256V)
|
||||||
#define FRAM_I2C_ADDR 0x50
|
#define FRAM_I2C_ADDR 0x50
|
||||||
// SPI (E-Ink display, Extern Bus)
|
// SPI (E-paper display, TFT display Extern Bus)
|
||||||
#define OBP_SPI_CS 39
|
#define OBP_SPI_CS 39
|
||||||
#define OBP_SPI_DC 40
|
#define OBP_SPI_DC 40
|
||||||
#define OBP_SPI_RST 41
|
#define OBP_SPI_RST 41
|
||||||
#define OBP_SPI_BUSY 42
|
#define OBP_SPI_BUSY 42
|
||||||
#define OBP_SPI_CLK 38
|
#define OBP_SPI_CLK 38
|
||||||
#define OBP_SPI_DIN 48
|
#define OBP_SPI_DIN 48
|
||||||
|
#define OBP_TFT_OFFSET_X 10 // ST7796, ILI9488 operating x-offset for centered 400x300 content
|
||||||
|
#define OBP_TFT_OFFSET_Y -20 // ST7796, ILI9488 operating y-offset for centered 400x300 content
|
||||||
|
#define TFT_BLACK 0x0109 // Replacement color for black on TFT (RGB565)
|
||||||
|
#define TFT_WHITE 0xFFFF // Replacement color for white on TFT (RGB565)
|
||||||
#define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code
|
#define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code
|
||||||
#define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function)
|
#define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function)
|
||||||
#define GxEPD_WIDTH 400 // Display width
|
#define GxEPD_WIDTH 400 // Display width
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ void initKeys(CommonData &commonData) {
|
|||||||
commonData.keydata[5].h = height;
|
commonData.keydata[5].h = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined HARDWARE_V20 || HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
// Keypad functions for original OBP60 hardware
|
// Keypad functions for original OBP60 hardware
|
||||||
int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
|
int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ void qrWiFi(String ssid, String passwd, uint16_t fgcolor, uint16_t bgcolor){
|
|||||||
getdisplay().setTextColor(fgcolor);
|
getdisplay().setTextColor(fgcolor);
|
||||||
getdisplay().setCursor(140, 285);
|
getdisplay().setCursor(140, 285);
|
||||||
getdisplay().print("WiFi");
|
getdisplay().print("WiFi");
|
||||||
getdisplay().nextPage(); // Full Refresh
|
displayNextPage(); // Full Refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+189
-130
@@ -1,5 +1,4 @@
|
|||||||
#include "OBPDataOperations.h"
|
#include "OBPDataOperations.h"
|
||||||
//#include "BoatDataCalibration.h" // Functions lib for data instance calibration
|
|
||||||
|
|
||||||
// --- Class CalibrationData ---------------
|
// --- Class CalibrationData ---------------
|
||||||
CalibrationData::CalibrationData(GwLog* log)
|
CalibrationData::CalibrationData(GwLog* log)
|
||||||
@@ -108,7 +107,6 @@ void CalibrationData::readConfig(GwConfigHandler* config)
|
|||||||
LOG_DEBUG(GwLog::LOG, "Calibration data type added: %s, offset: %f, slope: %f, smoothing: %f", instance.c_str(),
|
LOG_DEBUG(GwLog::LOG, "Calibration data type added: %s, offset: %f, slope: %f, smoothing: %f", instance.c_str(),
|
||||||
calibrationMap[instance].offset, calibrationMap[instance].slope, calibrationMap[instance].smooth);
|
calibrationMap[instance].offset, calibrationMap[instance].slope, calibrationMap[instance].smooth);
|
||||||
}
|
}
|
||||||
// LOG_DEBUG(GwLog::LOG, "All calibration data read");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle calibrationMap and calibrate all boat data values
|
// Handle calibrationMap and calibrate all boat data values
|
||||||
@@ -155,8 +153,7 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue)
|
|||||||
|
|
||||||
if (format == "formatWind") { // instance is of type angle
|
if (format == "formatWind") { // instance is of type angle
|
||||||
dataValue = (dataValue * slope) + offset;
|
dataValue = (dataValue * slope) + offset;
|
||||||
// dataValue = WindUtils::toPI(dataValue);
|
dataValue = WindUtils::to2PI(dataValue); // wind angle data is stored in [0..2PI] format
|
||||||
dataValue = WindUtils::to2PI(dataValue); // we should call <toPI> for format of [-180..180], but pages cannot display negative values properly yet
|
|
||||||
|
|
||||||
} else if (format == "formatCourse") { // instance is of type direction
|
} else if (format == "formatCourse") { // instance is of type direction
|
||||||
dataValue = (dataValue * slope) + offset;
|
dataValue = (dataValue * slope) + offset;
|
||||||
@@ -169,7 +166,6 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue)
|
|||||||
dataValue = (dataValue * slope) + offset;
|
dataValue = (dataValue * slope) + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
boatDataValue->value = dataValue; // update boat data value with calibrated value
|
boatDataValue->value = dataValue; // update boat data value with calibrated value
|
||||||
calibrationMap[instance].value = dataValue; // store the calibrated value in the list
|
calibrationMap[instance].value = dataValue; // store the calibrated value in the list
|
||||||
calibrationMap[instance].isCalibrated = true;
|
calibrationMap[instance].isCalibrated = true;
|
||||||
@@ -211,108 +207,107 @@ bool CalibrationData::smoothInstance(GwApi::BoatValue* boatDataValue)
|
|||||||
calibrationMap[instance].value = dataValue; // store the smoothed value in the list
|
calibrationMap[instance].value = dataValue; // store the smoothed value in the list
|
||||||
calibrationMap[instance].isCalibrated = true;
|
calibrationMap[instance].isCalibrated = true;
|
||||||
|
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: smooth: %f, oldValue: %f, result: %f", instance.c_str(), smoothFactor, oldValue, calibrationMap[instance].value);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// --- End Class CalibrationData ---------------
|
// --- End Class CalibrationData ---------------
|
||||||
|
|
||||||
// --- Class HstryBuf ---------------
|
// --- Class HstryBuf ---------------
|
||||||
HstryBuf::HstryBuf(const String& name, int size, BoatValueList* boatValues, GwLog* log)
|
HstryBuf::HstryBuf(const String& name, const int size, BoatValueList* boatValues, const bool smooth, GwLog* log)
|
||||||
: logger(log)
|
: logger(log)
|
||||||
|
, smoothing(smooth)
|
||||||
, boatDataName(name)
|
, boatDataName(name)
|
||||||
{
|
{
|
||||||
hstryBuf.resize(size);
|
hstryBuf.resize(size);
|
||||||
boatValue = boatValues->findValueOrCreate(name);
|
boatValue = boatValues->findValueOrCreate(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HstryBuf::init(const String& format, int updFreq, int mltplr, double minVal, double maxVal)
|
void HstryBuf::init(const String& format, int updFreq, double mltplr, double minVal, double maxVal)
|
||||||
{
|
{
|
||||||
hstryBuf.setMetaData(boatDataName, format, updFreq, mltplr, minVal, maxVal);
|
hstryBuf.setMetaData(boatDataName, format, updFreq, mltplr, minVal, maxVal);
|
||||||
hstryMin = minVal;
|
hstryMin = minVal;
|
||||||
hstryMax = maxVal;
|
hstryMax = maxVal;
|
||||||
|
bufUpdateTime = 0;
|
||||||
if (!boatValue->valid) {
|
if (!boatValue->valid) {
|
||||||
boatValue->setFormat(format);
|
|
||||||
boatValue->value = std::numeric_limits<double>::max(); // mark current value invalid
|
boatValue->value = std::numeric_limits<double>::max(); // mark current value invalid
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void HstryBuf::add(double value)
|
isTypeAngle = format == "formatCourse" || format == "formatWind" || format == "formatRot";
|
||||||
{
|
// initialize correct variant of buffer for averaging history data
|
||||||
if (value >= hstryMin && value <= hstryMax) {
|
if (smoothing) {
|
||||||
hstryBuf.add(value);
|
if (isTypeAngle) {
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "HstryBuf::add: name: %s, value: %.3f", hstryBuf.getName(), value);
|
chrtAvgAngle.begin();
|
||||||
|
} else {
|
||||||
|
chrtAvg.begin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HstryBuf::handle(bool useSimuData, CommonData& common)
|
void HstryBuf::add(const double value)
|
||||||
{
|
{
|
||||||
// GwApi::BoatValue* tmpBVal;
|
double bufVal = value;
|
||||||
std::unique_ptr<GwApi::BoatValue> tmpBVal; // Temp variable to get formatted and converted data value from OBP60Formatter
|
|
||||||
|
|
||||||
// create temporary boat value for calibration purposes and retrieval of simulation value
|
if (value >= hstryMin && value <= hstryMax) {
|
||||||
// tmpBVal = new GwApi::BoatValue(boatDataName.c_str());
|
|
||||||
tmpBVal = std::unique_ptr<GwApi::BoatValue>(new GwApi::BoatValue(boatDataName));
|
if (smoothing) {
|
||||||
tmpBVal->setFormat(boatValue->getFormat());
|
if (isTypeAngle) {
|
||||||
tmpBVal->value = boatValue->value;
|
bufVal = chrtAvgAngle.reading(value);
|
||||||
tmpBVal->valid = boatValue->valid;
|
} else {
|
||||||
|
bufVal = chrtAvg.reading(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hstryBuf.add(bufVal);
|
||||||
|
// LOG_DEBUG(GwLog::DEBUG, "HstryBuf::add: name: %s, value: %.3f, value buffer: %.3f", hstryBuf.getName(), value, hstryBuf.getLast());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HstryBuf::handle(const bool useSimuData, CommonData& common)
|
||||||
|
{
|
||||||
|
if ((millis() - bufUpdateTime) >= hstryBuf.getUpdFreq()) {
|
||||||
|
|
||||||
|
bufUpdateTime = millis();
|
||||||
|
|
||||||
if (boatValue->valid) {
|
if (boatValue->valid) {
|
||||||
// Calibrate boat value before adding it to history buffer
|
|
||||||
//calibrationData.calibrateInstance(tmpBVal.get(), logger);
|
|
||||||
//add(tmpBVal->value);
|
|
||||||
add(boatValue->value);
|
add(boatValue->value);
|
||||||
|
|
||||||
} else if (useSimuData) { // add simulated value to history buffer
|
} else if (useSimuData) { // add simulated value to history buffer
|
||||||
|
std::unique_ptr<GwApi::BoatValue> tmpBVal; // Temp variable to get formatted and converted data value from OBP60Formatter
|
||||||
|
tmpBVal = std::unique_ptr<GwApi::BoatValue>(new GwApi::BoatValue(boatDataName)); // create temporary boat value for retrieval of simulation value
|
||||||
|
tmpBVal->setFormat(boatValue->getFormat());
|
||||||
|
tmpBVal->value = boatValue->value;
|
||||||
|
tmpBVal->valid = boatValue->valid;
|
||||||
double simSIValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at <formatValue>; here: retreive SI value
|
double simSIValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at <formatValue>; here: retreive SI value
|
||||||
add(simSIValue);
|
add(simSIValue);
|
||||||
} else {
|
} else {
|
||||||
// here we will add invalid (DBL_MAX) value; this will mark periods of missing data in buffer together with a timestamp
|
// TODO: add invalid (DBL_MAX) value; this will mark periods of missing data in buffer together with a timestamp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// --- End Class HstryBuf ---------------
|
// --- End Class HstryBuf ---------------
|
||||||
|
|
||||||
// --- Class HstryBuffers ---------------
|
// --- Class HstryBuffers ---------------
|
||||||
HstryBuffers::HstryBuffers(int size, BoatValueList* boatValues, GwLog* log)
|
HstryBuffers::HstryBuffers(const int size, BoatValueList* boatValues, GwLog* log)
|
||||||
: size(size)
|
: size(size)
|
||||||
, boatValueList(boatValues)
|
, boatValueList(boatValues)
|
||||||
, logger(log)
|
, logger(log) { };
|
||||||
{
|
|
||||||
|
|
||||||
// collect boat values for true wind calculation
|
|
||||||
// should all have been already created at true wind object initialization
|
|
||||||
// potentially to be moved to history buffer handling
|
|
||||||
awaBVal = boatValueList->findValueOrCreate("AWA");
|
|
||||||
hdtBVal = boatValueList->findValueOrCreate("HDT");
|
|
||||||
hdmBVal = boatValueList->findValueOrCreate("HDM");
|
|
||||||
varBVal = boatValueList->findValueOrCreate("VAR");
|
|
||||||
cogBVal = boatValueList->findValueOrCreate("COG");
|
|
||||||
sogBVal = boatValueList->findValueOrCreate("SOG");
|
|
||||||
awdBVal = boatValueList->findValueOrCreate("AWD");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create history buffer for boat data type
|
// Create history buffer for boat data type
|
||||||
void HstryBuffers::addBuffer(const String& name)
|
void HstryBuffers::addBuffer(const String& name, const bool smooth)
|
||||||
{
|
{
|
||||||
if (HstryBuffers::getBuffer(name) != nullptr) { // buffer for this data type already exists
|
if (HstryBuffers::getBuffer(name) != nullptr) { // buffer for this data type already exists
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (bufferParams.find(name) == bufferParams.end()) { // requested boat data type is not supported in list of <bufferParams>
|
|
||||||
|
auto it = bufferParams.find(name);
|
||||||
|
if (it == bufferParams.end() && !name.startsWith("xdr")) { // requested boat data type is not supported in list of <bufferParams>
|
||||||
|
// we take any "XDR" type, though
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hstryBuffers[name] = std::unique_ptr<HstryBuf>(new HstryBuf(name, size, boatValueList, logger));
|
// create buffer only; initialization with metadata can only be done later after boat data have been updated first time
|
||||||
|
hstryBuffers[name] = std::unique_ptr<HstryBuf>(new HstryBuf(name, size, boatValueList, smooth, logger));
|
||||||
|
|
||||||
// Initialize metadata for buffer
|
LOG_DEBUG(GwLog::LOG, "HstryBuffers: new buffer added: name: %s", name);
|
||||||
String valueFormat = bufferParams[name].format; // Data format of boat data type
|
|
||||||
// String valueFormat = boatValueList->findValueOrCreate(name)->getFormat().c_str(); // Unfortunately, format is not yet available during system initialization
|
|
||||||
int hstryUpdFreq = bufferParams[name].hstryUpdFreq; // Update frequency for history buffers in ms
|
|
||||||
int mltplr = bufferParams[name].mltplr; // default multiplier which transforms original <double> value into buffer type format
|
|
||||||
double bufferMinVal = bufferParams[name].bufferMinVal; // Min value for this history buffer
|
|
||||||
double bufferMaxVal = bufferParams[name].bufferMaxVal; // Max value for this history buffer
|
|
||||||
|
|
||||||
hstryBuffers[name]->init(valueFormat, hstryUpdFreq, mltplr, bufferMinVal, bufferMaxVal);
|
|
||||||
LOG_DEBUG(GwLog::DEBUG, "HstryBuffers: new buffer added: name: %s, format: %s, multiplier: %d, min value: %.2f, max value: %.2f", name, valueFormat, mltplr, bufferMinVal, bufferMaxVal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle all registered history buffers
|
// Handle all registered history buffers
|
||||||
@@ -320,6 +315,35 @@ void HstryBuffers::handleHstryBufs(bool useSimuData, CommonData& common)
|
|||||||
{
|
{
|
||||||
for (auto& bufMap : hstryBuffers) {
|
for (auto& bufMap : hstryBuffers) {
|
||||||
auto& buf = bufMap.second;
|
auto& buf = bufMap.second;
|
||||||
|
|
||||||
|
if (!buf->hasMetaData()) { // meta data initialization for buffer has not been done before
|
||||||
|
|
||||||
|
String valueFormat = boatValueList->findValueOrCreate(buf->boatDataName)->getFormat().c_str();
|
||||||
|
// LOG_DEBUG(GwLog::DEBUG, "HstryBuffers: value name: %s, format: %s", boatValueList->findValueOrCreate(buf->boatDataName)->getName().c_str(), valueFormat);
|
||||||
|
|
||||||
|
if (!valueFormat.isEmpty()) {
|
||||||
|
String lookupKey = buf->boatDataName;
|
||||||
|
if (buf->boatDataName.startsWith("xdr")) { // current boat data is of type "XDR"
|
||||||
|
lookupKey = valueFormat;
|
||||||
|
}
|
||||||
|
auto it = bufferParams.find(lookupKey);
|
||||||
|
if (it == bufferParams.end()) { // requested boat data type is not supported in list of <bufferParams>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HistoryParams params = it->second; // this is the metadata for the new buffer
|
||||||
|
|
||||||
|
int hstryUpdFreq = params.hstryUpdFreq; // Update frequency for history buffers in ms
|
||||||
|
double mltplr = params.mltplr; // default multiplier which transforms original <double> value into buffer type format
|
||||||
|
double bufferMinVal = params.bufferMinVal; // Min value for this history buffer
|
||||||
|
double bufferMaxVal = params.bufferMaxVal; // Max value for this history buffer
|
||||||
|
|
||||||
|
hstryBuffers[buf->boatDataName]->init(valueFormat, hstryUpdFreq, mltplr, bufferMinVal, bufferMaxVal);
|
||||||
|
buf->metaDataDefined = true;
|
||||||
|
LOG_DEBUG(GwLog::LOG, "HstryBuffers::handleBufs: metadata added: name: %s, format: %s, frequency: %d, multiplier: %f, min value: %.2f, max value: %.2f", buf->boatDataName, valueFormat, hstryUpdFreq,
|
||||||
|
mltplr, bufferMinVal, bufferMaxVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buf->handle(useSimuData, common);
|
buf->handle(useSimuData, common);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,8 +353,9 @@ RingBuffer<uint16_t>* HstryBuffers::getBuffer(const String& name)
|
|||||||
auto it = hstryBuffers.find(name);
|
auto it = hstryBuffers.find(name);
|
||||||
if (it != hstryBuffers.end()) {
|
if (it != hstryBuffers.end()) {
|
||||||
return &it->second->hstryBuf;
|
return &it->second->hstryBuf;
|
||||||
}
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// --- End Class HstryBuffers ---------------
|
// --- End Class HstryBuffers ---------------
|
||||||
|
|
||||||
@@ -396,56 +421,73 @@ void WindUtils::addPolar(const double* phi1, const double* r1,
|
|||||||
toPol(&x1, &y1, phi, r);
|
toPol(&x1, &y1, phi, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
|
void WindUtils::calcTwdSA(const double* AWA, const double* AWS, const double* AWD, const double* CTW, const double* STW, const double* HDT,
|
||||||
const double* CTW, const double* STW, const double* HDT,
|
double* TWA, double* TWS, double* TWD)
|
||||||
double* TWD, double* TWS, double* TWA, double* AWD)
|
|
||||||
{
|
{
|
||||||
*AWD = *AWA + *HDT;
|
|
||||||
*AWD = to2PI(*AWD);
|
|
||||||
double stw = -*STW;
|
double stw = -*STW;
|
||||||
addPolar(AWD, AWS, CTW, &stw, TWD, TWS);
|
addPolar(AWD, AWS, CTW, &stw, TWD, TWS);
|
||||||
|
|
||||||
// Normalize TWD to [0..360°] (2PI) and TWA to [-180..180] (PI)
|
// Normalize to [0..2PI]
|
||||||
*TWD = to2PI(*TWD);
|
*TWD = to2PI(*TWD);
|
||||||
*TWA = toPI(*TWD - *HDT);
|
*TWA = to2PI(*TWD - *HDT); // for internal storage we normalize wind angle to [0..2PI], as well
|
||||||
}
|
}
|
||||||
|
|
||||||
double WindUtils::calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal)
|
// calculate HDT from either HDM + VAR, HDM only, or COG
|
||||||
|
bool WindUtils::calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal, double* hdtVal)
|
||||||
{
|
{
|
||||||
double hdt;
|
|
||||||
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
|
||||||
|
|
||||||
if (*hdmVal != DBL_MAX) {
|
|
||||||
hdt = *hdmVal + (*varVal != DBL_MAX ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available)
|
|
||||||
hdt = to2PI(hdt);
|
|
||||||
} else if (*cogVal != DBL_MAX && *sogVal >= minSogVal) {
|
|
||||||
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
|
|
||||||
} else {
|
|
||||||
hdt = DBL_MAX; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
|
|
||||||
}
|
|
||||||
|
|
||||||
return hdt;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WindUtils::calcWinds(const double* awaVal, const double* awsVal,
|
|
||||||
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
|
||||||
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal, double* awdVal)
|
|
||||||
{
|
|
||||||
double stw, hdt, ctw;
|
|
||||||
double twd, tws, twa, awd;
|
|
||||||
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
||||||
|
|
||||||
if (*hdtVal != DBL_MAX) {
|
if (*hdtVal != DBL_MAX) {
|
||||||
hdt = *hdtVal; // Use HDT if available
|
// HDT already available-> nothing to do
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*hdmVal != DBL_MAX) {
|
||||||
|
*hdtVal = *hdmVal + (*varVal != DBL_MAX ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available)
|
||||||
|
*hdtVal = to2PI(*hdtVal);
|
||||||
|
} else if (*cogVal != DBL_MAX && *sogVal >= minSogVal) {
|
||||||
|
*hdtVal = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
|
||||||
} else {
|
} else {
|
||||||
hdt = calcHDT(hdmVal, varVal, cogVal, sogVal);
|
*hdtVal = DBL_MAX; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate wind direction (either TWD or AWD) from wind angle (TWA or AWA) and HDT
|
||||||
|
bool WindUtils::calcWD(const double* waVal, const double* hdtVal, double* wdVal)
|
||||||
|
{
|
||||||
|
if (*waVal != DBL_MAX && *hdtVal != DBL_MAX) {
|
||||||
|
*wdVal = *waVal + *hdtVal;
|
||||||
|
*wdVal = to2PI(*wdVal);
|
||||||
|
} else {
|
||||||
|
*wdVal = DBL_MAX; // Cannot calculate wind direction WD without valid wind angle WA and HDT
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate full set of true winds (TWA, TWS, TWD) apparent winds and more data
|
||||||
|
bool WindUtils::calcTrueWinds(const double* awaVal, const double* awsVal, const double* awd,
|
||||||
|
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
||||||
|
double* twaVal, double* twsVal, double* twdVal)
|
||||||
|
{
|
||||||
|
double stw, ctw;
|
||||||
|
double twd, tws, twa;
|
||||||
|
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
||||||
|
|
||||||
|
if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) {
|
||||||
|
// Cannot calculate true winds at all without valid AWA, AWS
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*cogVal != DBL_MAX && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG
|
if (*cogVal != DBL_MAX && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG
|
||||||
|
|
||||||
ctw = *cogVal; // Use COG for CTW if available
|
ctw = *cogVal; // Use COG for CTW if available
|
||||||
} else {
|
} else {
|
||||||
ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code
|
ctw = *hdtVal; // 2nd approximation for CTW; HDT must exist if we reach this part of code
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*stwVal != DBL_MAX) {
|
if (*stwVal != DBL_MAX) {
|
||||||
@@ -456,28 +498,34 @@ bool WindUtils::calcWinds(const double* awaVal, const double* awsVal,
|
|||||||
// If STW and SOG are not available, we cannot calculate true wind
|
// If STW and SOG are not available, we cannot calculate true wind
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "WindUtils:calcWinds: HDT: %.1f, CTW %.1f, STW %.1f", hdt, ctw, stw);
|
|
||||||
|
|
||||||
if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) {
|
calcTwdSA(awaVal, awsVal, awd, &ctw, &stw, hdtVal, &twa, &tws, &twd);
|
||||||
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
calcTwdSA(awaVal, awsVal, &ctw, &stw, &hdt, &twd, &tws, &twa, &awd);
|
|
||||||
*twdVal = twd;
|
|
||||||
*twsVal = tws;
|
|
||||||
*twaVal = twa;
|
*twaVal = twa;
|
||||||
*awdVal = awd;
|
*twsVal = tws;
|
||||||
|
*twdVal = twd;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate true wind data and add to obp60task boat data list
|
// Set max wind speed
|
||||||
bool WindUtils::addWinds()
|
void WindUtils::setMaxWs(GwApi::BoatValue* wsMaxValue, const double* wsVal)
|
||||||
|
{
|
||||||
|
static double maxWs = 0.0; // maintain own maxTWS value in obp user task; core gateway would reset MaxTWS obp boat value if true wind data is not available
|
||||||
|
|
||||||
|
if (*wsVal != DBL_MAX && *wsVal > maxWs) {
|
||||||
|
maxWs = *wsVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxWs > 0.0 && maxWs >= wsMaxValue->value) {
|
||||||
|
wsMaxValue->value = maxWs; // overwrite core gateway value each second again with own user task value if that value is larger
|
||||||
|
wsMaxValue->valid = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate true wind data and add to obp60task boat data list
|
||||||
|
bool WindUtils::handleWinds(bool calcWinds)
|
||||||
{
|
{
|
||||||
double twd, tws, twa, awd, hdt;
|
|
||||||
bool twCalculated = false;
|
bool twCalculated = false;
|
||||||
bool awdCalculated = false;
|
|
||||||
|
|
||||||
double awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX;
|
double awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX;
|
||||||
double awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX;
|
double awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX;
|
||||||
@@ -487,49 +535,60 @@ bool WindUtils::addWinds()
|
|||||||
double hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX;
|
double hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX;
|
||||||
double hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX;
|
double hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX;
|
||||||
double varVal = varBVal->valid ? varBVal->value : DBL_MAX;
|
double varVal = varBVal->valid ? varBVal->value : DBL_MAX;
|
||||||
//LOG_DEBUG(GwLog::DEBUG, "WindUtils:addWinds: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
|
double twaVal = twaBVal->valid ? twaBVal->value : DBL_MAX;
|
||||||
// cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
|
double twsVal = twsBVal->valid ? twsBVal->value : DBL_MAX;
|
||||||
|
|
||||||
// Check if TWD can be calculated from TWA and HDT/HDM
|
if (calcHDT(&hdmVal, &varVal, &cogVal, &sogVal, &hdtVal)) {
|
||||||
if (twaBVal->valid) {
|
hdtBVal->value = hdtVal;
|
||||||
if (!twdBVal->valid) {
|
hdtBVal->valid = true;
|
||||||
if (hdtVal != DBL_MAX) {
|
|
||||||
hdt = hdtVal; // Use HDT if available
|
|
||||||
} else {
|
} else {
|
||||||
hdt = calcHDT(&hdmVal, &varVal, &cogVal, &sogVal);
|
// determination of HDT was not possible due to missing prerequisite data
|
||||||
|
hdtBVal->valid = false;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
twd = twaBVal->value + hdt;
|
|
||||||
twd = to2PI(twd);
|
// calculate AWD if it does not exist yet and AWA is available
|
||||||
|
if (!awdBVal->valid) {
|
||||||
|
if (calcWD(&awaVal, &hdtVal, &awd)) {
|
||||||
|
awdBVal->value = awd;
|
||||||
|
awdBVal->valid = true;
|
||||||
|
} else {
|
||||||
|
awdBVal->valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate TWD if it does not exist yet and TWA is available
|
||||||
|
if (!twdBVal->valid) {
|
||||||
|
if (calcWD(&twaVal, &hdtVal, &twd)) {
|
||||||
twdBVal->value = twd;
|
twdBVal->value = twd;
|
||||||
twdBVal->valid = true;
|
twdBVal->valid = true;
|
||||||
|
} else {
|
||||||
|
twdBVal->valid = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
if (!calcWinds) { // don't calculate true winds from apparent winds if not set in configuration
|
||||||
// Calculate true winds and AWD; if true winds exist, use at least AWD calculation
|
return twCalculated;
|
||||||
twCalculated = calcWinds(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa, &awd);
|
}
|
||||||
|
|
||||||
|
if (!twaBVal->valid || !twsBVal->valid || !twdBVal->valid) {
|
||||||
|
// calculate true winds at least one of three true wind values does not exist
|
||||||
|
twCalculated = calcTrueWinds(&awaVal, &awsVal, &awd, &cogVal, &stwVal, &sogVal, &hdtVal, &twa, &tws, &twd);
|
||||||
if (twCalculated) { // Replace values only, if successfully calculated and not already available
|
if (twCalculated) { // Replace values only, if successfully calculated and not already available
|
||||||
if (!twdBVal->valid) {
|
if (!twaBVal->valid) {
|
||||||
twdBVal->value = twd;
|
twaBVal->value = twa;
|
||||||
twdBVal->valid = true;
|
twaBVal->valid = true;
|
||||||
}
|
}
|
||||||
if (!twsBVal->valid) {
|
if (!twsBVal->valid) {
|
||||||
twsBVal->value = tws;
|
twsBVal->value = tws;
|
||||||
twsBVal->valid = true;
|
twsBVal->valid = true;
|
||||||
}
|
}
|
||||||
if (!twaBVal->valid) {
|
if (!twdBVal->valid) {
|
||||||
//twaBVal->value = twa;
|
twdBVal->value = twd;
|
||||||
twaBVal->value = to2PI(twa); // convert to [0..360], because pages cannot display negative values properly yet
|
twdBVal->valid = true;
|
||||||
twaBVal->valid = true;
|
|
||||||
}
|
|
||||||
if (!awdBVal->valid) {
|
|
||||||
awdBVal->value = awd;
|
|
||||||
awdBVal->valid = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "WindUtils:addWinds: twCalculated %d, TWD %.1f, TWA %.1f, TWS %.2f kn, AWD: %.1f", twCalculated, twdBVal->value * RAD_TO_DEG,
|
|
||||||
// twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852, awdBVal->value * RAD_TO_DEG);
|
|
||||||
|
|
||||||
return twCalculated;
|
return twCalculated;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "OBPRingBuffer.h"
|
#include "OBPRingBuffer.h"
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "obp60task.h"
|
#include "obp60task.h"
|
||||||
|
#include "movingAvg.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ private:
|
|||||||
} tCalibrationData;
|
} tCalibrationData;
|
||||||
|
|
||||||
std::unordered_map<std::string, tCalibrationData> calibrationMap; // list of calibration data instances
|
std::unordered_map<std::string, tCalibrationData> calibrationMap; // list of calibration data instances
|
||||||
std::unordered_map<std::string, double> lastValue; // array for last smoothed values of boat data values
|
std::unordered_map<std::string, double> lastValue; // array for last smoothed value of boat data values
|
||||||
GwLog* logger;
|
GwLog* logger;
|
||||||
|
|
||||||
static constexpr int8_t MAX_CALIBRATION_DATA = 4; // maximum number of calibration data instances
|
static constexpr int8_t MAX_CALIBRATION_DATA = 4; // maximum number of calibration data instances
|
||||||
@@ -32,108 +33,138 @@ public:
|
|||||||
bool smoothInstance(GwApi::BoatValue* boatDataValue); // Smooth single boat data value
|
bool smoothInstance(GwApi::BoatValue* boatDataValue); // Smooth single boat data value
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Class for a single history buffer of boat values
|
||||||
class HstryBuf {
|
class HstryBuf {
|
||||||
private:
|
private:
|
||||||
RingBuffer<uint16_t> hstryBuf; // Circular buffer to store history values
|
RingBuffer<uint16_t> hstryBuf; // Circular buffer to store history values
|
||||||
|
movingAvg<double> chrtAvg {6}; // Store average of the last 6 chart values if chart gradient shall be smoothed
|
||||||
|
movingAvgAngle<double> chrtAvgAngle {6}; // Store average of the last 6 chart values (angle data) if chart gradient shall be smoothed
|
||||||
String boatDataName;
|
String boatDataName;
|
||||||
double hstryMin;
|
double hstryMin;
|
||||||
double hstryMax;
|
double hstryMax;
|
||||||
|
bool metaDataDefined = false;
|
||||||
|
bool smoothing = false;
|
||||||
|
bool isTypeAngle = false;
|
||||||
|
unsigned long bufUpdateTime;
|
||||||
GwApi::BoatValue* boatValue;
|
GwApi::BoatValue* boatValue;
|
||||||
GwLog* logger;
|
GwLog* logger;
|
||||||
|
|
||||||
friend class HstryBuffers;
|
friend class HstryBuffers;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HstryBuf(const String& name, int size, BoatValueList* boatValues, GwLog* log);
|
HstryBuf(const String& name, const int size, BoatValueList* boatValues, const bool smooth, GwLog* log);
|
||||||
void init(const String& format, int updFreq, int mltplr, double minVal, double maxVal);
|
bool hasMetaData() const { return metaDataDefined; };
|
||||||
|
void init(const String& format, int updFreq, double mltplr, double minVal, double maxVal);
|
||||||
void add(double value);
|
void add(double value);
|
||||||
void handle(bool useSimuData, CommonData& common);
|
void handle(bool useSimuData, CommonData& common);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Manage list of history buffers for supported boat data; used by boat data charts
|
||||||
class HstryBuffers {
|
class HstryBuffers {
|
||||||
private:
|
private:
|
||||||
std::map<String, std::unique_ptr<HstryBuf>> hstryBuffers;
|
std::map<String, std::unique_ptr<HstryBuf>> hstryBuffers;
|
||||||
int size; // size of all history buffers
|
int size; // size of all history buffers
|
||||||
BoatValueList* boatValueList;
|
BoatValueList* boatValueList;
|
||||||
GwLog* logger;
|
GwLog* logger;
|
||||||
GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal, *awdBVal; // boat values for true wind calculation
|
|
||||||
|
|
||||||
struct HistoryParams {
|
struct HistoryParams {
|
||||||
int hstryUpdFreq; // update frequency of history buffer (documentation only)
|
int hstryUpdFreq; // update frequency of history buffer (documentation only)
|
||||||
int mltplr; // specifies actual value precision being storable:
|
double mltplr; // specifies actual value precision being storable:
|
||||||
// [10000: 0 - 6.5535 | 1000: 0 - 65.535 | 100: 0 - 650.35 | 10: 0 - 6503.5
|
// [10000: 0 - 6.5535 | 1000: 0 - 65.535 | 100: 0 - 655.35 | 10: 0 - 6553.5 | 1: 0 - 65535 | 0.1: 0 - 655350]
|
||||||
double bufferMinVal; // minimum valid data value
|
double bufferMinVal; // minimum valid data value
|
||||||
double bufferMaxVal; // maximum valid data value
|
double bufferMaxVal; // maximum valid data value
|
||||||
String format; // format of data type
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define buffer parameters for supported boat data type
|
|
||||||
std::map<String, HistoryParams> bufferParams = {
|
std::map<String, HistoryParams> bufferParams = {
|
||||||
{ "AWA", { 1000, 10000, 0.0, M_TWOPI, "formatWind" } },
|
{ "AWA", { 1000, 10000, 0.0, M_TWOPI } },
|
||||||
{ "AWD", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
{ "AWD", { 1000, 10000, 0.0, M_TWOPI } },
|
||||||
{ "AWS", { 1000, 1000, 0.0, 65.0, "formatKnots" } },
|
{ "AWS", { 1000, 1000, 0.0, 65.0 } },
|
||||||
{ "COG", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
{ "COG", { 1000, 10000, 0.0, M_TWOPI } },
|
||||||
{ "DBS", { 1000, 100, 0.0, 650.0, "formatDepth" } },
|
{ "DBK", { 1000, 100, 0.0, 650.0 } },
|
||||||
{ "DBT", { 1000, 100, 0.0, 650.0, "formatDepth" } },
|
{ "DBS", { 1000, 100, 0.0, 650.0 } },
|
||||||
{ "DPT", { 1000, 100, 0.0, 650.0, "formatDepth" } },
|
{ "DBT", { 1000, 100, 0.0, 650.0 } },
|
||||||
{ "HDM", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
{ "DPT", { 1000, 100, 0.0, 650.0 } },
|
||||||
{ "HDT", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
{ "HDM", { 1000, 10000, 0.0, M_TWOPI } },
|
||||||
{ "ROT", { 1000, 10000, -M_PI / 180.0 * 99.0, M_PI / 180.0 * 99.0, "formatRot" } }, // min/max is -/+ 99 degrees for "rate of turn"
|
{ "HDT", { 1000, 10000, 0.0, M_TWOPI } },
|
||||||
{ "SOG", { 1000, 1000, 0.0, 65.0, "formatKnots" } },
|
{ "ROT", { 1000, 10000, -M_PI / 180.0 * 99.0, M_PI / 180.0 * 99.0 } }, // min/max is -/+ 99 degrees for "rate of turn"
|
||||||
{ "STW", { 1000, 1000, 0.0, 65.0, "formatKnots" } },
|
{ "SOG", { 1000, 1000, 0.0, 65.0 } },
|
||||||
{ "TWA", { 1000, 10000, 0.0, M_TWOPI, "formatWind" } },
|
{ "STW", { 1000, 1000, 0.0, 65.0 } },
|
||||||
{ "TWD", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
{ "TWA", { 1000, 10000, 0.0, M_TWOPI } },
|
||||||
{ "TWS", { 1000, 1000, 0.0, 65.0, "formatKnots" } },
|
{ "TWD", { 1000, 10000, 0.0, M_TWOPI } },
|
||||||
{ "WTemp", { 1000, 100, 233.0, 650.0, "kelvinToC" } } // [-50..376] °C
|
{ "TWS", { 1000, 1000, 0.0, 65.0 } },
|
||||||
|
{ "WTemp", { 1000, 100, 263.15, 403.15 } }, // water temp [-10..130] °C
|
||||||
|
{ "formatXdr:C:K", { 1000, 100, 223.15, 423.15 } }, // temperature [-50..150] deg celsius
|
||||||
|
{ "formatXdr:P:B", { 60000, 1000, 0, 65.0 } }, // pressure [0..65] bar
|
||||||
|
{ "formatXdr:P:P", { 60000, 0.1, 0, 650000 } }, // pressure [0..6500] hPa
|
||||||
|
{ "formatXdr:H:P", { 1000, 100, 0, 100 } }, // humidity [0..100] percent
|
||||||
|
{ "formatXdr:I:A", { 1000, 100, 0, 650.0 } }, // current [0..650] amperes
|
||||||
|
{ "formatXdr:U:V", { 1000, 1000, 0, 65.0 } }, // voltage [0..65] volts
|
||||||
|
{ "formatXdr:T:R", { 1000, 1, 0, 30000 } }, // tachometer [0..30000] rpm
|
||||||
|
{ "formatXdr:V:L", { 10000, 100, 0, 650 } }, // volume [0..650] litres
|
||||||
|
{ "formatXdr:V:M", { 10000, 10000, 0, 6.50 } }, // volume [0..6.5] m^3
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HstryBuffers(int size, BoatValueList* boatValues, GwLog* log);
|
HstryBuffers(int size, BoatValueList* boatValues, GwLog* log);
|
||||||
void addBuffer(const String& name);
|
void addBuffer(const String& name, const bool smooth);
|
||||||
void handleHstryBufs(bool useSimuData, CommonData& common);
|
void handleHstryBufs(bool useSimuData, CommonData& common);
|
||||||
RingBuffer<uint16_t>* getBuffer(const String& name);
|
RingBuffer<uint16_t>* getBuffer(const String& name);
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindUtils {
|
class WindUtils {
|
||||||
private:
|
private:
|
||||||
GwApi::BoatValue *twaBVal, *twsBVal, *twdBVal;
|
GwApi::BoatValue *twaBVal, *twsBVal, *twdBVal, *maxtwsBVal;
|
||||||
GwApi::BoatValue *awaBVal, *awsBVal, *awdBVal, *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal;
|
GwApi::BoatValue *awaBVal, *awsBVal, *awdBVal;
|
||||||
|
GwApi::BoatValue *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal;
|
||||||
|
double twd, tws, twa, awd;
|
||||||
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
|
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
|
||||||
GwLog* logger;
|
GwLog* logger;
|
||||||
|
|
||||||
|
// specify missing data for boat value type AWD; AWD is not available in core gateway and need to be specified here
|
||||||
|
void defineAWD() {
|
||||||
|
awdBVal->setFormat("formatCourse");
|
||||||
|
awdBVal->valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WindUtils(BoatValueList* boatValues, GwLog* log)
|
WindUtils(BoatValueList* boatValues, GwLog* log)
|
||||||
: logger(log)
|
: logger(log)
|
||||||
{
|
{
|
||||||
twaBVal = boatValues->findValueOrCreate("TWA");
|
twaBVal = boatValues->findValueOrCreate("TWA");
|
||||||
twsBVal = boatValues->findValueOrCreate("TWS");
|
twsBVal = boatValues->findValueOrCreate("TWS");
|
||||||
|
maxtwsBVal = boatValues->findValueOrCreate("MaxTws");
|
||||||
twdBVal = boatValues->findValueOrCreate("TWD");
|
twdBVal = boatValues->findValueOrCreate("TWD");
|
||||||
awaBVal = boatValues->findValueOrCreate("AWA");
|
awaBVal = boatValues->findValueOrCreate("AWA");
|
||||||
awsBVal = boatValues->findValueOrCreate("AWS");
|
awsBVal = boatValues->findValueOrCreate("AWS");
|
||||||
awdBVal = boatValues->findValueOrCreate("AWD");
|
|
||||||
cogBVal = boatValues->findValueOrCreate("COG");
|
cogBVal = boatValues->findValueOrCreate("COG");
|
||||||
stwBVal = boatValues->findValueOrCreate("STW");
|
stwBVal = boatValues->findValueOrCreate("STW");
|
||||||
sogBVal = boatValues->findValueOrCreate("SOG");
|
sogBVal = boatValues->findValueOrCreate("SOG");
|
||||||
hdtBVal = boatValues->findValueOrCreate("HDT");
|
hdtBVal = boatValues->findValueOrCreate("HDT");
|
||||||
hdmBVal = boatValues->findValueOrCreate("HDM");
|
hdmBVal = boatValues->findValueOrCreate("HDM");
|
||||||
varBVal = boatValues->findValueOrCreate("VAR");
|
varBVal = boatValues->findValueOrCreate("VAR");
|
||||||
|
|
||||||
|
awdBVal = boatValues->findValueOrCreate("AWD");
|
||||||
|
defineAWD();
|
||||||
};
|
};
|
||||||
|
|
||||||
static double to2PI(double a);
|
static double to2PI(double a);
|
||||||
static double toPI(double a);
|
static double toPI(double a);
|
||||||
static double to360(double a);
|
static double to360(double a);
|
||||||
static double to180(double a);
|
static double to180(double a);
|
||||||
|
|
||||||
void toCart(const double* phi, const double* r, double* x, double* y);
|
void toCart(const double* phi, const double* r, double* x, double* y);
|
||||||
void toPol(const double* x, const double* y, double* phi, double* r);
|
void toPol(const double* x, const double* y, double* phi, double* r);
|
||||||
void addPolar(const double* phi1, const double* r1,
|
void addPolar(const double* phi1, const double* r1,
|
||||||
const double* phi2, const double* r2,
|
const double* phi2, const double* r2,
|
||||||
double* phi, double* r);
|
double* phi, double* r);
|
||||||
void calcTwdSA(const double* AWA, const double* AWS,
|
void calcTwdSA(const double* AWA, const double* AWS, const double* AWD, const double* CTW, const double* STW, const double* HDT,
|
||||||
const double* CTW, const double* STW, const double* HDT,
|
double* TWA, double* TWS, double* TWD);
|
||||||
double* TWD, double* TWS, double* TWA, double* AWD);
|
bool calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal, double* hdtVal);
|
||||||
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
|
bool calcWD(const double* waVal, const double* hdtVal, double* wdVal);
|
||||||
bool calcWinds(const double* awaVal, const double* awsVal,
|
bool calcTrueWinds(const double* awaVal, const double* awsVal, const double* awd,
|
||||||
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
||||||
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal, double* awdVal);
|
double* twdVal, double* twsVal, double* twaVal);
|
||||||
bool addWinds();
|
void setMaxWs(GwApi::BoatValue *wsMaxValue, const double * wsVal);
|
||||||
|
void setMaxWs() { setMaxWs(maxtwsBVal, &tws); };
|
||||||
|
bool handleWinds(bool calcWinds);
|
||||||
};
|
};
|
||||||
@@ -45,9 +45,9 @@ private:
|
|||||||
size_t last; // Points to the last (newest) valid element
|
size_t last; // Points to the last (newest) valid element
|
||||||
size_t count; // Number of valid elements currently in buffer
|
size_t count; // Number of valid elements currently in buffer
|
||||||
bool is_Full; // Indicates that all buffer elements are used and ringing is in use
|
bool is_Full; // Indicates that all buffer elements are used and ringing is in use
|
||||||
T MIN_VAL; // lowest possible value of buffer of type <T>
|
T NUMLIMIT_LOW; // internally lowest possible value of buffer of type <T>
|
||||||
T MAX_VAL; // highest possible value of buffer of type <T> -> indicates invalid value in buffer
|
T NUMLIMIT_HIGH; // internally highest possible value of buffer of type <T>
|
||||||
double dblMIN_VAL, dblMAX_VAL; // MIN_VAL, MAX_VAL in double format
|
double BUFMIN_VAL, BUFMAX_VAL; // lowest/highest possible buffer value considering multiplier -> externally used
|
||||||
mutable SemaphoreHandle_t bufLocker;
|
mutable SemaphoreHandle_t bufLocker;
|
||||||
|
|
||||||
// metadata for buffer
|
// metadata for buffer
|
||||||
@@ -55,8 +55,8 @@ private:
|
|||||||
String dataFmt; // Format of boat data in buffer
|
String dataFmt; // Format of boat data in buffer
|
||||||
int updFreq; // Update frequency in milliseconds
|
int updFreq; // Update frequency in milliseconds
|
||||||
double mltplr; // Multiplier which transforms original <double> value into buffer type format
|
double mltplr; // Multiplier which transforms original <double> value into buffer type format
|
||||||
double smallest; // Value range of buffer: smallest value; needs to be => MIN_VAL
|
double lowest; // low value range for boat data in this buffer; needs to be => BUFMIN_VAL
|
||||||
double largest; // Value range of buffer: biggest value; needs to be < MAX_VAL, since MAX_VAL indicates invalid entries
|
double highest; // high value range for boat data in this buffer; needs to be < BUFMAX_VAL, since BUFMAX_VAL indicates invalid entries
|
||||||
|
|
||||||
void initCommon();
|
void initCommon();
|
||||||
|
|
||||||
@@ -64,10 +64,12 @@ public:
|
|||||||
RingBuffer();
|
RingBuffer();
|
||||||
RingBuffer(size_t size);
|
RingBuffer(size_t size);
|
||||||
void setMetaData(String name, String format, int updateFrequency, double multiplier, double minValue, double maxValue); // Set meta data for buffer
|
void setMetaData(String name, String format, int updateFrequency, double multiplier, double minValue, double maxValue); // Set meta data for buffer
|
||||||
|
void setFormat(String format); // Specify format of buffer
|
||||||
bool getMetaData(String& name, String& format, int& updateFrequency, double& multiplier, double& minValue, double& maxValue); // Get meta data of buffer
|
bool getMetaData(String& name, String& format, int& updateFrequency, double& multiplier, double& minValue, double& maxValue); // Get meta data of buffer
|
||||||
bool getMetaData(String& name, String& format);
|
bool getMetaData(String& name, String& format);
|
||||||
String getName() const; // Get buffer name
|
String getName() const; // Get buffer name
|
||||||
String getFormat() const; // Get buffer data format
|
String getFormat() const; // Get buffer data format
|
||||||
|
int getUpdFreq() const; // Get buffer update frequency
|
||||||
void add(const double& value); // Add a new value to buffer
|
void add(const double& value); // Add a new value to buffer
|
||||||
double get(size_t index) const; // Get value at specific position (0-based index from oldest to newest)
|
double get(size_t index) const; // Get value at specific position (0-based index from oldest to newest)
|
||||||
double getFirst() const; // Get the first (oldest) value in buffer
|
double getFirst() const; // Get the first (oldest) value in buffer
|
||||||
@@ -78,6 +80,8 @@ public:
|
|||||||
double getMax(size_t amount) const; // Get maximum value of the last <amount> values of buffer
|
double getMax(size_t amount) const; // Get maximum value of the last <amount> values of buffer
|
||||||
double getMid() const; // Get mid value between <min> and <max> value in buffer
|
double getMid() const; // Get mid value between <min> and <max> value in buffer
|
||||||
double getMid(size_t amount) const; // Get mid value between <min> and <max> value of the last <amount> values of buffer
|
double getMid(size_t amount) const; // Get mid value between <min> and <max> value of the last <amount> values of buffer
|
||||||
|
double getCircularMid() const; // Get mid value of circle (degree) values of buffer
|
||||||
|
double getCircularMid(size_t amount) const; // Get mid value of circle (degree) values of the last <amount> values of buffer
|
||||||
double getMedian() const; // Get the median value in buffer
|
double getMedian() const; // Get the median value in buffer
|
||||||
double getMedian(size_t amount) const; // Get the median value of the last <amount> values of buffer
|
double getMedian(size_t amount) const; // Get the median value of the last <amount> values of buffer
|
||||||
size_t getCapacity() const; // Get the buffer capacity (maximum size)
|
size_t getCapacity() const; // Get the buffer capacity (maximum size)
|
||||||
|
|||||||
+134
-110
@@ -1,24 +1,7 @@
|
|||||||
#include "OBPRingBuffer.h"
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void RingBuffer<T>::initCommon()
|
|
||||||
{
|
|
||||||
MIN_VAL = std::numeric_limits<T>::lowest();
|
|
||||||
MAX_VAL = std::numeric_limits<T>::max();
|
|
||||||
dblMIN_VAL = static_cast<double>(MIN_VAL);
|
|
||||||
dblMAX_VAL = static_cast<double>(MAX_VAL);
|
|
||||||
dataName = "";
|
|
||||||
dataFmt = "";
|
|
||||||
updFreq = -1;
|
|
||||||
mltplr = 1;
|
|
||||||
smallest = dblMIN_VAL;
|
|
||||||
largest = dblMAX_VAL;
|
|
||||||
bufLocker = xSemaphoreCreateMutex();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
RingBuffer<T>::RingBuffer()
|
RingBuffer<T>::RingBuffer()
|
||||||
: capacity(0)
|
: capacity(0)
|
||||||
@@ -44,7 +27,23 @@ RingBuffer<T>::RingBuffer(size_t size)
|
|||||||
initCommon();
|
initCommon();
|
||||||
|
|
||||||
buffer.reserve(size);
|
buffer.reserve(size);
|
||||||
buffer.resize(size, MAX_VAL); // MAX_VAL indicate invalid values
|
buffer.resize(size, NUMLIMIT_HIGH); // NUMLIMIT_HIGH indicate invalid values
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void RingBuffer<T>::initCommon()
|
||||||
|
{
|
||||||
|
NUMLIMIT_LOW = std::numeric_limits<T>::lowest();
|
||||||
|
NUMLIMIT_HIGH = std::numeric_limits<T>::max();
|
||||||
|
dataName = "";
|
||||||
|
dataFmt = "";
|
||||||
|
updFreq = -1;
|
||||||
|
mltplr = 1;
|
||||||
|
BUFMIN_VAL = static_cast<double>(NUMLIMIT_LOW);
|
||||||
|
BUFMAX_VAL = static_cast<double>(NUMLIMIT_HIGH);
|
||||||
|
lowest = BUFMIN_VAL;
|
||||||
|
highest = BUFMAX_VAL;
|
||||||
|
bufLocker = xSemaphoreCreateMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specify meta data of buffer content
|
// Specify meta data of buffer content
|
||||||
@@ -56,8 +55,18 @@ void RingBuffer<T>::setMetaData(String name, String format, int updateFrequency,
|
|||||||
dataFmt = format;
|
dataFmt = format;
|
||||||
updFreq = updateFrequency;
|
updFreq = updateFrequency;
|
||||||
mltplr = multiplier;
|
mltplr = multiplier;
|
||||||
smallest = std::max(dblMIN_VAL, minValue);
|
BUFMIN_VAL = static_cast<double>(NUMLIMIT_LOW) / mltplr; // lowest possible buffer value; converted to external view
|
||||||
largest = std::min(dblMAX_VAL, maxValue);
|
BUFMAX_VAL = static_cast<double>(NUMLIMIT_HIGH) / mltplr; // highest possible buffer value; converted to external view
|
||||||
|
lowest = std::max(BUFMIN_VAL, minValue); // low value range, set by user
|
||||||
|
highest = std::min(std::nextafter(BUFMAX_VAL, -std::numeric_limits<double>::infinity()), maxValue); // high value range, set by user; maximum is 1 tick lower than BUFMAX_VAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify format of buffer content
|
||||||
|
template <typename T>
|
||||||
|
void RingBuffer<T>::setFormat(String format)
|
||||||
|
{
|
||||||
|
GWSYNCHRONIZED(&bufLocker);
|
||||||
|
dataFmt = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get meta data of buffer content
|
// Get meta data of buffer content
|
||||||
@@ -73,8 +82,8 @@ bool RingBuffer<T>::getMetaData(String& name, String& format, int& updateFrequen
|
|||||||
format = dataFmt;
|
format = dataFmt;
|
||||||
updateFrequency = updFreq;
|
updateFrequency = updFreq;
|
||||||
multiplier = mltplr;
|
multiplier = mltplr;
|
||||||
minValue = smallest;
|
minValue = lowest;
|
||||||
maxValue = largest;
|
maxValue = highest;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,13 +115,20 @@ String RingBuffer<T>::getFormat() const
|
|||||||
return dataFmt;
|
return dataFmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get buffer update frequency
|
||||||
|
template <typename T>
|
||||||
|
int RingBuffer<T>::getUpdFreq() const
|
||||||
|
{
|
||||||
|
return updFreq;
|
||||||
|
}
|
||||||
|
|
||||||
// Add a new value to buffer
|
// Add a new value to buffer
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void RingBuffer<T>::add(const double& value)
|
void RingBuffer<T>::add(const double& value)
|
||||||
{
|
{
|
||||||
GWSYNCHRONIZED(&bufLocker);
|
GWSYNCHRONIZED(&bufLocker);
|
||||||
if (value < smallest || value > largest) {
|
if (value < lowest || value > highest) {
|
||||||
buffer[head] = MAX_VAL; // Store MAX_VAL if value is out of range
|
buffer[head] = NUMLIMIT_HIGH; // Store maximum buffer value if data value is out of range
|
||||||
} else {
|
} else {
|
||||||
buffer[head] = static_cast<T>(std::round(value * mltplr));
|
buffer[head] = static_cast<T>(std::round(value * mltplr));
|
||||||
}
|
}
|
||||||
@@ -136,11 +152,11 @@ double RingBuffer<T>::get(size_t index) const
|
|||||||
{
|
{
|
||||||
GWSYNCHRONIZED(&bufLocker);
|
GWSYNCHRONIZED(&bufLocker);
|
||||||
if (isEmpty() || index < 0 || index >= count) {
|
if (isEmpty() || index < 0 || index >= count) {
|
||||||
return dblMAX_VAL;
|
return BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t realIndex = (first + index) % capacity;
|
size_t realIndex = (first + index) % capacity;
|
||||||
return static_cast<double>(buffer[realIndex] / mltplr);
|
return static_cast<double>(buffer[realIndex] / mltplr); // is BUFMAX_VAL if value is invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operator[] for convenient access (same as get())
|
// Operator[] for convenient access (same as get())
|
||||||
@@ -155,7 +171,7 @@ template <typename T>
|
|||||||
double RingBuffer<T>::getFirst() const
|
double RingBuffer<T>::getFirst() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return dblMAX_VAL;
|
return BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
return get(0);
|
return get(0);
|
||||||
}
|
}
|
||||||
@@ -165,7 +181,7 @@ template <typename T>
|
|||||||
double RingBuffer<T>::getLast() const
|
double RingBuffer<T>::getLast() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return dblMAX_VAL;
|
return BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
return get(count - 1);
|
return get(count - 1);
|
||||||
}
|
}
|
||||||
@@ -174,19 +190,7 @@ double RingBuffer<T>::getLast() const
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
double RingBuffer<T>::getMin() const
|
double RingBuffer<T>::getMin() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
return getMin(getCurrentSize());
|
||||||
return dblMAX_VAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
double minVal = dblMAX_VAL;
|
|
||||||
double value;
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
value = get(i);
|
|
||||||
if (value < minVal && value != dblMAX_VAL) {
|
|
||||||
minVal = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return minVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get minimum value of the last <amount> values of buffer
|
// Get minimum value of the last <amount> values of buffer
|
||||||
@@ -194,16 +198,16 @@ template <typename T>
|
|||||||
double RingBuffer<T>::getMin(size_t amount) const
|
double RingBuffer<T>::getMin(size_t amount) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || amount <= 0) {
|
if (isEmpty() || amount <= 0) {
|
||||||
return dblMAX_VAL;
|
return BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
amount = count;
|
amount = count;
|
||||||
|
|
||||||
double minVal = dblMAX_VAL;
|
double minVal = BUFMAX_VAL;
|
||||||
double value;
|
double value;
|
||||||
for (size_t i = 0; i < amount; i++) {
|
for (size_t i = 0; i < amount; i++) {
|
||||||
value = get(count - 1 - i);
|
value = get(count - 1 - i);
|
||||||
if (value < minVal && value != dblMAX_VAL) {
|
if (value < minVal && value != BUFMAX_VAL) {
|
||||||
minVal = value;
|
minVal = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,22 +218,7 @@ double RingBuffer<T>::getMin(size_t amount) const
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
double RingBuffer<T>::getMax() const
|
double RingBuffer<T>::getMax() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
return getMax(getCurrentSize());
|
||||||
return dblMAX_VAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
double maxVal = dblMIN_VAL;
|
|
||||||
double value;
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
value = get(i);
|
|
||||||
if (value > maxVal && value != dblMAX_VAL) {
|
|
||||||
maxVal = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (maxVal == dblMIN_VAL) { // no change of initial value -> buffer has only invalid values (MAX_VAL)
|
|
||||||
maxVal = dblMAX_VAL;
|
|
||||||
}
|
|
||||||
return maxVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get maximum value of the last <amount> values of buffer
|
// Get maximum value of the last <amount> values of buffer
|
||||||
@@ -237,21 +226,21 @@ template <typename T>
|
|||||||
double RingBuffer<T>::getMax(size_t amount) const
|
double RingBuffer<T>::getMax(size_t amount) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || amount <= 0) {
|
if (isEmpty() || amount <= 0) {
|
||||||
return dblMAX_VAL;
|
return BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
amount = count;
|
amount = count;
|
||||||
|
|
||||||
double maxVal = dblMIN_VAL;
|
double maxVal = BUFMIN_VAL;
|
||||||
double value;
|
double value;
|
||||||
for (size_t i = 0; i < amount; i++) {
|
for (size_t i = 0; i < amount; i++) {
|
||||||
value = get(count - 1 - i);
|
value = get(count - 1 - i);
|
||||||
if (value > maxVal && value != dblMAX_VAL) {
|
if (value > maxVal && value != BUFMAX_VAL) {
|
||||||
maxVal = value;
|
maxVal = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (maxVal == dblMIN_VAL) { // no change of initial value -> buffer has only invalid values (MAX_VAL)
|
if (maxVal == BUFMIN_VAL) { // no change of initial value -> buffer has only invalid values (BUFMAX_VAL)
|
||||||
maxVal = dblMAX_VAL;
|
maxVal = BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
return maxVal;
|
return maxVal;
|
||||||
}
|
}
|
||||||
@@ -260,11 +249,7 @@ double RingBuffer<T>::getMax(size_t amount) const
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
double RingBuffer<T>::getMid() const
|
double RingBuffer<T>::getMid() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
return getMid(getCurrentSize());
|
||||||
return dblMAX_VAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (getMin() + getMax()) / 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get mid value between <min> and <max> value of the last <amount> values of buffer
|
// Get mid value between <min> and <max> value of the last <amount> values of buffer
|
||||||
@@ -272,7 +257,7 @@ template <typename T>
|
|||||||
double RingBuffer<T>::getMid(size_t amount) const
|
double RingBuffer<T>::getMid(size_t amount) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || amount <= 0) {
|
if (isEmpty() || amount <= 0) {
|
||||||
return dblMAX_VAL;
|
return BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
@@ -285,29 +270,7 @@ double RingBuffer<T>::getMid(size_t amount) const
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
double RingBuffer<T>::getMedian() const
|
double RingBuffer<T>::getMedian() const
|
||||||
{
|
{
|
||||||
if (isEmpty()) {
|
return getMedian(getCurrentSize());
|
||||||
return dblMAX_VAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a temporary vector with current valid elements
|
|
||||||
std::vector<T> temp;
|
|
||||||
temp.reserve(count);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
temp.push_back(get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort to find median
|
|
||||||
std::sort(temp.begin(), temp.end());
|
|
||||||
|
|
||||||
if (count % 2 == 1) {
|
|
||||||
// Odd number of elements
|
|
||||||
return static_cast<double>(temp[count / 2]);
|
|
||||||
} else {
|
|
||||||
// Even number of elements - return average of middle two
|
|
||||||
// Note: For integer types, this truncates. For floating point, it's exact.
|
|
||||||
return static_cast<double>((temp[count / 2 - 1] + temp[count / 2]) / 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the median value of the last <amount> values of buffer
|
// Get the median value of the last <amount> values of buffer
|
||||||
@@ -315,13 +278,13 @@ template <typename T>
|
|||||||
double RingBuffer<T>::getMedian(size_t amount) const
|
double RingBuffer<T>::getMedian(size_t amount) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || amount <= 0) {
|
if (isEmpty() || amount <= 0) {
|
||||||
return dblMAX_VAL;
|
return BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
if (amount > count)
|
if (amount > count)
|
||||||
amount = count;
|
amount = count;
|
||||||
|
|
||||||
// Create a temporary vector with current valid elements
|
// Create a temporary vector with current valid elements
|
||||||
std::vector<T> temp;
|
std::vector<double> temp;
|
||||||
temp.reserve(amount);
|
temp.reserve(amount);
|
||||||
|
|
||||||
for (size_t i = 0; i < amount; i++) {
|
for (size_t i = 0; i < amount; i++) {
|
||||||
@@ -331,16 +294,84 @@ double RingBuffer<T>::getMedian(size_t amount) const
|
|||||||
// Sort to find median
|
// Sort to find median
|
||||||
std::sort(temp.begin(), temp.end());
|
std::sort(temp.begin(), temp.end());
|
||||||
|
|
||||||
|
if (temp[0] == BUFMAX_VAL) { // 1st element of sorted vector is already BUFMAX_VAL -> only invalid entries in buffer, so we return invalid value
|
||||||
|
return BUFMAX_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (amount % 2 == 1) {
|
if (amount % 2 == 1) {
|
||||||
// Odd number of elements
|
// Odd number of elements
|
||||||
return static_cast<double>(temp[amount / 2]);
|
return temp[amount / 2];
|
||||||
} else {
|
} else {
|
||||||
// Even number of elements - return average of middle two
|
// Even number of elements - return average of middle two
|
||||||
// Note: For integer types, this truncates. For floating point, it's exact.
|
return (temp[amount / 2 - 1] + temp[amount / 2]) / 2;
|
||||||
return static_cast<double>((temp[amount / 2 - 1] + temp[amount / 2]) / 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get mid value of circle (degree) values of buffer
|
||||||
|
template <typename T>
|
||||||
|
double RingBuffer<T>::getCircularMid() const
|
||||||
|
{
|
||||||
|
return getCircularMid(getCurrentSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get mid value of circle (degree) values of the last <amount> values of buffer
|
||||||
|
template <typename T>
|
||||||
|
double RingBuffer<T>::getCircularMid(size_t amount) const
|
||||||
|
{
|
||||||
|
if (isEmpty() || amount <= 0) {
|
||||||
|
return BUFMAX_VAL;
|
||||||
|
}
|
||||||
|
if (amount > count)
|
||||||
|
amount = count;
|
||||||
|
|
||||||
|
std::vector<double> a;
|
||||||
|
// Create a temporary vector with current valid elements
|
||||||
|
std::vector<double> temp;
|
||||||
|
temp.reserve(amount);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < amount; i++) {
|
||||||
|
temp.push_back(get(count - 1 - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort to find largest gap
|
||||||
|
std::sort(temp.begin(), temp.end());
|
||||||
|
|
||||||
|
if (temp[0] == BUFMAX_VAL) { // 1st element of sorted vector is already BUFMAX_VAL -> only invalid entries in buffer, so we return invalid value
|
||||||
|
return BUFMAX_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the largest gap
|
||||||
|
double largestGap = BUFMIN_VAL;
|
||||||
|
std::size_t gapIndex = 0;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < temp.size(); ++i)
|
||||||
|
{
|
||||||
|
std::size_t next = (i + 1) % temp.size();
|
||||||
|
|
||||||
|
double gap;
|
||||||
|
if (next == 0)
|
||||||
|
gap = (temp[0] + M_TWOPI) - temp[i];
|
||||||
|
else
|
||||||
|
gap = temp[next] - temp[i];
|
||||||
|
|
||||||
|
if (gap > largestGap)
|
||||||
|
{
|
||||||
|
largestGap = gap;
|
||||||
|
gapIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double start = temp[(gapIndex + 1) % temp.size()]; // Start of occupied arc = first angle after largest gap
|
||||||
|
double arcWidth = M_TWOPI - largestGap; // Width of occupied arc
|
||||||
|
arcWidth = start + arcWidth / 2.0;
|
||||||
|
arcWidth = fmod(arcWidth, M_TWOPI);
|
||||||
|
if (arcWidth < 0.0) {
|
||||||
|
arcWidth += M_TWOPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arcWidth; // Midpoint of occupied arc
|
||||||
|
}
|
||||||
|
|
||||||
// Get the buffer capacity (maximum size)
|
// Get the buffer capacity (maximum size)
|
||||||
template <typename T>
|
template <typename T>
|
||||||
size_t RingBuffer<T>::getCapacity() const
|
size_t RingBuffer<T>::getCapacity() const
|
||||||
@@ -387,14 +418,14 @@ bool RingBuffer<T>::isFull() const
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
double RingBuffer<T>::getMinVal() const
|
double RingBuffer<T>::getMinVal() const
|
||||||
{
|
{
|
||||||
return dblMIN_VAL;
|
return BUFMIN_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get highest possible value for buffer; used for unset/invalid buffer data
|
// Get highest possible value for buffer; used for unset/invalid buffer data
|
||||||
template <typename T>
|
template <typename T>
|
||||||
double RingBuffer<T>::getMaxVal() const
|
double RingBuffer<T>::getMaxVal() const
|
||||||
{
|
{
|
||||||
return dblMAX_VAL;
|
return BUFMAX_VAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear buffer
|
// Clear buffer
|
||||||
@@ -423,21 +454,14 @@ void RingBuffer<T>::resize(size_t newSize)
|
|||||||
|
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
buffer.reserve(newSize);
|
buffer.reserve(newSize);
|
||||||
buffer.resize(newSize, MAX_VAL);
|
buffer.resize(newSize, NUMLIMIT_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all current values in native buffer format as a vector
|
// Get all current values in native buffer format as a vector
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::vector<double> RingBuffer<T>::getAllValues() const
|
std::vector<double> RingBuffer<T>::getAllValues() const
|
||||||
{
|
{
|
||||||
std::vector<double> result;
|
return getAllValues(getCurrentSize());
|
||||||
result.reserve(count);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
result.push_back(get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get last <amount> values in native buffer format as a vector
|
// Get last <amount> values in native buffer format as a vector
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ void sensorTask(void *param){
|
|||||||
const int avgsize = 300;
|
const int avgsize = 300;
|
||||||
constexpr int arrayBatV{avgsize};
|
constexpr int arrayBatV{avgsize};
|
||||||
constexpr int arrayBatC{avgsize};
|
constexpr int arrayBatC{avgsize};
|
||||||
movingAvg batV(arrayBatV);
|
movingAvg<int> batV(arrayBatV);
|
||||||
movingAvg batC(arrayBatC);
|
movingAvg<int> batC(arrayBatC);
|
||||||
batV.begin();
|
batV.begin();
|
||||||
batC.begin();
|
batC.begin();
|
||||||
|
|
||||||
@@ -371,7 +371,7 @@ void sensorTask(void *param){
|
|||||||
GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP);
|
GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP);
|
||||||
GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop};
|
GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop};
|
||||||
|
|
||||||
// Internal RTC with NTP init
|
// Internal iRTC with NTP init
|
||||||
ESP32Time rtc(0);
|
ESP32Time rtc(0);
|
||||||
if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") {
|
if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") {
|
||||||
GwApi::Status status;
|
GwApi::Status status;
|
||||||
@@ -432,17 +432,17 @@ void sensorTask(void *param){
|
|||||||
|
|
||||||
iRTC RTC GPS N2K
|
iRTC RTC GPS N2K
|
||||||
0 0 0 (1)
|
0 0 0 (1)
|
||||||
0 0 (1) (X)
|
0 0 (1) X
|
||||||
0 (1) 0 (X)
|
0 (1) 0 X
|
||||||
0 1 <-(1) (X)
|
0 1 <-(1) X
|
||||||
(1) 0 0 (X)
|
(1) 0 0 X
|
||||||
1 0 (1) (X)
|
1 0 (1) X
|
||||||
1 ->(1) 0 (X)
|
1 ->(1) 0 X
|
||||||
1 1 <-(1) (X)
|
1 1 <-(1) X
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// If RTC DS1388 ready, then copy iRTC and GPS data to RTC all 1min
|
// If RTC DS1388 ready, then copy iRTC and GPS data to RTC all 1 min
|
||||||
if(millis() > starttime11 + 1*60*1000){
|
if(millis() > starttime11 + 1*60*1000){
|
||||||
starttime11 = millis();
|
starttime11 = millis();
|
||||||
// Set RTC chip via iRTC (NTP)
|
// Set RTC chip via iRTC (NTP)
|
||||||
@@ -524,7 +524,7 @@ void sensorTask(void *param){
|
|||||||
// N2K sysTime is double in n2klib
|
// N2K sysTime is double in n2klib
|
||||||
double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second();
|
double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second();
|
||||||
if(!isnan(daysAt1970) && !isnan(sysTime)){
|
if(!isnan(daysAt1970) && !isnan(sysTime)){
|
||||||
//api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",sensors.rtcTime.tm_year+1900,sensors.rtcTime.tm_mon, sensors.rtcTime.tm_mday, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
|
//api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",sensors.rtcTime.tm_year+1900,sensors.rtcTime.tm_mon+1, sensors.rtcTime.tm_mday, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
|
||||||
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
||||||
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
|
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
|
||||||
api->sendN2kMessage(N2kMsg);
|
api->sendN2kMessage(N2kMsg);
|
||||||
@@ -533,25 +533,26 @@ void sensorTask(void *param){
|
|||||||
}
|
}
|
||||||
// Send date and time from software RTC (iRTC)
|
// Send date and time from software RTC (iRTC)
|
||||||
if (iRTC_ready == true && RTC_ready == false && GPS_ready == false) {
|
if (iRTC_ready == true && RTC_ready == false && GPS_ready == false) {
|
||||||
// Use internal RTC feature
|
sensors.rtcTime = rtc.getTimeStruct();
|
||||||
sensors.rtcTime = rtc.getTimeStruct(); // Save software RTC values in SensorData
|
|
||||||
// TODO implement daysAt1970 and sysTime as methods of DateTime
|
|
||||||
const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
|
const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
|
||||||
uint16_t switchYear = ((sensors.rtcTime.tm_year-1)-1968)/4 - ((sensors.rtcTime.tm_year-1)-1900)/100 + ((sensors.rtcTime.tm_year-1)-1600)/400;
|
int year = sensors.rtcTime.tm_year + 1900;
|
||||||
long daysAt1970 = (sensors.rtcTime.tm_year-1970)*365 + switchYear + daysOfYear[sensors.rtcTime.tm_mon-1] + sensors.rtcTime.tm_mday-1;
|
int month = sensors.rtcTime.tm_mon;
|
||||||
// If switch year then add one day
|
int day = sensors.rtcTime.tm_mday;
|
||||||
if ((sensors.rtcTime.tm_mon > 2) && (sensors.rtcTime.tm_year % 4 == 0 && (sensors.rtcTime.tm_year % 100 != 0 || sensors.rtcTime.tm_year % 400 == 0))) {
|
uint16_t switchYear = ((year - 1) - 1968) / 4 - ((year - 1) - 1900) / 100 + ((year - 1) - 1600) / 400;
|
||||||
|
long daysAt1970 = (year - 1970) * 365L + switchYear + daysOfYear[month] + day - 1;
|
||||||
|
|
||||||
|
// Leap day add if date is after Feb (i.e. month >= March)
|
||||||
|
if (month >= 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) {
|
||||||
daysAt1970 += 1;
|
daysAt1970 += 1;
|
||||||
}
|
}
|
||||||
// N2K sysTime is double in n2klib
|
double sysTime = sensors.rtcTime.tm_hour * 3600.0 + sensors.rtcTime.tm_min * 60.0 + sensors.rtcTime.tm_sec;
|
||||||
double sysTime = (sensors.rtcTime.tm_hour * 3600) + (sensors.rtcTime.tm_min * 60) + sensors.rtcTime.tm_sec;
|
//api->getLogger()->logDebug(GwLog::LOG, "iRTC time: %04d/%02d/%02d %02d:%02d:%02d", year, month + 1, day, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
|
||||||
if(!isnan(daysAt1970) && !isnan(sysTime)){
|
|
||||||
//api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",sensors.rtcTime.tm_year+1900,sensors.rtcTime.tm_mon, sensors.rtcTime.tm_mday, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
|
|
||||||
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
||||||
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
|
SetN2kPGN126992(N2kMsg, 0, daysAt1970, sysTime, N2ktimes_LocalCrystalClock);
|
||||||
api->sendN2kMessage(N2kMsg);
|
api->sendN2kMessage(N2kMsg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send 1Wire data for all temperature sensors to N2K all 2s
|
// Send 1Wire data for all temperature sensors to N2K all 2s
|
||||||
|
|||||||
+253
-176
@@ -3,14 +3,6 @@
|
|||||||
#include "OBPDataOperations.h"
|
#include "OBPDataOperations.h"
|
||||||
#include "OBPRingBuffer.h"
|
#include "OBPRingBuffer.h"
|
||||||
|
|
||||||
std::map<String, ChartProps> Chart::dfltChrtDta = {
|
|
||||||
{ "formatWind", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees
|
|
||||||
{ "formatCourse", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees
|
|
||||||
{ "formatKnots", { 7.71, 2.56 } }, // default speed range in m/s
|
|
||||||
{ "formatDepth", { 15.0, 5.0 } }, // default depth range in m
|
|
||||||
{ "kelvinToC", { 30.0, 5.0 } } // default temp range in °C/K
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- Class Chart ---------------
|
// --- Class Chart ---------------
|
||||||
|
|
||||||
// Chart - object holding the actual chart, incl. data buffer and format definition
|
// Chart - object holding the actual chart, incl. data buffer and format definition
|
||||||
@@ -18,9 +10,8 @@ std::map<String, ChartProps> Chart::dfltChrtDta = {
|
|||||||
// <dfltRng> default range of chart, e.g. 30 = [0..30]
|
// <dfltRng> default range of chart, e.g. 30 = [0..30]
|
||||||
// <common> common program data; required for logger and color data
|
// <common> common program data; required for logger and color data
|
||||||
// <useSimuData> flag to indicate if simulation data is active
|
// <useSimuData> flag to indicate if simulation data is active
|
||||||
Chart::Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common, bool useSimuData)
|
Chart::Chart(RingBuffer<uint16_t>& dataBuf, CommonData& common, bool useSimuData)
|
||||||
: dataBuf(dataBuf)
|
: dataBuf(dataBuf)
|
||||||
, dfltRng(dfltRng)
|
|
||||||
, commonData(&common)
|
, commonData(&common)
|
||||||
, useSimuData(useSimuData)
|
, useSimuData(useSimuData)
|
||||||
{
|
{
|
||||||
@@ -28,30 +19,55 @@ Chart::Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common,
|
|||||||
fgColor = commonData->fgcolor;
|
fgColor = commonData->fgcolor;
|
||||||
bgColor = commonData->bgcolor;
|
bgColor = commonData->bgcolor;
|
||||||
|
|
||||||
|
// display dimensions (avoid calling width()/height() on incomplete LGFX type)
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
dWidth = 480;
|
||||||
|
dHeight = 320;
|
||||||
|
#else
|
||||||
dWidth = getdisplay().width();
|
dWidth = getdisplay().width();
|
||||||
dHeight = getdisplay().height();
|
dHeight = getdisplay().height();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
|
Chart::~Chart()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chart::init()
|
||||||
|
{
|
||||||
|
chrtMin = 0.0;
|
||||||
|
chrtMax = 0.0;
|
||||||
|
chrtMid = 0.0;
|
||||||
|
chrtRng = 0.0;
|
||||||
|
|
||||||
dataBuf.getMetaData(dbName, dbFormat);
|
dataBuf.getMetaData(dbName, dbFormat);
|
||||||
dbMIN_VAL = dataBuf.getMinVal();
|
dbMIN_VAL = dataBuf.getMinVal();
|
||||||
dbMAX_VAL = dataBuf.getMaxVal();
|
dbMAX_VAL = dataBuf.getMaxVal();
|
||||||
bufSize = dataBuf.getCapacity();
|
bufSize = dataBuf.getCapacity();
|
||||||
|
// LOG_DEBUG(GwLog::DEBUG, "Chart Init: dbMIN_VAL: %.2f, dbMAX_VAL: %.2fd, bufSize: %d", dbMIN_VAL, dbMAX_VAL, bufSize);
|
||||||
|
|
||||||
// Initialize chart data format; shorter version of standard format indicator
|
// Initialize chart data format; shorter version of standard format indicator
|
||||||
if (dbFormat == "formatCourse" || dbFormat == "formatWind" || dbFormat == "formatRot") {
|
if (dbFormat == "formatCourse" || dbFormat == "formatWind") {
|
||||||
chrtDataFmt = WIND; // Chart is showing data of course / wind <degree> format
|
chrtDataFmt = WIND; // Chart is showing data of course / wind <degree> format
|
||||||
} else if (dbFormat == "formatRot") {
|
} else if (dbFormat == "formatRot") {
|
||||||
chrtDataFmt = ROTATION; // Chart is showing data of rotational <degree> format
|
chrtDataFmt = ROTATION; // Chart is showing data of rotational <degree> format
|
||||||
} else if (dbFormat == "formatKnots") {
|
} else if (dbFormat == "formatKnots") {
|
||||||
chrtDataFmt = SPEED; // Chart is showing data of speed or windspeed format
|
chrtDataFmt = SPEED; // Chart is showing data of speed or windspeed format
|
||||||
} else if (dbFormat == "formatDepth") {
|
} else if (dbFormat == "formatDepth") {
|
||||||
chrtDataFmt = DEPTH; // Chart ist showing data of <depth> format
|
chrtDataFmt = DEPTH;
|
||||||
} else if (dbFormat == "kelvinToC") {
|
} else if (dbFormat == "kelvinToC") {
|
||||||
chrtDataFmt = TEMPERATURE; // Chart ist showing data of <temp> format
|
chrtDataFmt = TEMPERATURE;
|
||||||
|
} else if (dbFormat.startsWith("formatXdr:P")) {
|
||||||
|
chrtDataFmt = PRESSURE;
|
||||||
|
} else if (dbFormat.startsWith("formatXdr:H")) {
|
||||||
|
chrtDataFmt = HUMIDITY;
|
||||||
} else {
|
} else {
|
||||||
chrtDataFmt = OTHER; // Chart is showing any other data format
|
chrtDataFmt = OTHER; // Chart is showing any other data format
|
||||||
}
|
}
|
||||||
|
|
||||||
// "0" value is the same for any data format but for user defined temperature format
|
// "0" value is the same for any data format but for user defined temperature and pressure format
|
||||||
zeroValue = 0.0;
|
zeroValue = 0.0;
|
||||||
if (chrtDataFmt == TEMPERATURE) {
|
if (chrtDataFmt == TEMPERATURE) {
|
||||||
tempFormat = commonData->config->getString(commonData->config->tempFormat); // [K|°C|°F]
|
tempFormat = commonData->config->getString(commonData->config->tempFormat); // [K|°C|°F]
|
||||||
@@ -62,6 +78,8 @@ Chart::Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common,
|
|||||||
} else if (tempFormat == "F") {
|
} else if (tempFormat == "F") {
|
||||||
zeroValue = 255.37;
|
zeroValue = 255.37;
|
||||||
}
|
}
|
||||||
|
} else if (chrtDataFmt == PRESSURE) {
|
||||||
|
zeroValue = 98000.0; // typical low pressure area value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read default range and range step for this chart type
|
// Read default range and range step for this chart type
|
||||||
@@ -69,7 +87,7 @@ Chart::Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common,
|
|||||||
dfltRng = dfltChrtDta[dbFormat].range;
|
dfltRng = dfltChrtDta[dbFormat].range;
|
||||||
rngStep = dfltChrtDta[dbFormat].step;
|
rngStep = dfltChrtDta[dbFormat].step;
|
||||||
} else {
|
} else {
|
||||||
dfltRng = 15.0;
|
dfltRng = 10.0;
|
||||||
rngStep = 5.0;
|
rngStep = 5.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,14 +96,19 @@ Chart::Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common,
|
|||||||
chrtMax = chrtMin + dfltRng;
|
chrtMax = chrtMin + dfltRng;
|
||||||
chrtMid = (chrtMin + chrtMax) / 2;
|
chrtMid = (chrtMin + chrtMax) / 2;
|
||||||
chrtRng = dfltRng;
|
chrtRng = dfltRng;
|
||||||
recalcRngMid = true; // initialize <chrtMid> and chart borders on first screen call
|
recalcRngMid = true; // initialize <chrtMid> and chart borders on first chart display call
|
||||||
|
allLeft = true;
|
||||||
|
allRight = true;
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::DEBUG, "Chart Init: dWidth: %d, dHeight: %d, timAxis: %d, valAxis: %d, cRoot {x,y}: %d, %d, dbname: %s, rngStep: %.4f, chrtDataFmt: %d",
|
if (dbFormat.isEmpty()) { // data buffer may not exist yet, because boat data object is not available yet
|
||||||
dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, dbName, rngStep, chrtDataFmt);
|
initValid = false; // chart object will get invalid data during initialization
|
||||||
};
|
} else {
|
||||||
|
initValid = true;
|
||||||
|
}
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "Chart Init: dWidth: %d, dHeight: %d, timAxis: %d, valAxis: %d, cRoot {x,y}: %d, %d, dbname: %s, rngStep: %.4f, chrtDataFmt: %d, initValid: %d",
|
||||||
|
dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, dbName, rngStep, chrtDataFmt, initValid);
|
||||||
|
|
||||||
Chart::~Chart()
|
return isValid();
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform all actions to draw chart
|
// Perform all actions to draw chart
|
||||||
@@ -95,7 +118,7 @@ Chart::~Chart()
|
|||||||
// <prntName>; print data name on horizontal half chart [true|false]
|
// <prntName>; print data name on horizontal half chart [true|false]
|
||||||
// <showCurrValue>: print current boat data value [true|false]
|
// <showCurrValue>: print current boat data value [true|false]
|
||||||
// <currValue>: current boat data value; used only for test on valid data
|
// <currValue>: current boat data value; used only for test on valid data
|
||||||
void Chart::showChrt(char chrtDir, int8_t chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue)
|
void Chart::showChrt(const ChrtDir chrtDir, ChrtSize chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue)
|
||||||
{
|
{
|
||||||
if (!setChartDimensions(chrtDir, chrtSz)) {
|
if (!setChartDimensions(chrtDir, chrtSz)) {
|
||||||
return; // wrong chart dimension parameters
|
return; // wrong chart dimension parameters
|
||||||
@@ -110,64 +133,74 @@ void Chart::showChrt(char chrtDir, int8_t chrtSz, const int8_t chrtIntv, bool pr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showCurrValue) { // show latest value from history buffer; this should be the most current one
|
if (showCurrValue) {
|
||||||
currValue.value = dataBuf.getLast();
|
currValue.value = dataBuf.getLast(); // show latest value from history buffer; this should be the most current one
|
||||||
currValue.valid = currValue.value != dbMAX_VAL;
|
currValue.valid = currValue.value != dbMAX_VAL;
|
||||||
prntCurrValue(chrtDir, currValue);
|
prntCurrValue(chrtDir, currValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// define dimensions and start points for chart
|
// define dimensions and start points for chart
|
||||||
bool Chart::setChartDimensions(const char direction, const int8_t size)
|
bool Chart::setChartDimensions(const ChrtDir chrtDir, const ChrtSize chrtSz)
|
||||||
{
|
{
|
||||||
if ((direction != HORIZONTAL && direction != VERTICAL) || (size < 0 || size > 2)) {
|
if ((chrtDir != HORIZONTAL && chrtDir != VERTICAL) || (chrtSz < 0 || chrtSz > 3)) {
|
||||||
LOG_DEBUG(GwLog::ERROR, "obp60:setChartDimensions %s: wrong parameters", dataBuf.getName());
|
LOG_DEBUG(GwLog::ERROR, "obp60:setChartDimensions %s: wrong parameters", dataBuf.getName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (direction == HORIZONTAL) {
|
if (chrtDir == HORIZONTAL) {
|
||||||
// horizontal chart timeline direction
|
// horizontal chart timeline direction
|
||||||
timAxis = dWidth - 1;
|
timAxis = dWidth - 1;
|
||||||
switch (size) {
|
switch (chrtSz) {
|
||||||
case 0:
|
case ChrtSize::FULL_SIZE:
|
||||||
valAxis = dHeight - top - bottom;
|
valAxis = dHeight - top - bottom;
|
||||||
cRoot = { 0, top - 1 };
|
cRoot = { 0, top - 1 };
|
||||||
break;
|
break;
|
||||||
case 1:
|
case HALF_SIZE_LEFT_TOP:
|
||||||
valAxis = (dHeight - top - bottom) / 2 - hGap;
|
valAxis = (dHeight - top - bottom) / 2 - hGap;
|
||||||
cRoot = { 0, top - 1 };
|
cRoot = { 0, top - 1 };
|
||||||
break;
|
break;
|
||||||
case 2:
|
case HALF_SIZE_RIGHT_BOTTOM:
|
||||||
valAxis = (dHeight - top - bottom) / 2 - hGap;
|
valAxis = (dHeight - top - bottom) / 2 - hGap;
|
||||||
cRoot = { 0, top + (valAxis + hGap) + hGap - 1 };
|
cRoot = { 0, top + (valAxis + hGap) + hGap - 1 };
|
||||||
break;
|
break;
|
||||||
|
case TWO_THIRD_TOP:
|
||||||
|
valAxis = (dHeight - top - bottom) * 0.667 - hGap;
|
||||||
|
cRoot = { 0, top - 1 };
|
||||||
|
break;
|
||||||
|
default: // same as case 0; should never happen
|
||||||
|
valAxis = dHeight - top - bottom;
|
||||||
|
cRoot = { 0, top - 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (direction == VERTICAL) {
|
} else {
|
||||||
// vertical chart timeline direction
|
// vertical chart timeline direction
|
||||||
timAxis = dHeight - top - bottom;
|
timAxis = dHeight - top - bottom;
|
||||||
switch (size) {
|
switch (chrtSz) {
|
||||||
case 0:
|
case FULL_SIZE:
|
||||||
valAxis = dWidth - 1;
|
valAxis = dWidth - 1;
|
||||||
cRoot = { 0, top - 1 };
|
cRoot = { 0, top - 1 };
|
||||||
break;
|
break;
|
||||||
case 1:
|
case HALF_SIZE_LEFT_TOP:
|
||||||
valAxis = dWidth / 2 - vGap;
|
valAxis = dWidth / 2 - vGap;
|
||||||
cRoot = { 0, top - 1 };
|
cRoot = { 0, top - 1 };
|
||||||
break;
|
break;
|
||||||
case 2:
|
case HALF_SIZE_RIGHT_BOTTOM:
|
||||||
valAxis = dWidth / 2 - vGap;
|
valAxis = dWidth / 2 - vGap;
|
||||||
cRoot = { dWidth / 2 + vGap - 1, top - 1 };
|
cRoot = { dWidth / 2 + vGap, top - 1 };
|
||||||
break;
|
break;
|
||||||
|
default: // same as case 0; should never happen
|
||||||
|
valAxis = dWidth - 1;
|
||||||
|
cRoot = { 0, top - 1 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//LOG_DEBUG(GwLog::DEBUG, "obp60:setChartDimensions %s: direction: %c, size: %d, dWidth: %d, dHeight: %d, timAxis: %d, valAxis: %d, cRoot{%d, %d}, top: %d, bottom: %d, hGap: %d, vGap: %d",
|
// LOG_DEBUG(GwLog::DEBUG, "obp60:setChartDimensions %s: chrtDir: %c, size: %d, dWidth: %d, dHeight: %d, timAxis: %d, valAxis: %d, cRoot{%d, %d}, top: %d, bottom: %d, hGap: %d, vGap: %d",
|
||||||
// dataBuf.getName(), direction, size, dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, top, bottom, hGap, vGap);
|
// dataBuf.getName(), chrtDir, size, dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, top, bottom, hGap, vGap);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw chart
|
// draw chart
|
||||||
void Chart::drawChrt(const char chrtDir, const int8_t chrtIntv, GwApi::BoatValue& currValue)
|
void Chart::drawChrt(const ChrtDir chrtDir, const int8_t chrtIntv, GwApi::BoatValue& currValue)
|
||||||
{
|
{
|
||||||
double chrtScale; // Scale for data values in pixels per value
|
double chrtScale; // Scale for data values in pixels per value
|
||||||
|
|
||||||
@@ -176,7 +209,7 @@ void Chart::drawChrt(const char chrtDir, const int8_t chrtIntv, GwApi::BoatValue
|
|||||||
// LOG_DEBUG(GwLog::DEBUG, "Chart:drawChart: min: %.1f, mid: %.1f, max: %.1f, rng: %.1f", chrtMin, chrtMid, chrtMax, chrtRng);
|
// LOG_DEBUG(GwLog::DEBUG, "Chart:drawChart: min: %.1f, mid: %.1f, max: %.1f, rng: %.1f", chrtMin, chrtMid, chrtMax, chrtRng);
|
||||||
calcChrtBorders(chrtMin, chrtMid, chrtMax, chrtRng);
|
calcChrtBorders(chrtMin, chrtMid, chrtMax, chrtRng);
|
||||||
chrtScale = double(valAxis) / chrtRng; // Chart scale: pixels per value step
|
chrtScale = double(valAxis) / chrtRng; // Chart scale: pixels per value step
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "Chart:drawChart: min: %.1f, mid: %.1f, max: %.1f, rng: %.1f", chrtMin, chrtMid, chrtMax, chrtRng);
|
// LOG_DEBUG(GwLog::DEBUG, "Chart:drawChart: min: %.1f, mid: %.1f, max: %.1f, rng: %.1f, data valid: %d", chrtMin, chrtMid, chrtMax, chrtRng, currValue.valid);
|
||||||
|
|
||||||
// Do we have valid buffer data?
|
// Do we have valid buffer data?
|
||||||
if (dataBuf.getMax() == dbMAX_VAL) { // only <MAX_VAL> values in buffer -> no valid wind data available
|
if (dataBuf.getMax() == dbMAX_VAL) { // only <MAX_VAL> values in buffer -> no valid wind data available
|
||||||
@@ -191,7 +224,7 @@ void Chart::drawChrt(const char chrtDir, const int8_t chrtIntv, GwApi::BoatValue
|
|||||||
numNoData++;
|
numNoData++;
|
||||||
bufDataValid = true;
|
bufDataValid = true;
|
||||||
|
|
||||||
if (numNoData > THRESHOLD_NO_DATA) { // If more than 4 invalid values in a row, flag for invalid data
|
if (numNoData > THRESHOLD_NO_DATA * (dataBuf.getUpdFreq() / 1000)) { // If more than <THRESHOLD> invalid values in a row, flag for invalid data
|
||||||
bufDataValid = false;
|
bufDataValid = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -206,17 +239,15 @@ void Chart::getBufferStartNSize(const int8_t chrtIntv)
|
|||||||
count = dataBuf.getCurrentSize();
|
count = dataBuf.getCurrentSize();
|
||||||
currIdx = dataBuf.getLastIdx();
|
currIdx = dataBuf.getLastIdx();
|
||||||
numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display
|
numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display
|
||||||
|
lastAddedIdx = currIdx;
|
||||||
|
|
||||||
if (chrtIntv != oldChrtIntv || count == 1) {
|
if (chrtIntv != oldChrtIntv || count == 1) { // new data interval selected by user
|
||||||
// new data interval selected by user; this is only x * 230 values instead of 240 seconds (4 minutes) per interval step
|
|
||||||
numBufVals = min(count, (timAxis - MIN_FREE_VALUES) * chrtIntv); // keep free or release MIN_FREE_VALUES on chart for plotting of new values
|
numBufVals = min(count, (timAxis - MIN_FREE_VALUES) * chrtIntv); // keep free or release MIN_FREE_VALUES on chart for plotting of new values
|
||||||
bufStart = max(0, count - numBufVals);
|
bufStart = max(0, count - numBufVals);
|
||||||
lastAddedIdx = currIdx;
|
|
||||||
oldChrtIntv = chrtIntv;
|
oldChrtIntv = chrtIntv;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
numBufVals = numBufVals + numAddedBufVals;
|
numBufVals = numBufVals + numAddedBufVals;
|
||||||
lastAddedIdx = currIdx;
|
|
||||||
if (count == bufSize) {
|
if (count == bufSize) {
|
||||||
bufStart = max(0, bufStart - numAddedBufVals);
|
bufStart = max(0, bufStart - numAddedBufVals);
|
||||||
}
|
}
|
||||||
@@ -228,6 +259,7 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub
|
|||||||
{
|
{
|
||||||
if (chrtDataFmt == WIND || chrtDataFmt == ROTATION) {
|
if (chrtDataFmt == WIND || chrtDataFmt == ROTATION) {
|
||||||
|
|
||||||
|
// calculate rngMid
|
||||||
if (chrtDataFmt == ROTATION) {
|
if (chrtDataFmt == ROTATION) {
|
||||||
// if chart data is of type 'rotation', we want to have <rndMid> always to be '0'
|
// if chart data is of type 'rotation', we want to have <rndMid> always to be '0'
|
||||||
rngMid = 0;
|
rngMid = 0;
|
||||||
@@ -242,22 +274,12 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub
|
|||||||
if (recalcRngMid) {
|
if (recalcRngMid) {
|
||||||
// Set rngMid
|
// Set rngMid
|
||||||
|
|
||||||
rngMid = dataBuf.getMid(numBufVals);
|
rngMid = dataBuf.getCircularMid(numBufVals);
|
||||||
|
|
||||||
if (rngMid == dbMAX_VAL) {
|
if (rngMid == dbMAX_VAL) {
|
||||||
rngMid = 0;
|
rngMid = 0;
|
||||||
} else {
|
} else {
|
||||||
rngMid = std::round(rngMid / rngStep) * rngStep; // Set new center value; round to next <rngStep> value
|
rngMid = std::round(rngMid / rngStep) * rngStep; // round new center value to next <rngStep> value
|
||||||
|
|
||||||
// Check if range between 'min' and 'max' is > 180° or crosses '0'
|
|
||||||
rngMin = dataBuf.getMin(numBufVals);
|
|
||||||
rngMax = dataBuf.getMax(numBufVals);
|
|
||||||
rng = (rngMax >= rngMin ? rngMax - rngMin : M_TWOPI - rngMin + rngMax);
|
|
||||||
rng = std::max(rng, dfltRng); // keep at least default chart range
|
|
||||||
|
|
||||||
if (rng > M_PI) { // If wind range > 180°, adjust wndCenter to smaller wind range end
|
|
||||||
rngMid = WindUtils::to2PI(rngMid + M_PI);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
recalcRngMid = false; // Reset flag for <rngMid> determination
|
recalcRngMid = false; // Reset flag for <rngMid> determination
|
||||||
|
|
||||||
@@ -268,16 +290,11 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub
|
|||||||
|
|
||||||
// check and adjust range between left, mid, and right chart limit
|
// check and adjust range between left, mid, and right chart limit
|
||||||
double halfRng = rng / 2.0; // we calculate with range between <rngMid> and edges
|
double halfRng = rng / 2.0; // we calculate with range between <rngMid> and edges
|
||||||
double tmpRng = getAngleRng(rngMid, numBufVals);
|
double tmpRng = getCircularRng(rngMid, numBufVals);
|
||||||
tmpRng = (tmpRng == dbMAX_VAL ? 0 : std::ceil(tmpRng / rngStep) * rngStep);
|
tmpRng = (tmpRng == dbMAX_VAL ? 0 : std::ceil(tmpRng / rngStep) * rngStep);
|
||||||
|
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "calcChrtBorders: tmpRng: %.1f°, halfRng: %.1f°", tmpRng * RAD_TO_DEG, halfRng * RAD_TO_DEG);
|
|
||||||
|
|
||||||
if (tmpRng > halfRng) { // expand chart range to new value
|
if (tmpRng > halfRng) { // expand chart range to new value
|
||||||
halfRng = tmpRng;
|
halfRng = tmpRng;
|
||||||
}
|
} else if (tmpRng + rngStep < halfRng) { // Contract chart range for higher resolution if possible
|
||||||
|
|
||||||
else if (tmpRng + rngStep < halfRng) { // Contract chart range for higher resolution if possible
|
|
||||||
halfRng = std::max(dfltRng / 2.0, tmpRng);
|
halfRng = std::max(dfltRng / 2.0, tmpRng);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +303,6 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub
|
|||||||
rngMax = WindUtils::to2PI(rngMax);
|
rngMax = WindUtils::to2PI(rngMax);
|
||||||
|
|
||||||
rng = halfRng * 2.0;
|
rng = halfRng * 2.0;
|
||||||
|
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "calcChrtBorders: rngMin: %.1f°, rngMid: %.1f°, rngMax: %.1f°, tmpRng: %.1f°, rng: %.1f°, rngStep: %.1f°", rngMin * RAD_TO_DEG, rngMid * RAD_TO_DEG, rngMax * RAD_TO_DEG,
|
// LOG_DEBUG(GwLog::DEBUG, "calcChrtBorders: rngMin: %.1f°, rngMid: %.1f°, rngMax: %.1f°, tmpRng: %.1f°, rng: %.1f°, rngStep: %.1f°", rngMin * RAD_TO_DEG, rngMid * RAD_TO_DEG, rngMax * RAD_TO_DEG,
|
||||||
// tmpRng * RAD_TO_DEG, rng * RAD_TO_DEG, rngStep * RAD_TO_DEG);
|
// tmpRng * RAD_TO_DEG, rng * RAD_TO_DEG, rngStep * RAD_TO_DEG);
|
||||||
|
|
||||||
@@ -295,8 +311,15 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub
|
|||||||
double currMinVal = dataBuf.getMin(numBufVals);
|
double currMinVal = dataBuf.getMin(numBufVals);
|
||||||
double currMaxVal = dataBuf.getMax(numBufVals);
|
double currMaxVal = dataBuf.getMax(numBufVals);
|
||||||
|
|
||||||
if (currMinVal == dbMAX_VAL || currMaxVal == dbMAX_VAL) {
|
if (currMinVal == dbMAX_VAL || currMaxVal == dbMAX_VAL) { // no valid data
|
||||||
return; // no valid data
|
auto chkIsNaN = [](double& val) { if (std::isnan(val)) val = 0.0; }; // define inline function
|
||||||
|
// range values can be undefined if boat data type has not been created at time of chart printing (e.g. XDR data types)
|
||||||
|
// we set them to "0.0" then
|
||||||
|
chkIsNaN(rngMin);
|
||||||
|
chkIsNaN(rngMid);
|
||||||
|
chkIsNaN(rngMax);
|
||||||
|
chkIsNaN(rng);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if current chart border have to be adjusted
|
// check if current chart border have to be adjusted
|
||||||
@@ -319,14 +342,13 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub
|
|||||||
|
|
||||||
rngMid = (rngMin + rngMax) / 2.0;
|
rngMid = (rngMin + rngMax) / 2.0;
|
||||||
rng = rngMax - rngMin;
|
rng = rngMax - rngMin;
|
||||||
|
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "calcChrtRange-end: currMinVal: %.1f, currMaxVal: %.1f, rngMin: %.1f, rngMid: %.1f, rngMax: %.1f, rng: %.1f, rngStep: %.1f, zeroValue: %.1f, dbMIN_VAL: %.1f",
|
// LOG_DEBUG(GwLog::DEBUG, "calcChrtRange-end: currMinVal: %.1f, currMaxVal: %.1f, rngMin: %.1f, rngMid: %.1f, rngMax: %.1f, rng: %.1f, rngStep: %.1f, zeroValue: %.1f, dbMIN_VAL: %.1f",
|
||||||
// currMinVal, currMaxVal, rngMin, rngMid, rngMax, rng, rngStep, zeroValue, dbMIN_VAL);
|
// currMinVal, currMaxVal, rngMin, rngMid, rngMax, rng, rngStep, zeroValue, dbMIN_VAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw chart graph
|
// Draw chart graph
|
||||||
void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const double chrtScale)
|
void Chart::drawChartLines(const ChrtDir chrtDir, const int8_t chrtIntv, const double chrtScale)
|
||||||
{
|
{
|
||||||
double chrtVal; // Current data value
|
double chrtVal; // Current data value
|
||||||
Pos point, prevPoint; // current and previous chart point
|
Pos point, prevPoint; // current and previous chart point
|
||||||
@@ -339,14 +361,12 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do
|
|||||||
chrtPrevVal = dbMAX_VAL;
|
chrtPrevVal = dbMAX_VAL;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
point = setCurrentChartPoint(i, direction, chrtVal, chrtScale);
|
point = setChartPoint(i, chrtDir, chrtVal, chrtScale);
|
||||||
|
// if (i >= (numBufVals / chrtIntv) - 5) // log chart data of x lines (adjust for test purposes)
|
||||||
// if (i >= (numBufVals / chrtIntv) - 5) // log chart data of 1 line (adjust for test purposes)
|
// LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %.2f, chrtMin: %.2f, {x,y} {%d,%d}", i, chrtVal, chrtMin, point.x, point.y);
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %.2f, chrtMin: %.2f, {x,y} {%d,%d}", i, chrtVal, chrtMin, x, y);
|
|
||||||
|
|
||||||
if ((i == 0) || (chrtPrevVal == dbMAX_VAL)) {
|
if ((i == 0) || (chrtPrevVal == dbMAX_VAL)) {
|
||||||
// just a dot for 1st chart point or after some invalid values
|
prevPoint = point; // just a dot for 1st chart point or after some invalid values
|
||||||
prevPoint = point;
|
|
||||||
|
|
||||||
} else if (chrtDataFmt == WIND || chrtDataFmt == ROTATION) {
|
} else if (chrtDataFmt == WIND || chrtDataFmt == ROTATION) {
|
||||||
// cross borders check for degree values; shift values to [-PI..0..PI]; when crossing borders, range is 2x PI degrees
|
// cross borders check for degree values; shift values to [-PI..0..PI]; when crossing borders, range is 2x PI degrees
|
||||||
@@ -360,7 +380,7 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do
|
|||||||
// LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: crossedBorders: %d, chrtVal: %.2f, chrtPrevVal: %.2f", crossedBorders, chrtVal, chrtPrevVal);
|
// LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: crossedBorders: %d, chrtVal: %.2f, chrtPrevVal: %.2f", crossedBorders, chrtVal, chrtPrevVal);
|
||||||
bool wrappingFromHighToLow = normCurrVal < normPrevVal; // Determine which edge we're crossing
|
bool wrappingFromHighToLow = normCurrVal < normPrevVal; // Determine which edge we're crossing
|
||||||
|
|
||||||
if (direction == HORIZONTAL) {
|
if (chrtDir == HORIZONTAL) {
|
||||||
int ySplit = wrappingFromHighToLow ? (cRoot.y + valAxis) : cRoot.y;
|
int ySplit = wrappingFromHighToLow ? (cRoot.y + valAxis) : cRoot.y;
|
||||||
drawBoldLine(prevPoint.x, prevPoint.y, point.x, ySplit);
|
drawBoldLine(prevPoint.x, prevPoint.y, point.x, ySplit);
|
||||||
prevPoint.y = wrappingFromHighToLow ? cRoot.y : (cRoot.y + valAxis);
|
prevPoint.y = wrappingFromHighToLow ? cRoot.y : (cRoot.y + valAxis);
|
||||||
@@ -371,10 +391,19 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do
|
|||||||
prevPoint.x = wrappingFromHighToLow ? cRoot.x : (cRoot.x + valAxis);
|
prevPoint.x = wrappingFromHighToLow ? cRoot.x : (cRoot.x + valAxis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test position of chart point against chart middle; ignore first values which we don't show next time anyway
|
||||||
|
if (i > MIN_FREE_VALUES) {
|
||||||
|
double d = WindUtils::toPI(chrtVal - chrtMid);
|
||||||
|
if (d > 0) // point is right of mid point
|
||||||
|
allLeft = false;
|
||||||
|
if (d < 0) // point is left of mid point
|
||||||
|
allRight = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chrtDataFmt == DEPTH) {
|
if (chrtDataFmt == DEPTH) {
|
||||||
if (direction == HORIZONTAL) { // horizontal chart
|
if (chrtDir == HORIZONTAL) { // horizontal chart
|
||||||
drawBoldLine(point.x, point.y, point.x, cRoot.y + valAxis);
|
drawBoldLine(point.x, point.y, point.x, cRoot.y + valAxis);
|
||||||
} else { // vertical chart
|
} else { // vertical chart
|
||||||
drawBoldLine(point.x, point.y, cRoot.x + valAxis, point.y);
|
drawBoldLine(point.x, point.y, cRoot.x + valAxis, point.y);
|
||||||
@@ -391,31 +420,36 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do
|
|||||||
if (i >= timAxis - 1) {
|
if (i >= timAxis - 1) {
|
||||||
oldChrtIntv = 0; // force reset of buffer start and number of values to show in next display loop
|
oldChrtIntv = 0; // force reset of buffer start and number of values to show in next display loop
|
||||||
|
|
||||||
if (chrtDataFmt == WIND) { // degree of course or wind
|
if (chrtDataFmt == WIND) {
|
||||||
|
if (allLeft || allRight || chrtRng == M_TWOPI) { // check if we should recalculate chart axis middle value
|
||||||
recalcRngMid = true;
|
recalcRngMid = true;
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: chart end: timAxis: %d, i: %d, bufStart: %d, numBufVals: %d, recalcRngCntr: %d", timAxis, i, bufStart, numBufVals, recalcRngMid);
|
}
|
||||||
|
// LOG_DEBUG(GwLog::DEBUG, "OBPcharts: WIND chart end: timAxis: %d, i: %d, bufStart: %d, numBufVals: %d, recalcRngCntr: %d, allLeft: %d, allRight: %d", timAxis, i, bufStart,
|
||||||
|
// numBufVals, recalcRngMid, allLeft, allRight);
|
||||||
|
allLeft = true; // reset marker for all left/right chart points
|
||||||
|
allRight = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
taskYIELD(); // we run for 50-150ms; be polite to other tasks with same priority
|
taskYIELD(); // we run for 50-150ms, so we want to be polite to other tasks with same priority
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set current chart point to draw
|
// Set current chart point to draw
|
||||||
Pos Chart::setCurrentChartPoint(const int i, const char direction, const double chrtVal, const double chrtScale)
|
Pos Chart::setChartPoint(const int i, const ChrtDir chrtDir, const double chrtVal, const double chrtScale)
|
||||||
{
|
{
|
||||||
Pos currentPoint;
|
Pos currentPoint;
|
||||||
|
|
||||||
if (direction == HORIZONTAL) {
|
if (chrtDir == HORIZONTAL) {
|
||||||
currentPoint.x = cRoot.x + i; // Position in chart area
|
currentPoint.x = cRoot.x + i; // Position in chart area
|
||||||
|
|
||||||
if (chrtDataFmt == WIND || chrtDataFmt == ROTATION) { // degree type value
|
if (chrtDataFmt == WIND || chrtDataFmt == ROTATION) { // degree type value
|
||||||
currentPoint.y = cRoot.y + static_cast<int>((WindUtils::to2PI(chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round
|
currentPoint.y = cRoot.y + static_cast<int>((WindUtils::to2PI(chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round
|
||||||
} else if (chrtDataFmt == SPEED or chrtDataFmt == TEMPERATURE) { // speed or temperature data format -> print low values at bottom
|
} else if (chrtDataFmt == WIND || chrtDataFmt == ROTATION || chrtDataFmt == DEPTH) { // print low values at bottom
|
||||||
currentPoint.y = cRoot.y + valAxis - static_cast<int>(((chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round
|
|
||||||
} else { // any other data format
|
|
||||||
currentPoint.y = cRoot.y + static_cast<int>(((chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round
|
currentPoint.y = cRoot.y + static_cast<int>(((chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round
|
||||||
|
} else { // any other data format
|
||||||
|
currentPoint.y = cRoot.y + valAxis - static_cast<int>(((chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round
|
||||||
}
|
}
|
||||||
|
|
||||||
} else { // vertical chart
|
} else { // vertical chart
|
||||||
@@ -432,58 +466,60 @@ Pos Chart::setCurrentChartPoint(const int i, const char direction, const double
|
|||||||
}
|
}
|
||||||
|
|
||||||
// chart time axis label + lines
|
// chart time axis label + lines
|
||||||
void Chart::drawChrtTimeAxis(const char chrtDir, const int8_t chrtSz, const int8_t chrtIntv)
|
void Chart::drawChrtTimeAxis(const ChrtDir chrtDir, const ChrtSize chrtSz, const int8_t chrtIntv)
|
||||||
{
|
{
|
||||||
float axSlots, intv, i;
|
int axSlots, intv, i, timeRng;
|
||||||
char sTime[6];
|
char sTime[6];
|
||||||
int timeRng = chrtIntv * 4; // chart time interval: [1] 4 min., [2] 8 min., [3] 12 min., [4] 16 min., [8] 32 min.
|
int tOffset;
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setTextColor(fgColor);
|
getdisplay().setTextColor(fgColor);
|
||||||
|
|
||||||
axSlots = 5; // number of axis labels
|
intv = 60; // print axis label each 60 pixels = seconds
|
||||||
intv = timAxis / (axSlots - 1); // minutes per chart axis interval (interval is 1 less than axSlots)
|
axSlots = timAxis / intv; // number of axis labels; value will be truncated to full number
|
||||||
i = timeRng; // Chart axis label start at -32, -16, -12, ... minutes
|
|
||||||
|
|
||||||
if (chrtDir == HORIZONTAL) {
|
if (chrtDir == HORIZONTAL) {
|
||||||
getdisplay().fillRect(0, cRoot.y, dWidth, 2, fgColor);
|
getdisplay().fillRect(0, cRoot.y, dWidth, 2, fgColor);
|
||||||
|
|
||||||
for (float j = 0; j < timAxis - 1; j += intv) { // fill time axis with values but keep area free on right hand side for value label
|
i = axSlots * chrtIntv * -1; // current minute label
|
||||||
|
// LOG_DEBUG(GwLog::DEBUG, "Chart::drawChrtTimeAxis: intv: %d, axSlots: %d, timAxis: %d, chrtIntv: %d", intv, axSlots, timAxis, chrtIntv);
|
||||||
|
for (int j = intv; j < timAxis - 1; j += intv) { // fill time axis with values but keep area free on right hand side for value label
|
||||||
|
|
||||||
// draw text with appropriate offset
|
// draw text with appropriate offset
|
||||||
int tOffset = j == 0 ? 13 : -4;
|
tOffset = j == 0 ? 13 : -4;
|
||||||
snprintf(sTime, sizeof(sTime), "-%.0f", i);
|
snprintf(sTime, sizeof(sTime), "%d", i);
|
||||||
drawTextCenter(cRoot.x + j + tOffset, cRoot.y - 8, sTime);
|
drawTextCenter(cRoot.x + j + tOffset, cRoot.y - 8, sTime);
|
||||||
getdisplay().drawLine(cRoot.x + j, cRoot.y, cRoot.x + j, cRoot.y + 5, fgColor); // draw short vertical time mark
|
getdisplay().drawLine(cRoot.x + j, cRoot.y, cRoot.x + j, cRoot.y + 5, fgColor); // draw short vertical time mark
|
||||||
|
|
||||||
i -= chrtIntv;
|
i += chrtIntv;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else { // vertical chart
|
} else { // vertical chart
|
||||||
|
|
||||||
|
i = chrtIntv * -1; // current minute label
|
||||||
for (float j = intv; j < timAxis - 1; j += intv) { // don't print time label at upper and lower end of time axis
|
for (float j = intv; j < timAxis - 1; j += intv) { // don't print time label at upper and lower end of time axis
|
||||||
|
|
||||||
i -= chrtIntv; // we start not at top chart position
|
snprintf(sTime, sizeof(sTime), "%d", i);
|
||||||
snprintf(sTime, sizeof(sTime), "-%.0f", i);
|
getdisplay().drawLine(cRoot.x, cRoot.y + j, cRoot.x + valAxis - 2, cRoot.y + j, fgColor); // Grid line
|
||||||
getdisplay().drawLine(cRoot.x, cRoot.y + j, cRoot.x + valAxis, cRoot.y + j, fgColor); // Grid line
|
|
||||||
|
|
||||||
if (chrtSz == FULL_SIZE) { // full size chart
|
if (chrtSz == FULL_SIZE) { // full size chart
|
||||||
getdisplay().fillRect(0, cRoot.y + j - 9, 32, 15, bgColor); // clear small area to remove potential chart lines
|
getdisplay().fillRect(0, cRoot.y + j - 9, 32, 15, bgColor); // clear small area to remove potential chart lines
|
||||||
getdisplay().setCursor((4 - strlen(sTime)) * 7, cRoot.y + j + 3); // time value; print left screen; value right-formated
|
getdisplay().setCursor((4 - strlen(sTime)) * 7, cRoot.y + j + 3); // time value; print left screen; value right-formated
|
||||||
getdisplay().printf("%s", sTime); // Range value
|
getdisplay().printf("%s", sTime); // time value
|
||||||
} else if (chrtSz == HALF_SIZE_RIGHT) { // half size chart; right side
|
} else if (chrtSz == HALF_SIZE_RIGHT_BOTTOM) { // half size chart; right side
|
||||||
drawTextCenter(dWidth / 2, cRoot.y + j, sTime); // time value; print mid screen
|
drawTextCenter(dWidth / 2, cRoot.y + j, sTime); // time value; print mid screen
|
||||||
}
|
}
|
||||||
|
i -= chrtIntv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// chart value axis labels + lines
|
// chart value axis labels + lines
|
||||||
void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntName)
|
void Chart::drawChrtValAxis(const ChrtDir chrtDir, const ChrtSize chrtSz, const bool prntName)
|
||||||
{
|
{
|
||||||
const GFXfont* font;
|
const GFXfont* font;
|
||||||
constexpr bool NO_LABEL = false;
|
// constexpr bool NO_LABEL = false;
|
||||||
constexpr bool LABEL = true;
|
// constexpr bool LABEL = true;
|
||||||
|
|
||||||
getdisplay().setTextColor(fgColor);
|
getdisplay().setTextColor(fgColor);
|
||||||
|
|
||||||
@@ -491,29 +527,33 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa
|
|||||||
|
|
||||||
if (chrtSz == FULL_SIZE) {
|
if (chrtSz == FULL_SIZE) {
|
||||||
|
|
||||||
|
if (prntName) {
|
||||||
|
// print buffer data name on left hand side of time axis (max. size 5 characters)
|
||||||
font = &Ubuntu_Bold12pt8b;
|
font = &Ubuntu_Bold12pt8b;
|
||||||
|
|
||||||
// print buffer data name on right hand side of time axis (max. size 5 characters)
|
|
||||||
getdisplay().setFont(font);
|
getdisplay().setFont(font);
|
||||||
drawTextRalign(cRoot.x + timAxis, cRoot.y - 3, dbName.substring(0, 5));
|
getdisplay().fillRect(cRoot.x + timAxis - 57, cRoot.y + 2, 58, 20, bgColor); // clear small area to remove potential chart lines
|
||||||
|
String name = xdrDelete(dbName); // Value name
|
||||||
|
drawTextRalign(cRoot.x + timAxis - 1, cRoot.y + 19, name.substring(0, 5));
|
||||||
|
}
|
||||||
if (chrtDataFmt == WIND) {
|
if (chrtDataFmt == WIND) {
|
||||||
prntHorizChartThreeValueAxisLabel(font);
|
prntHorizChartThreeValueAxisLabel(font);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for any other data formats print multiple axis value lines on full charts
|
// for any other data formats print multiple axis value lines on full charts
|
||||||
|
font = &Ubuntu_Bold10pt8b;
|
||||||
prntHorizChartMultiValueAxisLabel(font);
|
prntHorizChartMultiValueAxisLabel(font);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else { // half size chart -> just print edge values + middle chart line
|
} else { // half size chart -> just print edge values + middle chart line
|
||||||
|
|
||||||
font = &Ubuntu_Bold10pt8b;
|
font = &Ubuntu_Bold10pt8b;
|
||||||
|
|
||||||
if (prntName) {
|
if (prntName) {
|
||||||
// print buffer data name on right hand side of time axis (max. size 5 characters)
|
// print buffer data name on right hand side of time axis (max. size 6 characters)
|
||||||
getdisplay().setFont(font);
|
getdisplay().setFont(font);
|
||||||
drawTextRalign(cRoot.x + timAxis, cRoot.y - 3, dbName.substring(0, 5));
|
getdisplay().fillRect(cRoot.x + timAxis - 57, cRoot.y + 2, 58, 16, bgColor); // clear small area to remove potential chart lines
|
||||||
|
String name = xdrDelete(dbName); // Value name
|
||||||
|
drawTextRalign(cRoot.x + timAxis - 1, cRoot.y + 16, name.substring(0, 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
prntHorizChartThreeValueAxisLabel(font);
|
prntHorizChartThreeValueAxisLabel(font);
|
||||||
@@ -522,53 +562,67 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa
|
|||||||
|
|
||||||
} else { // vertical chart
|
} else { // vertical chart
|
||||||
|
|
||||||
|
if (prntName) {
|
||||||
if (chrtSz == FULL_SIZE) {
|
if (chrtSz == FULL_SIZE) {
|
||||||
font = &Ubuntu_Bold12pt8b;
|
font = &Ubuntu_Bold12pt8b;
|
||||||
getdisplay().setFont(font); // use larger font
|
|
||||||
drawTextRalign(cRoot.x + (valAxis * 0.42), cRoot.y - 2, dbName.substring(0, 6)); // print buffer data name (max. size 5 characters)
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
font = &Ubuntu_Bold10pt8b;
|
font = &Ubuntu_Bold10pt8b;
|
||||||
}
|
}
|
||||||
|
getdisplay().setFont(font);
|
||||||
|
String name = xdrDelete(dbName); // Value name
|
||||||
|
drawTextRalign(cRoot.x + (valAxis * 0.42), cRoot.y - 2, name.substring(0, 6)); // print buffer data name (max. size 6 characters)
|
||||||
|
}
|
||||||
|
|
||||||
|
font = &Ubuntu_Bold10pt8b;
|
||||||
prntVerticChartThreeValueAxisLabel(font);
|
prntVerticChartThreeValueAxisLabel(font);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print current data value
|
// Print current data value
|
||||||
void Chart::prntCurrValue(const char direction, GwApi::BoatValue& currValue)
|
void Chart::prntCurrValue(const ChrtDir chrtDir, GwApi::BoatValue& currValue)
|
||||||
{
|
{
|
||||||
const int xPosVal = (direction == HORIZONTAL) ? cRoot.x + (timAxis / 2) - 56 : cRoot.x + 32;
|
static constexpr int8_t LEFT = 0;
|
||||||
const int yPosVal = (direction == HORIZONTAL) ? cRoot.y + valAxis - 7 : cRoot.y + timAxis - 7;
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
static constexpr bool smallDecimals = true; // print data value always with small decimals
|
||||||
|
|
||||||
|
const int xPosVal = (chrtDir == HORIZONTAL) ? cRoot.x + (timAxis / 2) - 74 : cRoot.x + 31;
|
||||||
|
const int yPosVal = (chrtDir == HORIZONTAL) ? cRoot.y + valAxis : cRoot.y + timAxis;
|
||||||
|
|
||||||
FormattedData frmtDbData = formatValue(&currValue, *commonData, NO_SIMUDATA);
|
FormattedData frmtDbData = formatValue(&currValue, *commonData, NO_SIMUDATA);
|
||||||
String sdbValue = frmtDbData.svalue; // value as formatted string
|
String sdbValue = frmtDbData.svalue; // value as formatted string
|
||||||
String dbUnit = frmtDbData.unit; // Unit of value; limit length to 3 characters
|
String dbUnit = frmtDbData.unit; // Unit of value; limit length to 3 characters
|
||||||
|
|
||||||
getdisplay().fillRect(xPosVal - 1, yPosVal - 35, 128, 41, bgColor); // Clear area for TWS value
|
// box
|
||||||
getdisplay().drawRect(xPosVal, yPosVal - 34, 126, 40, fgColor); // Draw box for TWS value
|
getdisplay().fillRect(xPosVal - 1, yPosVal - 40, 148, 39, bgColor); // Clear area for value
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
getdisplay().drawRect(xPosVal, yPosVal - 39, 146, 37, fgColor); // Draw box for value
|
||||||
getdisplay().setCursor(xPosVal + 1, yPosVal);
|
|
||||||
getdisplay().print(sdbValue); // value
|
|
||||||
|
|
||||||
|
// value
|
||||||
|
printBoatValue(sdbValue, xPosVal + 93, yPosVal - 6, RIGHT, 16, smallDecimals);
|
||||||
|
// getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
||||||
|
// getdisplay().setCursor(xPosVal + 1, yPosVal - 6);
|
||||||
|
// getdisplay().print(sdbValue);
|
||||||
|
|
||||||
|
// name
|
||||||
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
||||||
getdisplay().setCursor(xPosVal + 76, yPosVal - 17);
|
getdisplay().setCursor(xPosVal + 97, yPosVal - 23);
|
||||||
getdisplay().print(dbName.substring(0, 3)); // Name, limited to 3 characters
|
String name = xdrDelete(dbName);
|
||||||
|
getdisplay().print(name.substring(0, 3)); // Name, limited to 3 characters
|
||||||
|
|
||||||
|
// unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(xPosVal + 76, yPosVal + 0);
|
getdisplay().setCursor(xPosVal + 98, yPosVal - 8);
|
||||||
getdisplay().print(dbUnit); // Unit
|
getdisplay().print(dbUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// print message for no valid data availabletemplate <typename T>
|
// print message for no valid data availabletemplate <typename T>
|
||||||
void Chart::prntNoValidData(const char direction)
|
void Chart::prntNoValidData(const ChrtDir chrtDir)
|
||||||
{
|
{
|
||||||
Pos p;
|
Pos p;
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
||||||
|
|
||||||
if (direction == HORIZONTAL) {
|
if (chrtDir == HORIZONTAL) {
|
||||||
p.x = cRoot.x + (timAxis / 2);
|
p.x = cRoot.x + (timAxis / 2);
|
||||||
p.y = cRoot.y + (valAxis / 2) - 10;
|
p.y = cRoot.y + (valAxis / 2) - 10;
|
||||||
} else {
|
} else {
|
||||||
@@ -583,7 +637,7 @@ void Chart::prntNoValidData(const char direction)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get maximum difference of last <amount> of dataBuf ringbuffer values to center chart; for angle data only
|
// Get maximum difference of last <amount> of dataBuf ringbuffer values to center chart; for angle data only
|
||||||
double Chart::getAngleRng(const double center, size_t amount)
|
double Chart::getCircularRng(const double center, size_t amount)
|
||||||
{
|
{
|
||||||
size_t count = dataBuf.getCurrentSize();
|
size_t count = dataBuf.getCurrentSize();
|
||||||
|
|
||||||
@@ -617,8 +671,8 @@ double Chart::getAngleRng(const double center, size_t amount)
|
|||||||
return (maxRng != dbMIN_VAL ? maxRng : dbMAX_VAL); // Return range from <mid> to <max>
|
return (maxRng != dbMIN_VAL ? maxRng : dbMAX_VAL); // Return range from <mid> to <max>
|
||||||
}
|
}
|
||||||
|
|
||||||
// print value axis label with only three values: top, mid, and bottom for vertical chart
|
// print value axis label with only three values: top, mid, and bottom for vertical chart
|
||||||
void Chart::prntVerticChartThreeValueAxisLabel(const GFXfont* font)
|
void Chart::prntVerticChartThreeValueAxisLabel(const GFXfont* font)
|
||||||
{
|
{
|
||||||
double cVal;
|
double cVal;
|
||||||
char sVal[7];
|
char sVal[7];
|
||||||
@@ -654,13 +708,13 @@ void Chart::prntHorizChartThreeValueAxisLabel(const GFXfont* font)
|
|||||||
double axLabel;
|
double axLabel;
|
||||||
double chrtMin, chrtMid, chrtMax;
|
double chrtMin, chrtMid, chrtMax;
|
||||||
int xOffset, yOffset; // offset for text position of x axis label for different font sizes
|
int xOffset, yOffset; // offset for text position of x axis label for different font sizes
|
||||||
String sVal;
|
char sVal[11]; // data value have max. 6 digits + decimal point + 2 decimals + sign
|
||||||
|
|
||||||
if (font == &Ubuntu_Bold10pt8b) {
|
if (font == &Ubuntu_Bold10pt8b) {
|
||||||
xOffset = 39;
|
xOffset = 32;
|
||||||
yOffset = 16;
|
yOffset = 16;
|
||||||
} else if (font == &Ubuntu_Bold12pt8b) {
|
} else if (font == &Ubuntu_Bold12pt8b) {
|
||||||
xOffset = 51;
|
xOffset = 42;
|
||||||
yOffset = 18;
|
yOffset = 18;
|
||||||
}
|
}
|
||||||
getdisplay().setFont(font);
|
getdisplay().setFont(font);
|
||||||
@@ -669,77 +723,99 @@ void Chart::prntHorizChartThreeValueAxisLabel(const GFXfont* font)
|
|||||||
chrtMin = convertValue(this->chrtMin, dbName, dbFormat, *commonData);
|
chrtMin = convertValue(this->chrtMin, dbName, dbFormat, *commonData);
|
||||||
chrtMid = convertValue(this->chrtMid, dbName, dbFormat, *commonData);
|
chrtMid = convertValue(this->chrtMid, dbName, dbFormat, *commonData);
|
||||||
chrtMax = convertValue(this->chrtMax, dbName, dbFormat, *commonData);
|
chrtMax = convertValue(this->chrtMax, dbName, dbFormat, *commonData);
|
||||||
chrtMin = std::round(chrtMin * 100.0) / 100.0;
|
|
||||||
chrtMid = std::round(chrtMid * 100.0) / 100.0;
|
|
||||||
chrtMax = std::round(chrtMax * 100.0) / 100.0;
|
|
||||||
|
|
||||||
// print top axis label
|
// print top axis label
|
||||||
axLabel = (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE) ? chrtMax : chrtMin;
|
axLabel = (chrtDataFmt == WIND || chrtDataFmt == ROTATION || chrtDataFmt == DEPTH) ? chrtMin : chrtMax;
|
||||||
sVal = formatLabel(axLabel);
|
formatLabel(axLabel).toCharArray(sVal, 11);
|
||||||
|
if (chrtDataFmt == PRESSURE) {
|
||||||
|
snprintf(sVal, sizeof(sVal), "%4.0f", axLabel);
|
||||||
|
getdisplay().fillRect(cRoot.x, cRoot.y + 2, xOffset + 12, yOffset, bgColor); // Clear small area to remove potential chart lines
|
||||||
|
drawTextRalign(cRoot.x + xOffset + 11, cRoot.y + yOffset, sVal); // range value
|
||||||
|
} else {
|
||||||
|
if (char* dot = strchr(sVal, '.'))
|
||||||
|
*dot = '\0'; // no decimal for top axis label
|
||||||
getdisplay().fillRect(cRoot.x, cRoot.y + 2, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines
|
getdisplay().fillRect(cRoot.x, cRoot.y + 2, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines
|
||||||
drawTextRalign(cRoot.x + xOffset, cRoot.y + yOffset, sVal); // range value
|
drawTextRalign(cRoot.x + xOffset, cRoot.y + yOffset, sVal); // range value
|
||||||
|
}
|
||||||
|
|
||||||
// print mid axis label
|
// print mid axis label
|
||||||
axLabel = chrtMid;
|
axLabel = chrtMid;
|
||||||
sVal = formatLabel(axLabel);
|
formatLabel(axLabel).toCharArray(sVal, 11);
|
||||||
getdisplay().fillRect(cRoot.x, cRoot.y + (valAxis / 2) - 8, xOffset + 3, 16, bgColor); // Clear small area to remove potential chart lines
|
if (chrtDataFmt == PRESSURE) { // print 4-digit value
|
||||||
|
getdisplay().fillRect(cRoot.x, cRoot.y + (valAxis / 2) - 8, xOffset + 12, yOffset, bgColor); // Clear small area to remove potential chart lines
|
||||||
|
drawTextRalign(cRoot.x + xOffset + 11, cRoot.y + (valAxis / 2) + 6, sVal); // range value
|
||||||
|
getdisplay().drawLine(cRoot.x + xOffset + 14, cRoot.y + (valAxis / 2), cRoot.x + timAxis, cRoot.y + (valAxis / 2), fgColor);
|
||||||
|
} else { // print 3-digit value
|
||||||
|
getdisplay().fillRect(cRoot.x, cRoot.y + (valAxis / 2) - 8, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines
|
||||||
drawTextRalign(cRoot.x + xOffset, cRoot.y + (valAxis / 2) + 6, sVal); // range value
|
drawTextRalign(cRoot.x + xOffset, cRoot.y + (valAxis / 2) + 6, sVal); // range value
|
||||||
getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + (valAxis / 2), cRoot.x + timAxis, cRoot.y + (valAxis / 2), fgColor);
|
getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + (valAxis / 2), cRoot.x + timAxis, cRoot.y + (valAxis / 2), fgColor);
|
||||||
|
}
|
||||||
|
|
||||||
// print bottom axis label
|
// print bottom axis label
|
||||||
axLabel = (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE) ? chrtMin : chrtMax;
|
axLabel = (chrtDataFmt == WIND || chrtDataFmt == ROTATION || chrtDataFmt == DEPTH) ? chrtMax : chrtMin;
|
||||||
sVal = formatLabel(axLabel);
|
formatLabel(axLabel).toCharArray(sVal, 11);
|
||||||
getdisplay().fillRect(cRoot.x, cRoot.y + valAxis - 14, xOffset + 3, 15, bgColor); // Clear small area to remove potential chart lines
|
if (chrtDataFmt == PRESSURE) {
|
||||||
|
getdisplay().fillRect(cRoot.x, cRoot.y + valAxis - 14, xOffset + 12, yOffset, bgColor); // Clear small area to remove potential chart lines
|
||||||
|
drawTextRalign(cRoot.x + xOffset + 11, cRoot.y + valAxis, sVal); // range value
|
||||||
|
getdisplay().drawLine(cRoot.x + xOffset + 14, cRoot.y + valAxis, cRoot.x + timAxis, cRoot.y + valAxis, fgColor);
|
||||||
|
} else {
|
||||||
|
if (char* dot = strchr(sVal, '.'))
|
||||||
|
*dot = '\0'; // no decimal for bottom axis label
|
||||||
|
getdisplay().fillRect(cRoot.x, cRoot.y + valAxis - 14, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines
|
||||||
drawTextRalign(cRoot.x + xOffset, cRoot.y + valAxis, sVal); // range value
|
drawTextRalign(cRoot.x + xOffset, cRoot.y + valAxis, sVal); // range value
|
||||||
getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + valAxis, cRoot.x + timAxis, cRoot.y + valAxis, fgColor);
|
getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + valAxis, cRoot.x + timAxis, cRoot.y + valAxis, fgColor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// print value axis label with multiple axis lines for horizontal chart
|
// print value axis label with multiple axis lines for horizontal chart
|
||||||
void Chart::prntHorizChartMultiValueAxisLabel(const GFXfont* font)
|
void Chart::prntHorizChartMultiValueAxisLabel(const GFXfont* font)
|
||||||
{
|
{
|
||||||
double chrtMin, chrtMax, chrtRng;
|
double chrtMin, chrtMax, chrtRng;
|
||||||
double axSlots, axIntv, axLabel;
|
|
||||||
int xOffset; // offset for text position of x axis label for different font sizes
|
int xOffset; // offset for text position of x axis label for different font sizes
|
||||||
String sVal;
|
char sVal[11]; // data value have max. 6 digits + decimal point + 2 decimals + sign
|
||||||
|
|
||||||
if (font == &Ubuntu_Bold10pt8b) {
|
if (font == &Ubuntu_Bold10pt8b) {
|
||||||
xOffset = 38;
|
xOffset = 32;
|
||||||
} else if (font == &Ubuntu_Bold12pt8b) {
|
} else if (font == &Ubuntu_Bold12pt8b) {
|
||||||
xOffset = 50;
|
xOffset = 42;
|
||||||
}
|
}
|
||||||
getdisplay().setFont(font);
|
getdisplay().setFont(font);
|
||||||
|
|
||||||
chrtMin = convertValue(this->chrtMin, dbName, dbFormat, *commonData);
|
chrtMin = convertValue(this->chrtMin, dbName, dbFormat, *commonData);
|
||||||
// chrtMin = std::floor(chrtMin / rngStep) * rngStep;
|
|
||||||
chrtMin = std::round(chrtMin * 100.0) / 100.0;
|
|
||||||
chrtMax = convertValue(this->chrtMax, dbName, dbFormat, *commonData);
|
chrtMax = convertValue(this->chrtMax, dbName, dbFormat, *commonData);
|
||||||
// chrtMax = std::ceil(chrtMax / rngStep) * rngStep;
|
chrtRng = chrtMax - chrtMin;
|
||||||
chrtMax = std::round(chrtMax * 100.0) / 100.0;
|
|
||||||
chrtRng = std::round((chrtMax - chrtMin) * 100) / 100;
|
|
||||||
|
|
||||||
axSlots = valAxis / static_cast<double>(VALAXIS_STEP); // number of axis labels (and we want to have a double calculation, no integer)
|
double axIntv = chrtRng / VALAXIS_SLOTS; // axis label interval
|
||||||
axIntv = chrtRng / axSlots;
|
double axLabel = chrtMin + axIntv; // current axis label
|
||||||
axLabel = chrtMin + axIntv;
|
double chrtScale = double(valAxis) / chrtRng; // Chart scale: pixels per value step
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "Chart::printHorizMultiValueAxisLabel: chrtRng: %.2f, th-chrtRng: %.2f, axSlots: %.2f, axIntv: %.2f, axLabel: %.2f, chrtMin: %.2f, chrtMid: %.2f, chrtMax: %.2f", chrtRng, this->chrtRng, axSlots, axIntv, axLabel, this->chrtMin, chrtMid, chrtMax);
|
double valAxisStep = axIntv * chrtScale; // pixel per axis label interval
|
||||||
|
|
||||||
|
// LOG_DEBUG(GwLog::DEBUG, "Chart::printHorizMultiValueAxisLabel: chrtRng: %.2f, th-chrtRng: %.2f, axSlots: %.2f, axIntv: %.2f, axLabel: %.2f, chrtMin: %.2f, chrtMid: %.2f, chrtMax: %.2f", chrtRng, this->chrtRng, VALAXIS_SLOTS, axIntv, axLabel, this->chrtMin, chrtMid, chrtMax);
|
||||||
|
|
||||||
int loopStrt, loopEnd, loopStp;
|
int loopStrt, loopEnd, loopStp;
|
||||||
if (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE || chrtDataFmt == OTHER) {
|
if (chrtDataFmt == WIND || chrtDataFmt == ROTATION || chrtDataFmt == DEPTH) {
|
||||||
// High value at top
|
|
||||||
loopStrt = valAxis - VALAXIS_STEP;
|
|
||||||
loopEnd = VALAXIS_STEP / 2;
|
|
||||||
loopStp = VALAXIS_STEP * -1;
|
|
||||||
} else {
|
|
||||||
// Low value at top
|
// Low value at top
|
||||||
loopStrt = VALAXIS_STEP;
|
loopStrt = valAxisStep;
|
||||||
loopEnd = valAxis - (VALAXIS_STEP / 2);
|
loopEnd = valAxis - (valAxisStep / 2);
|
||||||
loopStp = VALAXIS_STEP;
|
loopStp = valAxisStep;
|
||||||
|
} else {
|
||||||
|
// high value at top
|
||||||
|
loopStrt = valAxis - valAxisStep;
|
||||||
|
loopEnd = valAxisStep / 2;
|
||||||
|
loopStp = valAxisStep * -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = loopStrt; (loopStp > 0) ? (j < loopEnd) : (j > loopEnd); j += loopStp) {
|
for (int j = loopStrt; (loopStp > 0) ? (j < loopEnd) : (j > loopEnd); j += loopStp) {
|
||||||
sVal = formatLabel(axLabel);
|
formatLabel(axLabel).toCharArray(sVal, 11);
|
||||||
|
if (chrtDataFmt == PRESSURE) { // print 4-digit value
|
||||||
|
getdisplay().fillRect(cRoot.x, cRoot.y + j - 11, xOffset + 12, 21, bgColor); // Clear small area to remove potential chart lines
|
||||||
|
drawTextRalign(cRoot.x + xOffset + 11, cRoot.y + j + 7, sVal); // range value
|
||||||
|
getdisplay().drawLine(cRoot.x + xOffset + 14, cRoot.y + j, cRoot.x + timAxis, cRoot.y + j, fgColor);
|
||||||
|
} else { // print 3-digit value
|
||||||
getdisplay().fillRect(cRoot.x, cRoot.y + j - 11, xOffset + 3, 21, bgColor); // Clear small area to remove potential chart lines
|
getdisplay().fillRect(cRoot.x, cRoot.y + j - 11, xOffset + 3, 21, bgColor); // Clear small area to remove potential chart lines
|
||||||
drawTextRalign(cRoot.x + xOffset, cRoot.y + j + 7, sVal); // range value
|
drawTextRalign(cRoot.x + xOffset, cRoot.y + j + 7, sVal); // range value
|
||||||
getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + j, cRoot.x + timAxis, cRoot.y + j, fgColor);
|
getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + j, cRoot.x + timAxis, cRoot.y + j, fgColor);
|
||||||
|
}
|
||||||
|
|
||||||
axLabel += axIntv;
|
axLabel += axIntv;
|
||||||
}
|
}
|
||||||
@@ -782,20 +858,21 @@ String Chart::formatLabel(const double& label)
|
|||||||
{
|
{
|
||||||
char sVal[11];
|
char sVal[11];
|
||||||
|
|
||||||
if (dbFormat == "formatCourse" || dbFormat == "formatWind") {
|
if (chrtDataFmt == WIND) {
|
||||||
// Format 3 numbers with prefix zero
|
snprintf(sVal, sizeof(sVal), "%03.0f", label); // Format 3 numbers with prefix zero
|
||||||
snprintf(sVal, sizeof(sVal), "%03.0f", label);
|
|
||||||
|
|
||||||
} else if (dbFormat == "formatRot") {
|
} else if (chrtDataFmt == ROTATION) {
|
||||||
if (label > -10 && label < 10) {
|
if (label > -9.995 && label < 9.995) {
|
||||||
snprintf(sVal, sizeof(sVal), "%3.2f", label);
|
snprintf(sVal, sizeof(sVal), "%3.2f", label);
|
||||||
} else {
|
} else {
|
||||||
snprintf(sVal, sizeof(sVal), "%3.0f", label);
|
snprintf(sVal, sizeof(sVal), "%3.0f", label);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
} else if (chrtDataFmt == PRESSURE) {
|
||||||
if (label < 10) {
|
snprintf(sVal, sizeof(sVal), "%4.0f", label);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (label < 9.95) {
|
||||||
snprintf(sVal, sizeof(sVal), "%3.1f", label);
|
snprintf(sVal, sizeof(sVal), "%3.1f", label);
|
||||||
} else {
|
} else {
|
||||||
snprintf(sVal, sizeof(sVal), "%3.0f", label);
|
snprintf(sVal, sizeof(sVal), "%3.0f", label);
|
||||||
|
|||||||
+55
-31
@@ -2,6 +2,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
|
#include "movingAvg.h"
|
||||||
|
|
||||||
struct Pos {
|
struct Pos {
|
||||||
int x;
|
int x;
|
||||||
@@ -18,6 +19,30 @@ class RingBuffer;
|
|||||||
class GwLog;
|
class GwLog;
|
||||||
|
|
||||||
class Chart {
|
class Chart {
|
||||||
|
public:
|
||||||
|
enum ChrtDir {
|
||||||
|
HORIZONTAL,
|
||||||
|
VERTICAL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ChrtSize {
|
||||||
|
FULL_SIZE,
|
||||||
|
HALF_SIZE_LEFT_TOP,
|
||||||
|
HALF_SIZE_RIGHT_BOTTOM,
|
||||||
|
TWO_THIRD_TOP
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr bool PRNT_NAME = true;
|
||||||
|
static constexpr bool NO_PRNT_NAME = false;
|
||||||
|
static constexpr bool PRNT_VALUE = true;
|
||||||
|
static constexpr bool NO_PRNT_VALUE = false;
|
||||||
|
|
||||||
|
Chart(RingBuffer<uint16_t>& dataBuf, CommonData& common, bool useSimuData); // Chart object of data chart
|
||||||
|
~Chart();
|
||||||
|
bool init(); // initialize chart object parameters
|
||||||
|
bool isValid() { return initValid; }; // Checks if chart object has been fully initialized
|
||||||
|
void showChrt(const ChrtDir chrtDir, const ChrtSize chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue); // Perform all actions to draw chart
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CommonData* commonData;
|
CommonData* commonData;
|
||||||
GwLog* logger;
|
GwLog* logger;
|
||||||
@@ -28,28 +53,24 @@ protected:
|
|||||||
SPEED,
|
SPEED,
|
||||||
DEPTH,
|
DEPTH,
|
||||||
TEMPERATURE,
|
TEMPERATURE,
|
||||||
|
PRESSURE,
|
||||||
|
HUMIDITY,
|
||||||
OTHER
|
OTHER
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr char HORIZONTAL = 'H';
|
|
||||||
static constexpr char VERTICAL = 'V';
|
|
||||||
static constexpr int8_t FULL_SIZE = 0;
|
|
||||||
static constexpr int8_t HALF_SIZE_LEFT = 1;
|
|
||||||
static constexpr int8_t HALF_SIZE_RIGHT = 2;
|
|
||||||
|
|
||||||
static constexpr int8_t MIN_FREE_VALUES = 60; // free 60 values when chart line reaches chart end
|
static constexpr int8_t MIN_FREE_VALUES = 60; // free 60 values when chart line reaches chart end
|
||||||
static constexpr int8_t THRESHOLD_NO_DATA = 3; // max. seconds of invalid values in a row
|
static constexpr int8_t THRESHOLD_NO_DATA = 3; // max. seconds of invalid values in a row
|
||||||
static constexpr int8_t VALAXIS_STEP = 60; // pixels between two chart value axis labels
|
static constexpr int8_t VALAXIS_SLOTS = 5; // no. of value axis labels
|
||||||
|
|
||||||
static constexpr bool NO_SIMUDATA = true; // switch off simulation feature of <formatValue> function
|
static constexpr bool NO_SIMUDATA = true; // switch off simulation feature of <formatValue> function
|
||||||
|
|
||||||
RingBuffer<uint16_t>& dataBuf; // Buffer to display
|
RingBuffer<uint16_t>& dataBuf; // Buffer to display
|
||||||
//char chrtDir; // Chart timeline direction: 'H' = horizontal, 'V' = vertical
|
bool initValid = false; // indicates whether object holds valid initialization data or not
|
||||||
//int8_t chrtSz; // Chart size: [0] = full size, [1] = half size left/top, [2] half size right/bottom
|
|
||||||
double dfltRng; // Default range of chart, e.g. 30 = [0..30]
|
double dfltRng; // Default range of chart, e.g. 30 = [0..30]
|
||||||
uint16_t fgColor; // color code for any screen writing
|
uint16_t fgColor; // color code for any screen writing
|
||||||
uint16_t bgColor; // color code for screen background
|
uint16_t bgColor; // color code for screen background
|
||||||
bool useSimuData; // flag to indicate if simulation data is active
|
bool useSimuData; // flag to indicate if simulation data is active
|
||||||
|
bool smoothCharts; // flag to indicate if charts shall be printed with smoothed gradient
|
||||||
String tempFormat; // user defined format for temperature
|
String tempFormat; // user defined format for temperature
|
||||||
double zeroValue; // "0" SI value for temperature
|
double zeroValue; // "0" SI value for temperature
|
||||||
|
|
||||||
@@ -66,7 +87,8 @@ protected:
|
|||||||
double chrtMax; // Range high end value
|
double chrtMax; // Range high end value
|
||||||
double chrtMid; // Range mid value
|
double chrtMid; // Range mid value
|
||||||
double rngStep; // Defines the step of adjustment (e.g. 10 m/s) for value axis range
|
double rngStep; // Defines the step of adjustment (e.g. 10 m/s) for value axis range
|
||||||
bool recalcRngMid = false; // Flag for re-calculation of mid value of chart for wind data types
|
bool recalcRngMid; // Flag for re-calculation of mid value of chart for wind data types
|
||||||
|
bool allLeft, allRight; // indicate whether all chart points are left or right of middle value (only for wind data type)
|
||||||
|
|
||||||
String dbName, dbFormat; // Name and format of data buffer
|
String dbName, dbFormat; // Name and format of data buffer
|
||||||
ChrtDataFormat chrtDataFmt; // Data format of chart boat data type
|
ChrtDataFormat chrtDataFmt; // Data format of chart boat data type
|
||||||
@@ -77,40 +99,42 @@ protected:
|
|||||||
int numBufVals; // number of wind values available for current interval selection
|
int numBufVals; // number of wind values available for current interval selection
|
||||||
int bufStart; // 1st data value in buffer to show
|
int bufStart; // 1st data value in buffer to show
|
||||||
int numAddedBufVals; // Number of values added to buffer since last display
|
int numAddedBufVals; // Number of values added to buffer since last display
|
||||||
size_t currIdx; // Current index in TWD history buffer
|
size_t currIdx; // Current index in history buffer
|
||||||
size_t lastIdx; // Last index of TWD history buffer
|
size_t lastIdx; // Last index of history buffer
|
||||||
size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added
|
size_t lastAddedIdx = 0; // Last index of history buffer when new data was added
|
||||||
int numNoData; // Counter for multiple invalid data values in a row
|
int numNoData; // Counter for multiple invalid data values in a row
|
||||||
bool bufDataValid = false; // Flag to indicate if buffer data is valid
|
bool bufDataValid = false; // Flag to indicate if buffer data is valid
|
||||||
int oldChrtIntv = 0; // remember recent user selection of data interval
|
int oldChrtIntv = 0; // remember recent user selection of data interval
|
||||||
|
|
||||||
double chrtPrevVal; // Last data value in chart area
|
double chrtPrevVal; // Last data value in chart area
|
||||||
int x, y; // x and y coordinates for drawing
|
|
||||||
int prevX, prevY; // Last x and y coordinates for drawing
|
int prevX, prevY; // Last x and y coordinates for drawing
|
||||||
|
|
||||||
bool setChartDimensions(const char direction, const int8_t size); //define dimensions and start points for chart
|
// Default ranges for various boat data types
|
||||||
void drawChrt(const char chrtDir, const int8_t chrtIntv, GwApi::BoatValue& currValue); // Draw chart line
|
// Parameters: <1st value> default range, <2nd value> step for range adjustment
|
||||||
|
std::map<String, ChartProps> dfltChrtDta = {
|
||||||
|
{ "formatWind", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default wind range 60 degrees
|
||||||
|
{ "formatCourse", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees
|
||||||
|
{ "formatKnots", { 2.572, 2.572 } }, // default speed range in m/s
|
||||||
|
{ "formatDepth", { 10.0, 5.0 } }, // default depth range in m
|
||||||
|
{ "kelvinToC", { 20.0, 5.0 } }, // default temp range in °C/K
|
||||||
|
{ "formatXdr:P:P", { 4000.0, 1000.0 } } // default pressure range in Pascal (hPa * 100); XDR <B> (bar) format is represented in gateway in the same way
|
||||||
|
};
|
||||||
|
|
||||||
|
bool setChartDimensions(const ChrtDir direction, const ChrtSize chrtSz); // define dimensions and start points for chart
|
||||||
|
void drawChrt(const ChrtDir chrtDir, const int8_t chrtIntv, GwApi::BoatValue& currValue); // Draw chart line
|
||||||
void getBufferStartNSize(const int8_t chrtIntv); // Identify buffer size and buffer start position for chart
|
void getBufferStartNSize(const int8_t chrtIntv); // Identify buffer size and buffer start position for chart
|
||||||
void calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, double& rng); // Calculate chart points for value axis and return range between <min> and <max>
|
void calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, double& rng); // Calculate chart points for value axis and return range between <min> and <max>
|
||||||
void drawChartLines(const char direction, const int8_t chrtIntv, const double chrtScale); // Draw chart graph
|
void drawChartLines(const ChrtDir direction, const int8_t chrtIntv, const double chrtScale); // Draw chart graph
|
||||||
Pos setCurrentChartPoint(const int i, const char direction, const double chrtVal, const double chrtScale); // Set current chart point to draw
|
Pos setChartPoint(const int i, const ChrtDir chrtDir, const double chrtVal, const double chrtScale); // Set current chart point to draw
|
||||||
void drawChrtTimeAxis(const char chrtDir, const int8_t chrtSz, const int8_t chrtIntv); // Draw time axis of chart, value and lines
|
void drawChrtTimeAxis(const ChrtDir chrtDir, const ChrtSize chrtSz, const int8_t chrtIntv); // Draw time axis of chart, value and lines
|
||||||
void drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntLabel); // Draw value axis of chart, value and lines
|
void drawChrtValAxis(const ChrtDir chrtDir, const ChrtSize chrtSz, const bool prntLabel); // Draw value axis of chart, value and lines
|
||||||
void prntCurrValue(const char chrtDir, GwApi::BoatValue& currValue); // Add current boat data value to chart
|
void prntCurrValue(const ChrtDir chrtDir, GwApi::BoatValue& currValue); // Add current boat data value to chart
|
||||||
void prntNoValidData(const char chrtDir); // print message for no valid data available
|
void prntNoValidData(const ChrtDir chrtDir); // print message for no valid data available
|
||||||
double getAngleRng(const double center, size_t amount); // Calculate range between chart center and edges
|
double getCircularRng(const double center, size_t amount); // Calculate range between chart center and edges
|
||||||
void prntVerticChartThreeValueAxisLabel(const GFXfont* font); // print value axis label with only three values: top, mid, and bottom for vertical chart
|
void prntVerticChartThreeValueAxisLabel(const GFXfont* font); // print value axis label with only three values: top, mid, and bottom for vertical chart
|
||||||
void prntHorizChartThreeValueAxisLabel(const GFXfont* font); // print value axis label with only three values: top, mid, and bottom for horizontal chart
|
void prntHorizChartThreeValueAxisLabel(const GFXfont* font); // print value axis label with only three values: top, mid, and bottom for horizontal chart
|
||||||
void prntHorizChartMultiValueAxisLabel(const GFXfont* font); // print value axis label with multiple axis lines for horizontal chart
|
void prntHorizChartMultiValueAxisLabel(const GFXfont* font); // print value axis label with multiple axis lines for horizontal chart
|
||||||
void drawBoldLine(const int16_t x1, const int16_t y1, const int16_t x2, const int16_t y2); // Draw chart line with thickness of 2px
|
void drawBoldLine(const int16_t x1, const int16_t y1, const int16_t x2, const int16_t y2); // Draw chart line with thickness of 2px
|
||||||
String convNformatLabel(const double& label); // Convert and format current axis label to user defined format; helper function for easier handling of OBP60Formatter
|
String convNformatLabel(const double& label); // Convert and format current axis label to user defined format; helper function for easier handling of OBP60Formatter
|
||||||
String formatLabel(const double& label); // Format current axis label for printing w/o data format conversion (has been done earlier)
|
String formatLabel(const double& label); // Format current axis label for printing w/o data format conversion (has been done earlier)
|
||||||
|
|
||||||
public:
|
|
||||||
// Define default chart range and range step for each boat data type
|
|
||||||
static std::map<String, ChartProps> dfltChrtDta;
|
|
||||||
|
|
||||||
Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common, bool useSimuData); // Chart object of data chart
|
|
||||||
~Chart();
|
|
||||||
void showChrt(char chrtDir, int8_t chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue); // Perform all actions to draw chart
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,771 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
|
|
||||||
|
|
||||||
/*
|
|
||||||
This page is in experimental stage so be warned!
|
|
||||||
North is up.
|
|
||||||
Anchor page with background map from mapservice
|
|
||||||
|
|
||||||
Boatdata used
|
|
||||||
DBS - Water depth
|
|
||||||
HDT - Boat heading, true
|
|
||||||
AWS - Wind strength; Boat not moving so we assume AWS=TWS and AWD=TWD
|
|
||||||
AWD - Wind direction
|
|
||||||
LAT/LON - Boat position, current
|
|
||||||
HDOP - Position error, horizontal
|
|
||||||
|
|
||||||
Raise function in device OBP40 has to be done inside config mode
|
|
||||||
because of limited number of buttons.
|
|
||||||
|
|
||||||
TODO
|
|
||||||
gzip for data transfer,
|
|
||||||
miniz.c from ROM?
|
|
||||||
manually inflating with tinflate from ROM
|
|
||||||
Save position in FRAM
|
|
||||||
Alarm: gps fix lost
|
|
||||||
switch unit feet/meter
|
|
||||||
force map update if new position is different from old position by
|
|
||||||
a certain level (e.g. 10m)
|
|
||||||
windlass integration
|
|
||||||
chain counter
|
|
||||||
|
|
||||||
Map service options / URL parameters
|
|
||||||
- mandatory
|
|
||||||
lat: latitude
|
|
||||||
lon: longitude
|
|
||||||
width: image width in px (400)
|
|
||||||
height: image height in px (300)
|
|
||||||
- optional
|
|
||||||
zoom: zoom level, default 15
|
|
||||||
mrot: map rotation angle in degrees
|
|
||||||
mtype: map type, default="Open Street Map"
|
|
||||||
dtype: dithering type, default="Atkinson"
|
|
||||||
cutout: image cutout type 0=none
|
|
||||||
alpha: alpha blending for cutout
|
|
||||||
tab: tab size, 0=none
|
|
||||||
border: border line zize in px, default 2
|
|
||||||
symbol: synmol number, default=2 triangle
|
|
||||||
srot: symbol rotation in degrees
|
|
||||||
ssize: symbol size in px, default=15
|
|
||||||
grid: show map grid
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
#include "Pagedata.h"
|
|
||||||
#include "OBP60Extensions.h"
|
|
||||||
#include "ConfigMenu.h"
|
|
||||||
// #include "miniz.h" // devices without PSRAM use <rom/miniz.h>
|
|
||||||
|
|
||||||
// extern "C" {
|
|
||||||
#include "rom/miniz.h"
|
|
||||||
// }
|
|
||||||
|
|
||||||
#define anchor_width 16
|
|
||||||
#define anchor_height 16
|
|
||||||
static unsigned char anchor_bits[] PROGMEM = {
|
|
||||||
0x80, 0x01, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xf0, 0x0f, 0x80, 0x01,
|
|
||||||
0x80, 0x01, 0x88, 0x11, 0x8c, 0x31, 0x8e, 0x71, 0x84, 0x21, 0x86, 0x61,
|
|
||||||
0x86, 0x61, 0xfc, 0x3f, 0xf8, 0x1f, 0x80, 0x01 };
|
|
||||||
|
|
||||||
class PageAnchor : public Page
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
char mode = 'N'; // (N)ormal, (C)onfig
|
|
||||||
int8_t editmode = -1; // marker for menu/edit/set function
|
|
||||||
ConfigMenu *menu;
|
|
||||||
|
|
||||||
//uint8_t *mapbuf = new uint8_t[10000]; // 8450 Byte without header
|
|
||||||
//int mapbuf_size = 10000;
|
|
||||||
//uint8_t *mapbuf = (uint8_t*) heap_caps_malloc(mapbuf_size, MALLOC_CAP_SPIRAM);
|
|
||||||
GFXcanvas1 *canvas;
|
|
||||||
const uint16_t map_width = 264;
|
|
||||||
const uint16_t map_height = 260;
|
|
||||||
bool map_valid = false;
|
|
||||||
char map_service = 'R'; // (O)BP Service, (L)ocal Service, (R)emote Service
|
|
||||||
double map_lat = 0; // current center of valid map
|
|
||||||
double map_lon = 0;
|
|
||||||
String server_name; // server with map service
|
|
||||||
uint16_t server_port = 80;
|
|
||||||
String tile_path;
|
|
||||||
|
|
||||||
String lengthformat;
|
|
||||||
|
|
||||||
double scale = 50; // Radius of display circle in meter, depends on lat
|
|
||||||
uint8_t zoom = 15; // map zoom level
|
|
||||||
|
|
||||||
bool alarm = false;
|
|
||||||
bool alarm_enabled = false;
|
|
||||||
uint8_t alarm_range;
|
|
||||||
|
|
||||||
uint8_t chain_length;
|
|
||||||
uint8_t chain = 0;
|
|
||||||
|
|
||||||
bool anchor_set = false;
|
|
||||||
double anchor_lat;
|
|
||||||
double anchor_lon;
|
|
||||||
double anchor_depth;
|
|
||||||
int anchor_ts; // time stamp anchor dropped
|
|
||||||
|
|
||||||
GwApi::BoatValue *bv_dbs; // depth below surface
|
|
||||||
GwApi::BoatValue *bv_hdt; // true heading
|
|
||||||
GwApi::BoatValue *bv_aws; // apparent wind speed
|
|
||||||
GwApi::BoatValue *bv_awd; // apparent wind direction
|
|
||||||
GwApi::BoatValue *bv_lat; // latitude, current
|
|
||||||
GwApi::BoatValue *bv_lon; // longitude, current
|
|
||||||
GwApi::BoatValue *bv_hdop; // horizontal position error
|
|
||||||
|
|
||||||
bool simulation = false;
|
|
||||||
int last_mapsize = 0;
|
|
||||||
String errmsg = "";
|
|
||||||
int loops;
|
|
||||||
int readbytes = 0;
|
|
||||||
|
|
||||||
void displayModeNormal(PageData &pageData) {
|
|
||||||
|
|
||||||
// get currrent boatvalues
|
|
||||||
bv_dbs = pageData.values[0]; // DBS
|
|
||||||
String sval_dbs = formatValue(bv_dbs, *commonData).svalue;
|
|
||||||
String sunit_dbs = formatValue(bv_dbs, *commonData).unit;
|
|
||||||
bv_hdt = pageData.values[1]; // HDT
|
|
||||||
String sval_hdt = formatValue(bv_hdt, *commonData).svalue;
|
|
||||||
bv_aws = pageData.values[2]; // AWS
|
|
||||||
String sval_aws = formatValue(bv_aws, *commonData).svalue;
|
|
||||||
String sunit_aws = formatValue(bv_aws, *commonData).unit;
|
|
||||||
bv_awd = pageData.values[3]; // AWD
|
|
||||||
String sval_awd = formatValue(bv_awd, *commonData).svalue;
|
|
||||||
bv_lat = pageData.values[4]; // LAT
|
|
||||||
String sval_lat = formatValue(bv_lat, *commonData).svalue;
|
|
||||||
bv_lon = pageData.values[5]; // LON
|
|
||||||
String sval_lon = formatValue(bv_lon, *commonData).svalue;
|
|
||||||
bv_hdop = pageData.values[6]; // HDOP
|
|
||||||
String sval_hdop = formatValue(bv_hdop, *commonData).svalue;
|
|
||||||
String sunit_hdop = formatValue(bv_hdop, *commonData).unit;
|
|
||||||
|
|
||||||
commonData->logger->logDebug(GwLog::DEBUG, "Drawing at PageAnchor; DBS=%f, HDT=%f, AWS=%f", bv_dbs->value, bv_hdt->value, bv_aws->value);
|
|
||||||
|
|
||||||
// Draw canvas with background map
|
|
||||||
// rhumb(map_lat, map_lon, bv_lat->value, bv_lon->value)
|
|
||||||
int posdiff = 0;
|
|
||||||
if (map_valid) {
|
|
||||||
if (bv_lat->valid and bv_lon->valid) {
|
|
||||||
// calculate movement since last map refresh
|
|
||||||
posdiff = rhumb(map_lat, map_lon, bv_lat->value, bv_lon->value);
|
|
||||||
if (posdiff > 25) {
|
|
||||||
map_lat = bv_lat->value;
|
|
||||||
map_lon = bv_lon->value;
|
|
||||||
map_valid = getBackgroundMap(map_lat, map_lon, zoom);
|
|
||||||
if (map_valid) {
|
|
||||||
// prepare visible space for anchor-symbol or boat
|
|
||||||
canvas->fillCircle(132, 130, 12, commonData->fgcolor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getdisplay().drawBitmap(68, 20, canvas->getBuffer(), map_width, map_height, commonData->fgcolor);
|
|
||||||
}
|
|
||||||
|
|
||||||
Point c = {200, 150}; // center = anchor position
|
|
||||||
uint16_t r = 125;
|
|
||||||
|
|
||||||
// Circle as map border
|
|
||||||
getdisplay().drawCircle(c.x, c.y, r, commonData->fgcolor);
|
|
||||||
getdisplay().drawCircle(c.x, c.y, r + 1, commonData->fgcolor);
|
|
||||||
|
|
||||||
Point b = {200, 180}; // boat position while dropping anchor
|
|
||||||
|
|
||||||
const std::vector<Point> pts_boat = { // polygon lines
|
|
||||||
{b.x - 5, b.y},
|
|
||||||
{b.x - 5, b.y - 10},
|
|
||||||
{b.x, b.y - 16},
|
|
||||||
{b.x + 5, b.y - 10},
|
|
||||||
{b.x + 5, b.y}
|
|
||||||
};
|
|
||||||
//rotatePoints then draw lines
|
|
||||||
// TODO rotate boat according to current heading
|
|
||||||
if (bv_hdt->valid) {
|
|
||||||
if (map_valid) {
|
|
||||||
Point b1 = rotatePoint(c, {b.x, b.y - 8}, bv_hdt->value * RAD_TO_DEG);
|
|
||||||
getdisplay().fillCircle(b1.x, b1.y, 10, commonData->bgcolor);
|
|
||||||
}
|
|
||||||
drawPoly(rotatePoints(c, pts_boat, bv_hdt->value * RAD_TO_DEG), commonData->fgcolor);
|
|
||||||
} else {
|
|
||||||
// no heading available: draw north oriented
|
|
||||||
if (map_valid) {
|
|
||||||
getdisplay().fillCircle(b.x, b.y - 8, 10, commonData->bgcolor);
|
|
||||||
}
|
|
||||||
drawPoly(pts_boat, commonData->fgcolor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw wind arrow
|
|
||||||
const std::vector<Point> pts_wind = {
|
|
||||||
{c.x, c.y - r + 25},
|
|
||||||
{c.x - 12, c.y - r - 4},
|
|
||||||
{c.x, c.y - r + 6},
|
|
||||||
{c.x + 12, c.y - r - 4}
|
|
||||||
};
|
|
||||||
if (bv_awd->valid) {
|
|
||||||
fillPoly4(rotatePoints(c, pts_wind, bv_awd->value), commonData->fgcolor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Title and corner value headings
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
|
||||||
// Left
|
|
||||||
getdisplay().setCursor(8, 36);
|
|
||||||
getdisplay().print("Anchor");
|
|
||||||
getdisplay().setCursor(8, 210);
|
|
||||||
getdisplay().print("Depth");
|
|
||||||
// Right
|
|
||||||
drawTextRalign(392, 80, "Chain");
|
|
||||||
drawTextRalign(392, 210, "Wind");
|
|
||||||
|
|
||||||
// Units
|
|
||||||
getdisplay().setCursor(8, 272);
|
|
||||||
getdisplay().print(sunit_dbs);
|
|
||||||
drawTextRalign(392, 272, sunit_aws);
|
|
||||||
// drawTextRalign(392, 100, lengthformat); // chain unit not implemented
|
|
||||||
|
|
||||||
// Corner values
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
|
||||||
getdisplay().setCursor(8, 54);
|
|
||||||
getdisplay().print(anchor_set ? "Dropped" : "Ready"); // Anchor state
|
|
||||||
getdisplay().setCursor(8, 72);
|
|
||||||
getdisplay().print("Alarm: "); // Alarm state
|
|
||||||
getdisplay().print(alarm_enabled ? "on" : "off");
|
|
||||||
|
|
||||||
getdisplay().setCursor(8, 120);
|
|
||||||
getdisplay().print("Zoom");
|
|
||||||
getdisplay().setCursor(8, 136);
|
|
||||||
getdisplay().print(zoom);
|
|
||||||
|
|
||||||
getdisplay().setCursor(8, 160);
|
|
||||||
getdisplay().print("diff");
|
|
||||||
getdisplay().setCursor(8, 176);
|
|
||||||
if (map_valid and bv_lat->valid and bv_lon->valid) {
|
|
||||||
getdisplay().print(String(posdiff));
|
|
||||||
} else {
|
|
||||||
getdisplay().print("n/a");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chain out TODO lengthformat ft/m
|
|
||||||
drawTextRalign(392, 96, String(chain) + " m");
|
|
||||||
drawTextRalign(392, 96+16, "of " + String(chain_length) + " m");
|
|
||||||
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
|
||||||
|
|
||||||
// Depth
|
|
||||||
getdisplay().setCursor(8, 250);
|
|
||||||
getdisplay().print(sval_dbs);
|
|
||||||
|
|
||||||
// Wind
|
|
||||||
getdisplay().setCursor(320, 250);
|
|
||||||
getdisplay().print(sval_aws);
|
|
||||||
|
|
||||||
// Position of boat in center of map
|
|
||||||
getdisplay().setFont(&IBM8x8px);
|
|
||||||
drawTextRalign(392, 34, sval_lat);
|
|
||||||
drawTextRalign(392, 44, sval_lon);
|
|
||||||
// quality
|
|
||||||
String hdop = "HDOP: ";
|
|
||||||
if (bv_hdop->valid) {
|
|
||||||
hdop += String(round(bv_hdop->value));
|
|
||||||
} else {
|
|
||||||
hdop += " n/a";
|
|
||||||
}
|
|
||||||
drawTextRalign(392, 54, hdop);
|
|
||||||
|
|
||||||
// zoom scale
|
|
||||||
getdisplay().drawLine(c.x + 10, c.y, c.x + r - 4, c.y, commonData->fgcolor);
|
|
||||||
// arrow left
|
|
||||||
getdisplay().drawLine(c.x + 10, c.y, c.x + 16, c.y - 4, commonData->fgcolor);
|
|
||||||
getdisplay().drawLine(c.x + 10, c.y, c.x + 16, c.y + 4, commonData->fgcolor);
|
|
||||||
// arrow right
|
|
||||||
getdisplay().drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y - 4, commonData->fgcolor);
|
|
||||||
getdisplay().drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y + 4, commonData->fgcolor);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
|
||||||
drawTextCenter(c.x + r / 2, c.y + 8, String(scale, 0) + "m");
|
|
||||||
|
|
||||||
// draw anchor symbol (as bitmap)
|
|
||||||
getdisplay().drawXBitmap(c.x - anchor_width / 2, c.y - anchor_height / 2,
|
|
||||||
anchor_bits, anchor_width, anchor_height, commonData->fgcolor);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayModeConfig(PageData &pageData) {
|
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
|
||||||
getdisplay().setCursor(8, 48);
|
|
||||||
getdisplay().print("Anchor configuration");
|
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
|
||||||
|
|
||||||
getdisplay().setCursor(8, 260);
|
|
||||||
getdisplay().print("Press BACK to leave config");
|
|
||||||
|
|
||||||
/* getdisplay().setCursor(8, 68);
|
|
||||||
getdisplay().printf("Server: %s", server_name.c_str());
|
|
||||||
getdisplay().setCursor(8, 88);
|
|
||||||
getdisplay().printf("Port: %d", server_port);
|
|
||||||
getdisplay().setCursor(8, 108);
|
|
||||||
getdisplay().printf("Tilepath: %s", tile_path.c_str());
|
|
||||||
getdisplay().setCursor(8, 128);
|
|
||||||
getdisplay().printf("Last mapsize: %d", last_mapsize);
|
|
||||||
getdisplay().setCursor(8, 148);
|
|
||||||
getdisplay().printf("Last error: %s", errmsg);
|
|
||||||
getdisplay().setCursor(8, 168);
|
|
||||||
getdisplay().printf("Loops: %d, Readbytes: %d", loops, readbytes);
|
|
||||||
*/
|
|
||||||
|
|
||||||
GwApi::BoatValue *bv_lat = pageData.values[4]; // LAT
|
|
||||||
GwApi::BoatValue *bv_lon = pageData.values[5]; // LON
|
|
||||||
if (!bv_lat->valid or !bv_lon->valid) {
|
|
||||||
getdisplay().setCursor(8, 228);
|
|
||||||
getdisplay().printf("No valid position: background map disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display menu
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
|
||||||
for (int i = 0 ; i < menu->getItemCount(); i++) {
|
|
||||||
ConfigMenuItem *itm = menu->getItemByIndex(i);
|
|
||||||
if (!itm) {
|
|
||||||
commonData->logger->logDebug(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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
PageAnchor(CommonData &common)
|
|
||||||
{
|
|
||||||
commonData = &common;
|
|
||||||
common.logger->logDebug(GwLog::LOG,"Instantiate PageAnchor");
|
|
||||||
|
|
||||||
String mapsource = common.config->getString(common.config->mapsource);
|
|
||||||
if (mapsource == "Local Service") {
|
|
||||||
map_service = 'L';
|
|
||||||
server_name = common.config->getString(common.config->ipAddress);
|
|
||||||
server_port = common.config->getInt(common.config->localPort);
|
|
||||||
tile_path = "";
|
|
||||||
} else if (mapsource == "Remote Service") {
|
|
||||||
map_service = 'R';
|
|
||||||
server_name = common.config->getString(common.config->mapServer);
|
|
||||||
tile_path = common.config->getString(common.config->mapTilePath);
|
|
||||||
} else { // OBP Service or undefined
|
|
||||||
map_service = 'O';
|
|
||||||
server_name = "norbert-walter.dnshome.de";
|
|
||||||
tile_path = "";
|
|
||||||
}
|
|
||||||
zoom = common.config->getInt(common.config->zoomlevel);
|
|
||||||
|
|
||||||
lengthformat = common.config->getString(common.config->lengthFormat);
|
|
||||||
chain_length = common.config->getInt(common.config->chainLength);
|
|
||||||
|
|
||||||
if (simulation) {
|
|
||||||
map_lat = 53.56938345759218;
|
|
||||||
map_lon = 9.679658234303275;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas = new GFXcanvas1(264, 260); // Byte aligned, no padding!
|
|
||||||
|
|
||||||
// Initialize config menu
|
|
||||||
menu = new ConfigMenu("Options", 40, 80);
|
|
||||||
menu->setItemDimension(150, 20);
|
|
||||||
ConfigMenuItem *newitem;
|
|
||||||
newitem = menu->addItem("chain", "Chain out", "int", 0, "m");
|
|
||||||
newitem->setRange(0, 200, {1, 2, 5, 10});
|
|
||||||
newitem = menu->addItem("alarm", "Alarm", "bool", 0, "");
|
|
||||||
newitem = menu->addItem("alarm", "Alarm range", "int", 50, "m");
|
|
||||||
newitem->setRange(0, 200, {1, 2, 5, 10});
|
|
||||||
newitem = menu->addItem("raise", "Raise Anchor", "bool", 0, "");
|
|
||||||
newitem = menu->addItem("zoom", "Zoom", "int", 15, "");
|
|
||||||
newitem->setRange(14, 17, {1});
|
|
||||||
menu->setItemActive("chain");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupKeys(){
|
|
||||||
Page::setupKeys();
|
|
||||||
commonData->keydata[0].label = "CFG";
|
|
||||||
#ifdef BOARD_OBP40S3
|
|
||||||
commonData->keydata[1].label = "DROP";
|
|
||||||
#endif
|
|
||||||
#ifdef BOARD_OBP60S3
|
|
||||||
commonData->keydata[4].label = "DROP";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO OBP40 / OBP60 different handling
|
|
||||||
int handleKey(int key) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Page Anchor handle key %d", key);
|
|
||||||
if (key == 1) { // Switch between normal and config mode
|
|
||||||
if (mode == 'N') {
|
|
||||||
mode = 'C';
|
|
||||||
#ifdef BOARD_OBP40S3
|
|
||||||
commonData->keydata[0].label = "BACK";
|
|
||||||
commonData->keydata[1].label = "EDIT";
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
mode = 'N';
|
|
||||||
#ifdef BOARD_OBP40S3
|
|
||||||
commonData->keydata[0].label = "CFG";
|
|
||||||
commonData->keydata[1].label = anchor_set ? "RAISE": "DROP";
|
|
||||||
#endif
|
|
||||||
#ifdef BOARD_OBP60S3
|
|
||||||
commonData->keydata[4].label = anchor_set ? "RAISE": "DROP";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (key == 2) {
|
|
||||||
if (mode == 'N') {
|
|
||||||
anchor_set = !anchor_set;
|
|
||||||
commonData->keydata[1].label = anchor_set ? "ALARM": "DROP";
|
|
||||||
if (anchor_set) {
|
|
||||||
anchor_lat = bv_lat->value;
|
|
||||||
anchor_lon = bv_lon->value;
|
|
||||||
anchor_depth = bv_dbs->value;
|
|
||||||
// TODO set timestamp
|
|
||||||
// anchor_ts =
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
} else if (mode == 'C') {
|
|
||||||
// Change edit mode
|
|
||||||
if (editmode > 0) {
|
|
||||||
editmode = 0;
|
|
||||||
commonData->keydata[1].label = "EDIT";
|
|
||||||
} else {
|
|
||||||
editmode = 1;
|
|
||||||
commonData->keydata[1].label = "OK";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (key == 9) {
|
|
||||||
// OBP40 Down
|
|
||||||
if (mode == 'C') {
|
|
||||||
if (editmode > 0) {
|
|
||||||
// decrease current menu item
|
|
||||||
menu->getActiveItem()->decValue();
|
|
||||||
} else {
|
|
||||||
// move to next menu item
|
|
||||||
menu->goNext();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (key == 10) {
|
|
||||||
// OBP40 Up
|
|
||||||
if (mode == 'C') {
|
|
||||||
if (editmode > 0) {
|
|
||||||
// increase current menu item
|
|
||||||
ConfigMenuItem *itm = menu->getActiveItem();
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "step = %d", itm->getStep());
|
|
||||||
itm->incValue();
|
|
||||||
} else {
|
|
||||||
// move to previous menu item
|
|
||||||
menu->goPrev();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Code for keylock
|
|
||||||
if (key == 11) {
|
|
||||||
commonData->keylock = !commonData->keylock;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rhumb(double lat1, double lon1, double lat2, double lon2) {
|
|
||||||
// calc distance in m between two geo points
|
|
||||||
static const double degToRad = M_PI / 180.0;
|
|
||||||
lat1 = degToRad * lat1;
|
|
||||||
lon1 = degToRad * lon1;
|
|
||||||
lat2 = degToRad * lat2;
|
|
||||||
lon2 = degToRad * lon2;
|
|
||||||
double dlon = lon2 - lon1;
|
|
||||||
double dlat = lat2 - lat1;
|
|
||||||
double mlat = (lat1 + lat2) / 2;
|
|
||||||
return (int) (6371000 * sqrt(pow(dlat, 2) + pow(cos(mlat) * dlon, 2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getBackgroundMap(double lat, double lon, uint8_t zoom) {
|
|
||||||
// HTTP-Request for map
|
|
||||||
// TODO über pagedata -> status abfragen?
|
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool valid = false;
|
|
||||||
HTTPClient http;
|
|
||||||
const char* headerKeys[] = { "Content-Encoding", "Content-Length" };
|
|
||||||
http.collectHeaders(headerKeys, 2);
|
|
||||||
String url = "http://" + server_name + "/" + tile_path;
|
|
||||||
String parameter = "?lat=" + String(lat, 6) + "&lon=" + String(lon, 6)+ "&zoom=" + String(zoom)
|
|
||||||
+ "&width=" + String(map_width) + "&height=" + String(map_height);
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "HTTP query: %s", String(url + parameter).c_str());
|
|
||||||
http.begin(url + parameter);
|
|
||||||
http.addHeader("Accept-Encoding", "deflate");
|
|
||||||
int httpCode = http.GET();
|
|
||||||
if (httpCode > 0) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "HTTP GET result code: %d", httpCode);
|
|
||||||
if (httpCode == HTTP_CODE_OK) {
|
|
||||||
WiFiClient* stream = http.getStreamPtr();
|
|
||||||
int size = http.getSize();
|
|
||||||
String encoding = http.header("Content-Encoding");
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "HTTP size: %d, encoding: '%s'", size, encoding);
|
|
||||||
bool is_gzip = encoding.equalsIgnoreCase("deflate");
|
|
||||||
|
|
||||||
uint8_t header[14]; // max: P4<LF>wwww wwww<LF>
|
|
||||||
int header_size = 0;
|
|
||||||
bool header_read = false;
|
|
||||||
int n = 0;
|
|
||||||
int ix = 0;
|
|
||||||
|
|
||||||
uint8_t* buf = canvas->getBuffer();
|
|
||||||
|
|
||||||
if (is_gzip) {
|
|
||||||
/* gzip compressed data
|
|
||||||
* has to be decompressed into a buffer big enough
|
|
||||||
* to hold the whole data.
|
|
||||||
* so the PBM header is included
|
|
||||||
* search a method to use that as canvas without
|
|
||||||
* additional copy
|
|
||||||
*/
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Map received in gzip encoding");
|
|
||||||
|
|
||||||
#define HEADER_MAX 24
|
|
||||||
#define HTTP_CHUNK 512
|
|
||||||
uint8_t in_buf[HTTP_CHUNK];
|
|
||||||
uint8_t header_buf[HEADER_MAX];
|
|
||||||
tinfl_decompressor decomp;
|
|
||||||
tinfl_init(&decomp);
|
|
||||||
size_t bitmap_written = 0;
|
|
||||||
size_t header_written = 0;
|
|
||||||
bool header_done = false;
|
|
||||||
int row_bytes = 0;
|
|
||||||
size_t expected_bitmap = 0;
|
|
||||||
|
|
||||||
while (stream->connected() || stream->available()) {
|
|
||||||
int bytes_read = stream->read(in_buf, HTTP_CHUNK);
|
|
||||||
if (bytes_read <= 0) break;
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "stream: bytes_read=%d", bytes_read);
|
|
||||||
size_t in_ofs = 0; // offset
|
|
||||||
while (in_ofs < (size_t)bytes_read) {
|
|
||||||
size_t in_size = bytes_read - in_ofs;
|
|
||||||
size_t out_size;
|
|
||||||
uint8_t *out_ptr;
|
|
||||||
uint8_t *out_ptr_next;
|
|
||||||
if (!header_done) {
|
|
||||||
if (header_written >= HEADER_MAX) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "PBM header too large");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ptr = header_buf + header_written;
|
|
||||||
out_size = HEADER_MAX - header_written;
|
|
||||||
} else {
|
|
||||||
out_ptr = buf + bitmap_written;
|
|
||||||
out_size = expected_bitmap - bitmap_written;
|
|
||||||
}
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "in_size=%d, out_size=%d", in_size, out_size);
|
|
||||||
// TODO correct loop !!!
|
|
||||||
// tinfl_status tinfl_decompress(
|
|
||||||
// tinfl_decompressor *r,
|
|
||||||
// const mz_uint8 *pIn_buf_next,
|
|
||||||
// size_t *pIn_buf_size,
|
|
||||||
// mz_uint8 *pOut_buf_start
|
|
||||||
// mz_uint8 *pOut_buf_next,
|
|
||||||
// size_t *pOut_buf_size,
|
|
||||||
// const mz_uint32 decomp_flags)
|
|
||||||
tinfl_status status = tinfl_decompress(
|
|
||||||
&decomp,
|
|
||||||
in_buf + in_ofs, // start address in input buffer
|
|
||||||
&in_size, // number of bytes to process
|
|
||||||
out_ptr, // start of output buffer
|
|
||||||
out_ptr, // next write position in output buffer
|
|
||||||
&out_size, // free size in output buffer
|
|
||||||
// TINFL_FLAG_PARSE_ZLIB_HEADER |
|
|
||||||
TINFL_FLAG_HAS_MORE_INPUT |
|
|
||||||
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF
|
|
||||||
);
|
|
||||||
if (status < 0) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Decompression error (%d)", status);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
in_ofs += in_size;
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "in_size=%d, in_ofs=%d", in_size, in_ofs);
|
|
||||||
|
|
||||||
if (!header_done) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Decoding header");
|
|
||||||
header_written += out_size;
|
|
||||||
|
|
||||||
// Detect header end: two '\n'
|
|
||||||
char *first_nl = strchr((char*)header_buf, '\n');
|
|
||||||
if (!first_nl) continue;
|
|
||||||
|
|
||||||
char *second_nl = strchr(first_nl + 1, '\n');
|
|
||||||
if (!second_nl) continue;
|
|
||||||
|
|
||||||
// Null-terminate header for sscanf
|
|
||||||
header_buf[header_written < HEADER_MAX ? header_written : HEADER_MAX - 1] = 0;
|
|
||||||
|
|
||||||
// Check magic
|
|
||||||
if (strncmp((char*)header_buf, "P4", 2) != 0) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Invalid PBM magic");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse width and height strictly
|
|
||||||
int header_width = 0, header_height = 0;
|
|
||||||
if (sscanf((char*)header_buf, "P4\n%d %d", &header_width, &header_height) != 2) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Failed to parse PBM dimensions");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header_width != map_width || header_height != map_height) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "PBM size mismatch: header %dx%d, requested %dx%d\n",
|
|
||||||
header_width, header_height, map_width, map_height);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Header: %dx%d", header_width, header_height);
|
|
||||||
|
|
||||||
// Compute row bytes and expected bitmap size
|
|
||||||
row_bytes = (header_width + 7) / 8;
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "row_bytes=%d", row_bytes);
|
|
||||||
expected_bitmap = (size_t)row_bytes * header_height;
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "expected_bitmap=%d", expected_bitmap);
|
|
||||||
|
|
||||||
// Copy any extra decompressed bitmap after header
|
|
||||||
size_t header_size = (second_nl + 1) - (char*)header_buf;
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "header_size=%d", header_size);
|
|
||||||
size_t extra_bitmap = header_written - header_size;
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "extra bitmap=%d", extra_bitmap);
|
|
||||||
|
|
||||||
header_done = true;
|
|
||||||
|
|
||||||
if (extra_bitmap > 0) {
|
|
||||||
memcpy(buf, header_buf + header_size, extra_bitmap);
|
|
||||||
bitmap_written = extra_bitmap;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bitmap_written += out_size;
|
|
||||||
if (bitmap_written >= expected_bitmap) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Image fully received");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "bitmap_written=%d", bitmap_written);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// uncompressed data
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Map received uncompressed");
|
|
||||||
while (stream->available()) {
|
|
||||||
uint8_t b = stream->read();
|
|
||||||
n += 1;
|
|
||||||
if ((! header_read) and (n < 13) ) {
|
|
||||||
header[n-1] = b;
|
|
||||||
if ((n > 3) and (b == 0x0a)) {
|
|
||||||
header_read = true;
|
|
||||||
header_size = n;
|
|
||||||
header[n] = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// write image data to canvas buffer
|
|
||||||
buf[ix++] = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (n == size) {
|
|
||||||
valid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "HTTP: final bytesRead=%d, header-size=%d", n, header_size);
|
|
||||||
} else {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "HTTP result #%d", httpCode);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
commonData->logger->logDebug(GwLog::ERROR, "HTTP error #%d", httpCode);
|
|
||||||
}
|
|
||||||
http.end();
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayNew(PageData &pageData){
|
|
||||||
|
|
||||||
GwApi::BoatValue *bv_lat = pageData.values[4]; // LAT
|
|
||||||
GwApi::BoatValue *bv_lon = pageData.values[5]; // LON
|
|
||||||
|
|
||||||
// check if valid data available
|
|
||||||
if (!bv_lat->valid or !bv_lon->valid) {
|
|
||||||
map_valid = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
errmsg = "";
|
|
||||||
|
|
||||||
map_lat = bv_lat->value; // save for later comparison
|
|
||||||
map_lon = bv_lon->value;
|
|
||||||
map_valid = getBackgroundMap(map_lat, map_lon, zoom);
|
|
||||||
|
|
||||||
if (map_valid) {
|
|
||||||
// prepare visible space for anchor-symbol or boat
|
|
||||||
canvas->fillCircle(132, 130, 10, commonData->fgcolor);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void display_side_keys() {
|
|
||||||
// An rechter Seite neben dem Rad inc, dec, set etc ?
|
|
||||||
}
|
|
||||||
|
|
||||||
int displayPage(PageData &pageData) {
|
|
||||||
|
|
||||||
// Logging boat values
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "Drawing at PageAnchor; Mode=%c", mode);
|
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
|
||||||
|
|
||||||
if (mode == 'N') {
|
|
||||||
displayModeNormal(pageData);
|
|
||||||
} else if (mode == 'C') {
|
|
||||||
displayModeConfig(pageData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
static Page *createPage(CommonData &common){
|
|
||||||
return new PageAnchor(common);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* with the code below we make this page known to the PageTask
|
|
||||||
* we give it a type (name) that can be selected in the config
|
|
||||||
* we define which function is to be called
|
|
||||||
* and we provide the number of user parameters we expect
|
|
||||||
* this will be number of BoatValue pointers in pageData.values
|
|
||||||
*/
|
|
||||||
PageDescription registerPageAnchor(
|
|
||||||
"Anchor", // Page name
|
|
||||||
createPage, // Action
|
|
||||||
0, // Number of bus values depends on selection in Web configuration
|
|
||||||
{"DBS", "HDT", "AWS", "AWD", "LAT", "LON", "HDOP"}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
|
|
||||||
true // Show display header on/off
|
|
||||||
);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -5,8 +5,9 @@
|
|||||||
|
|
||||||
// These constants have to match the declaration below in :
|
// These constants have to match the declaration below in :
|
||||||
// PageDescription registerPageAutopilot(
|
// PageDescription registerPageAutopilot(
|
||||||
// {"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW"}, // Bus values we need in the page
|
// {"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW", "RPOS", "ROT"}, // Bus values we need in the page
|
||||||
const int HowManyValues = 9;
|
|
||||||
|
const int HowManyValues = 11;
|
||||||
|
|
||||||
const int AverageValues = 4;
|
const int AverageValues = 4;
|
||||||
|
|
||||||
@@ -19,14 +20,21 @@ const int ShowDBT = 5;
|
|||||||
const int ShowXTE = 6;
|
const int ShowXTE = 6;
|
||||||
const int ShowDTW = 7;
|
const int ShowDTW = 7;
|
||||||
const int ShowBTW = 8;
|
const int ShowBTW = 8;
|
||||||
|
const int ShowRPOS = 9;
|
||||||
|
const int ShowROT = 10;
|
||||||
|
|
||||||
const int Compass_X0 = 200; // X center point of compass band
|
const int Compass_X0 = 200; // X center point of compass band
|
||||||
const int Compass_Y0 = 220; // Y position of compass lines
|
const int Compass_Y0 = 90; // Y position of compass lines
|
||||||
const int Compass_LineLength = 22; // Length of compass lines
|
//const int Compass_LineLength = 22; // Length of compass lines
|
||||||
|
const int Compass_LineLength = 15; // Length of compass lines
|
||||||
const float Compass_LineDelta = 8.0;// Compass band: 1deg = 5 Pixels, 10deg = 50 Pixels
|
const float Compass_LineDelta = 8.0;// Compass band: 1deg = 5 Pixels, 10deg = 50 Pixels
|
||||||
|
|
||||||
class PageAutopilot : public Page
|
class PageAutopilot : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
int WhichDataCompass = ShowHDM; // Start value
|
int WhichDataCompass = ShowHDM; // Start value
|
||||||
int WhichDataDisplay = ShowHDM; // Start value
|
int WhichDataDisplay = ShowHDM; // Start value
|
||||||
|
|
||||||
@@ -38,8 +46,11 @@ class PageAutopilot : public Page
|
|||||||
|
|
||||||
virtual void setupKeys(){
|
virtual void setupKeys(){
|
||||||
Page::setupKeys();
|
Page::setupKeys();
|
||||||
commonData->keydata[0].label = "CMP";
|
commonData->keydata[0].label = "-10";
|
||||||
commonData->keydata[1].label = "SRC";
|
commonData->keydata[1].label = "-1";
|
||||||
|
commonData->keydata[2].label = "Auto";
|
||||||
|
commonData->keydata[3].label = "+1";
|
||||||
|
commonData->keydata[4].label = "+10";
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int handleKey(int key){
|
virtual int handleKey(int key){
|
||||||
@@ -69,8 +80,8 @@ class PageAutopilot : public Page
|
|||||||
GwLog *logger = commonData->logger;
|
GwLog *logger = commonData->logger;
|
||||||
|
|
||||||
// Old values for hold function
|
// Old values for hold function
|
||||||
static String OldDataText[HowManyValues] = {"", "", "","", "", "","", "", ""};
|
static String OldDataText[HowManyValues] = {"", "", "", "", "", "","", "", "", "", ""};
|
||||||
static String OldDataUnits[HowManyValues] = {"", "", "","", "", "","", "", ""};
|
static String OldDataUnits[HowManyValues] = {"", "", "", "", "", "","", "", "", "", ""};
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
String lengthformat = config->getString(config->lengthFormat);
|
||||||
@@ -78,6 +89,7 @@ class PageAutopilot : public Page
|
|||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
GwApi::BoatValue *bvalue;
|
GwApi::BoatValue *bvalue;
|
||||||
String DataName[HowManyValues];
|
String DataName[HowManyValues];
|
||||||
@@ -107,14 +119,12 @@ class PageAutopilot : public Page
|
|||||||
setFlashLED(false);
|
setFlashLED(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
|
|
||||||
|
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
/*
|
||||||
// Horizontal line 2 pix top & bottom
|
// Horizontal line 2 pix top & bottom
|
||||||
// Print data on top half
|
// Print data on top half
|
||||||
getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor);
|
getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor);
|
||||||
@@ -125,20 +135,18 @@ class PageAutopilot : public Page
|
|||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(10, 120);
|
getdisplay().setCursor(10, 120);
|
||||||
getdisplay().print(DataUnits[WhichDataDisplay]);
|
getdisplay().print(DataUnits[WhichDataDisplay]);
|
||||||
getdisplay().setCursor(190, 120);
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
|
|
||||||
|
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(DataText[WhichDataDisplay]); // Real value as formated string
|
printBoatValue(DataText[WhichDataDisplay], 190, 120, LEFT, 42, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(OldDataText[WhichDataDisplay]); // Old value as formated string
|
printBoatValue(OldDataText[WhichDataDisplay], 190, 120, LEFT, 42, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(DataValid[WhichDataDisplay] == true){
|
if(DataValid[WhichDataDisplay] == true){
|
||||||
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
|
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
|
||||||
OldDataUnits[WhichDataDisplay] = DataUnits[WhichDataDisplay]; // Save the old unit
|
OldDataUnits[WhichDataDisplay] = DataUnits[WhichDataDisplay]; // Save the old unit
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// Now draw compass band
|
// Now draw compass band
|
||||||
// Get the data
|
// Get the data
|
||||||
double TheAngle = DataValue[WhichDataCompass];
|
double TheAngle = DataValue[WhichDataCompass];
|
||||||
@@ -152,13 +160,13 @@ class PageAutopilot : public Page
|
|||||||
buffer[0]=0;
|
buffer[0]=0;
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().setCursor(10, Compass_Y0-60);
|
getdisplay().setCursor(10, Compass_Y0-40);
|
||||||
getdisplay().print(DataName[WhichDataCompass]); // Page name
|
getdisplay().print(DataName[WhichDataCompass]); // Page name
|
||||||
|
|
||||||
|
|
||||||
// Draw compass base line and pointer
|
// Draw compass base line and pointer
|
||||||
getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
|
getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
|
||||||
getdisplay().fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,commonData->fgcolor);
|
//getdisplay().fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,commonData->fgcolor);
|
||||||
|
getdisplay().fillTriangle(Compass_X0,Compass_Y0-30,Compass_X0-10,Compass_Y0-60,Compass_X0+10,Compass_Y0-60,commonData->fgcolor);
|
||||||
// Draw trendlines
|
// Draw trendlines
|
||||||
for ( int i = 1; i < abs(TheTrend) / 2; i++){
|
for ( int i = 1; i < abs(TheTrend) / 2; i++){
|
||||||
int x1;
|
int x1;
|
||||||
@@ -238,6 +246,8 @@ class PageAutopilot : public Page
|
|||||||
// if ( x_test > 390)
|
// if ( x_test > 390)
|
||||||
// x_test = 320;
|
// x_test = 320;
|
||||||
|
|
||||||
|
displayRudderPosition(DataValue[ShowSOG], 20, 200, 160, commonData->fgcolor, commonData->bgcolor);
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -256,7 +266,7 @@ PageDescription registerPageAutopilot(
|
|||||||
"Autopilot", // Page name
|
"Autopilot", // Page name
|
||||||
createPage, // Action
|
createPage, // Action
|
||||||
0, // Number of bus values depends on selection in Web configuration
|
0, // Number of bus values depends on selection in Web configuration
|
||||||
{"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW"}, // Bus values we need in the page
|
{"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW", "RPOS", "ROT"}, // Bus values we need in the page
|
||||||
true // Show display header on/off
|
true // Show display header on/off
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
class PageBME280 : public Page
|
class PageBME280 : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageBME280(CommonData &common){
|
PageBME280(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
@@ -37,6 +41,7 @@ class PageBME280 : public Page
|
|||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
String useenvsensor = config->getString(config->useEnvSensor);
|
String useenvsensor = config->getString(config->useEnvSensor);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
// Get sensor values #1
|
// Get sensor values #1
|
||||||
String name1 = "Temp"; // Value name
|
String name1 = "Temp"; // Value name
|
||||||
@@ -105,7 +110,7 @@ class PageBME280 : public Page
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -121,12 +126,8 @@ class PageBME280 : public Page
|
|||||||
getdisplay().setCursor(20, 90);
|
getdisplay().setCursor(20, 90);
|
||||||
getdisplay().print(unit1); // Unit
|
getdisplay().print(unit1); // Unit
|
||||||
|
|
||||||
// Switch font if format for any values
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
getdisplay().setCursor(180, 90);
|
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
getdisplay().print(svalue1); // Real value as formated string
|
printBoatValue(svalue1, 380, 90, RIGHT, 30, smallDecimals); // Real value as formated string
|
||||||
|
|
||||||
// ############### Horizontal Line ################
|
// ############### Horizontal Line ################
|
||||||
|
|
||||||
@@ -145,12 +146,8 @@ class PageBME280 : public Page
|
|||||||
getdisplay().setCursor(20, 180);
|
getdisplay().setCursor(20, 180);
|
||||||
getdisplay().print(unit2); // Unit
|
getdisplay().print(unit2); // Unit
|
||||||
|
|
||||||
// Switch font if format for any values
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
getdisplay().setCursor(180, 180);
|
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
getdisplay().print(svalue2); // Real value as formated string
|
printBoatValue(svalue2, 380, 180, RIGHT, 30, smallDecimals); // Real value as formated string
|
||||||
|
|
||||||
// ############### Horizontal Line ################
|
// ############### Horizontal Line ################
|
||||||
|
|
||||||
@@ -169,12 +166,8 @@ class PageBME280 : public Page
|
|||||||
getdisplay().setCursor(20, 270);
|
getdisplay().setCursor(20, 270);
|
||||||
getdisplay().print(unit3); // Unit
|
getdisplay().print(unit3); // Unit
|
||||||
|
|
||||||
// Switch font if format for any values
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
getdisplay().setCursor(140, 270);
|
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
getdisplay().print(svalue3); // Real value as formated string
|
printBoatValue(svalue3, 380, 270, RIGHT, 30, smallDecimals); // Real value as formated string
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
class PageBattery : public Page
|
class PageBattery : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
|
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -56,6 +60,7 @@ class PageBattery : public Page
|
|||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
String powsensor1 = config->getString(config->usePowSensor1);
|
String powsensor1 = config->getString(config->usePowSensor1);
|
||||||
bool simulation = config->getBool(config->useSimuData);
|
bool simulation = config->getBool(config->useSimuData);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
// Get voltage value
|
// Get voltage value
|
||||||
String name1 = "VBat"; // Value name
|
String name1 = "VBat"; // Value name
|
||||||
@@ -158,7 +163,7 @@ class PageBattery : public Page
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
// Show average settings
|
// Show average settings
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
@@ -218,17 +223,13 @@ class PageBattery : public Page
|
|||||||
getdisplay().setCursor(20, 90);
|
getdisplay().setCursor(20, 90);
|
||||||
getdisplay().print(unit1); // Unit
|
getdisplay().print(unit1); // Unit
|
||||||
|
|
||||||
// Show value
|
// Show bus value
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
getdisplay().setCursor(180, 90);
|
|
||||||
|
|
||||||
// Show bus data
|
|
||||||
if(String(powsensor1) != "off"){
|
if(String(powsensor1) != "off"){
|
||||||
getdisplay().print(value1,2); // Real value as formated string
|
svalue1 = formatValue(value1, String("formatXdr:U:V"), *commonData);
|
||||||
}
|
} else{
|
||||||
else{
|
svalue1 = "---";
|
||||||
getdisplay().print("---"); // No sensor data (sensor is off)
|
|
||||||
}
|
}
|
||||||
|
printBoatValue(svalue1, 380, 90, RIGHT, 30, smallDecimals); // Real value as formated string
|
||||||
|
|
||||||
// ############### Horizontal Line ################
|
// ############### Horizontal Line ################
|
||||||
|
|
||||||
@@ -247,17 +248,13 @@ class PageBattery : public Page
|
|||||||
getdisplay().setCursor(20, 180);
|
getdisplay().setCursor(20, 180);
|
||||||
getdisplay().print(unit2); // Unit
|
getdisplay().print(unit2); // Unit
|
||||||
|
|
||||||
// Show value
|
// Show bus value
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
getdisplay().setCursor(180, 180);
|
|
||||||
|
|
||||||
// Show bus data
|
|
||||||
if(String(powsensor1) != "off"){
|
if(String(powsensor1) != "off"){
|
||||||
getdisplay().print(value2,1); // Real value as formated string
|
svalue2 = formatValue(value2, String("formatXdr:U:V"), *commonData);
|
||||||
}
|
} else{
|
||||||
else{
|
svalue2 = "---";
|
||||||
getdisplay().print("---"); // No sensor data (sensor is off)
|
|
||||||
}
|
}
|
||||||
|
printBoatValue(svalue2, 380, 180, RIGHT, 30, smallDecimals); // Real value as formated string
|
||||||
|
|
||||||
// ############### Horizontal Line ################
|
// ############### Horizontal Line ################
|
||||||
|
|
||||||
@@ -276,17 +273,13 @@ class PageBattery : public Page
|
|||||||
getdisplay().setCursor(20, 270);
|
getdisplay().setCursor(20, 270);
|
||||||
getdisplay().print(unit3); // Unit
|
getdisplay().print(unit3); // Unit
|
||||||
|
|
||||||
// Show value
|
// Show bus value
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
getdisplay().setCursor(180, 270);
|
|
||||||
|
|
||||||
// Show bus data
|
|
||||||
if(String(powsensor1) != "off"){
|
if(String(powsensor1) != "off"){
|
||||||
getdisplay().print(value3,1); // Real value as formated string
|
svalue3 = formatValue(value3, String("formatXdr:U:V"), *commonData);
|
||||||
}
|
} else{
|
||||||
else{
|
svalue3 = "---";
|
||||||
getdisplay().print("---"); // No sensor data (sensor is off)
|
|
||||||
}
|
}
|
||||||
|
printBoatValue(svalue3, 380, 270, RIGHT, 30, smallDecimals); // Real value as formated string
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "movingAvg.h" // Lib for moving average building
|
|
||||||
|
|
||||||
class PageBattery2 : public Page
|
class PageBattery2 : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
bool init = false; // Marker for init done
|
bool init = false; // Marker for init done
|
||||||
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
|
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
|
||||||
bool trend = true; // Trend indicator [0|1], 0=off, 1=on
|
bool trend = true; // Trend indicator [0|1], 0=off, 1=on
|
||||||
@@ -66,6 +69,7 @@ public:
|
|||||||
String batType = config->getString(config->batteryType);
|
String batType = config->getString(config->batteryType);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
String powerSensor = config->getString(config->usePowSensor1);
|
String powerSensor = config->getString(config->usePowSensor1);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
double value1 = 0; // Battery voltage
|
double value1 = 0; // Battery voltage
|
||||||
double value2 = 0; // Battery current
|
double value2 = 0; // Battery current
|
||||||
@@ -184,7 +188,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -199,20 +203,16 @@ public:
|
|||||||
getdisplay().print(batType);
|
getdisplay().print(batType);
|
||||||
|
|
||||||
// Show voltage type
|
// Show voltage type
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
|
||||||
getdisplay().setCursor(10, 140);
|
|
||||||
int bvoltage = 0;
|
int bvoltage = 0;
|
||||||
if(String(batVoltage) == "12V") bvoltage = 12;
|
if(String(batVoltage) == "12V") bvoltage = 12;
|
||||||
else bvoltage = 24;
|
else bvoltage = 24;
|
||||||
getdisplay().print(bvoltage);
|
printBoatValue(String(bvoltage), 10, 140, LEFT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("V");
|
getdisplay().print("V");
|
||||||
|
|
||||||
// Show battery capacity
|
// Show battery capacity
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalueCapacity = (batCapacity <= 999) ? String(batCapacity) : String(float(batCapacity/1000.0), 1);
|
||||||
getdisplay().setCursor(10, 200);
|
printBoatValue(svalueCapacity, 10, 200, LEFT, 20, smallDecimals);
|
||||||
if(batCapacity <= 999) getdisplay().print(batCapacity, 0);
|
|
||||||
if(batCapacity > 999) getdisplay().print(float(batCapacity/1000.0), 1);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
if(batCapacity <= 999) getdisplay().print("Ah");
|
if(batCapacity <= 999) getdisplay().print("Ah");
|
||||||
if(batCapacity > 999) getdisplay().print("kAh");
|
if(batCapacity > 999) getdisplay().print("kAh");
|
||||||
@@ -249,20 +249,19 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show fill level in percent
|
// Show fill level in percent
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(String(batPercentage), 150, 200, LEFT, 20, smallDecimals);
|
||||||
getdisplay().setCursor(150, 200);
|
|
||||||
getdisplay().print(batPercentage);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("%");
|
getdisplay().print("%");
|
||||||
|
|
||||||
// Show time to full discharge
|
// Show time to full discharge
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalueRange;
|
||||||
getdisplay().setCursor(150, 260);
|
|
||||||
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
||||||
if(batRange < 9.9) getdisplay().print(batRange, 1);
|
svalueRange = (batRange < 9.9) ? String(batRange, 1) : String(batRange, 0);
|
||||||
else getdisplay().print(batRange, 0);
|
|
||||||
}
|
}
|
||||||
else getdisplay().print("--");
|
else{
|
||||||
|
svalueRange = "--";
|
||||||
|
}
|
||||||
|
printBoatValue(svalueRange, 150, 260, LEFT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("h");
|
getdisplay().print("h");
|
||||||
|
|
||||||
@@ -283,54 +282,38 @@ public:
|
|||||||
getdisplay().print("Sensor Modul");
|
getdisplay().print("Sensor Modul");
|
||||||
|
|
||||||
// Reading bus data or using simulation data
|
// Reading bus data or using simulation data
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue1;
|
||||||
getdisplay().setCursor(260, 140);
|
|
||||||
if(simulation == true){
|
|
||||||
if(batVoltage == "12V"){
|
|
||||||
value1 = 12.0;
|
|
||||||
}
|
|
||||||
if(batVoltage == "24V"){
|
|
||||||
value1 = 24.0;
|
|
||||||
}
|
|
||||||
value1 += float(random(0, 5)) / 10; // Simulation data
|
|
||||||
getdisplay().print(value1,1);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// Check for valid real data, display also if hold values activated
|
// Check for valid real data, display also if hold values activated
|
||||||
if(valid1 == true || holdvalues == true){
|
if(valid1 == true || holdvalues == true){
|
||||||
// Resolution switching
|
svalue1 = formatValue(value1, String("formatXdr:U:V"), *commonData);
|
||||||
if(value1 <= 9.9) getdisplay().print(value1, 2);
|
} else {
|
||||||
if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1);
|
svalue1 = "---"; // Missing bus data
|
||||||
if(value1 > 99.9) getdisplay().print(value1, 0);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print("---"); // Missing bus data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
printBoatValue(svalue1, 355, 140, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("V");
|
getdisplay().print("V");
|
||||||
|
|
||||||
// Show actual current in A
|
// Show actual current in A
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue2;
|
||||||
getdisplay().setCursor(260, 200);
|
|
||||||
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
||||||
if(value2 <= 9.9) getdisplay().print(value2, 2);
|
svalue2 = formatValue(value2, String("formatXdr:I:A"), *commonData);
|
||||||
if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1);
|
|
||||||
if(value2 > 99.9) getdisplay().print(value2, 0);
|
|
||||||
}
|
}
|
||||||
else getdisplay().print("---");
|
else{
|
||||||
|
svalue2 = "---";
|
||||||
|
}
|
||||||
|
printBoatValue(svalue2, 355, 200, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("A");
|
getdisplay().print("A");
|
||||||
|
|
||||||
// Show actual consumption in W
|
// Show actual consumption in W
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue3;
|
||||||
getdisplay().setCursor(260, 260);
|
|
||||||
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
||||||
if(value3 <= 9.9) getdisplay().print(value3, 2);
|
svalue3 = formatValue(value3, String("formatXdr:G:"), *commonData);
|
||||||
if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1);
|
|
||||||
if(value3 > 99.9) getdisplay().print(value3, 0);
|
|
||||||
}
|
}
|
||||||
else getdisplay().print("---");
|
else{
|
||||||
|
svalue3 = "---";
|
||||||
|
}
|
||||||
|
printBoatValue(svalue3, 355, 260, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("W");
|
getdisplay().print("W");
|
||||||
|
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -442,8 +442,8 @@ public:
|
|||||||
uint16_t wDigit, hDigit;
|
uint16_t wDigit, hDigit;
|
||||||
uint16_t wColon, hColon;
|
uint16_t wColon, hColon;
|
||||||
|
|
||||||
getdisplay().getTextBounds("00", 0, 0, &x0, &y0, &wDigit, &hDigit);
|
displayGetTextBounds("00", 0, 0, &x0, &y0, &wDigit, &hDigit);
|
||||||
getdisplay().getTextBounds(":", 0, 0, &x0, &y0, &wColon, &hColon);
|
displayGetTextBounds(":", 0, 0, &x0, &y0, &wColon, &hColon);
|
||||||
|
|
||||||
uint16_t totalWidth = 3 * wDigit + 2 * wColon;
|
uint16_t totalWidth = 3 * wDigit + 2 * wColon;
|
||||||
|
|
||||||
@@ -453,7 +453,7 @@ public:
|
|||||||
// Draw time string centered
|
// Draw time string centered
|
||||||
int16_t x1b, y1b;
|
int16_t x1b, y1b;
|
||||||
uint16_t wb, hb;
|
uint16_t wb, hb;
|
||||||
getdisplay().getTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
|
displayGetTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
|
||||||
int16_t textX = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
|
int16_t textX = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
|
||||||
int16_t textY = centerY + hb / 2;
|
int16_t textY = centerY + hb / 2;
|
||||||
|
|
||||||
@@ -520,7 +520,7 @@ public:
|
|||||||
|
|
||||||
int16_t x1b, y1b;
|
int16_t x1b, y1b;
|
||||||
uint16_t wb, hb;
|
uint16_t wb, hb;
|
||||||
getdisplay().getTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
|
displayGetTextBounds(timeStr, 0, 0, &x1b, &y1b, &wb, &hb);
|
||||||
|
|
||||||
int16_t x = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
|
int16_t x = (static_cast<int16_t>(getdisplay().width()) - static_cast<int16_t>(wb)) / 2;
|
||||||
int16_t y = 150 + hb / 2;
|
int16_t y = 150 + hb / 2;
|
||||||
@@ -665,7 +665,7 @@ public:
|
|||||||
// Print text centered on position x, y
|
// Print text centered on position x, y
|
||||||
int16_t x1c, y1c; // Return values of getTextBounds
|
int16_t x1c, y1c; // Return values of getTextBounds
|
||||||
uint16_t wc, hc; // Return values of getTextBounds
|
uint16_t wc, hc; // Return values of getTextBounds
|
||||||
getdisplay().getTextBounds(ii, int(x), int(y), &x1c, &y1c, &wc, &hc); // Calc width of new string
|
displayGetTextBounds(ii, int(x), int(y), &x1c, &y1c, &wc, &hc); // Calc width of new string
|
||||||
getdisplay().setCursor(x - wc / 2, y + hc / 2);
|
getdisplay().setCursor(x - wc / 2, y + hc / 2);
|
||||||
if (i % 90 == 0) {
|
if (i % 90 == 0) {
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ const float Compass_LineDelta = 8.0;// Compass band: 1deg = 5 Pixels, 10deg = 50
|
|||||||
|
|
||||||
class PageCompass : public Page
|
class PageCompass : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
int WhichDataCompass = ShowHDM;
|
int WhichDataCompass = ShowHDM;
|
||||||
int WhichDataDisplay = ShowHDM;
|
int WhichDataDisplay = ShowHDM;
|
||||||
|
|
||||||
@@ -75,6 +79,7 @@ class PageCompass : public Page
|
|||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
GwApi::BoatValue *bvalue;
|
GwApi::BoatValue *bvalue;
|
||||||
String DataName[HowManyValues];
|
String DataName[HowManyValues];
|
||||||
@@ -109,7 +114,7 @@ class PageCompass : public Page
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
// Horizontal line 2 pix top & bottom
|
// Horizontal line 2 pix top & bottom
|
||||||
@@ -122,14 +127,12 @@ class PageCompass : public Page
|
|||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(10, 120);
|
getdisplay().setCursor(10, 120);
|
||||||
getdisplay().print(DataUnits[WhichDataDisplay]);
|
getdisplay().print(DataUnits[WhichDataDisplay]);
|
||||||
getdisplay().setCursor(190, 120);
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
|
|
||||||
|
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(DataText[WhichDataDisplay]); // Real value as formated string
|
printBoatValue(DataText[WhichDataDisplay], 190, 120, LEFT, 42, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(OldDataText[WhichDataDisplay]); // Old value as formated string
|
printBoatValue(OldDataText[WhichDataDisplay], 190, 120, LEFT, 42, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(DataValid[WhichDataDisplay] == true){
|
if(DataValid[WhichDataDisplay] == true){
|
||||||
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
|
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
|
||||||
|
|||||||
@@ -0,0 +1,519 @@
|
|||||||
|
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
|
||||||
|
|
||||||
|
#include "Pagedata.h"
|
||||||
|
#include "OBP60Extensions.h"
|
||||||
|
#include "OBPDataOperations.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
// leeway K coefficient selection options from OBP configuration page
|
||||||
|
static const std::unordered_map<std::string, int> leeKMap = {
|
||||||
|
{ "---", 0 },
|
||||||
|
{ "Racer [2]", 2 },
|
||||||
|
{ "Multihull Draggerboard [4]", 4 },
|
||||||
|
{ "Performance Cruiser [6]", 6 },
|
||||||
|
{ "Family Cruiser [9]", 9 },
|
||||||
|
{ "Heavy Displacement [13]", 13 },
|
||||||
|
{ "Multihull Fixed Keel [16]", 16 }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum bValueIdx {
|
||||||
|
ROLL = 0,
|
||||||
|
SET,
|
||||||
|
DFT,
|
||||||
|
HDT,
|
||||||
|
STW,
|
||||||
|
COG,
|
||||||
|
SOG,
|
||||||
|
HDM,
|
||||||
|
VAR,
|
||||||
|
AWA,
|
||||||
|
NUM_VALS
|
||||||
|
};
|
||||||
|
|
||||||
|
// Screen coordinates for boat value, name, unit
|
||||||
|
struct Points {
|
||||||
|
int16_t x1, y1;
|
||||||
|
int16_t x2, y2;
|
||||||
|
int16_t x3, y3;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Screen coordinates for four boat data values (top-left, bottom-left, top-right, bottom-right)
|
||||||
|
static constexpr Points POS[] = {
|
||||||
|
{ 101, 65, 10, 95, 10, 115 }, // Position top left for value, name, unit
|
||||||
|
{ 101, 270, 10, 220, 10, 190 }, // Position bottom left
|
||||||
|
{ 395, 65, 390, 95, 390, 115 }, // Position top right
|
||||||
|
{ 395, 270, 390, 220, 390, 190 } // Position bottom right
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define wave visual (XBM Format)
|
||||||
|
static constexpr int WAVE_W = 132;
|
||||||
|
static constexpr int WAVE_H = 20;
|
||||||
|
static const uint8_t wave_bitmap[] PROGMEM = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, 0x80, 0x00,
|
||||||
|
0x7f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x03, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xf0,
|
||||||
|
0x01, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xbf, 0xe0, 0x3f, 0xc0, 0xe2, 0x07,
|
||||||
|
0xff, 0x1f, 0xf8, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x80, 0x07, 0xff, 0x80,
|
||||||
|
0x00, 0xff, 0xff, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0xfe, 0x00, 0x00, 0x78,
|
||||||
|
0x0f, 0x80, 0x3f, 0xff, 0x80, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x01, 0xff, 0x00,
|
||||||
|
0x00, 0x3f, 0xf0, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x3f, 0xff, 0x80, 0x07, 0xff,
|
||||||
|
0xe0, 0x00, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x7e, 0x00, 0xff, 0xff, 0xf0, 0x3f,
|
||||||
|
0xe3, 0xf8, 0x01, 0xc0, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe7, 0xfc, 0x0f, 0xff,
|
||||||
|
0xff, 0x00, 0x3f, 0x00, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xf0, 0x03,
|
||||||
|
0xff, 0xfc, 0x00, 0x03, 0xf0, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc0,
|
||||||
|
0x00, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
|
||||||
|
0x00, 0x00, 0x07, 0x80, 0xfc, 0x00, 0x00, 0x3f, 0xc0, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xe0, 0x00, 0x03, 0xff, 0x80, 0x01, 0xff, 0xf8, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1e, 0x00, 0x0f, 0xff, 0xf0, 0x07, 0xff, 0xff, 0x00, 0xfc, 0x0f, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x3e, 0x03, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xe0, 0x01, 0xe0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0x80, 0x00,
|
||||||
|
0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0xfe, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
class PageCurrent : public Page {
|
||||||
|
private:
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
|
GwLog* logger;
|
||||||
|
|
||||||
|
int width; // Screen width
|
||||||
|
int height; // Screen height
|
||||||
|
|
||||||
|
bool keylock = false; // Keylock
|
||||||
|
|
||||||
|
bool useSimuData;
|
||||||
|
bool holdValues;
|
||||||
|
String flashLED;
|
||||||
|
String backlightMode;
|
||||||
|
bool smallDecimals;
|
||||||
|
String leeKStd;
|
||||||
|
double leeK;
|
||||||
|
movingAvgAngle<double> SetAvg {8}; // Store average of the last 8 values of SET angle values
|
||||||
|
movingAvg<double> DftAvg {8}; // Store average of the last 8 values of DFT speed value
|
||||||
|
|
||||||
|
// Old values for hold function
|
||||||
|
String sValueOld[NUM_VALS] = { "", "", "", "", "", "", "", "", "", "" };
|
||||||
|
String unitOld[NUM_VALS] = { "", "", "", "", "", "", "", "", "", "" };
|
||||||
|
|
||||||
|
struct Current {
|
||||||
|
double set; // direction TO which current flows (0..2*PI, true dir)
|
||||||
|
double dft; // current drift speed (m/s)
|
||||||
|
};
|
||||||
|
|
||||||
|
double calcLeeway(
|
||||||
|
double leeK, // leeway coefficient
|
||||||
|
double roll, // heel of boat (rad)
|
||||||
|
double stw // speed through water (m/s)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
static constexpr double MINSTW = 0.5; // minimum speed (m/s) for leeway calculation to prevent math explosion
|
||||||
|
static constexpr double MAXLAY = 30.0 * DEG_TO_RAD; // cap leeway at this level of degree;
|
||||||
|
|
||||||
|
if (stw < MINSTW) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double lay = leeK * roll / (stw * stw);
|
||||||
|
return std::min(lay, MAXLAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
double calcCTW(
|
||||||
|
double awa, // apparent wind angle (0..2PI -> representation within OBP60)
|
||||||
|
double hdt, // heading true (rad)
|
||||||
|
double lay // leeway angle (rad)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
double ctw; // course through water (rad true)
|
||||||
|
|
||||||
|
if (awa >= M_PI) { // apparent wind > 180° -> comes from port
|
||||||
|
ctw = WindUtils::to2PI(hdt + lay);
|
||||||
|
} else { // wind comes from starboard
|
||||||
|
ctw = WindUtils::to2PI(hdt - lay);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctw;
|
||||||
|
}
|
||||||
|
|
||||||
|
Current calcSetAndDrift(
|
||||||
|
double lay, // leeway angle (rad)
|
||||||
|
double sog, // speed over ground (m/s)
|
||||||
|
double cog, // course over ground (rad true)
|
||||||
|
double stw, // speed through water (m/s)
|
||||||
|
double hdt, // heading true (rad)
|
||||||
|
double awa // apparent wind angle (rad)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Current crnt;
|
||||||
|
double ctw; // course through water (rad true)
|
||||||
|
|
||||||
|
ctw = calcCTW(awa, hdt, lay);
|
||||||
|
|
||||||
|
// Ground velocity vector (east, north)
|
||||||
|
double vg_x = sog * std::sin(cog);
|
||||||
|
double vg_y = sog * std::cos(cog);
|
||||||
|
|
||||||
|
// Water-relative velocity vector (east, north)
|
||||||
|
double vw_x = stw * std::sin(ctw);
|
||||||
|
double vw_y = stw * std::cos(ctw);
|
||||||
|
|
||||||
|
// Current vector = ground - water
|
||||||
|
double vc_x = vg_x - vw_x;
|
||||||
|
double vc_y = vg_y - vw_y;
|
||||||
|
|
||||||
|
crnt.dft = std::sqrt(vc_x * vc_x + vc_y * vc_y);
|
||||||
|
|
||||||
|
crnt.set = std::atan2(vc_x, vc_y); // atan2(x, y) because 0° = North, clockwise positive
|
||||||
|
crnt.set = WindUtils::to2PI(crnt.set); // internal respresentation of wind is [0..2PI]
|
||||||
|
|
||||||
|
// LOG_DEBUG(GwLog::DEBUG, "PageCurrent-setDrift: sog: %.3f, ctw: %.3f, stw: %.3f, vc_x: %.3f, vc_y: %.3f, set: %.3f, drift: %.3f",
|
||||||
|
// sog, ctw, stw, vc_x, vc_y, crnt.set, crnt.dft);
|
||||||
|
|
||||||
|
return crnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw compass rose with boat direction up; top heading of compass rose in degree [0..2PI]
|
||||||
|
void drawCompassRose(double topHeading)
|
||||||
|
{
|
||||||
|
int radius = 110;
|
||||||
|
String sDegree;
|
||||||
|
int centerX = 200;
|
||||||
|
int centerY = 150;
|
||||||
|
|
||||||
|
getdisplay().fillCircle(centerX, centerY, radius + 10, commonData->fgcolor);
|
||||||
|
getdisplay().fillCircle(centerX, centerY, radius + 7, commonData->bgcolor);
|
||||||
|
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
|
||||||
|
for (int i = 0; i < 360; i += 10) {
|
||||||
|
|
||||||
|
float topAngle = (float)(i * DEG_TO_RAD - topHeading); // Calculate the relative angle for drawing; subtracting topHeading shifts the entire rose
|
||||||
|
float sinVal = sin(topAngle);
|
||||||
|
float cosVal = cos(topAngle);
|
||||||
|
|
||||||
|
// Coordinates for the labels (positioned slightly inside the rose)
|
||||||
|
float xLabel = centerX + (radius - 27) * sinVal;
|
||||||
|
float yLabel = centerY - (radius - 24) * cosVal;
|
||||||
|
|
||||||
|
if (i % 30 == 0) {
|
||||||
|
// Draw scale markers (triangles)
|
||||||
|
float dx = 1;
|
||||||
|
float xx1 = -dx;
|
||||||
|
float xx2 = +dx;
|
||||||
|
float yy1 = -(radius - 10);
|
||||||
|
float yy2 = -(radius + 10);
|
||||||
|
|
||||||
|
getdisplay().fillTriangle(centerX + (int)(cosVal * xx1 - sinVal * yy1), centerY + (int)(sinVal * xx1 + cosVal * yy1),
|
||||||
|
centerX + (int)(cosVal * xx2 - sinVal * yy1), centerY + (int)(sinVal * xx2 + cosVal * yy1),
|
||||||
|
centerX + (int)(cosVal * xx1 - sinVal * yy2), centerY + (int)(sinVal * xx1 + cosVal * yy2), commonData->fgcolor);
|
||||||
|
getdisplay().fillTriangle(centerX + (int)(cosVal * xx2 - sinVal * yy1), centerY + (int)(sinVal * xx2 + cosVal * yy1),
|
||||||
|
centerX + (int)(cosVal * xx1 - sinVal * yy2), centerY + (int)(sinVal * xx1 + cosVal * yy2),
|
||||||
|
centerX + (int)(cosVal * xx2 - sinVal * yy2), centerY + (int)(sinVal * xx2 + cosVal * yy2), commonData->fgcolor);
|
||||||
|
|
||||||
|
// Print degree labels
|
||||||
|
sDegree = String(i);
|
||||||
|
int16_t x1, y1;
|
||||||
|
uint16_t w, h;
|
||||||
|
displayGetTextBounds(sDegree, (int)xLabel, (int)yLabel, &x1, &y1, &w, &h);
|
||||||
|
getdisplay().setCursor(xLabel - w / 2, yLabel + h / 2);
|
||||||
|
getdisplay().print(sDegree);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Draw dots for intermediate 10 degree steps
|
||||||
|
float xDot = centerX + radius * sinVal;
|
||||||
|
float yDot = centerY - radius * cosVal;
|
||||||
|
getdisplay().fillCircle((int)xDot, (int)yDot, 2, commonData->fgcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawRotatedArrow(
|
||||||
|
float size, // size of the arrow = speed in m/s [0.5..5.2]
|
||||||
|
float direction // angle the arrow is pointing to [0..2PI]
|
||||||
|
)
|
||||||
|
{
|
||||||
|
static constexpr double MINSIZE = 0.05; // minimum current set (size) (m/s) for arrow size
|
||||||
|
|
||||||
|
if (size < MINSIZE) { // no arrow for current set below 0.05 m/s
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// size [0.5 .. 5.2] -> length [60 .. 130]
|
||||||
|
float maxsize = constrain(size, 0.5, 5.2);
|
||||||
|
float length = 60.0 + ((maxsize - 0.5) / (5.2 - 0.5)) * (130.0 - 60.0);
|
||||||
|
int16_t cx = width / 2;
|
||||||
|
int16_t cy = height / 2;
|
||||||
|
|
||||||
|
// geometry (arrow upwards)
|
||||||
|
float h = length / 2.0;
|
||||||
|
float w = length / 4.0;
|
||||||
|
float headW = length / 1.5;
|
||||||
|
float splitY = -h / 3;
|
||||||
|
|
||||||
|
struct Pt {
|
||||||
|
float x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 7 edges of arrow
|
||||||
|
Pt p[7] = {
|
||||||
|
{ -w / 2, h }, { w / 2, h }, { w / 2, splitY }, { headW / 2, splitY },
|
||||||
|
{ 0, -h }, { -headW / 2, splitY }, { -w / 2, splitY }
|
||||||
|
};
|
||||||
|
|
||||||
|
// edges of arrow with reduced size of 1 px for thicker frame
|
||||||
|
float offset = 1.0;
|
||||||
|
Pt p_in[7] = {
|
||||||
|
{ -w / 2 + offset, h - offset }, { w / 2 - offset, h - offset },
|
||||||
|
{ w / 2 - offset, splitY + offset }, { headW / 2 - offset, splitY + offset },
|
||||||
|
{ 0, -h + offset }, { -headW / 2 + offset, splitY + offset },
|
||||||
|
{ -w / 2 + offset, splitY + offset }
|
||||||
|
};
|
||||||
|
|
||||||
|
// calculate rotation
|
||||||
|
float s = sin(direction);
|
||||||
|
float c = cos(direction);
|
||||||
|
int16_t rx[7], ry[7];
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
// rotate frame
|
||||||
|
rx[i] = cx + (int16_t)(p[i].x * c - p[i].y * s);
|
||||||
|
ry[i] = cy + (int16_t)(p[i].x * s + p[i].y * c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill arrow with white area to delete any background data
|
||||||
|
getdisplay().fillTriangle(rx[0], ry[0], rx[1], ry[1], rx[2], ry[2], commonData->bgcolor);
|
||||||
|
getdisplay().fillTriangle(rx[0], ry[0], rx[2], ry[2], rx[6], ry[6], commonData->bgcolor);
|
||||||
|
getdisplay().fillTriangle(rx[5], ry[5], rx[4], ry[4], rx[3], ry[3], commonData->bgcolor);
|
||||||
|
|
||||||
|
// draw arrow frame with "overdraw" for constant thickness
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
int next = (i + 1) % 7;
|
||||||
|
int16_t x0 = rx[i], y0 = ry[i];
|
||||||
|
int16_t x1 = rx[next], y1 = ry[next];
|
||||||
|
|
||||||
|
// original line
|
||||||
|
getdisplay().drawLine(x0, y0, x1, y1, commonData->fgcolor);
|
||||||
|
|
||||||
|
// offset by 1 pixel in x or y depending on delta
|
||||||
|
if (abs(x1 - x0) > abs(y1 - y0)) {
|
||||||
|
getdisplay().drawLine(x0, y0 + 1, x1, y1 + 1, commonData->fgcolor);
|
||||||
|
} else {
|
||||||
|
getdisplay().drawLine(x0 + 1, y0, x1 + 1, y1, commonData->fgcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
PageCurrent(CommonData& common)
|
||||||
|
{
|
||||||
|
commonData = &common;
|
||||||
|
logger = commonData->logger;
|
||||||
|
LOG_DEBUG(GwLog::LOG, "Instantiate PageCurrent");
|
||||||
|
|
||||||
|
width = getdisplay().width(); // Screen width
|
||||||
|
height = getdisplay().height(); // Screen height
|
||||||
|
|
||||||
|
// Get config data
|
||||||
|
useSimuData = commonData->config->getBool(commonData->config->useSimuData);
|
||||||
|
holdValues = commonData->config->getBool(commonData->config->holdvalues);
|
||||||
|
flashLED = commonData->config->getString(commonData->config->flashLED);
|
||||||
|
backlightMode = commonData->config->getString(commonData->config->backlight);
|
||||||
|
smallDecimals = commonData->config->getBool(commonData->config->smallDecimals);
|
||||||
|
|
||||||
|
leeKStd = commonData->config->getString(commonData->config->leeKStd);
|
||||||
|
auto it = leeKMap.find(leeKStd.c_str());
|
||||||
|
if (it != leeKMap.end()) {
|
||||||
|
leeK = it->second;
|
||||||
|
} else if (leeKStd == "Individual value") {
|
||||||
|
leeK = (commonData->config->getString(commonData->config->leeK)).toDouble();
|
||||||
|
}
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: leeKStd: %s, leeK: %.3f", leeKStd.c_str(), leeK);
|
||||||
|
|
||||||
|
// Initialize average buffer for SET and DFT
|
||||||
|
SetAvg.begin();
|
||||||
|
DftAvg.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int handleKey(int key)
|
||||||
|
{
|
||||||
|
// Keylock function
|
||||||
|
if (key == 11) { // Code for keylock
|
||||||
|
commonData->keylock = !commonData->keylock;
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void displayNew(PageData& pageData)
|
||||||
|
{
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
|
// Clear optical warning
|
||||||
|
if (flashLED == "Limit Violation") {
|
||||||
|
setBlinkingLED(false);
|
||||||
|
setFlashLED(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int displayPage(PageData& pageData)
|
||||||
|
{
|
||||||
|
double lay;
|
||||||
|
Current current;
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::LOG, "Display PageCurrent");
|
||||||
|
|
||||||
|
// Get boat values for page
|
||||||
|
// 0=ROLL, 1=SET, 2=DFT, 3=HDT, 4=STW, 5=COG, 6=SOG, 7=HDM, 8=VAR, 9=AWA
|
||||||
|
std::vector<GwApi::BoatValue*> bValue(pageData.values.begin(), pageData.values.end());
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: printing #1: %s, %.3f, valid: %d, #2: %s, %.3f, valid: %d, #3: %s, %.3f, valid: %d, #4: %s, %.3f, valid: %d, #5: %s, %.3f, valid: %d",
|
||||||
|
bValue[ROLL]->getName().c_str(), bValue[ROLL]->value, bValue[ROLL]->valid, bValue[SET]->getName().c_str(), bValue[SET]->value, bValue[SET]->valid,
|
||||||
|
bValue[DFT]->getName().c_str(), bValue[DFT]->value, bValue[DFT]->valid, bValue[HDT]->getName().c_str(), bValue[HDT]->value, bValue[HDT]->valid,
|
||||||
|
bValue[STW]->getName().c_str(), bValue[STW]->value, bValue[STW]->valid);
|
||||||
|
|
||||||
|
// Calculate current
|
||||||
|
//***********************************************************
|
||||||
|
|
||||||
|
if (!bValue[SET]->valid || !bValue[DFT]->valid) { // If SET or DRIFT not available, try to calculate them
|
||||||
|
if (bValue[SOG]->valid && bValue[COG]->valid && bValue[STW]->valid) {
|
||||||
|
// calculate current only if all required values are available; we accept missing AWA, because this is only required for leeway adjustment
|
||||||
|
|
||||||
|
if (!bValue[HDT]->valid) { // HDT not available
|
||||||
|
if (bValue[HDM]->valid) {
|
||||||
|
if (bValue[VAR]->valid) {
|
||||||
|
bValue[HDT]->value = bValue[HDM]->value + bValue[VAR]->value; // Use corrected HDM if HDT is not available
|
||||||
|
bValue[HDT]->value = WindUtils::to2PI(bValue[HDT]->value);
|
||||||
|
} else {
|
||||||
|
// if HDT cannot be fully substituted by HDM+VAR, continue with HDM only
|
||||||
|
bValue[HDT] = bValue[HDM];
|
||||||
|
}
|
||||||
|
bValue[HDT]->valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bValue[ROLL]->valid || (bValue[ROLL]->getFormat() != "formatXdr:A:D" && bValue[ROLL]->getFormat() != "formatXdr:A:rd" && bValue[ROLL]->getFormat() != "formatCourse")) {
|
||||||
|
bValue[ROLL]->value = 0; // delete last value if roll value is not valid anymore (gateway keeps last value) or wrong type selected by user
|
||||||
|
}
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: Roll: %s, %.3f, valid: %d, format: %s",
|
||||||
|
bValue[ROLL]->getName().c_str(), bValue[ROLL]->value, bValue[ROLL]->valid, bValue[ROLL]->getFormat().c_str());
|
||||||
|
|
||||||
|
lay = calcLeeway(leeK, bValue[ROLL]->value, bValue[STW]->value);
|
||||||
|
|
||||||
|
if (bValue[HDT]->valid) { // That's either HDT or HDM
|
||||||
|
double awaVal = (bValue[AWA]->valid ? bValue[AWA]->value : 0.0); // catch potentially missing AWA
|
||||||
|
current = calcSetAndDrift(lay, bValue[SOG]->value, bValue[COG]->value, bValue[STW]->value, bValue[HDT]->value, awaVal);
|
||||||
|
bValue[SET]->value = current.set;
|
||||||
|
bValue[SET]->setFormat("formatCourse");
|
||||||
|
bValue[SET]->valid = true;
|
||||||
|
bValue[DFT]->value = current.dft;
|
||||||
|
bValue[DFT]->setFormat("formatKnots");
|
||||||
|
bValue[DFT]->valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// averaging current for smoother output
|
||||||
|
if (bValue[SET]->valid && bValue[DFT]->valid) { // Are SET and DRIFT now available?
|
||||||
|
bValue[SET]->value = SetAvg.reading(bValue[SET]->value);
|
||||||
|
bValue[DFT]->value = DftAvg.reading(bValue[DFT]->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageCurrent: leeKStd: %s, leeK: %.3f, lay: %.3f, roll: %.3f, stw: %.3f, awa: %.3f, hdt: %.3f, cog: %.3f, sog: %.3f, set: %.3f, dft: %.3f",
|
||||||
|
leeKStd.c_str(), leeK, lay, bValue[ROLL]->value, bValue[STW]->value, bValue[AWA]->value, bValue[HDT]->value, bValue[COG]->value, bValue[SOG]->value,
|
||||||
|
bValue[SET]->value, bValue[DFT]->value);
|
||||||
|
|
||||||
|
// Draw page
|
||||||
|
//***********************************************************
|
||||||
|
|
||||||
|
displaySetPartialWindow(0, 0, width, height); // Set partial update
|
||||||
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
|
static const int bValType[] = { HDT, STW, SET, DFT }; // Sequence of boat data types to show on page
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
String name = xdrDelete(bValue[bValType[i]]->getName()); // Value name
|
||||||
|
name = name.substring(0, 4); // String length limit for value name
|
||||||
|
String sValue = formatValue(bValue[bValType[i]], *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||||
|
String unit = formatValue(bValue[bValType[i]], *commonData).unit; // Unit of value
|
||||||
|
unit = unit.substring(0, 6); // String length limit for value unit
|
||||||
|
|
||||||
|
// Show boat data value
|
||||||
|
if (!holdValues || useSimuData) {
|
||||||
|
printBoatValue(sValue, POS[i].x1, POS[i].y1, RIGHT, 20, smallDecimals);
|
||||||
|
} else {
|
||||||
|
printBoatValue(sValueOld[i], POS[i].x1, POS[i].y1, RIGHT, 20, smallDecimals);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show name
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
|
if (i < 2) { // left side values
|
||||||
|
getdisplay().setCursor(POS[i].x2, POS[i].y2);
|
||||||
|
getdisplay().print(name);
|
||||||
|
} else { // right side values
|
||||||
|
drawTextRalign(POS[i].x2, POS[i].y2, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String dUnit; // Unit to show
|
||||||
|
if (holdValues) {
|
||||||
|
dUnit = unitOld[i];
|
||||||
|
} else {
|
||||||
|
dUnit = unit;
|
||||||
|
}
|
||||||
|
// Show unit
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
if (i < 2) { // left side values
|
||||||
|
getdisplay().setCursor(POS[i].x3, POS[i].y3);
|
||||||
|
getdisplay().print(dUnit);
|
||||||
|
} else { // right side values
|
||||||
|
drawTextRalign(POS[i].x3, POS[i].y3, dUnit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bValue[bValType[i]]->valid) {
|
||||||
|
sValueOld[i] = sValue; // Save the old value
|
||||||
|
unitOld[i] = unit; // Save the old unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal separator left
|
||||||
|
getdisplay().fillRect(0, 149, 60, 2, commonData->fgcolor);
|
||||||
|
// Horizontal separator right
|
||||||
|
getdisplay().fillRect(339, 149, 60, 2, commonData->fgcolor);
|
||||||
|
|
||||||
|
if (bValue[HDT]->valid) {
|
||||||
|
drawCompassRose(bValue[HDT]->value);
|
||||||
|
} else {
|
||||||
|
drawCompassRose(0.0);
|
||||||
|
};
|
||||||
|
getdisplay().drawBitmap((width - WAVE_W) / 2, (height - WAVE_H) / 2, wave_bitmap, WAVE_W, WAVE_H, commonData->fgcolor);
|
||||||
|
if (bValue[SET]->valid && bValue[DFT]->valid) {
|
||||||
|
drawRotatedArrow(bValue[DFT]->value, WindUtils::to2PI(bValue[SET]->value - bValue[HDT]->value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PAGE_UPDATE;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static Page* createPage(CommonData& common)
|
||||||
|
{
|
||||||
|
return new PageCurrent(common);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* with the code below we make this page known to the PageTask
|
||||||
|
* we give it a type (name) that can be selected in the config
|
||||||
|
* we define which function is to be called
|
||||||
|
* and we provide the number of user parameters we expect
|
||||||
|
* this will be number of BoatValue pointers in pageData.values
|
||||||
|
*/
|
||||||
|
PageDescription registerPageCurrent(
|
||||||
|
"Current", // Page name
|
||||||
|
createPage, // Action
|
||||||
|
1, // Number of bus values depends on selection in Web configuration -> boat value for ROLL
|
||||||
|
{ "SET", "DFT", "HDT", "STW", "COG", "SOG", "HDM", "VAR", "AWA" }, // Bus values we need in the page
|
||||||
|
true // Show display header on/off
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -5,6 +5,11 @@
|
|||||||
|
|
||||||
class PageDST810 : public Page
|
class PageDST810 : public Page
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageDST810(CommonData &common){
|
PageDST810(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
@@ -24,6 +29,9 @@ public:
|
|||||||
GwConfigHandler *config = commonData->config;
|
GwConfigHandler *config = commonData->config;
|
||||||
GwLog *logger = commonData->logger;
|
GwLog *logger = commonData->logger;
|
||||||
|
|
||||||
|
int valueX, valueY;
|
||||||
|
int DsegFontSize;
|
||||||
|
|
||||||
// Old values for hold function
|
// Old values for hold function
|
||||||
static String svalue1old = "";
|
static String svalue1old = "";
|
||||||
static String unit1old = "";
|
static String unit1old = "";
|
||||||
@@ -40,6 +48,7 @@ public:
|
|||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
// Get boat values #1
|
// Get boat values #1
|
||||||
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
||||||
@@ -91,7 +100,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -100,7 +109,8 @@ public:
|
|||||||
// Show name
|
// Show name
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
||||||
getdisplay().setCursor(20, 55);
|
getdisplay().setCursor(20, 55);
|
||||||
getdisplay().print("Depth"); // Page name
|
// getdisplay().print("Depth"); // Page name
|
||||||
|
getdisplay().print(name1); // Page name
|
||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
@@ -112,16 +122,15 @@ public:
|
|||||||
getdisplay().print(unit1old);
|
getdisplay().print(unit1old);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set font
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
getdisplay().setCursor(180, 90);
|
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
|
valueX = 380;
|
||||||
|
valueY = 90;
|
||||||
|
DsegFontSize = 30;
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue1); // Real value as formated string
|
printBoatValue(svalue1, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue1old); // Old value as formated string
|
printBoatValue(svalue1old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid1 == true){
|
if(valid1 == true){
|
||||||
svalue1old = svalue1; // Save the old value
|
svalue1old = svalue1; // Save the old value
|
||||||
@@ -138,7 +147,8 @@ public:
|
|||||||
// Show name
|
// Show name
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
||||||
getdisplay().setCursor(20, 145);
|
getdisplay().setCursor(20, 145);
|
||||||
getdisplay().print("Speed"); // Page name
|
// getdisplay().print("Speed"); // Page name
|
||||||
|
getdisplay().print(name2); // Page name
|
||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
@@ -150,16 +160,15 @@ public:
|
|||||||
getdisplay().print(unit2old);
|
getdisplay().print(unit2old);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setfont
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
getdisplay().setCursor(180, 180);
|
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
|
valueX = 380;
|
||||||
|
valueY = 180;
|
||||||
|
DsegFontSize = 30;
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue2); // Real value as formated string
|
printBoatValue(svalue2, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue2old); // Old value as formated string
|
printBoatValue(svalue2old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid2 == true){
|
if(valid2 == true){
|
||||||
svalue2old = svalue2; // Save the old value
|
svalue2old = svalue2; // Save the old value
|
||||||
@@ -176,7 +185,8 @@ public:
|
|||||||
// Show name
|
// Show name
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(20, 220);
|
getdisplay().setCursor(20, 220);
|
||||||
getdisplay().print("Log"); // Page name
|
// getdisplay().print("Log"); // Page name
|
||||||
|
getdisplay().print(name3); // Page name
|
||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
@@ -188,16 +198,15 @@ public:
|
|||||||
getdisplay().print(unit3old);
|
getdisplay().print(unit3old);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set font
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
|
||||||
getdisplay().setCursor(80, 270);
|
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
|
valueX = 190;
|
||||||
|
valueY = 270;
|
||||||
|
DsegFontSize = 20;
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue3); // Real value as formated string
|
printBoatValue(svalue3, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue3old); // Old value as formated string
|
printBoatValue(svalue3old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid3 == true){
|
if(valid3 == true){
|
||||||
svalue3old = svalue3; // Save the old value
|
svalue3old = svalue3; // Save the old value
|
||||||
@@ -214,7 +223,8 @@ public:
|
|||||||
// Show name
|
// Show name
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(220, 220);
|
getdisplay().setCursor(220, 220);
|
||||||
getdisplay().print("Temp"); // Page name
|
// getdisplay().print("Temp"); // Page name
|
||||||
|
getdisplay().print(name4); // Page name
|
||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
@@ -226,16 +236,15 @@ public:
|
|||||||
getdisplay().print(unit4old);
|
getdisplay().print(unit4old);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set font
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
|
||||||
getdisplay().setCursor(280, 270);
|
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
|
valueX = 390;
|
||||||
|
valueY = 270;
|
||||||
|
DsegFontSize = 20;
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue4); // Real value as formated string
|
printBoatValue(svalue4, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue4old); // Old value as formated string
|
printBoatValue(svalue4old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid4 == true){
|
if(valid4 == true){
|
||||||
svalue4old = svalue4; // Save the old value
|
svalue4old = svalue4; // Save the old value
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ bool button5 = false;
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
// Write text
|
// Write text
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class PageFluid : public Page
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height());
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height());
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
|
|
||||||
class PageFourValues : public Page
|
class PageFourValues : public Page
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageFourValues(CommonData &common){
|
PageFourValues(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
@@ -24,6 +29,9 @@ class PageFourValues : public Page
|
|||||||
GwConfigHandler *config = commonData->config;
|
GwConfigHandler *config = commonData->config;
|
||||||
GwLog *logger = commonData->logger;
|
GwLog *logger = commonData->logger;
|
||||||
|
|
||||||
|
int valueX, valueY;
|
||||||
|
int DsegFontSize;
|
||||||
|
|
||||||
// Old values for hold function
|
// Old values for hold function
|
||||||
static String svalue1old = "";
|
static String svalue1old = "";
|
||||||
static String unit1old = "";
|
static String unit1old = "";
|
||||||
@@ -35,11 +43,9 @@ class PageFourValues : public Page
|
|||||||
static String unit4old = "";
|
static String unit4old = "";
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
|
||||||
// bool simulation = config->getBool(config->useSimuData);
|
|
||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
// Get boat values #1
|
// Get boat values #1
|
||||||
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
||||||
@@ -91,7 +97,7 @@ class PageFourValues : public Page
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -104,7 +110,7 @@ class PageFourValues : public Page
|
|||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(20, 65);
|
getdisplay().setCursor(20, 67);
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(unit1); // Unit
|
getdisplay().print(unit1); // Unit
|
||||||
}
|
}
|
||||||
@@ -113,25 +119,26 @@ class PageFourValues : public Page
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
|
valueX = 380;
|
||||||
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
|
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 55;
|
||||||
getdisplay().setCursor(120, 55);
|
DsegFontSize = 12;
|
||||||
}
|
}
|
||||||
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
|
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 67;
|
||||||
getdisplay().setCursor(150, 58);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
valueY = 67;
|
||||||
getdisplay().setCursor(180, 65);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue1); // Real value as formated string
|
printBoatValue(svalue1, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue1old); // Old value as formated string
|
printBoatValue(svalue1old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid1 == true){
|
if(valid1 == true){
|
||||||
svalue1old = svalue1; // Save the old value
|
svalue1old = svalue1; // Save the old value
|
||||||
@@ -152,7 +159,7 @@ class PageFourValues : public Page
|
|||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(20, 133);
|
getdisplay().setCursor(20, 135);
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(unit2); // Unit
|
getdisplay().print(unit2); // Unit
|
||||||
}
|
}
|
||||||
@@ -162,24 +169,24 @@ class PageFourValues : public Page
|
|||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
|
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 123;
|
||||||
getdisplay().setCursor(120, 123);
|
DsegFontSize = 12;
|
||||||
}
|
}
|
||||||
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
|
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 135;
|
||||||
getdisplay().setCursor(150, 123);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
valueY = 135;
|
||||||
getdisplay().setCursor(180, 133);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue2); // Real value as formated string
|
printBoatValue(svalue2, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue2old); // Old value as formated string
|
printBoatValue(svalue2old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid2 == true){
|
if(valid2 == true){
|
||||||
svalue2old = svalue2; // Save the old value
|
svalue2old = svalue2; // Save the old value
|
||||||
@@ -200,7 +207,7 @@ class PageFourValues : public Page
|
|||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(20, 201);
|
getdisplay().setCursor(20, 203);
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(unit3); // Unit
|
getdisplay().print(unit3); // Unit
|
||||||
}
|
}
|
||||||
@@ -210,24 +217,24 @@ class PageFourValues : public Page
|
|||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
|
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 191;
|
||||||
getdisplay().setCursor(120, 191);
|
DsegFontSize = 16;
|
||||||
}
|
}
|
||||||
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
|
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 203;
|
||||||
getdisplay().setCursor(150, 191);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
valueY = 203;
|
||||||
getdisplay().setCursor(180, 201);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue3); // Real value as formated string
|
printBoatValue(svalue3, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue3old); // Old value as formated string
|
printBoatValue(svalue3old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid3 == true){
|
if(valid3 == true){
|
||||||
svalue3old = svalue3; // Save the old value
|
svalue3old = svalue3; // Save the old value
|
||||||
@@ -248,7 +255,7 @@ class PageFourValues : public Page
|
|||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(20, 269);
|
getdisplay().setCursor(20, 271);
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(unit4); // Unit
|
getdisplay().print(unit4); // Unit
|
||||||
}
|
}
|
||||||
@@ -258,24 +265,24 @@ class PageFourValues : public Page
|
|||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
|
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 271;
|
||||||
getdisplay().setCursor(120, 259);
|
DsegFontSize = 16;
|
||||||
}
|
}
|
||||||
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
|
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 271;
|
||||||
getdisplay().setCursor(150, 259);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
valueY = 271;
|
||||||
getdisplay().setCursor(180, 269);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue4); // Real value as formated string
|
printBoatValue(svalue4, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue4old); // Old value as formated string
|
printBoatValue(svalue4old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid4 == true){
|
if(valid4 == true){
|
||||||
svalue4old = svalue4; // Save the old value
|
svalue4old = svalue4; // Save the old value
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
|
|
||||||
class PageFourValues2 : public Page
|
class PageFourValues2 : public Page
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageFourValues2(CommonData &common){
|
PageFourValues2(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
@@ -24,6 +29,9 @@ class PageFourValues2 : public Page
|
|||||||
GwConfigHandler *config = commonData->config;
|
GwConfigHandler *config = commonData->config;
|
||||||
GwLog *logger = commonData->logger;
|
GwLog *logger = commonData->logger;
|
||||||
|
|
||||||
|
int valueX, valueY;
|
||||||
|
int DsegFontSize;
|
||||||
|
|
||||||
// Old values for hold function
|
// Old values for hold function
|
||||||
static String svalue1old = "";
|
static String svalue1old = "";
|
||||||
static String unit1old = "";
|
static String unit1old = "";
|
||||||
@@ -35,11 +43,9 @@ class PageFourValues2 : public Page
|
|||||||
static String unit4old = "";
|
static String unit4old = "";
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
|
||||||
// bool simulation = config->getBool(config->useSimuData);
|
|
||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
// Get boat values #1
|
// Get boat values #1
|
||||||
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
||||||
@@ -91,7 +97,7 @@ class PageFourValues2 : public Page
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -113,25 +119,26 @@ class PageFourValues2 : public Page
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
|
valueX = 380;
|
||||||
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
|
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 90;
|
||||||
getdisplay().setCursor(100, 90);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
|
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 80;
|
||||||
getdisplay().setCursor(180, 77);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
valueY = 90;
|
||||||
getdisplay().setCursor(180, 90);
|
DsegFontSize = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue1); // Real value as formated string
|
printBoatValue(svalue1, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue1old); // Old value as formated string
|
printBoatValue(svalue1old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid1 == true){
|
if(valid1 == true){
|
||||||
svalue1old = svalue1; // Save the old value
|
svalue1old = svalue1; // Save the old value
|
||||||
@@ -162,24 +169,24 @@ class PageFourValues2 : public Page
|
|||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
|
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 180;
|
||||||
getdisplay().setCursor(100, 180);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
|
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
valueY = 170;
|
||||||
getdisplay().setCursor(180, 158);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
valueY = 180;
|
||||||
getdisplay().setCursor(180, 180);
|
DsegFontSize = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue2); // Real value as formated string
|
printBoatValue(svalue2, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue2old); // Old value as formated string
|
printBoatValue(svalue2old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid2 == true){
|
if(valid2 == true){
|
||||||
svalue2old = svalue2; // Save the old value
|
svalue2old = svalue2; // Save the old value
|
||||||
@@ -194,13 +201,13 @@ class PageFourValues2 : public Page
|
|||||||
// ############### Value 3 ################
|
// ############### Value 3 ################
|
||||||
|
|
||||||
// Show name
|
// Show name
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().setCursor(20, 220);
|
getdisplay().setCursor(20, 223);
|
||||||
getdisplay().print(name3); // Page name
|
getdisplay().print(name3); // Page name
|
||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
||||||
getdisplay().setCursor(20, 240);
|
getdisplay().setCursor(20, 270);
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(unit3); // Unit
|
getdisplay().print(unit3); // Unit
|
||||||
}
|
}
|
||||||
@@ -209,25 +216,26 @@ class PageFourValues2 : public Page
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
|
valueX = 190;
|
||||||
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
|
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
valueY = 250;
|
||||||
getdisplay().setCursor(50, 240);
|
DsegFontSize = 10;
|
||||||
}
|
}
|
||||||
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
|
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
valueY = 253;
|
||||||
getdisplay().setCursor(100, 240);
|
DsegFontSize = 10;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
valueY = 270;
|
||||||
getdisplay().setCursor(80, 270);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue3); // Real value as formated string
|
printBoatValue(svalue3, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue3old); // Old value as formated string
|
printBoatValue(svalue3old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid3 == true){
|
if(valid3 == true){
|
||||||
svalue3old = svalue3; // Save the old value
|
svalue3old = svalue3; // Save the old value
|
||||||
@@ -242,13 +250,13 @@ class PageFourValues2 : public Page
|
|||||||
// ############### Value 4 ################
|
// ############### Value 4 ################
|
||||||
|
|
||||||
// Show name
|
// Show name
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().setCursor(220, 220);
|
getdisplay().setCursor(220, 223);
|
||||||
getdisplay().print(name4); // Page name
|
getdisplay().print(name4); // Page name
|
||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
||||||
getdisplay().setCursor(220, 240);
|
getdisplay().setCursor(220, 270);
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(unit4); // Unit
|
getdisplay().print(unit4); // Unit
|
||||||
}
|
}
|
||||||
@@ -257,25 +265,26 @@ class PageFourValues2 : public Page
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
|
valueX = 390;
|
||||||
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
|
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
valueY = 250;
|
||||||
getdisplay().setCursor(250, 240);
|
DsegFontSize = 10;
|
||||||
}
|
}
|
||||||
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
|
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
valueY = 253;
|
||||||
getdisplay().setCursor(300, 240);
|
DsegFontSize = 10;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
valueY = 270;
|
||||||
getdisplay().setCursor(280, 270);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue4); // Real value as formated string
|
printBoatValue(svalue4, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue4old); // Old value as formated string
|
printBoatValue(svalue4old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid4 == true){
|
if(valid4 == true){
|
||||||
svalue4old = svalue4; // Save the old value
|
svalue4old = svalue4; // Save the old value
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "movingAvg.h" // Lib for moving average building
|
|
||||||
|
|
||||||
class PageGenerator : public Page
|
class PageGenerator : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageGenerator(CommonData &common){
|
PageGenerator(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
@@ -33,6 +36,7 @@ public:
|
|||||||
int genPower = config->getInt(config->genPower);
|
int genPower = config->getInt(config->genPower);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
String powerSensor = config->getString(config->usePowSensor3);
|
String powerSensor = config->getString(config->usePowSensor3);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
double value1 = 0; // Solar voltage
|
double value1 = 0; // Solar voltage
|
||||||
double value2 = 0; // Solar current
|
double value2 = 0; // Solar current
|
||||||
@@ -83,7 +87,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -96,20 +100,16 @@ public:
|
|||||||
getdisplay().print("Generator");
|
getdisplay().print("Generator");
|
||||||
|
|
||||||
// Show voltage type
|
// Show voltage type
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
|
||||||
getdisplay().setCursor(10, 140);
|
|
||||||
int bvoltage = 0;
|
int bvoltage = 0;
|
||||||
if(String(batVoltage) == "12V") bvoltage = 12;
|
if(String(batVoltage) == "12V") bvoltage = 12;
|
||||||
else bvoltage = 24;
|
else bvoltage = 24;
|
||||||
getdisplay().print(bvoltage);
|
printBoatValue(String(bvoltage), 10, 140, LEFT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("V");
|
getdisplay().print("V");
|
||||||
|
|
||||||
// Show solar power
|
// Show solar power
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalueGenPower = (genPower <= 999) ? String(genPower) : String(float(genPower/1000.0), 1);
|
||||||
getdisplay().setCursor(10, 200);
|
printBoatValue(svalueGenPower, 10, 200, LEFT, 20, smallDecimals);
|
||||||
if(genPower <= 999) getdisplay().print(genPower, 0);
|
|
||||||
if(genPower > 999) getdisplay().print(float(genPower/1000.0), 1);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
if(genPower <= 999) getdisplay().print("W");
|
if(genPower <= 999) getdisplay().print("W");
|
||||||
if(genPower > 999) getdisplay().print("kW");
|
if(genPower > 999) getdisplay().print("kW");
|
||||||
@@ -125,9 +125,7 @@ public:
|
|||||||
generatorGraphic(200, 95, commonData->fgcolor, commonData->bgcolor);
|
generatorGraphic(200, 95, commonData->fgcolor, commonData->bgcolor);
|
||||||
|
|
||||||
// Show load level in percent
|
// Show load level in percent
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(String(genPercentage), 150, 200, LEFT, 20, smallDecimals);
|
||||||
getdisplay().setCursor(150, 200);
|
|
||||||
getdisplay().print(genPercentage);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("%");
|
getdisplay().print("%");
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
@@ -152,54 +150,37 @@ public:
|
|||||||
getdisplay().print("Sensor Modul");
|
getdisplay().print("Sensor Modul");
|
||||||
|
|
||||||
// Reading bus data or using simulation data
|
// Reading bus data or using simulation data
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue1;
|
||||||
getdisplay().setCursor(260, 140);
|
|
||||||
if(simulation == true){
|
|
||||||
if(batVoltage == "12V"){
|
|
||||||
value1 = 12.0;
|
|
||||||
}
|
|
||||||
if(batVoltage == "24V"){
|
|
||||||
value1 = 24.0;
|
|
||||||
}
|
|
||||||
value1 += float(random(0, 5)) / 10; // Simulation data
|
|
||||||
getdisplay().print(value1,1);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// Check for valid real data, display also if hold values activated
|
|
||||||
if(valid1 == true || holdvalues == true){
|
if(valid1 == true || holdvalues == true){
|
||||||
// Resolution switching
|
svalue1 = formatValue(value1, String("formatXdr:U:V"), *commonData);
|
||||||
if(value1 <= 9.9) getdisplay().print(value1, 2);
|
} else {
|
||||||
if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1);
|
svalue1 = "---"; // Missing bus data
|
||||||
if(value1 > 99.9) getdisplay().print(value1, 0);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print("---"); // Missing bus data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
printBoatValue(svalue1, 355, 140, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("V");
|
getdisplay().print("V");
|
||||||
|
|
||||||
// Show actual current in A
|
// Show actual current in A
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue2;
|
||||||
getdisplay().setCursor(260, 200);
|
|
||||||
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
||||||
if(value2 <= 9.9) getdisplay().print(value2, 2);
|
svalue2 = formatValue(value2, String("formatXdr:I:A"), *commonData);
|
||||||
if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1);
|
|
||||||
if(value2 > 99.9) getdisplay().print(value2, 0);
|
|
||||||
}
|
}
|
||||||
else getdisplay().print("---");
|
else{
|
||||||
|
svalue2 = "---";
|
||||||
|
}
|
||||||
|
printBoatValue(svalue2, 355, 200, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("A");
|
getdisplay().print("A");
|
||||||
|
|
||||||
// Show actual consumption in W
|
// Show actual consumption in W
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue3;
|
||||||
getdisplay().setCursor(260, 260);
|
|
||||||
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
||||||
if(value3 <= 9.9) getdisplay().print(value3, 2);
|
svalue3 = formatValue(value3, String("formatXdr:G:"), *commonData);
|
||||||
if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1);
|
|
||||||
if(value3 > 99.9) getdisplay().print(value3, 0);
|
|
||||||
}
|
}
|
||||||
else getdisplay().print("---");
|
else{
|
||||||
|
svalue3 = "---";
|
||||||
|
}
|
||||||
|
printBoatValue(svalue3, 355, 260, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("W");
|
getdisplay().print("W");
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
//*******************************************************************************************
|
//*******************************************************************************************
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ public:
|
|||||||
// Print text centered on position x, y
|
// Print text centered on position x, y
|
||||||
int16_t x1, y1; // Return values of getTextBounds
|
int16_t x1, y1; // Return values of getTextBounds
|
||||||
uint16_t w, h; // Return values of getTextBounds
|
uint16_t w, h; // Return values of getTextBounds
|
||||||
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
displayGetTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
||||||
getdisplay().setCursor(x-w/2, y+h/2);
|
getdisplay().setCursor(x-w/2, y+h/2);
|
||||||
if(i % 30 == 0){
|
if(i % 30 == 0){
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "NetworkClient.h" // Network connection
|
#include "NetworkClient.h" // Network connection
|
||||||
#include "ImageDecoder.h" // Image decoder for navigation map
|
#include "ImageDecoder.h" // Image decoder for navigation map
|
||||||
|
#include <mbedtls/base64.h>
|
||||||
|
|
||||||
#include "Logo_OBP_400x300_sw.h"
|
#include "Logo_OBP_400x300_sw.h"
|
||||||
|
|
||||||
@@ -12,6 +13,54 @@
|
|||||||
NetworkClient net(JSON_BUFFER); // Define network client
|
NetworkClient net(JSON_BUFFER); // Define network client
|
||||||
ImageDecoder decoder; // Define image decoder
|
ImageDecoder decoder; // Define image decoder
|
||||||
|
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// Set to true to render a generated RGB565 color-bar test image.
|
||||||
|
static constexpr bool kShowRgb565StripeTestImage = false;
|
||||||
|
|
||||||
|
static void drawRgb565Image(int16_t x, int16_t y, const uint16_t *img, int16_t w, int16_t h) {
|
||||||
|
if (img == nullptr || w <= 0 || h <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int16_t yy = 0; yy < h; ++yy) {
|
||||||
|
const uint16_t *row = img + ((size_t)yy * (size_t)w);
|
||||||
|
for (int16_t xx = 0; xx < w; ++xx) {
|
||||||
|
getdisplay().drawPixel(x + xx, y + yy, row[xx]);
|
||||||
|
}
|
||||||
|
if ((yy & 0x0F) == 0) {
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createRgb565StripeImage(uint16_t *img, int16_t w, int16_t h) {
|
||||||
|
if (img == nullptr || w <= 0 || h <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
static const uint16_t stripes[] = {
|
||||||
|
0xF800, // red
|
||||||
|
0xFD20, // orange
|
||||||
|
0xFFE0, // yellow
|
||||||
|
0x07E0, // green
|
||||||
|
0x07FF, // cyan
|
||||||
|
0x001F, // blue
|
||||||
|
0xF81F, // magenta
|
||||||
|
0xFFFF // white
|
||||||
|
};
|
||||||
|
const int stripeCount = (int)(sizeof(stripes) / sizeof(stripes[0]));
|
||||||
|
|
||||||
|
for (int16_t y = 0; y < h; ++y) {
|
||||||
|
uint16_t *row = img + ((size_t)y * (size_t)w);
|
||||||
|
for (int16_t x = 0; x < w; ++x) {
|
||||||
|
int idx = ((int)x * stripeCount) / (int)w;
|
||||||
|
if (idx >= stripeCount) {
|
||||||
|
idx = stripeCount - 1;
|
||||||
|
}
|
||||||
|
row[x] = stripes[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
class PageNavigation : public Page
|
class PageNavigation : public Page
|
||||||
{
|
{
|
||||||
// Values for buttons
|
// Values for buttons
|
||||||
@@ -24,13 +73,19 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
|
|||||||
int imageBackupWidth = 0;
|
int imageBackupWidth = 0;
|
||||||
int imageBackupHeight = 0;
|
int imageBackupHeight = 0;
|
||||||
size_t imageBackupSize = 0;
|
size_t imageBackupSize = 0;
|
||||||
|
size_t imageBackupCapacity = 0;
|
||||||
bool hasImageBackup = false;
|
bool hasImageBackup = false;
|
||||||
|
bool imageBackupIsRgb565 = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageNavigation(CommonData &common){
|
PageNavigation(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
common.logger->logDebug(GwLog::LOG,"Instantiate PageNavigation");
|
common.logger->logDebug(GwLog::LOG,"Instantiate PageNavigation");
|
||||||
imageBackupData = (uint8_t*)heap_caps_malloc((GxEPD_WIDTH * GxEPD_HEIGHT), MALLOC_CAP_SPIRAM);
|
imageBackupCapacity = (size_t)GxEPD_WIDTH * (size_t)GxEPD_HEIGHT;
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
imageBackupCapacity *= 2U;
|
||||||
|
#endif
|
||||||
|
imageBackupData = (uint8_t*)heap_caps_malloc(imageBackupCapacity, MALLOC_CAP_SPIRAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set botton labels
|
// Set botton labels
|
||||||
@@ -295,6 +350,18 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
|
|||||||
mType = 9;
|
mType = 9;
|
||||||
dType = 1;
|
dType = 1;
|
||||||
}
|
}
|
||||||
|
else if(mapType == "C-Map"){
|
||||||
|
mType = 103486987;
|
||||||
|
dType = 1;
|
||||||
|
}
|
||||||
|
else if(mapType == "Garmin Fish"){
|
||||||
|
mType = 113486987;
|
||||||
|
dType = 1;
|
||||||
|
}
|
||||||
|
else if(mapType == "Garmin Nav"){
|
||||||
|
mType = 123486987;
|
||||||
|
dType = 1;
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
mType = 1;
|
mType = 1;
|
||||||
dType = 1;
|
dType = 1;
|
||||||
@@ -348,20 +415,31 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
|
|||||||
// For more details see: https://github.com/norbert-walter/maps-converter
|
// For more details see: https://github.com/norbert-walter/maps-converter
|
||||||
String url = String("http://") + server + ":" + port + // OBP Server
|
String url = String("http://") + server + ":" + port + // OBP Server
|
||||||
String("/get_image_json?") + // Service: Output B&W picture as JSON (Base64 + gzip)
|
String("/get_image_json?") + // Service: Output B&W picture as JSON (Base64 + gzip)
|
||||||
"zoom=" + zoom + // Default zoom level: 15
|
#ifdef TFT_DISPLAY
|
||||||
|
"oformat=3" + // Image output format in JSON: 3=RGB565 format
|
||||||
|
#else
|
||||||
|
"oformat=4" + // Image output format in JSON: 4=b/w 1-Bit format
|
||||||
|
#endif
|
||||||
|
"&zoom=" + zoom + // Default zoom level: 15
|
||||||
"&lat=" + String(latitude, 6) + // Latitude
|
"&lat=" + String(latitude, 6) + // Latitude
|
||||||
"&lon=" + String(longitude, 6) + // Longitude
|
"&lon=" + String(longitude, 6) + // Longitude
|
||||||
"&mrot=" + mapRot + // Rotation angle navigation map in degree
|
"&mrot=" + mapRot + // Rotation angle navigation map in degree
|
||||||
"&mtype=" + mType + // Default Map: Open Street Map
|
"&mtype=" + mType + // Default Map: Open Street Map
|
||||||
"&dtype=" + dType + // Dithering type: Atkinson dithering
|
#ifdef TFT_DISPLAY
|
||||||
|
"&itype=1" + // Image type: 1=Color
|
||||||
|
#else
|
||||||
|
"&itype=4" + // Image type: 4=b/w with dithering
|
||||||
|
#endif
|
||||||
|
"&dtype=" + dType + // Dithering type: Atkinson dithering (only activ when itype=4 otherwise inactive)
|
||||||
"&width=400" + // With navigation map
|
"&width=400" + // With navigation map
|
||||||
"&height=250" + // Height navigation map
|
"&height=250" + // Height navigation map
|
||||||
"&cutout=0" + // No picture cutouts
|
"&cutout=0" + // No picture cutouts (tab, border and alpha are unused when cutout=0)
|
||||||
"&tab=0" + // No tab size
|
"&tab=0" + // No tab size (only available when sqare cutouts selected coutout=3...7)
|
||||||
"&border=2" + // Border line size: 2 pixel
|
"&border=2" + // Border line size: 2 pixel (only available when sqare cutouts selected)
|
||||||
|
"&alpha=80" + // Alpha for tabs: 80% visible (only available when sqare cutouts selected)
|
||||||
"&symbol=2" + // Symbol: Triangle
|
"&symbol=2" + // Symbol: Triangle
|
||||||
"&srot=" + symbolRot + // Symbol rotation angle
|
"&srot=" + symbolRot + // Symbol rotation angle
|
||||||
"&ssize=15" + // Symbole size: 15 pixel
|
"&ssize=15" + // Symbole size: 15 pixel (center pointer)
|
||||||
"&grid=" + mapGrid // Show grid: On
|
"&grid=" + mapGrid // Show grid: On
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -371,19 +449,45 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
|
|||||||
// ############### Draw Navigation Map ################
|
// ############### Draw Navigation Map ################
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
|
// NEW: simple exponential backoff for 1 Hz polling (prevents connection-refused storms)
|
||||||
|
static uint32_t nextAllowedMs = 0;
|
||||||
|
static uint8_t failCount = 0;
|
||||||
|
|
||||||
|
uint32_t now = millis();
|
||||||
|
|
||||||
|
// NEW: if we are in backoff window, skip network call and use backup immediately
|
||||||
|
bool allowFetch = ((int32_t)(now - nextAllowedMs) >= 0);
|
||||||
|
|
||||||
// If a network connection to URL then load the navigation map
|
// If a network connection to URL then load the navigation map
|
||||||
if (net.fetchAndDecompressJson(url)) {
|
if (allowFetch && net.fetchAndDecompressJson(url)) {
|
||||||
|
|
||||||
auto& json = net.json(); // Extract JSON content
|
// NEW: reset backoff on success
|
||||||
int numPix = json["number_pixels"] | 0; // Read number of pixels
|
failCount = 0;
|
||||||
imgWidth = json["width"] | 0; // Read width of image
|
nextAllowedMs = now + 1000; // keep 1 Hz on success
|
||||||
imgHeight = json["height"] | 0; // Read height og image
|
|
||||||
|
|
||||||
const char* b64src = json["picture_base64"].as<const char*>(); // Read picture as Base64 content
|
int numPix = net.numberPixels(); // Read number of pixels
|
||||||
size_t b64len = strlen(b64src); // Calculate length of Base64 content
|
imgWidth = net.imageWidth(); // Read width of image
|
||||||
|
imgHeight = net.imageHeight(); // Read height of image
|
||||||
|
size_t requiredBytesMono = 0;
|
||||||
|
size_t requiredBytesRgb565 = 0;
|
||||||
|
if (imgWidth > 0 && imgHeight > 0){
|
||||||
|
requiredBytesMono = (size_t)((imgWidth + 7) / 8) * (size_t)imgHeight;
|
||||||
|
requiredBytesRgb565 = (size_t)imgWidth * (size_t)imgHeight * 2U;
|
||||||
|
}
|
||||||
|
if (requiredBytesMono == 0){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"Error PageNavigation: invalid image geometry w=%d h=%d",imgWidth,imgHeight);
|
||||||
|
return PAGE_UPDATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* b64src = net.pictureBase64(); // Read picture as Base64 content
|
||||||
|
if (b64src == nullptr){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"Error PageNavigation: picture_base64 missing");
|
||||||
|
return PAGE_UPDATE;
|
||||||
|
}
|
||||||
|
size_t b64len = net.pictureBase64Len(); // Calculate length of Base64 content
|
||||||
// Copy Base64 content in PSRAM
|
// Copy Base64 content in PSRAM
|
||||||
char* b64 = (char*) heap_caps_malloc(b64len + 1, MALLOC_CAP_SPIRAM); // Allcate PSRAM for Base64 content
|
char* b64 = (char*) heap_caps_malloc(b64len + 1, MALLOC_CAP_SPIRAM); // Allcate PSRAM for Base64 content
|
||||||
if (!b64) {
|
if (!b64) {
|
||||||
@@ -393,32 +497,81 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
|
|||||||
memcpy(b64, b64src, b64len + 1); // Copy Base64 content in PSRAM
|
memcpy(b64, b64src, b64len + 1); // Copy Base64 content in PSRAM
|
||||||
|
|
||||||
// Set image buffer in PSRAM
|
// Set image buffer in PSRAM
|
||||||
//size_t imgSize = getdisplay().width() * getdisplay().height();
|
size_t imgSize = (numPix > 0) ? (size_t)numPix : requiredBytesMono; // Calculate image size
|
||||||
size_t imgSize = numPix; // Calculate image size
|
if (imgSize < requiredBytesMono){
|
||||||
|
imgSize = requiredBytesMono;
|
||||||
|
}
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
if (imgSize < requiredBytesRgb565){
|
||||||
|
imgSize = requiredBytesRgb565;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
uint8_t* imageData = (uint8_t*) heap_caps_malloc(imgSize, MALLOC_CAP_SPIRAM); // Allocate PSRAM for image
|
uint8_t* imageData = (uint8_t*) heap_caps_malloc(imgSize, MALLOC_CAP_SPIRAM); // Allocate PSRAM for image
|
||||||
if (!imageData) {
|
if (!imageData) {
|
||||||
LOG_DEBUG(GwLog::ERROR,"Error PageNavigation: PPSRAM alloc image buffer failed");
|
LOG_DEBUG(GwLog::ERROR,"Error PageNavigation: PSRAM alloc image buffer failed");
|
||||||
free(b64);
|
free(b64);
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode Base64 content to image
|
// Decode Base64 content to image
|
||||||
size_t decodedSize = 0;
|
size_t decodedSize = 0;
|
||||||
decoder.decodeBase64(b64, imageData, imgSize, decodedSize);
|
bool decodeOk = decoder.decodeBase64(b64, b64len, imageData, imgSize, decodedSize);
|
||||||
|
if (!decodeOk || decodedSize < requiredBytesMono){
|
||||||
|
int base64Ret = mbedtls_base64_decode(
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
&decodedSize,
|
||||||
|
(const unsigned char*)b64,
|
||||||
|
b64len
|
||||||
|
);
|
||||||
|
LOG_DEBUG(GwLog::ERROR,
|
||||||
|
"Error PageNavigation: decode failed (ok=%d, decoded=%u, required=%u, b64ret=%d)",
|
||||||
|
decodeOk ? 1 : 0,
|
||||||
|
(unsigned int)decodedSize,
|
||||||
|
(unsigned int)requiredBytesMono,
|
||||||
|
base64Ret
|
||||||
|
);
|
||||||
|
free(b64);
|
||||||
|
free(imageData);
|
||||||
|
return PAGE_UPDATE;
|
||||||
|
}
|
||||||
|
|
||||||
// Copy actual navigation man to ackup map
|
bool imageIsRgb565 = false;
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
imageIsRgb565 = (decodedSize >= requiredBytesRgb565);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
if (kShowRgb565StripeTestImage) {
|
||||||
|
createRgb565StripeImage(reinterpret_cast<uint16_t*>(imageData), imgWidth, imgHeight);
|
||||||
|
decodedSize = requiredBytesRgb565;
|
||||||
|
imageIsRgb565 = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Copy actual navigation map to backup map
|
||||||
imageBackupWidth = imgWidth;
|
imageBackupWidth = imgWidth;
|
||||||
imageBackupHeight = imgHeight;
|
imageBackupHeight = imgHeight;
|
||||||
imageBackupSize = imgSize;
|
imageBackupSize = imgSize;
|
||||||
if (decodedSize > 0) {
|
if (decodedSize > 0 && imageBackupData != nullptr) {
|
||||||
memcpy(imageBackupData, imageData, decodedSize);
|
size_t copySize = (decodedSize > imageBackupCapacity) ? imageBackupCapacity : decodedSize;
|
||||||
imageBackupSize = decodedSize;
|
memcpy(imageBackupData, imageData, copySize);
|
||||||
|
imageBackupSize = copySize;
|
||||||
}
|
}
|
||||||
hasImageBackup = true;
|
imageBackupIsRgb565 = imageIsRgb565;
|
||||||
|
hasImageBackup = (imageBackupData != nullptr);
|
||||||
lostCounter = 0;
|
lostCounter = 0;
|
||||||
|
|
||||||
// Show image (navigation map)
|
// Show image (navigation map)
|
||||||
getdisplay().drawBitmap(0, 25, imageData, imgWidth, imgHeight, commonData->fgcolor);
|
#ifdef TFT_DISPLAY
|
||||||
|
if (imageIsRgb565) {
|
||||||
|
drawRgb565Image(0, 25, reinterpret_cast<const uint16_t*>(imageData), imgWidth, imgHeight);
|
||||||
|
} else {
|
||||||
|
displayDrawBitmap(0, 25, imageData, imgWidth, imgHeight, commonData->fgcolor);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
displayDrawBitmap(0, 25, imageData, imgWidth, imgHeight, commonData->fgcolor);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Clean PSRAM
|
// Clean PSRAM
|
||||||
free(b64);
|
free(b64);
|
||||||
@@ -426,12 +579,33 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
|
|||||||
}
|
}
|
||||||
// If no network connection then use backup navigation map
|
// If no network connection then use backup navigation map
|
||||||
else{
|
else{
|
||||||
// Show backup image (backup navigation map)
|
|
||||||
if (hasImageBackup) {
|
// NEW: update backoff only if we actually attempted a fetch (not when skipping due to backoff)
|
||||||
getdisplay().drawBitmap(0, 25, imageBackupData, imageBackupWidth, imageBackupHeight, commonData->fgcolor);
|
if (allowFetch) {
|
||||||
|
// NEW: exponential backoff: 1s,2s,4s,8s,16s,30s (capped)
|
||||||
|
if (failCount < 6) failCount++;
|
||||||
|
uint32_t backoffMs = 1000u << failCount;
|
||||||
|
if (backoffMs > 30000u) backoffMs = 30000u;
|
||||||
|
nextAllowedMs = now + backoffMs;
|
||||||
|
} else {
|
||||||
|
// NEW: we are currently backing off; do not increase failCount further
|
||||||
|
// nextAllowedMs stays unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show info: Connection lost when 5 page refreshes has a connection lost to the map server
|
// Show backup image (backup navigation map)
|
||||||
|
if (hasImageBackup) {
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
if (imageBackupIsRgb565) {
|
||||||
|
drawRgb565Image(0, 25, reinterpret_cast<const uint16_t*>(imageBackupData), imageBackupWidth, imageBackupHeight);
|
||||||
|
} else {
|
||||||
|
displayDrawBitmap(0, 25, imageBackupData, imageBackupWidth, imageBackupHeight, commonData->fgcolor);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
displayDrawBitmap(0, 25, imageBackupData, imageBackupWidth, imageBackupHeight, commonData->fgcolor);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show connection lost info when 5 page refreshes has a connection lost to the map server
|
||||||
// Short connection losts are uncritical
|
// Short connection losts are uncritical
|
||||||
if(lostCounter >= 5){
|
if(lostCounter >= 5){
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
@@ -444,7 +618,6 @@ bool showValues = false; // Show values HDT, SOG, DBT in navigation map
|
|||||||
lostCounter++; // Increment lost counter
|
lostCounter++; // Increment lost counter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ############### Draw Values ################
|
// ############### Draw Values ################
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
|
|
||||||
|
|||||||
@@ -19,17 +19,6 @@ private:
|
|||||||
HALF
|
HALF
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr char HORIZONTAL = 'H';
|
|
||||||
static constexpr char VERTICAL = 'V';
|
|
||||||
static constexpr int8_t FULL_SIZE = 0;
|
|
||||||
static constexpr int8_t HALF_SIZE_TOP = 1;
|
|
||||||
static constexpr int8_t HALF_SIZE_BOTTOM = 2;
|
|
||||||
|
|
||||||
static constexpr bool PRNT_NAME = true;
|
|
||||||
static constexpr bool NO_PRNT_NAME = false;
|
|
||||||
static constexpr bool PRNT_VALUE = true;
|
|
||||||
static constexpr bool NO_PRNT_VALUE = false;
|
|
||||||
|
|
||||||
int width; // Screen width
|
int width; // Screen width
|
||||||
int height; // Screen height
|
int height; // Screen height
|
||||||
|
|
||||||
@@ -42,8 +31,11 @@ private:
|
|||||||
bool useSimuData;
|
bool useSimuData;
|
||||||
bool holdValues;
|
bool holdValues;
|
||||||
String flashLED;
|
String flashLED;
|
||||||
String backlightMode;
|
bool smallDecimals;
|
||||||
String tempFormat;
|
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
// Old values for hold function
|
// Old values for hold function
|
||||||
String sValue1Old = "";
|
String sValue1Old = "";
|
||||||
@@ -51,38 +43,41 @@ private:
|
|||||||
|
|
||||||
// Data buffer pointer (owned by HstryBuffers)
|
// Data buffer pointer (owned by HstryBuffers)
|
||||||
RingBuffer<uint16_t>* dataHstryBuf = nullptr;
|
RingBuffer<uint16_t>* dataHstryBuf = nullptr;
|
||||||
std::unique_ptr<Chart> dataChart; // Chart object
|
std::unique_ptr<Chart> dataChart = nullptr; // Chart object
|
||||||
|
|
||||||
// display data value in display <mode> [FULL|HALF]
|
// display data value in display <mode> [FULL|HALF]
|
||||||
void showData(GwApi::BoatValue* bValue1, DisplayMode mode)
|
void showData(GwApi::BoatValue* bValue1, DisplayMode mode)
|
||||||
{
|
{
|
||||||
int nameXoff, nameYoff, unitXoff, unitYoff, value1Xoff, value1Yoff;
|
int nameXoff, nameYoff, unitX, unitY, unitXoff, unitYoff, valueX, valueY, valueXoff, valueYoff;
|
||||||
const GFXfont *nameFnt, *unitFnt, *valueFnt1, *valueFnt2, *valueFnt3;
|
const GFXfont *nameFnt, *unitFnt;
|
||||||
|
int valueFontSize1, valueFontSize2, valueFontSize3, DsegFontSize;
|
||||||
|
|
||||||
if (mode == FULL) { // full size data display
|
if (mode == FULL) { // full size data display
|
||||||
nameXoff = 0;
|
nameXoff = 0;
|
||||||
nameYoff = 0;
|
nameYoff = 0;
|
||||||
nameFnt = &Ubuntu_Bold32pt8b;
|
nameFnt = &Ubuntu_Bold32pt8b;
|
||||||
|
unitX = 380;
|
||||||
|
unitY = 100;
|
||||||
unitXoff = 0;
|
unitXoff = 0;
|
||||||
unitYoff = 0;
|
unitYoff = 0;
|
||||||
unitFnt = &Ubuntu_Bold20pt8b;
|
unitFnt = &Ubuntu_Bold20pt8b;
|
||||||
value1Xoff = 0;
|
valueXoff = 0;
|
||||||
value1Yoff = 0;
|
valueYoff = 0;
|
||||||
valueFnt1 = &Ubuntu_Bold20pt8b;
|
valueFontSize1 = 20;
|
||||||
valueFnt2 = &Ubuntu_Bold32pt8b;
|
valueFontSize2 = 30;
|
||||||
valueFnt3 = &DSEG7Classic_BoldItalic60pt7b;
|
valueFontSize3 = 60;
|
||||||
} else { // half size data and chart display
|
} else { // half size data and chart display
|
||||||
nameXoff = -10;
|
nameXoff = -10;
|
||||||
nameYoff = -34;
|
nameYoff = -34;
|
||||||
nameFnt = &Ubuntu_Bold20pt8b;
|
nameFnt = &Ubuntu_Bold20pt8b;
|
||||||
unitXoff = -295;
|
unitX = 10;
|
||||||
unitYoff = -119;
|
unitY = 121;
|
||||||
unitFnt = &Ubuntu_Bold12pt8b;
|
unitFnt = &Ubuntu_Bold12pt8b;
|
||||||
valueFnt1 = &Ubuntu_Bold12pt8b;
|
valueXoff = 0;
|
||||||
value1Xoff = 153;
|
valueYoff = -119;
|
||||||
value1Yoff = -119;
|
valueFontSize1 = 12;
|
||||||
valueFnt2 = &Ubuntu_Bold20pt8b;
|
valueFontSize2 = 20;
|
||||||
valueFnt3 = &DSEG7Classic_BoldItalic42pt7b;
|
valueFontSize3 = 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
String name1 = xdrDelete(bValue1->getName()); // Value name
|
String name1 = xdrDelete(bValue1->getName()); // Value name
|
||||||
@@ -99,32 +94,38 @@ private:
|
|||||||
getdisplay().print(name1); // name
|
getdisplay().print(name1); // name
|
||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
|
String unitName;
|
||||||
getdisplay().setFont(unitFnt);
|
getdisplay().setFont(unitFnt);
|
||||||
getdisplay().setCursor(305 + unitXoff, 240 + unitYoff);
|
|
||||||
|
|
||||||
if (holdValues) {
|
if (holdValues) {
|
||||||
getdisplay().print(unit1Old); // name
|
unitName = unit1Old;
|
||||||
} else {
|
} else {
|
||||||
getdisplay().print(unit1); // name
|
unitName = unit1;
|
||||||
|
}
|
||||||
|
if (mode == FULL) {
|
||||||
|
drawTextRalign(unitX, unitY, unitName);
|
||||||
|
} else {
|
||||||
|
getdisplay().setCursor(unitX, unitY);
|
||||||
|
getdisplay().print(unitName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch font depending on value format and adjust position
|
// Switch font depending on value format and adjust position
|
||||||
|
valueX = 380 + valueXoff;
|
||||||
if (bValue1->getFormat() == "formatLatitude" || bValue1->getFormat() == "formatLongitude") {
|
if (bValue1->getFormat() == "formatLatitude" || bValue1->getFormat() == "formatLongitude") {
|
||||||
getdisplay().setFont(valueFnt1);
|
valueY = 180 + valueYoff;
|
||||||
getdisplay().setCursor(20 + value1Xoff, 180 + value1Yoff);
|
DsegFontSize = valueFontSize1;
|
||||||
} else if (bValue1->getFormat() == "formatTime" || bValue1->getFormat() == "formatDate") {
|
} else if (bValue1->getFormat() == "formatTime" || bValue1->getFormat() == "formatDate") {
|
||||||
getdisplay().setFont(valueFnt2);
|
valueY = 200 + valueYoff;
|
||||||
getdisplay().setCursor(20 + value1Xoff, 200 + value1Yoff);
|
DsegFontSize = valueFontSize2;
|
||||||
} else {
|
} else {
|
||||||
getdisplay().setFont(valueFnt3);
|
valueY = 240 + valueYoff;
|
||||||
getdisplay().setCursor(20 + value1Xoff, 240 + value1Yoff);
|
DsegFontSize = valueFontSize3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if (!holdValues || useSimuData) {
|
if (!holdValues || useSimuData) {
|
||||||
getdisplay().print(sValue1); // Real value as formated string
|
printBoatValue(sValue1, valueX, valueY, RIGHT, DsegFontSize, smallDecimals);
|
||||||
} else {
|
} else {
|
||||||
getdisplay().print(sValue1Old); // Old value as formated string
|
printBoatValue(sValue1Old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid1 == true) {
|
if (valid1 == true) {
|
||||||
@@ -138,18 +139,19 @@ public:
|
|||||||
{
|
{
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
logger = commonData->logger;
|
logger = commonData->logger;
|
||||||
|
GwConfigHandler *config = commonData->config;
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::LOG, "Instantiate PageOneValue");
|
LOG_DEBUG(GwLog::LOG, "Instantiate PageOneValue");
|
||||||
|
|
||||||
width = getdisplay().width(); // Screen width
|
width = getdisplay().width(); // Screen width
|
||||||
height = getdisplay().height(); // Screen height
|
height = getdisplay().height(); // Screen height
|
||||||
|
getdisplay().setTextWrap(false);
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
// lengthformat = commonData->config->getString(commonData->config->lengthFormat);
|
useSimuData = config->getBool(config->useSimuData);
|
||||||
useSimuData = commonData->config->getBool(commonData->config->useSimuData);
|
|
||||||
holdValues = commonData->config->getBool(commonData->config->holdvalues);
|
holdValues = commonData->config->getBool(commonData->config->holdvalues);
|
||||||
flashLED = commonData->config->getString(commonData->config->flashLED);
|
flashLED = commonData->config->getString(commonData->config->flashLED);
|
||||||
backlightMode = commonData->config->getString(commonData->config->backlight);
|
smallDecimals = commonData->config->getBool(commonData->config->smallDecimals);
|
||||||
tempFormat = commonData->config->getString(commonData->config->tempFormat); // [K|°C|°F]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setupKeys()
|
virtual void setupKeys()
|
||||||
@@ -209,8 +211,6 @@ public:
|
|||||||
dataIntv = 3;
|
dataIntv = 3;
|
||||||
} else if (dataIntv == 3) {
|
} else if (dataIntv == 3) {
|
||||||
dataIntv = 4;
|
dataIntv = 4;
|
||||||
} else if (dataIntv == 4) {
|
|
||||||
dataIntv = 8;
|
|
||||||
} else {
|
} else {
|
||||||
dataIntv = 1;
|
dataIntv = 1;
|
||||||
}
|
}
|
||||||
@@ -245,10 +245,10 @@ public:
|
|||||||
dataHstryBuf = pageData.hstryBuffers->getBuffer(bValName1);
|
dataHstryBuf = pageData.hstryBuffers->getBuffer(bValName1);
|
||||||
|
|
||||||
if (dataHstryBuf) {
|
if (dataHstryBuf) {
|
||||||
dataChart.reset(new Chart(*dataHstryBuf, Chart::dfltChrtDta[bValFormat].range, *commonData, useSimuData));
|
dataChart.reset(new Chart(*dataHstryBuf, *commonData, useSimuData));
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: Created chart objects for %s", bValName1);
|
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: Created chart object for %s", bValName1);
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: No chart objects available for %s", bValName1);
|
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: No chart object available for %s", bValName1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,12 +262,13 @@ public:
|
|||||||
// Get boat value for page
|
// Get boat value for page
|
||||||
GwApi::BoatValue* bValue1 = pageData.values[0]; // Page boat data element
|
GwApi::BoatValue* bValue1 = pageData.values[0]; // Page boat data element
|
||||||
|
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
// Optical warning by limit violation (unused)
|
// Optical warning by limit violation (unused)
|
||||||
if (String(flashLED) == "Limit Violation") {
|
if (String(flashLED) == "Limit Violation") {
|
||||||
setBlinkingLED(false);
|
setBlinkingLED(false);
|
||||||
setFlashLED(false);
|
setFlashLED(false);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (bValue1 == NULL)
|
if (bValue1 == NULL)
|
||||||
return PAGE_OK; // no data, no page to display
|
return PAGE_OK; // no data, no page to display
|
||||||
|
|
||||||
@@ -276,21 +277,27 @@ public:
|
|||||||
// Draw page
|
// Draw page
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
|
displaySetPartialWindow(0, 0, width, height); // Set partial update
|
||||||
|
|
||||||
if (pageMode == VALUE || dataHstryBuf == nullptr) {
|
if (dataChart) { // Check only if dataChart object exists at all
|
||||||
|
if (!dataChart->isValid()) {
|
||||||
|
dataChart->init(); // try late initialization if chart object could not be properly initialized earlier due to missing boat data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageMode == VALUE || dataChart == nullptr) {
|
||||||
// show only data value; ignore other pageMode options if no chart supported boat data history buffer is available
|
// show only data value; ignore other pageMode options if no chart supported boat data history buffer is available
|
||||||
showData(bValue1, FULL);
|
showData(bValue1, FULL);
|
||||||
|
|
||||||
} else if (pageMode == CHART) { // show only data chart
|
} else if (pageMode == CHART) { // show only data chart
|
||||||
if (dataChart) {
|
if (dataChart) {
|
||||||
dataChart->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue1);
|
dataChart->showChrt(Chart::HORIZONTAL, Chart::FULL_SIZE, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *bValue1);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (pageMode == BOTH) { // show data value and chart
|
} else if (pageMode == BOTH) { // show data value and chart
|
||||||
showData(bValue1, HALF);
|
showData(bValue1, HALF);
|
||||||
if (dataChart) {
|
if (dataChart) {
|
||||||
dataChart->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue1);
|
dataChart->showChrt(Chart::HORIZONTAL, Chart::HALF_SIZE_RIGHT_BOTTOM, dataIntv, Chart::NO_PRNT_NAME, Chart::NO_PRNT_VALUE, *bValue1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
class PageRollPitch : public Page
|
class PageRollPitch : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageRollPitch(CommonData &common){
|
PageRollPitch(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
@@ -44,12 +48,15 @@ public:
|
|||||||
double rolloffset = roffset.toFloat()/360*(2*M_PI);
|
double rolloffset = roffset.toFloat()/360*(2*M_PI);
|
||||||
String poffset = config->getString(config->pitchOffset);
|
String poffset = config->getString(config->pitchOffset);
|
||||||
double pitchoffset = poffset.toFloat()/360*(2*M_PI);
|
double pitchoffset = poffset.toFloat()/360*(2*M_PI);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
// Get boat values for roll
|
// Get boat values for roll
|
||||||
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll)
|
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll)
|
||||||
String name1 = xdrDelete(bvalue1->getName()); // Value name
|
String name1 = xdrDelete(bvalue1->getName()); // Value name
|
||||||
name1 = name1.substring(0, 6); // String length limit for value name
|
name1 = name1.substring(0, 5); // String length limit for value name
|
||||||
bool valid1 = bvalue1->valid; // Valid information
|
bool valid1 = bvalue1->valid; // Valid information
|
||||||
|
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
|
||||||
|
unit1 = unit1.substring(0, 4); // String length limit for value unit
|
||||||
if(valid1 == true){
|
if(valid1 == true){
|
||||||
value1 = bvalue1->value + rolloffset; // Raw value for pitch
|
value1 = bvalue1->value + rolloffset; // Raw value for pitch
|
||||||
}
|
}
|
||||||
@@ -73,8 +80,10 @@ public:
|
|||||||
// Get boat values for pitch
|
// Get boat values for pitch
|
||||||
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (xdrPitch)
|
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (xdrPitch)
|
||||||
String name2 = xdrDelete(bvalue2->getName()); // Value name
|
String name2 = xdrDelete(bvalue2->getName()); // Value name
|
||||||
name2 = name2.substring(0, 6); // String length limit for value name
|
name2 = name2.substring(0, 5); // String length limit for value name
|
||||||
bool valid2 = bvalue2->valid; // Valid information
|
bool valid2 = bvalue2->valid; // Valid information
|
||||||
|
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
|
||||||
|
unit2 = unit2.substring(0, 4); // String length limit for value unit
|
||||||
if(valid2 == true){
|
if(valid2 == true){
|
||||||
value2 = bvalue2->value + pitchoffset; // Raw value for pitch
|
value2 = bvalue2->value + pitchoffset; // Raw value for pitch
|
||||||
}
|
}
|
||||||
@@ -86,7 +95,7 @@ public:
|
|||||||
value2 = 0;
|
value2 = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(value2/(2*PI)*360 > -10 && value2/(2*M_PI)*360 < 10){
|
if(value2/(2*M_PI)*360 > -10 && value2/(2*M_PI)*360 < 10){
|
||||||
svalue2 = String(value2/(2*M_PI)*360,1); // Convert raw value to string
|
svalue2 = String(value2/(2*M_PI)*360,1); // Convert raw value to string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -116,58 +125,49 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
// Show roll limit
|
// Show roll limit
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(String(rolllimit), 10, 65, LEFT, 20, smallDecimals); // Value
|
||||||
getdisplay().setCursor(10, 65);
|
|
||||||
getdisplay().print(rolllimit); // Value
|
|
||||||
//getdisplay().print(svalue1); // Value
|
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(10, 95);
|
getdisplay().setCursor(10, 95);
|
||||||
getdisplay().print("Limit"); // Name
|
getdisplay().print("Limit"); // Name
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(10, 115);
|
getdisplay().setCursor(10, 115);
|
||||||
getdisplay().print("DEG");
|
getdisplay().print("Deg");
|
||||||
|
|
||||||
// Horizintal separator left
|
// Horizintal separator left
|
||||||
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
|
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
|
||||||
|
|
||||||
// Show roll value
|
// Show roll value
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
if(holdvalues == false) printBoatValue(svalue1, 101, 270, RIGHT, 20, smallDecimals); // Value
|
||||||
getdisplay().setCursor(10, 270);
|
else printBoatValue(svalue1old, 101, 270, RIGHT, 20, smallDecimals);
|
||||||
if(holdvalues == false) getdisplay().print(svalue1); // Value
|
|
||||||
else getdisplay().print(svalue1old);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(10, 220);
|
getdisplay().setCursor(10, 220);
|
||||||
getdisplay().print(name1); // Name
|
getdisplay().print(name1); // Name
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(10, 190);
|
getdisplay().setCursor(10, 190);
|
||||||
getdisplay().print("Deg");
|
getdisplay().print(unit1); // Unit
|
||||||
|
|
||||||
// Horizintal separator right
|
// Horizintal separator right
|
||||||
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
|
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
|
||||||
|
|
||||||
// Show pitch value
|
// Show pitch value
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
if(holdvalues == false) printBoatValue(svalue2, 395, 270, RIGHT, 20, smallDecimals); // Value
|
||||||
getdisplay().setCursor(295, 270);
|
else printBoatValue(svalue2old, 395, 270, RIGHT, 20, smallDecimals);
|
||||||
if(holdvalues == false) getdisplay().print(svalue2); // Value
|
|
||||||
else getdisplay().print(svalue2old);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(335, 220);
|
drawTextRalign(395, 220, name2); // Name
|
||||||
getdisplay().print(name2); // Name
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(335, 190);
|
getdisplay().setCursor(335, 190);
|
||||||
getdisplay().print("Deg");
|
getdisplay().print(unit2); // Unit
|
||||||
|
|
||||||
//*******************************************************************************************
|
//*******************************************************************************************
|
||||||
|
|
||||||
// Draw instrument
|
// Draw instrument
|
||||||
int rInstrument = 100; // Radius of instrument
|
int rInstrument = 100; // Radius of instrument
|
||||||
float pi = 3.141592;
|
|
||||||
|
|
||||||
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
|
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
|
||||||
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
|
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
|
||||||
@@ -194,7 +194,7 @@ public:
|
|||||||
// Print text centered on position x, y
|
// Print text centered on position x, y
|
||||||
int16_t x1, y1; // Return values of getTextBounds
|
int16_t x1, y1; // Return values of getTextBounds
|
||||||
uint16_t w, h; // Return values of getTextBounds
|
uint16_t w, h; // Return values of getTextBounds
|
||||||
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
displayGetTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
||||||
getdisplay().setCursor(x-w/2, y+h/2);
|
getdisplay().setCursor(x-w/2, y+h/2);
|
||||||
if(i % 20 == 0){
|
if(i % 20 == 0){
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
@@ -264,11 +264,11 @@ public:
|
|||||||
int y0 = 150;
|
int y0 = 150;
|
||||||
int x1 = x0 + 50*cos(value1);
|
int x1 = x0 + 50*cos(value1);
|
||||||
int y1 = y0 + 50*sin(value1);
|
int y1 = y0 + 50*sin(value1);
|
||||||
int x2 = x0 + 50*cos(value1 - pi/2);
|
int x2 = x0 + 50*cos(value1 - M_PI/2);
|
||||||
int y2 = y0 + 50*sin(value1 - pi/2);
|
int y2 = y0 + 50*sin(value1 - M_PI/2);
|
||||||
getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (right triangle)
|
getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (right triangle)
|
||||||
x1 = x0 + 50*cos(value1 + pi);
|
x1 = x0 + 50*cos(value1 + M_PI);
|
||||||
y1 = y0 + 50*sin(value1 + pi);
|
y1 = y0 + 50*sin(value1 + M_PI);
|
||||||
getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (left triangle)
|
getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (left triangle)
|
||||||
getdisplay().fillRect(150, 160, 100, 4, commonData->fgcolor); // Water line
|
getdisplay().fillRect(150, 160, 100, 4, commonData->fgcolor); // Water line
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
//*******************************************************************************************
|
//*******************************************************************************************
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ public:
|
|||||||
// Print text centered on position x, y
|
// Print text centered on position x, y
|
||||||
int16_t x1, y1; // Return values of getTextBounds
|
int16_t x1, y1; // Return values of getTextBounds
|
||||||
uint16_t w, h; // Return values of getTextBounds
|
uint16_t w, h; // Return values of getTextBounds
|
||||||
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
displayGetTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
||||||
getdisplay().setCursor(x-w/2, y+h/2);
|
getdisplay().setCursor(x-w/2, y+h/2);
|
||||||
if(i % 30 == 0){
|
if(i % 30 == 0){
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
|||||||
@@ -3,157 +3,157 @@
|
|||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
|
|
||||||
const int SixValues_x1 = 5;
|
class PageSixValues : public Page {
|
||||||
const int SixValues_DeltaX = 200;
|
private:
|
||||||
|
GwLog* logger;
|
||||||
|
GwConfigHandler* config;
|
||||||
|
|
||||||
const int SixValues_y1 = 23;
|
bool holdValues;
|
||||||
const int SixValues_DeltaY = 83;
|
String flashLED;
|
||||||
|
String backlightMode;
|
||||||
|
bool smallDecimals;
|
||||||
|
|
||||||
const int HowManyValues = 6;
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
class PageSixValues : public Page
|
static constexpr int SixValues_x1 = 5;
|
||||||
{
|
static constexpr int SixValues_DeltaX = 200;
|
||||||
public:
|
static constexpr int SixValues_y1 = 22;
|
||||||
PageSixValues(CommonData &common){
|
static constexpr int SixValues_DeltaY = 83;
|
||||||
|
|
||||||
|
static constexpr int HowManyValues = 6;
|
||||||
|
// Old values for hold function
|
||||||
|
String OldDataText[HowManyValues] = { "", "", "", "", "", "" };
|
||||||
|
String OldDataUnit[HowManyValues] = { "", "", "", "", "", "" };
|
||||||
|
|
||||||
|
public:
|
||||||
|
PageSixValues(CommonData& common)
|
||||||
|
{
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
common.logger->logDebug(GwLog::LOG,"Instantiate PageSixValues");
|
config = commonData->config;
|
||||||
|
logger = commonData->logger;
|
||||||
|
LOG_DEBUG(GwLog::LOG, "Instantiate PageSixValues");
|
||||||
|
|
||||||
|
// Get config data
|
||||||
|
holdValues = config->getBool(config->holdvalues);
|
||||||
|
flashLED = config->getString(config->flashLED);
|
||||||
|
backlightMode = config->getString(config->backlight);
|
||||||
|
smallDecimals = config->getBool(config->smallDecimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int handleKey(int key){
|
virtual int handleKey(int key)
|
||||||
|
{
|
||||||
// Code for keylock
|
// Code for keylock
|
||||||
if(key == 11){
|
if (key == 11) {
|
||||||
commonData->keylock = !commonData->keylock;
|
commonData->keylock = !commonData->keylock;
|
||||||
return 0; // Commit the key
|
return 0; // Commit the key
|
||||||
}
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
int displayPage(PageData &pageData){
|
int displayPage(PageData& pageData)
|
||||||
GwConfigHandler *config = commonData->config;
|
{
|
||||||
GwLog *logger = commonData->logger;
|
GwApi::BoatValue* bvalue;
|
||||||
|
|
||||||
|
|
||||||
// Old values for hold function
|
|
||||||
static String OldDataText[HowManyValues] = {"", "", "", "", "", ""};
|
|
||||||
static String OldDataUnits[HowManyValues] = {"", "", "", "", "", ""};
|
|
||||||
|
|
||||||
// Get config data
|
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
|
||||||
// bool simulation = config->getBool(config->useSimuData);
|
|
||||||
bool holdvalues = config->getBool(config->holdvalues);
|
|
||||||
String flashLED = config->getString(config->flashLED);
|
|
||||||
String backlightMode = config->getString(config->backlight);
|
|
||||||
|
|
||||||
GwApi::BoatValue *bvalue;
|
|
||||||
String DataName[HowManyValues];
|
String DataName[HowManyValues];
|
||||||
double DataValue[HowManyValues];
|
double DataValue[HowManyValues];
|
||||||
bool DataValid[HowManyValues];
|
bool DataValid[HowManyValues];
|
||||||
String DataText[HowManyValues];
|
String DataText[HowManyValues];
|
||||||
String DataUnits[HowManyValues];
|
String DataUnit[HowManyValues];
|
||||||
String DataFormat[HowManyValues];
|
String DataFormat[HowManyValues];
|
||||||
|
int8_t DsegFontSize = 0;
|
||||||
|
|
||||||
for (int i = 0; i < HowManyValues; i++){
|
for (int i = 0; i < HowManyValues; i++) {
|
||||||
bvalue = pageData.values[i];
|
bvalue = pageData.values[i];
|
||||||
DataName[i] = xdrDelete(bvalue->getName());
|
DataName[i] = xdrDelete(bvalue->getName());
|
||||||
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
|
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
|
||||||
DataValue[i] = bvalue->value; // Value as double in SI unit
|
DataValue[i] = bvalue->value; // Value as double in SI unit
|
||||||
DataValid[i] = bvalue->valid;
|
DataValid[i] = bvalue->valid;
|
||||||
DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||||
DataUnits[i] = formatValue(bvalue, *commonData).unit;
|
DataUnit[i] = formatValue(bvalue, *commonData).unit;
|
||||||
DataFormat[i] = bvalue->getFormat(); // Unit of value
|
DataFormat[i] = bvalue->getFormat(); // Unit of value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
// Optical warning by limit violation (unused)
|
// Optical warning by limit violation (unused)
|
||||||
if(String(flashLED) == "Limit Violation"){
|
if (String(flashLED) == "Limit Violation") {
|
||||||
setBlinkingLED(false);
|
setBlinkingLED(false);
|
||||||
setFlashLED(false);
|
setFlashLED(false);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
|
if (bvalue == NULL)
|
||||||
|
return PAGE_OK;
|
||||||
|
|
||||||
// Draw page
|
// Draw page
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
getdisplay().setTextWrap(false);
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
for (int i = 0; i < ( HowManyValues / 2 ); i++){
|
for (int i = 0; i < (HowManyValues / 2); i++) {
|
||||||
if (i < (HowManyValues / 2) - 1) { // Don't draw horizontal line after last line of values -> standard design
|
if (i < (HowManyValues / 2) - 1) { // Don't draw horizontal line after last line of values
|
||||||
// Horizontal line 3 pix
|
// Horizontal line 3 px
|
||||||
getdisplay().fillRect(0, SixValues_y1+(i+1)*SixValues_DeltaY, 400, 3, commonData->fgcolor);
|
getdisplay().fillRect(0, SixValues_y1 + (i + 1) * SixValues_DeltaY, 400, 3, commonData->fgcolor);
|
||||||
}
|
}
|
||||||
for (int j = 0; j < 2; j++){
|
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
int ValueIndex = i * 2 + j;
|
int ValueIndex = i * 2 + j;
|
||||||
int x0 = SixValues_x1 + j * SixValues_DeltaX;
|
int x0 = SixValues_x1 + j * SixValues_DeltaX;
|
||||||
int y0 = SixValues_y1 + i * SixValues_DeltaY;
|
int y0 = SixValues_y1 + i * SixValues_DeltaY;
|
||||||
LOG_DEBUG(GwLog::LOG,"Drawing at PageSixValue: %d %s %f %s", ValueIndex, DataName[ValueIndex], DataValue[ValueIndex], DataFormat[ValueIndex] );
|
//LOG_DEBUG(GwLog::DEBUG, "Drawing at PageSixValue: %d %s %f %s %s", ValueIndex, DataName[ValueIndex],
|
||||||
|
// DataValue[ValueIndex], DataUnit[ValueIndex], DataFormat[ValueIndex]);
|
||||||
|
|
||||||
// Show name
|
// Show name
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(x0, y0+25);
|
getdisplay().setCursor(x0, y0 + 25);
|
||||||
getdisplay().print(DataName[ValueIndex]); // Page name
|
getdisplay().print(DataName[ValueIndex]); // Page name
|
||||||
|
|
||||||
// Show unit
|
// Show unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(x0, y0+72);
|
if (holdValues == false) {
|
||||||
if(holdvalues == false){
|
drawTextRalign(x0 + 187, y0 + 19, DataUnit[ValueIndex]); // Unit
|
||||||
getdisplay().print(DataUnits[ValueIndex]); // Unit
|
} else {
|
||||||
}
|
drawTextRalign(x0 + 187, y0 + 19, OldDataUnit[ValueIndex]);
|
||||||
else{
|
|
||||||
getdisplay().print(OldDataUnits[ValueIndex]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch font if format for any values
|
// Set font size depending on data type
|
||||||
if(DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude"){
|
if (DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude") {
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
DsegFontSize = 12;
|
||||||
getdisplay().setCursor(x0+10, y0+60);
|
} else if (DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate") {
|
||||||
|
DsegFontSize = 16;
|
||||||
}
|
}
|
||||||
else if(DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate"){
|
else if (DataFormat[ValueIndex] == "formatXdr:T:R") { // RPM
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
DsegFontSize = 20;
|
||||||
getdisplay().setCursor(x0+20,y0+55);
|
|
||||||
}
|
}
|
||||||
// pressure in hPa
|
else {
|
||||||
else if(DataFormat[ValueIndex] == "formatXdr:P:P"){
|
DsegFontSize = 26;
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
|
|
||||||
getdisplay().setCursor(x0+5, y0+70);
|
|
||||||
}
|
|
||||||
// RPM
|
|
||||||
else if(DataFormat[ValueIndex] == "formatXdr:T:R"){
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
|
||||||
getdisplay().setCursor(x0+25, y0+70);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
|
|
||||||
if ( DataText[ValueIndex][0] == '-' )
|
|
||||||
getdisplay().setCursor(x0+25, y0+70);
|
|
||||||
else
|
|
||||||
getdisplay().setCursor(x0+65, y0+70);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data value
|
||||||
if(holdvalues == false){
|
if (holdValues == false) {
|
||||||
getdisplay().print(DataText[ValueIndex]); // Real value as formated string
|
printBoatValue(DataText[ValueIndex], x0 + 187, y0 + 79, RIGHT, DsegFontSize, smallDecimals);
|
||||||
|
} else {
|
||||||
|
printBoatValue(OldDataText[ValueIndex], x0 + 187, y0 + 79, RIGHT, DsegFontSize, smallDecimals);
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
getdisplay().print(OldDataText[ValueIndex]); // Old value as formated string
|
if (DataValid[ValueIndex] == true) {
|
||||||
}
|
|
||||||
if(DataValid[ValueIndex] == true){
|
|
||||||
OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value
|
OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value
|
||||||
OldDataUnits[ValueIndex] = DataUnits[ValueIndex]; // Save the old unit
|
OldDataUnit[ValueIndex] = DataUnit[ValueIndex]; // Save the old unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Vertical line 3 pix
|
// Vertical line 3 px
|
||||||
getdisplay().fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
|
getdisplay().fillRect(SixValues_x1 + SixValues_DeltaX - 8, SixValues_y1 + i * SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
|
};
|
||||||
};
|
static Page* createPage(CommonData& common)
|
||||||
static Page *createPage(CommonData &common){
|
{
|
||||||
return new PageSixValues(common);
|
return new PageSixValues(common);
|
||||||
}/**
|
} /**
|
||||||
* with the code below we make this page known to the PageTask
|
* with the code below we make this page known to the PageTask
|
||||||
* we give it a type (name) that can be selected in the config
|
* we give it a type (name) that can be selected in the config
|
||||||
* we define which function is to be called
|
* we define which function is to be called
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
// current position
|
// current position
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
@@ -105,19 +105,19 @@ public:
|
|||||||
uint16_t w, h;
|
uint16_t w, h;
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
|
|
||||||
getdisplay().getTextBounds("N", 0, 150, &x1, &y1, &w, &h);
|
displayGetTextBounds("N", 0, 150, &x1, &y1, &w, &h);
|
||||||
getdisplay().setCursor(c.x - w / 2, c.y - r + h + 3);
|
getdisplay().setCursor(c.x - w / 2, c.y - r + h + 3);
|
||||||
getdisplay().print("N");
|
getdisplay().print("N");
|
||||||
|
|
||||||
getdisplay().getTextBounds("S", 0, 150, &x1, &y1, &w, &h);
|
displayGetTextBounds("S", 0, 150, &x1, &y1, &w, &h);
|
||||||
getdisplay().setCursor(c.x - w / 2, c.y + r - 3);
|
getdisplay().setCursor(c.x - w / 2, c.y + r - 3);
|
||||||
getdisplay().print("S");
|
getdisplay().print("S");
|
||||||
|
|
||||||
getdisplay().getTextBounds("E", 0, 150, &x1, &y1, &w, &h);
|
displayGetTextBounds("E", 0, 150, &x1, &y1, &w, &h);
|
||||||
getdisplay().setCursor(c.x + r - w - 3, c.y + h / 2);
|
getdisplay().setCursor(c.x + r - w - 3, c.y + h / 2);
|
||||||
getdisplay().print("E");
|
getdisplay().print("E");
|
||||||
|
|
||||||
getdisplay().getTextBounds("W", 0, 150, &x1, &y1, &w, &h);
|
displayGetTextBounds("W", 0, 150, &x1, &y1, &w, &h);
|
||||||
getdisplay().setCursor(c.x - r + 3 , c.y + h / 2);
|
getdisplay().setCursor(c.x - r + 3 , c.y + h / 2);
|
||||||
getdisplay().print("W");
|
getdisplay().print("W");
|
||||||
|
|
||||||
|
|||||||
+27
-46
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "movingAvg.h" // Lib for moving average building
|
|
||||||
|
|
||||||
class PageSolar : public Page
|
class PageSolar : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageSolar(CommonData &common){
|
PageSolar(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
@@ -32,6 +35,7 @@ public:
|
|||||||
int solPower = config->getInt(config->solarPower);
|
int solPower = config->getInt(config->solarPower);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
String powerSensor = config->getString(config->usePowSensor2);
|
String powerSensor = config->getString(config->usePowSensor2);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
double value1 = 0; // Solar voltage
|
double value1 = 0; // Solar voltage
|
||||||
double value2 = 0; // Solar current
|
double value2 = 0; // Solar current
|
||||||
@@ -82,7 +86,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -92,20 +96,16 @@ public:
|
|||||||
getdisplay().print("Solar");
|
getdisplay().print("Solar");
|
||||||
|
|
||||||
// Show voltage type
|
// Show voltage type
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
|
||||||
getdisplay().setCursor(10, 140);
|
|
||||||
int bvoltage = 0;
|
int bvoltage = 0;
|
||||||
if(String(batVoltage) == "12V") bvoltage = 12;
|
if(String(batVoltage) == "12V") bvoltage = 12;
|
||||||
else bvoltage = 24;
|
else bvoltage = 24;
|
||||||
getdisplay().print(bvoltage);
|
printBoatValue(String(bvoltage), 10, 140, LEFT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("V");
|
getdisplay().print("V");
|
||||||
|
|
||||||
// Show solar power
|
// Show solar power
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalueSolPower = (solPower <= 999) ? String(solPower) : String(float(solPower/1000.0), 1);
|
||||||
getdisplay().setCursor(10, 200);
|
printBoatValue(svalueSolPower, 10, 200, LEFT, 20, smallDecimals);
|
||||||
if(solPower <= 999) getdisplay().print(solPower, 0);
|
|
||||||
if(solPower > 999) getdisplay().print(float(solPower/1000.0), 1);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
if(solPower <= 999) getdisplay().print("W");
|
if(solPower <= 999) getdisplay().print("W");
|
||||||
if(solPower > 999) getdisplay().print("kW");
|
if(solPower > 999) getdisplay().print("kW");
|
||||||
@@ -121,9 +121,7 @@ public:
|
|||||||
solarGraphic(150, 45, commonData->fgcolor, commonData->bgcolor);
|
solarGraphic(150, 45, commonData->fgcolor, commonData->bgcolor);
|
||||||
|
|
||||||
// Show load level in percent
|
// Show load level in percent
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(String(solPercentage), 150, 200, LEFT, 20, smallDecimals);
|
||||||
getdisplay().setCursor(150, 200);
|
|
||||||
getdisplay().print(solPercentage);
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("%");
|
getdisplay().print("%");
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
@@ -148,54 +146,37 @@ public:
|
|||||||
getdisplay().print("Sensor Modul");
|
getdisplay().print("Sensor Modul");
|
||||||
|
|
||||||
// Reading bus data or using simulation data
|
// Reading bus data or using simulation data
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue1;
|
||||||
getdisplay().setCursor(260, 140);
|
|
||||||
if(simulation == true){
|
|
||||||
if(batVoltage == "12V"){
|
|
||||||
value1 = 12.0;
|
|
||||||
}
|
|
||||||
if(batVoltage == "24V"){
|
|
||||||
value1 = 24.0;
|
|
||||||
}
|
|
||||||
value1 += float(random(0, 5)) / 10; // Simulation data
|
|
||||||
getdisplay().print(value1,1);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// Check for valid real data, display also if hold values activated
|
|
||||||
if(valid1 == true || holdvalues == true){
|
if(valid1 == true || holdvalues == true){
|
||||||
// Resolution switching
|
svalue1 = formatValue(value1, String("formatXdr:U:V"), *commonData);
|
||||||
if(value1 <= 9.9) getdisplay().print(value1, 2);
|
} else {
|
||||||
if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1);
|
svalue1 = "---"; // Missing bus data
|
||||||
if(value1 > 99.9) getdisplay().print(value1, 0);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print("---"); // Missing bus data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
printBoatValue(svalue1, 355, 140, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("V");
|
getdisplay().print("V");
|
||||||
|
|
||||||
// Show actual current in A
|
// Show actual current in A
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue2;
|
||||||
getdisplay().setCursor(260, 200);
|
|
||||||
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
||||||
if(value2 <= 9.9) getdisplay().print(value2, 2);
|
svalue2 = formatValue(value2, String("formatXdr:I:A"), *commonData);
|
||||||
if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1);
|
|
||||||
if(value2 > 99.9) getdisplay().print(value2, 0);
|
|
||||||
}
|
}
|
||||||
else getdisplay().print("---");
|
else{
|
||||||
|
svalue2 = "---";
|
||||||
|
}
|
||||||
|
printBoatValue(svalue2, 355, 200, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("A");
|
getdisplay().print("A");
|
||||||
|
|
||||||
// Show actual consumption in W
|
// Show actual consumption in W
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
String svalue3;
|
||||||
getdisplay().setCursor(260, 260);
|
|
||||||
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
|
||||||
if(value3 <= 9.9) getdisplay().print(value3, 2);
|
svalue3 = formatValue(value3, String("formatXdr:G:"), *commonData);
|
||||||
if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1);
|
|
||||||
if(value3 > 99.9) getdisplay().print(value3, 0);
|
|
||||||
}
|
}
|
||||||
else getdisplay().print("---");
|
else{
|
||||||
|
svalue3 = "---";
|
||||||
|
}
|
||||||
|
printBoatValue(svalue3, 355, 260, RIGHT, 20, smallDecimals);
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().print("W");
|
getdisplay().print("W");
|
||||||
|
|
||||||
|
|||||||
+476
-155
@@ -6,8 +6,9 @@
|
|||||||
* Consists of some sub-pages with following content:
|
* Consists of some sub-pages with following content:
|
||||||
* 1. Hard and software information
|
* 1. Hard and software information
|
||||||
* 2. System settings
|
* 2. System settings
|
||||||
* 3. NMEA2000 device list
|
* 3. WiFi settings + message info
|
||||||
* 4. SD Card information if available
|
* 4. NMEA2000 device list
|
||||||
|
* 5. SD Card information if available
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
#include "images/logo64.xbm"
|
#include "images/logo64.xbm"
|
||||||
#include <esp32/clk.h>
|
#include <esp32/clk.h>
|
||||||
#include "qrcode.h"
|
#include "qrcode.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
#include "dirent.h"
|
#include "dirent.h"
|
||||||
@@ -37,9 +39,11 @@ private:
|
|||||||
String buzzer_mode;
|
String buzzer_mode;
|
||||||
uint8_t buzzer_power;
|
uint8_t buzzer_power;
|
||||||
String cpuspeed;
|
String cpuspeed;
|
||||||
|
String powermode;
|
||||||
String rtc_module;
|
String rtc_module;
|
||||||
String gps_module;
|
String gps_module;
|
||||||
String env_module;
|
String env_module;
|
||||||
|
String flashLED;
|
||||||
|
|
||||||
String batt_sensor;
|
String batt_sensor;
|
||||||
String solar_sensor;
|
String solar_sensor;
|
||||||
@@ -48,152 +52,68 @@ private:
|
|||||||
double homelat;
|
double homelat;
|
||||||
double homelon;
|
double homelon;
|
||||||
|
|
||||||
char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard
|
String systemName;
|
||||||
|
String apPasswd;
|
||||||
|
|
||||||
public:
|
char mode = 'N'; // (N)ormal, (S)ettings, (C)onfiguration, (D)evice list, c(A)rd
|
||||||
PageSystem(CommonData &common){
|
|
||||||
commonData = &common;
|
#ifdef PATCH_N2K
|
||||||
common.logger->logDebug(GwLog::LOG,"Instantiate PageSystem");
|
struct device {
|
||||||
if (hasFRAM) {
|
uint64_t NAME;
|
||||||
mode = fram.read(FRAM_SYSTEM_MODE);
|
uint8_t id;
|
||||||
common.logger->logDebug(GwLog::DEBUG, "Loaded mode '%c' from FRAM", mode);
|
char hex_name[17];
|
||||||
}
|
uint16_t manuf_code;
|
||||||
chipid = ESP.getEfuseMac();
|
const char *model;
|
||||||
simulation = common.config->getBool(common.config->useSimuData);
|
};
|
||||||
#ifdef BOARD_OBP40S3
|
std::vector<device> devicelist;
|
||||||
use_sdcard = common.config->getBool(common.config->useSDCard);
|
|
||||||
#endif
|
#endif
|
||||||
buzzer_mode = common.config->getString(common.config->buzzerMode);
|
|
||||||
buzzer_mode.toLowerCase();
|
|
||||||
buzzer_power = common.config->getInt(common.config->buzzerPower);
|
|
||||||
cpuspeed = common.config->getString(common.config->cpuSpeed);
|
|
||||||
env_module = common.config->getString(common.config->useEnvSensor);
|
|
||||||
rtc_module = common.config->getString(common.config->useRTC);
|
|
||||||
gps_module = common.config->getString(common.config->useGPS);
|
|
||||||
batt_sensor = common.config->getString(common.config->usePowSensor1);
|
|
||||||
solar_sensor = common.config->getString(common.config->usePowSensor2);
|
|
||||||
gen_sensor = common.config->getString(common.config->usePowSensor3);
|
|
||||||
rot_sensor = common.config->getString(common.config->useRotSensor);
|
|
||||||
homelat = common.config->getString(common.config->homeLAT).toDouble();
|
|
||||||
homelon = common.config->getString(common.config->homeLON).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupKeys() {
|
void incMode() {
|
||||||
commonData->keydata[0].label = "EXIT";
|
if (mode == 'N') { // Normal
|
||||||
commonData->keydata[1].label = "MODE";
|
|
||||||
commonData->keydata[2].label = "";
|
|
||||||
commonData->keydata[3].label = "RST";
|
|
||||||
commonData->keydata[4].label = "STBY";
|
|
||||||
commonData->keydata[5].label = "ILUM";
|
|
||||||
}
|
|
||||||
|
|
||||||
int handleKey(int key) {
|
|
||||||
// do *NOT* handle key #1 this handled by obp60task as exit
|
|
||||||
// Switch display mode
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler");
|
|
||||||
if (key == 2) {
|
|
||||||
if (mode == 'N') {
|
|
||||||
mode = 'S';
|
mode = 'S';
|
||||||
} else if (mode == 'S') {
|
} else if (mode == 'S') { // Settings
|
||||||
|
mode = 'W';
|
||||||
|
} else if (mode == 'W') { // Wifi
|
||||||
|
// mode = 'C';
|
||||||
mode = 'D';
|
mode = 'D';
|
||||||
} else if (mode == 'D') {
|
} else if (mode == 'C') { // Config
|
||||||
if (hasSDCard) {
|
mode = 'D';
|
||||||
mode = 'C';
|
} else if (mode == 'D') { // Device list
|
||||||
|
if (use_sdcard) {
|
||||||
|
mode = 'A'; // SD-Card
|
||||||
} else {
|
} else {
|
||||||
mode = 'N';
|
mode = 'N';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mode = 'N';
|
mode = 'N';
|
||||||
}
|
}
|
||||||
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#ifdef BOARD_OBP60S3
|
|
||||||
// grab cursor key to disable page navigation
|
|
||||||
if (key == 3) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// soft reset
|
|
||||||
if (key == 4) {
|
|
||||||
ESP.restart();
|
|
||||||
}
|
|
||||||
// standby / deep sleep
|
|
||||||
if (key == 5) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
|
|
||||||
deepSleep(*commonData);
|
|
||||||
}
|
|
||||||
// Code for keylock
|
|
||||||
if (key == 11) {
|
|
||||||
commonData->keylock = !commonData->keylock;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef BOARD_OBP40S3
|
|
||||||
// grab cursor keys to disable page navigation
|
|
||||||
if (key == 9 or key == 10) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// standby / deep sleep
|
|
||||||
if (key == 12) {
|
|
||||||
commonData->logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
|
|
||||||
deepSleep(*commonData);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayBarcode(String serialno, uint16_t x, uint16_t y, uint16_t s) {
|
void decMode() {
|
||||||
// Barcode with serial number
|
|
||||||
// x, y is top left corner
|
|
||||||
// s is pixel size of a single box
|
|
||||||
QRCode qrcode;
|
|
||||||
uint8_t qrcodeData[qrcode_getBufferSize(4)];
|
|
||||||
#ifdef BOARD_OBP40S3
|
|
||||||
String prefix = "OBP40:SN:";
|
|
||||||
#endif
|
|
||||||
#ifdef BOARD_OBP60S3
|
|
||||||
String prefix = "OBP60:SN:";
|
|
||||||
#endif
|
|
||||||
qrcode_initText(&qrcode, qrcodeData, 4, 0, (prefix + serialno).c_str());
|
|
||||||
int16_t x0 = x;
|
|
||||||
for (uint8_t j = 0; j < qrcode.size; j++) {
|
|
||||||
for (uint8_t i = 0; i < qrcode.size; i++) {
|
|
||||||
if (qrcode_getModule(&qrcode, i, j)) {
|
|
||||||
getdisplay().fillRect(x, y, s, s, commonData->fgcolor);
|
|
||||||
}
|
|
||||||
x += s;
|
|
||||||
}
|
|
||||||
y += s;
|
|
||||||
x = x0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int displayPage(PageData &pageData){
|
|
||||||
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"){
|
|
||||||
setBlinkingLED(false);
|
|
||||||
setFlashLED(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logging boat values
|
|
||||||
logger->logDebug(GwLog::LOG, "Drawing at PageSystem, Mode=%c", mode);
|
|
||||||
|
|
||||||
// Draw page
|
|
||||||
//***********************************************************
|
|
||||||
|
|
||||||
uint16_t x0 = 8; // left column
|
|
||||||
uint16_t y0 = 48; // data table starts here
|
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
|
||||||
|
|
||||||
if (mode == 'N') {
|
if (mode == 'N') {
|
||||||
|
if (use_sdcard) {
|
||||||
|
mode = 'A'; // SD-Card
|
||||||
|
} else {
|
||||||
|
mode = 'D'; // Device list
|
||||||
|
}
|
||||||
|
} else if (mode == 'S') { // Settings
|
||||||
|
mode = 'N';
|
||||||
|
} else if (mode == 'W') { // Wifi
|
||||||
|
mode = 'S';
|
||||||
|
} else if (mode == 'C') { // Config
|
||||||
|
mode = 'W';
|
||||||
|
} else if (mode == 'D') { // Device list
|
||||||
|
// mode = 'C';
|
||||||
|
mode = 'W';
|
||||||
|
} else {
|
||||||
|
mode = 'D';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayModeNormal() {
|
||||||
|
// Default system page view
|
||||||
|
|
||||||
|
uint16_t y0 = 155;
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(8, 48);
|
getdisplay().setCursor(8, 48);
|
||||||
@@ -202,7 +122,6 @@ public:
|
|||||||
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
|
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
y0 = 155;
|
|
||||||
|
|
||||||
char ssid[13];
|
char ssid[13];
|
||||||
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
|
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
|
||||||
@@ -315,16 +234,45 @@ public:
|
|||||||
getdisplay().setCursor(300, y0 + 32);
|
getdisplay().setCursor(300, y0 + 32);
|
||||||
getdisplay().print(String(RAM_free));
|
getdisplay().print(String(RAM_free));
|
||||||
|
|
||||||
} else if (mode == 'S') {
|
}
|
||||||
// Settings
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayModeSettings() {
|
||||||
|
// View some of the current settings
|
||||||
|
|
||||||
|
const uint16_t x0 = 8;
|
||||||
|
const uint16_t y0 = 72;
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(x0, 48);
|
getdisplay().setCursor(x0, 48);
|
||||||
getdisplay().print("System settings");
|
getdisplay().print("System Settings");
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
x0 = 8;
|
|
||||||
y0 = 72;
|
|
||||||
|
|
||||||
// left column
|
// left column
|
||||||
getdisplay().setCursor(x0, y0);
|
getdisplay().setCursor(x0, y0);
|
||||||
@@ -367,6 +315,12 @@ public:
|
|||||||
getdisplay().setCursor(120, y0 + 144);
|
getdisplay().setCursor(120, y0 + 144);
|
||||||
getdisplay().print(formatLongitude(homelon));
|
getdisplay().print(formatLongitude(homelon));
|
||||||
|
|
||||||
|
// Power
|
||||||
|
getdisplay().setCursor(x0, y0 + 176);
|
||||||
|
getdisplay().print("Power mode:");
|
||||||
|
getdisplay().setCursor(120, y0 + 176);
|
||||||
|
getdisplay().print(powermode);
|
||||||
|
|
||||||
// right column
|
// right column
|
||||||
getdisplay().setCursor(202, y0);
|
getdisplay().setCursor(202, y0);
|
||||||
getdisplay().print("Batt. sensor:");
|
getdisplay().print("Batt. sensor:");
|
||||||
@@ -385,18 +339,141 @@ public:
|
|||||||
getdisplay().setCursor(320, y0 + 32);
|
getdisplay().setCursor(320, y0 + 32);
|
||||||
getdisplay().print(gen_sensor);
|
getdisplay().print(gen_sensor);
|
||||||
|
|
||||||
// Gyro sensor
|
// TODO
|
||||||
|
// Gyro sensor (rotation)
|
||||||
|
getdisplay().setCursor(202, y0 + 48);
|
||||||
|
getdisplay().print("Rot. sensor:");
|
||||||
|
getdisplay().setCursor(320, y0 + 48);
|
||||||
|
getdisplay().print(rot_sensor);
|
||||||
|
|
||||||
|
// Temp.-sensor
|
||||||
|
// Power Mode
|
||||||
|
|
||||||
|
#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
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayModeNetwork() {
|
||||||
|
// View current network state
|
||||||
|
|
||||||
|
const uint16_t x0 = 5;
|
||||||
|
const uint16_t x1 = 210;
|
||||||
|
const uint16_t y0 = 72;
|
||||||
|
const uint16_t y1 = 170;
|
||||||
|
const uint16_t dy = 16;
|
||||||
|
|
||||||
|
String msgCount;
|
||||||
|
|
||||||
} else if (mode == 'C') {
|
|
||||||
// Card info
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(8, 48);
|
getdisplay().setCursor(x0, 48);
|
||||||
getdisplay().print("SD Card info");
|
getdisplay().print("Network Status");
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
|
||||||
x0 = 20;
|
getdisplay().setCursor(x0, y0);
|
||||||
y0 = 72;
|
getdisplay().print("Messages in/out:");
|
||||||
|
|
||||||
|
// left column
|
||||||
|
getdisplay().setCursor(x0, y0 + 1 * dy);
|
||||||
|
getdisplay().print("N2k:");
|
||||||
|
msgCount = String(commonData->status.n2kRx) + "/" + String(commonData->status.n2kTx);
|
||||||
|
getdisplay().setCursor(x0 + 80, y0 + 1 * dy);
|
||||||
|
getdisplay().print(msgCount);
|
||||||
|
|
||||||
|
getdisplay().setCursor(x0, y0 + 2 * dy);
|
||||||
|
getdisplay().print("TCPsrv:");
|
||||||
|
msgCount = String(commonData->status.tcpSerRx) + "/" + String(commonData->status.tcpSerTx);
|
||||||
|
getdisplay().setCursor(x0 + 80, y0 + 2 * dy);
|
||||||
|
getdisplay().print(msgCount);
|
||||||
|
|
||||||
|
getdisplay().setCursor(x0, y0 + 3 * dy);
|
||||||
|
getdisplay().print("TCPclnt:");
|
||||||
|
msgCount = String(commonData->status.tcpClRx) + "/" + String(commonData->status.tcpClTx);
|
||||||
|
getdisplay().setCursor(x0 + 80, y0 + 3 * dy);
|
||||||
|
getdisplay().print(msgCount);
|
||||||
|
|
||||||
|
// right column
|
||||||
|
getdisplay().setCursor(x1, y0 + 1 * dy);
|
||||||
|
getdisplay().print("UDP:");
|
||||||
|
msgCount = String(commonData->status.udprRx) + "/" + String(commonData->status.udpwTx);
|
||||||
|
getdisplay().setCursor(x1 + 80, y0 + 1 * dy);
|
||||||
|
getdisplay().print(msgCount);
|
||||||
|
|
||||||
|
getdisplay().setCursor(x1, y0 + 2 * dy);
|
||||||
|
getdisplay().print("Serial:");
|
||||||
|
msgCount = String(commonData->status.serRx) + "/" + String(commonData->status.serTx);
|
||||||
|
getdisplay().setCursor(x1 + 80, y0 + 2 * dy);
|
||||||
|
getdisplay().print(msgCount);
|
||||||
|
|
||||||
|
getdisplay().setCursor(x1, y0 + 3 * dy);
|
||||||
|
getdisplay().print("USB:");
|
||||||
|
msgCount = String(commonData->status.usbRx) + "/" + String(commonData->status.usbTx);
|
||||||
|
getdisplay().setCursor(x1 + 80, y0 + 3 * dy);
|
||||||
|
getdisplay().print(msgCount);
|
||||||
|
|
||||||
|
// Wifi state
|
||||||
|
getdisplay().setCursor(x0, y1 + 0 * dy);
|
||||||
|
getdisplay().print("WiFi:");
|
||||||
|
|
||||||
|
getdisplay().setCursor(x0, y1 + 1 * dy);
|
||||||
|
getdisplay().print("Access point:");
|
||||||
|
getdisplay().setCursor(x0 + 120, y1 + 1 * dy);
|
||||||
|
getdisplay().print(commonData->status.wifiApOn ? "on" : "off");
|
||||||
|
|
||||||
|
getdisplay().setCursor(x0, y1 + 2 * dy);
|
||||||
|
getdisplay().print("SSID:");
|
||||||
|
getdisplay().setCursor(x0 + 120, y1 + 2 * dy);
|
||||||
|
getdisplay().print(systemName);
|
||||||
|
|
||||||
|
getdisplay().setCursor(x0, y1 + 3 * dy);
|
||||||
|
getdisplay().print("IP address:");
|
||||||
|
getdisplay().setCursor(x0 + 120, y1 + 3 * dy);
|
||||||
|
getdisplay().print(commonData->status.wifiApIp);
|
||||||
|
|
||||||
|
getdisplay().setCursor(x0, y1 + 4 * dy + 5);
|
||||||
|
getdisplay().print("Client WiFi:");
|
||||||
|
getdisplay().setCursor(x0 + 120, y1 + 4 * dy + 5);
|
||||||
|
getdisplay().print(commonData->status.wifiClientOn ? "on" : "off");
|
||||||
|
|
||||||
|
getdisplay().setCursor(x0, y1 + 5 * dy + 5);
|
||||||
|
getdisplay().print("SSID:");
|
||||||
|
getdisplay().setCursor(x0 + 120, y1 + 5 * dy + 5);
|
||||||
|
getdisplay().print(commonData->status.wifiClientSSID);
|
||||||
|
|
||||||
|
getdisplay().setCursor(x0, y1 + 6 * dy + 5);
|
||||||
|
getdisplay().print("IP address:");
|
||||||
|
getdisplay().setCursor(x0+ 120, y1 + 6 * dy + 5);
|
||||||
|
getdisplay().print(commonData->status.wifiClientIp);
|
||||||
|
|
||||||
|
displayWifiBarcode(systemName, apPasswd, 320, y1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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().setCursor(x0, y0);
|
||||||
#ifdef BOARD_OBP60S3
|
#ifdef BOARD_OBP60S3
|
||||||
// This mode should not be callable by devices without card hardware
|
// This mode should not be callable by devices without card hardware
|
||||||
@@ -423,7 +500,7 @@ public:
|
|||||||
// Simple test for magic file in root
|
// Simple test for magic file in root
|
||||||
getdisplay().setCursor(x0, y0 + 32);
|
getdisplay().setCursor(x0, y0 + 32);
|
||||||
String file_magic = MOUNT_POINT "/magic.dat";
|
String file_magic = MOUNT_POINT "/magic.dat";
|
||||||
logger->logDebug(GwLog::LOG, "Test magicfile: %s", file_magic.c_str());
|
commonData->logger->logDebug(GwLog::LOG, "Test magicfile: %s", file_magic.c_str());
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(file_magic.c_str(), &st) == 0) {
|
if (stat(file_magic.c_str(), &st) == 0) {
|
||||||
getdisplay().printf("File %s exists", file_magic.c_str());
|
getdisplay().printf("File %s exists", file_magic.c_str());
|
||||||
@@ -435,7 +512,7 @@ public:
|
|||||||
DIR* dir = opendir(MOUNT_POINT);
|
DIR* dir = opendir(MOUNT_POINT);
|
||||||
int dy = 0;
|
int dy = 0;
|
||||||
if (dir != NULL) {
|
if (dir != NULL) {
|
||||||
logger->logDebug(GwLog::LOG, "Root directory: %s", MOUNT_POINT);
|
commonData->logger->logDebug(GwLog::LOG, "Root directory: %s", MOUNT_POINT);
|
||||||
struct dirent* entry;
|
struct dirent* entry;
|
||||||
while (((entry = readdir(dir)) != NULL) and (dy < 140)) {
|
while (((entry = readdir(dir)) != NULL) and (dy < 140)) {
|
||||||
getdisplay().setCursor(x0, y0 + 64 + dy);
|
getdisplay().setCursor(x0, y0 + 64 + dy);
|
||||||
@@ -445,32 +522,276 @@ public:
|
|||||||
getdisplay().print("/");
|
getdisplay().print("/");
|
||||||
}
|
}
|
||||||
dy += 20;
|
dy += 20;
|
||||||
logger->logDebug(GwLog::DEBUG, " %s type %d", entry->d_name, entry->d_type);
|
commonData->logger->logDebug(GwLog::DEBUG, " %s type %d", entry->d_name, entry->d_type);
|
||||||
}
|
}
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
} else {
|
} else {
|
||||||
logger->logDebug(GwLog::LOG, "Failed to open root directory");
|
commonData->logger->logDebug(GwLog::LOG, "Failed to open root directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
void displayModeDevicelist() {
|
||||||
// NMEA2000 device list
|
// NMEA2000 device list
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(8, 48);
|
getdisplay().setCursor(8, 48);
|
||||||
getdisplay().print("NMEA2000 device list");
|
getdisplay().print("NMEA2000 device list");
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(20, 80);
|
getdisplay().setCursor(20, 70);
|
||||||
getdisplay().print("RxD: ");
|
getdisplay().print("RxD: ");
|
||||||
getdisplay().print(String(commonData->status.n2kRx));
|
getdisplay().print(String(commonData->status.n2kRx));
|
||||||
getdisplay().setCursor(20, 100);
|
getdisplay().setCursor(120, 70);
|
||||||
getdisplay().print("TxD: ");
|
getdisplay().print("TxD: ");
|
||||||
getdisplay().print(String(commonData->status.n2kTx));
|
getdisplay().print(String(commonData->status.n2kTx));
|
||||||
|
|
||||||
|
#ifdef PATCH_N2K
|
||||||
|
uint16_t x0 = 20;
|
||||||
|
uint16_t y0 = 100;
|
||||||
|
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
||||||
|
getdisplay().setCursor(x0, y0);
|
||||||
|
getdisplay().print("ID");
|
||||||
|
getdisplay().setCursor(x0 + 50, y0);
|
||||||
|
getdisplay().print("Model");
|
||||||
|
getdisplay().setCursor(x0 + 250, y0);
|
||||||
|
getdisplay().print("Manuf.");
|
||||||
|
getdisplay().drawLine(18, y0 + 4, 360 , y0 + 4 , commonData->fgcolor);
|
||||||
|
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
y0 = 120;
|
||||||
|
uint8_t n_dev = 0;
|
||||||
|
for (const device& item : devicelist) {
|
||||||
|
if (n_dev > 8) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
getdisplay().setCursor(x0, y0 + n_dev * 20);
|
||||||
|
getdisplay().print(item.id);
|
||||||
|
getdisplay().setCursor(x0 + 50, y0 + n_dev * 20);
|
||||||
|
getdisplay().print(item.model);
|
||||||
|
getdisplay().setCursor(x0 + 250, y0 + n_dev * 20);
|
||||||
|
getdisplay().print(item.manuf_code);
|
||||||
|
n_dev++;
|
||||||
|
}
|
||||||
|
getdisplay().setCursor(x0, y0 + (n_dev + 1) * 20);
|
||||||
|
if (n_dev == 0) {
|
||||||
|
getdisplay().printf("no devices found on bus");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
getdisplay().drawLine(18, y0 + n_dev * 20, 360 , y0 + n_dev * 20, commonData->fgcolor);
|
||||||
|
getdisplay().printf("%d devices of %d in total", n_dev, devicelist.size());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
getdisplay().setCursor(20, 100);
|
||||||
|
getdisplay().print("NMEA2000 not exposed to obp60 task");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
PageSystem(CommonData &common){
|
||||||
|
commonData = &common;
|
||||||
|
commonData->logger->logDebug(GwLog::LOG,"Instantiate PageSystem");
|
||||||
|
if (hasFRAM) {
|
||||||
|
mode = fram.read(FRAM_SYSTEM_MODE);
|
||||||
|
commonData->logger->logDebug(GwLog::DEBUG, "Loaded mode '%c' from FRAM", mode);
|
||||||
|
}
|
||||||
|
chipid = ESP.getEfuseMac();
|
||||||
|
simulation = common.config->getBool(common.config->useSimuData);
|
||||||
|
#ifdef BOARD_OBP40S3
|
||||||
|
use_sdcard = common.config->getBool(common.config->useSDCard);
|
||||||
|
#endif
|
||||||
|
buzzer_mode = common.config->getString(common.config->buzzerMode);
|
||||||
|
buzzer_mode.toLowerCase();
|
||||||
|
buzzer_power = common.config->getInt(common.config->buzzerPower);
|
||||||
|
cpuspeed = common.config->getString(common.config->cpuSpeed);
|
||||||
|
powermode = common.config->getString(common.config->powerMode);
|
||||||
|
env_module = common.config->getString(common.config->useEnvSensor);
|
||||||
|
rtc_module = common.config->getString(common.config->useRTC);
|
||||||
|
gps_module = common.config->getString(common.config->useGPS);
|
||||||
|
batt_sensor = common.config->getString(common.config->usePowSensor1);
|
||||||
|
solar_sensor = common.config->getString(common.config->usePowSensor2);
|
||||||
|
gen_sensor = common.config->getString(common.config->usePowSensor3);
|
||||||
|
rot_sensor = common.config->getString(common.config->useRotSensor);
|
||||||
|
homelat = common.config->getString(common.config->homeLAT).toDouble();
|
||||||
|
homelon = common.config->getString(common.config->homeLON).toDouble();
|
||||||
|
flashLED = common.config->getString(common.config->flashLED);
|
||||||
|
systemName = common.config->getString(common.config->systemName);
|
||||||
|
apPasswd = common.config->getString(common.config->apPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupKeys() {
|
||||||
|
commonData->keydata[0].label = "EXIT";
|
||||||
|
commonData->keydata[1].label = "MODE";
|
||||||
|
commonData->keydata[2].label = "";
|
||||||
|
commonData->keydata[3].label = "RST";
|
||||||
|
commonData->keydata[4].label = "STBY";
|
||||||
|
commonData->keydata[5].label = "ILUM";
|
||||||
|
}
|
||||||
|
|
||||||
|
int handleKey(int key) {
|
||||||
|
// do *NOT* handle key #1 this handled by obp60task as exit
|
||||||
|
// Switch display mode
|
||||||
|
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler");
|
||||||
|
if (key == 2) {
|
||||||
|
incMode();
|
||||||
|
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
|
// grab cursor key to disable page navigation
|
||||||
|
if (key == 3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// soft reset
|
||||||
|
if (key == 4) {
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
// standby / deep sleep
|
||||||
|
if (key == 5) {
|
||||||
|
commonData->logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
|
||||||
|
deepSleep(*commonData);
|
||||||
|
}
|
||||||
|
// Code for keylock
|
||||||
|
if (key == 11) {
|
||||||
|
commonData->keylock = !commonData->keylock;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef BOARD_OBP40S3
|
||||||
|
// use cursor keys for local mode navigation
|
||||||
|
if (key == 9) {
|
||||||
|
incMode();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (key == 10) {
|
||||||
|
decMode();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// standby / deep sleep
|
||||||
|
if (key == 12) {
|
||||||
|
commonData->logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
|
||||||
|
deepSleep(*commonData);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayBarcode(String serialno, uint16_t x, uint16_t y, uint16_t s) {
|
||||||
|
// Barcode with serial number
|
||||||
|
// x, y is top left corner
|
||||||
|
// s is pixel size of a single box
|
||||||
|
QRCode qrcode;
|
||||||
|
uint8_t qrcodeData[qrcode_getBufferSize(4)];
|
||||||
|
#ifdef BOARD_OBP40S3
|
||||||
|
String prefix = "OBP40:SN:";
|
||||||
|
#endif
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
|
String prefix = "OBP60:SN:";
|
||||||
|
#endif
|
||||||
|
qrcode_initText(&qrcode, qrcodeData, 4, 0, (prefix + serialno).c_str());
|
||||||
|
int16_t x0 = x;
|
||||||
|
for (uint8_t j = 0; j < qrcode.size; j++) {
|
||||||
|
for (uint8_t i = 0; i < qrcode.size; i++) {
|
||||||
|
if (qrcode_getModule(&qrcode, i, j)) {
|
||||||
|
getdisplay().fillRect(x, y, s, s, commonData->fgcolor);
|
||||||
|
}
|
||||||
|
x += s;
|
||||||
|
}
|
||||||
|
y += s;
|
||||||
|
x = x0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayWifiBarcode(String ssid, String passwd, uint16_t x, uint16_t y, uint16_t s) {
|
||||||
|
// Barcode for wifi access point
|
||||||
|
// x, y is top left corner
|
||||||
|
// s is pixel size of a single box
|
||||||
|
|
||||||
|
QRCode qrcode;
|
||||||
|
uint8_t qrcodeData[qrcode_getBufferSize(4)];
|
||||||
|
String text = "WIFI:S:" + String(ssid) + ";T:WPA;P:" + String(passwd) + ";;"; // Content for QR code: "WIFI:S:mySSID;T:WPA;P:myPASSWORD;;"
|
||||||
|
const char *qrcodecontent = text.c_str();
|
||||||
|
|
||||||
|
qrcode_initText(&qrcode, qrcodeData, 4, 0, qrcodecontent);
|
||||||
|
int16_t x0 = x;
|
||||||
|
for (uint8_t j = 0; j < qrcode.size; j++) {
|
||||||
|
for (uint8_t i = 0; i < qrcode.size; i++) {
|
||||||
|
if (qrcode_getModule(&qrcode, i, j)) {
|
||||||
|
getdisplay().fillRect(x, y, s, s, commonData->fgcolor);
|
||||||
|
}
|
||||||
|
x += s;
|
||||||
|
}
|
||||||
|
y += s;
|
||||||
|
x = x0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayNew(PageData &pageData) {
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
|
// Clear optical warning
|
||||||
|
if (flashLED == "Limit Violation") {
|
||||||
|
setBlinkingLED(false);
|
||||||
|
setFlashLED(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PATCH_N2K
|
||||||
|
// load current device list
|
||||||
|
tN2kDeviceList *pDevList = pageData.api->getN2kDeviceList();
|
||||||
|
// TODO check if changed
|
||||||
|
if (pDevList->ReadResetIsListUpdated()) {
|
||||||
|
// only reload if changed
|
||||||
|
devicelist.clear();
|
||||||
|
for (uint8_t i = 0; i <= 252; i++) {
|
||||||
|
const tNMEA2000::tDevice *d = pDevList->FindDeviceBySource(i);
|
||||||
|
if (d == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
device dev;
|
||||||
|
dev.id = i;
|
||||||
|
dev.NAME = d->GetName();
|
||||||
|
snprintf(dev.hex_name, sizeof(dev.hex_name), "%08X%08X", (uint32_t)(dev.NAME >> 32), (uint32_t)(dev.NAME & 0xFFFFFFFF));
|
||||||
|
dev.manuf_code = d->GetManufacturerCode();
|
||||||
|
dev.model = d->GetModelID();
|
||||||
|
devicelist.push_back(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
int displayPage(PageData &pageData){
|
||||||
|
|
||||||
|
// Logging page information
|
||||||
|
commonData->logger->logDebug(GwLog::LOG, "Drawing at PageSystem, Mode=%c", mode);
|
||||||
|
|
||||||
|
// Set display in partial refresh mode
|
||||||
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
|
// call current system page
|
||||||
|
switch (mode) {
|
||||||
|
case 'N':
|
||||||
|
displayModeNormal();
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
displayModeSettings();
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
displayModeNetwork();
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
displayModeConfig();
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
displayModeSDCard();
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
displayModeDevicelist();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update display
|
// Update display
|
||||||
getdisplay().nextPage(); // Partial update (fast)
|
displayNextPage(); // Partial update (fast)
|
||||||
return PAGE_OK;
|
return PAGE_OK;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
|
|
||||||
class PageThreeValues : public Page
|
class PageThreeValues : public Page
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageThreeValues(CommonData &common){
|
PageThreeValues(CommonData &common){
|
||||||
commonData = &common;
|
commonData = &common;
|
||||||
@@ -24,6 +29,9 @@ class PageThreeValues : public Page
|
|||||||
GwConfigHandler *config = commonData->config;
|
GwConfigHandler *config = commonData->config;
|
||||||
GwLog *logger = commonData->logger;
|
GwLog *logger = commonData->logger;
|
||||||
|
|
||||||
|
int valueX, valueY;
|
||||||
|
int DsegFontSize;
|
||||||
|
|
||||||
// Old values for hold function
|
// Old values for hold function
|
||||||
static String svalue1old = "";
|
static String svalue1old = "";
|
||||||
static String unit1old = "";
|
static String unit1old = "";
|
||||||
@@ -33,11 +41,10 @@ class PageThreeValues : public Page
|
|||||||
static String unit3old = "";
|
static String unit3old = "";
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
|
||||||
// bool simulation = config->getBool(config->useSimuData);
|
// bool simulation = config->getBool(config->useSimuData);
|
||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
// Get boat values #1
|
// Get boat values #1
|
||||||
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
||||||
@@ -80,7 +87,7 @@ class PageThreeValues : public Page
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
/// Set display in partial refresh mode
|
/// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
// ############### Value 1 ################
|
// ############### Value 1 ################
|
||||||
|
|
||||||
@@ -101,25 +108,26 @@ class PageThreeValues : public Page
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
|
valueX = 380;
|
||||||
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
|
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
valueY = 90;
|
||||||
getdisplay().setCursor(50, 90);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
|
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
valueY = 80;
|
||||||
getdisplay().setCursor(170, 68);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
valueY = 90;
|
||||||
getdisplay().setCursor(180, 90);
|
DsegFontSize = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue1); // Real value as formated string
|
printBoatValue(svalue1, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue1old); // Old value as formated string
|
printBoatValue(svalue1old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
}
|
}
|
||||||
if(valid1 == true){
|
if(valid1 == true){
|
||||||
svalue1old = svalue1; // Save the old value
|
svalue1old = svalue1; // Save the old value
|
||||||
@@ -150,24 +158,30 @@ class PageThreeValues : public Page
|
|||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
|
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
|
||||||
|
valueY = 180;
|
||||||
|
DsegFontSize = 20;
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
||||||
getdisplay().setCursor(50, 180);
|
getdisplay().setCursor(50, 180);
|
||||||
}
|
}
|
||||||
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
|
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
|
||||||
|
valueY = 170;
|
||||||
|
DsegFontSize = 20;
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
||||||
getdisplay().setCursor(170, 158);
|
getdisplay().setCursor(170, 158);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
valueY = 180;
|
||||||
|
DsegFontSize = 30;
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
||||||
getdisplay().setCursor(180, 180);
|
getdisplay().setCursor(180, 180);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue2); // Real value as formated string
|
printBoatValue(svalue2, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue2old); // Old value as formated string
|
printBoatValue(svalue2old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
if(valid2 == true){
|
if(valid2 == true){
|
||||||
svalue2old = svalue2; // Save the old value
|
svalue2old = svalue2; // Save the old value
|
||||||
@@ -198,24 +212,24 @@ class PageThreeValues : public Page
|
|||||||
|
|
||||||
// Switch font if format for any values
|
// Switch font if format for any values
|
||||||
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
|
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
valueY = 270;
|
||||||
getdisplay().setCursor(50, 270);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
|
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
valueY = 260;
|
||||||
getdisplay().setCursor(170, 248);
|
DsegFontSize = 20;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
valueY = 270;
|
||||||
getdisplay().setCursor(180, 270);
|
DsegFontSize = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if(holdvalues == false){
|
if(holdvalues == false){
|
||||||
getdisplay().print(svalue3); // Real value as formated string
|
printBoatValue(svalue3, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print(svalue3old); // Old value as formated string
|
printBoatValue(svalue3old, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
}
|
}
|
||||||
if(valid3 == true){
|
if(valid3 == true){
|
||||||
svalue3old = svalue3; // Save the old value
|
svalue3old = svalue3; // Save the old value
|
||||||
|
|||||||
@@ -20,17 +20,6 @@ private:
|
|||||||
HALF
|
HALF
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr char HORIZONTAL = 'H';
|
|
||||||
static constexpr char VERTICAL = 'V';
|
|
||||||
static constexpr int8_t FULL_SIZE = 0;
|
|
||||||
static constexpr int8_t HALF_SIZE_TOP = 1;
|
|
||||||
static constexpr int8_t HALF_SIZE_BOTTOM = 2;
|
|
||||||
|
|
||||||
static constexpr bool PRNT_NAME = true;
|
|
||||||
static constexpr bool NO_PRNT_NAME = false;
|
|
||||||
static constexpr bool PRNT_VALUE = true;
|
|
||||||
static constexpr bool NO_PRNT_VALUE = false;
|
|
||||||
|
|
||||||
static constexpr int YOFFSET = 130; // y offset for display of 2nd boat value
|
static constexpr int YOFFSET = 130; // y offset for display of 2nd boat value
|
||||||
|
|
||||||
int width; // Screen width
|
int width; // Screen width
|
||||||
@@ -45,8 +34,11 @@ private:
|
|||||||
bool useSimuData;
|
bool useSimuData;
|
||||||
bool holdValues;
|
bool holdValues;
|
||||||
String flashLED;
|
String flashLED;
|
||||||
String backlightMode;
|
bool smallDecimals;
|
||||||
String tempFormat;
|
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
// Data buffer pointer (owned by HstryBuffers)
|
// Data buffer pointer (owned by HstryBuffers)
|
||||||
static constexpr int NUMVALUES = 2; // two data values in this page
|
static constexpr int NUMVALUES = 2; // two data values in this page
|
||||||
@@ -60,8 +52,10 @@ private:
|
|||||||
// display data values in display <mode> [FULL|HALF]
|
// display data values in display <mode> [FULL|HALF]
|
||||||
void showData(const std::vector<GwApi::BoatValue*>& bValue, DisplayMode mode)
|
void showData(const std::vector<GwApi::BoatValue*>& bValue, DisplayMode mode)
|
||||||
{
|
{
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
int valueX, valueY;
|
||||||
|
int DsegFontSize;
|
||||||
|
|
||||||
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
int numValues = bValue.size(); // do we have to handle 1 or 2 values?
|
int numValues = bValue.size(); // do we have to handle 1 or 2 values?
|
||||||
|
|
||||||
for (int i = 0; i < numValues; i++) {
|
for (int i = 0; i < numValues; i++) {
|
||||||
@@ -89,22 +83,23 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch font depending on value format and adjust position
|
// Switch font depending on value format and adjust position
|
||||||
|
valueX = 380;
|
||||||
if (bValue[i]->getFormat() == "formatLatitude" || bValue[i]->getFormat() == "formatLongitude") {
|
if (bValue[i]->getFormat() == "formatLatitude" || bValue[i]->getFormat() == "formatLongitude") {
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
valueY = 125 + yOffset;
|
||||||
getdisplay().setCursor(50, 125 + yOffset);
|
DsegFontSize = 20;
|
||||||
} else if (bValue[i]->getFormat() == "formatTime" || bValue[i]->getFormat() == "formatDate") {
|
} else if (bValue[i]->getFormat() == "formatTime" || bValue[i]->getFormat() == "formatDate") {
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
valueY = 105 + yOffset;
|
||||||
getdisplay().setCursor(170, 105 + yOffset);
|
DsegFontSize = 20;
|
||||||
} else { // Default font for other formats
|
} else { // Default font for other formats
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
|
valueY = 125 + yOffset;
|
||||||
getdisplay().setCursor(180, 125 + yOffset);
|
DsegFontSize = 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show bus data
|
// Show bus data
|
||||||
if (!holdValues || useSimuData) {
|
if (!holdValues || useSimuData) {
|
||||||
getdisplay().print(sValue); // Real value as formated string
|
printBoatValue(sValue, valueX, valueY, RIGHT, DsegFontSize, smallDecimals);
|
||||||
} else {
|
} else {
|
||||||
getdisplay().print(sValueOld[i]); // Old value as formated string
|
printBoatValue(sValueOld[i], valueX, valueY, RIGHT, DsegFontSize, smallDecimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid == true) {
|
if (valid == true) {
|
||||||
@@ -127,14 +122,13 @@ public:
|
|||||||
|
|
||||||
width = getdisplay().width(); // Screen width
|
width = getdisplay().width(); // Screen width
|
||||||
height = getdisplay().height(); // Screen height
|
height = getdisplay().height(); // Screen height
|
||||||
|
getdisplay().setTextWrap(false);
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
// lengthformat = commonData->config->getString(commonData->config->lengthFormat);
|
|
||||||
useSimuData = commonData->config->getBool(commonData->config->useSimuData);
|
useSimuData = commonData->config->getBool(commonData->config->useSimuData);
|
||||||
holdValues = commonData->config->getBool(commonData->config->holdvalues);
|
holdValues = commonData->config->getBool(commonData->config->holdvalues);
|
||||||
flashLED = commonData->config->getString(commonData->config->flashLED);
|
flashLED = commonData->config->getString(commonData->config->flashLED);
|
||||||
backlightMode = commonData->config->getString(commonData->config->backlight);
|
smallDecimals = commonData->config->getBool(commonData->config->smallDecimals);
|
||||||
tempFormat = commonData->config->getString(commonData->config->tempFormat); // [K|°C|°F]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setupKeys()
|
virtual void setupKeys()
|
||||||
@@ -211,8 +205,6 @@ public:
|
|||||||
dataIntv = 3;
|
dataIntv = 3;
|
||||||
} else if (dataIntv == 3) {
|
} else if (dataIntv == 3) {
|
||||||
dataIntv = 4;
|
dataIntv = 4;
|
||||||
} else if (dataIntv == 4) {
|
|
||||||
dataIntv = 8;
|
|
||||||
} else {
|
} else {
|
||||||
dataIntv = 1;
|
dataIntv = 1;
|
||||||
}
|
}
|
||||||
@@ -248,8 +240,8 @@ public:
|
|||||||
dataHstryBuf[i] = pageData.hstryBuffers->getBuffer(bValName);
|
dataHstryBuf[i] = pageData.hstryBuffers->getBuffer(bValName);
|
||||||
|
|
||||||
if (dataHstryBuf[i]) {
|
if (dataHstryBuf[i]) {
|
||||||
dataChart[i].reset(new Chart(*dataHstryBuf[i], Chart::dfltChrtDta[bValFormat].range, *commonData, useSimuData));
|
dataChart[i].reset(new Chart(*dataHstryBuf[i], *commonData, useSimuData));
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: Created chart object%d for %s", i, bValName.c_str());
|
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: Created chart object %d for %s", i, bValName.c_str());
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: No chart object available for %s", bValName.c_str());
|
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: No chart object available for %s", bValName.c_str());
|
||||||
}
|
}
|
||||||
@@ -268,12 +260,13 @@ public:
|
|||||||
bValue.push_back(pageData.values[0]); // Page boat data element 1
|
bValue.push_back(pageData.values[0]); // Page boat data element 1
|
||||||
bValue.push_back(pageData.values[1]); // Page boat data element 2
|
bValue.push_back(pageData.values[1]); // Page boat data element 2
|
||||||
|
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
// Optical warning by limit violation (unused)
|
// Optical warning by limit violation (unused)
|
||||||
if (String(flashLED) == "Limit Violation") {
|
if (String(flashLED) == "Limit Violation") {
|
||||||
setBlinkingLED(false);
|
setBlinkingLED(false);
|
||||||
setFlashLED(false);
|
setFlashLED(false);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (bValue[0] == NULL && bValue[1] == NULL)
|
if (bValue[0] == NULL && bValue[1] == NULL)
|
||||||
return PAGE_OK; // no data, no page to display
|
return PAGE_OK; // no data, no page to display
|
||||||
|
|
||||||
@@ -283,37 +276,45 @@ public:
|
|||||||
// Draw page
|
// Draw page
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
|
displaySetPartialWindow(0, 0, width, height); // Set partial update
|
||||||
|
|
||||||
if (pageMode == VALUES || (dataHstryBuf[0] == nullptr && dataHstryBuf[1] == nullptr)) {
|
for (int i = 0; i < NUMVALUES; i++) {
|
||||||
|
if (dataChart[i]) { // Check only if dataChart object exists at all
|
||||||
|
if (!dataChart[i]->isValid()) {
|
||||||
|
dataChart[i]->init(); // try late initialization if chart object could not be properly initialized earlier due to missing boat data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageMode == VALUES || (dataChart[0] == nullptr && dataChart[1] == nullptr)) {
|
||||||
// show only data value; ignore other pageMode options if no chart supported boat data history buffer is available
|
// show only data value; ignore other pageMode options if no chart supported boat data history buffer is available
|
||||||
showData(bValue, FULL);
|
showData(bValue, FULL);
|
||||||
|
|
||||||
} else if (pageMode == VAL1_CHART) { // show data value 1 and chart
|
} else if (pageMode == VAL1_CHART) { // show data value 1 and chart
|
||||||
showData({ bValue[0] }, HALF);
|
showData({ bValue[0] }, HALF);
|
||||||
if (dataChart[0]) {
|
if (dataChart[0]) {
|
||||||
dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[0]);
|
dataChart[0]->showChrt(Chart::HORIZONTAL, Chart::HALF_SIZE_RIGHT_BOTTOM, dataIntv, Chart::NO_PRNT_NAME, Chart::NO_PRNT_VALUE, *bValue[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (pageMode == VAL2_CHART) { // show data value 2 and chart
|
} else if (pageMode == VAL2_CHART) { // show data value 2 and chart
|
||||||
showData({ bValue[1] }, HALF);
|
showData({ bValue[1] }, HALF);
|
||||||
if (dataChart[1]) {
|
if (dataChart[1]) {
|
||||||
dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[1]);
|
dataChart[1]->showChrt(Chart::HORIZONTAL, Chart::HALF_SIZE_RIGHT_BOTTOM, dataIntv, Chart::NO_PRNT_NAME, Chart::NO_PRNT_VALUE, *bValue[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (pageMode == CHARTS) { // show both data charts
|
} else if (pageMode == CHARTS) { // show both data charts
|
||||||
if (dataChart[0]) {
|
if (dataChart[0]) {
|
||||||
if (dataChart[1]) {
|
if (dataChart[1]) {
|
||||||
dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_TOP, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]);
|
dataChart[0]->showChrt(Chart::HORIZONTAL, Chart::HALF_SIZE_LEFT_TOP, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *bValue[0]);
|
||||||
} else {
|
} else {
|
||||||
dataChart[0]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]);
|
dataChart[0]->showChrt(Chart::HORIZONTAL, Chart::FULL_SIZE, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *bValue[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dataChart[1]) {
|
if (dataChart[1]) {
|
||||||
if (dataChart[0]) {
|
if (dataChart[0]) {
|
||||||
dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[1]);
|
dataChart[1]->showChrt(Chart::HORIZONTAL, Chart::HALF_SIZE_RIGHT_BOTTOM, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *bValue[1]);
|
||||||
} else {
|
} else {
|
||||||
dataChart[1]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[1]);
|
dataChart[1]->showChrt(Chart::HORIZONTAL, Chart::FULL_SIZE, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *bValue[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,18 @@
|
|||||||
|
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "movingAvg.h" // Lib for moving average building
|
|
||||||
|
|
||||||
class PageVoltage : public Page
|
class PageVoltage : public Page
|
||||||
{
|
{
|
||||||
bool init = false; // Marker for init done
|
bool init = false; // Marker for init done
|
||||||
uint8_t average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
|
uint8_t average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
|
||||||
bool trend = true; // Trend indicator [0|1], 0=off, 1=on
|
bool trend = true; // Trend indicator [0|1], 0=off, 1=on
|
||||||
double raw = 0;
|
double raw = 0;
|
||||||
char mode = 'D'; // display mode (A)nalog | (D)igital
|
char mode = 'D'; // display mode (A)nalog | (D)igital
|
||||||
|
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageVoltage(CommonData &common){
|
PageVoltage(CommonData &common){
|
||||||
@@ -111,8 +114,10 @@ public:
|
|||||||
String batVoltage = config->getString(config->batteryVoltage);
|
String batVoltage = config->getString(config->batteryVoltage);
|
||||||
String batType = config->getString(config->batteryType);
|
String batType = config->getString(config->batteryType);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
double value1 = 0;
|
double value1 = 0;
|
||||||
|
String svalue = "---";
|
||||||
double valueTrend = 0; // Average over 10 values
|
double valueTrend = 0; // Average over 10 values
|
||||||
|
|
||||||
// Get voltage value
|
// Get voltage value
|
||||||
@@ -193,7 +198,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
if (mode == 'D') {
|
if (mode == 'D') {
|
||||||
// Display mode digital
|
// Display mode digital
|
||||||
@@ -235,36 +240,12 @@ public:
|
|||||||
printAvg(average, 320, 84, true);
|
printAvg(average, 320, 84, true);
|
||||||
|
|
||||||
// Reading bus data or using simulation data
|
// Reading bus data or using simulation data
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
|
|
||||||
getdisplay().setCursor(20, 240);
|
|
||||||
if(simulation == true){
|
|
||||||
if(batVoltage == "12V"){
|
|
||||||
value1 = 12.0;
|
|
||||||
}
|
|
||||||
if(batVoltage == "24V"){
|
|
||||||
value1 = 24.0;
|
|
||||||
}
|
|
||||||
value1 += float(random(0, 5)) / 10; // Simulation data
|
|
||||||
getdisplay().print(value1,1);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// Check for valid real data, display also if hold values activated
|
|
||||||
if(valid1 == true || holdvalues == true){
|
if(valid1 == true || holdvalues == true){
|
||||||
// Resolution switching
|
svalue = formatValue(value1, String("formatXdr:U:V"), *commonData);
|
||||||
if(value1 < 10){
|
} else {
|
||||||
getdisplay().print(value1,2);
|
svalue = "---"; // Missing bus data
|
||||||
}
|
|
||||||
if(value1 >= 10 && value1 < 100){
|
|
||||||
getdisplay().print(value1,1);
|
|
||||||
}
|
|
||||||
if(value1 >= 100){
|
|
||||||
getdisplay().print(value1,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print("---"); // Missing bus data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
printBoatValue(svalue, 300, 240, RIGHT, 60, smallDecimals);
|
||||||
|
|
||||||
// Show trend indicator
|
// Show trend indicator
|
||||||
if(trend == true){
|
if(trend == true){
|
||||||
|
|||||||
@@ -0,0 +1,280 @@
|
|||||||
|
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
|
||||||
|
|
||||||
|
#include "Pagedata.h"
|
||||||
|
#include "OBP60Extensions.h"
|
||||||
|
#include "OBPDataOperations.h"
|
||||||
|
#include "OBPcharts.h"
|
||||||
|
|
||||||
|
class PageWeather : public Page {
|
||||||
|
private:
|
||||||
|
GwLog* logger;
|
||||||
|
|
||||||
|
enum PageMode {
|
||||||
|
VAL_CHART,
|
||||||
|
CHART
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
|
static constexpr int XOFFSET = 133; // x offset for display of boat values
|
||||||
|
|
||||||
|
int width; // Screen width
|
||||||
|
int height; // Screen height
|
||||||
|
|
||||||
|
bool keylock = false; // Keylock
|
||||||
|
PageMode pageMode = VAL_CHART; // Page display mode
|
||||||
|
int8_t dataIntv = 1; // Update interval for barograph history chart:
|
||||||
|
// (1)|(3)|(6)|(12)|(24) x 60 min. for 1, 3, 6, 12, 24 hours history chart
|
||||||
|
|
||||||
|
// String lengthformat;
|
||||||
|
bool useSimuData;
|
||||||
|
bool holdValues;
|
||||||
|
String flashLED;
|
||||||
|
bool smallDecimals;
|
||||||
|
|
||||||
|
static constexpr int NUMVALUES = 4; // number of boat values used on this page
|
||||||
|
static constexpr int NUMCHARTS = 1; // one data buffer used on this page
|
||||||
|
|
||||||
|
// Data buffer pointer (owned by HstryBuffers)
|
||||||
|
RingBuffer<uint16_t>* dataHstryBuf[NUMCHARTS] = { nullptr };
|
||||||
|
std::unique_ptr<Chart> dataChart[NUMCHARTS]; // Chart object
|
||||||
|
|
||||||
|
// Old values for hold function
|
||||||
|
String sValueOld[NUMVALUES] = { "", "", "", "" };
|
||||||
|
String unitOld[NUMVALUES] = { "", "", "", "" };
|
||||||
|
|
||||||
|
// display data values in display mode <HALF>
|
||||||
|
void showData(const std::vector<GwApi::BoatValue*>& bValue)
|
||||||
|
{
|
||||||
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
|
int numValues = bValue.size(); // How many values do we have to handle? We will ignore value no. 1
|
||||||
|
|
||||||
|
for (int i = 1; i < numValues; i++) {
|
||||||
|
String name = xdrDelete(bValue[i]->getName()); // Value name
|
||||||
|
name = name.substring(0, 7); // String length limit for value name
|
||||||
|
double value = bValue[i]->value; // Value as double in SI unit
|
||||||
|
bool valid = bValue[i]->valid; // Valid information
|
||||||
|
String sValue = formatValue(bValue[i], *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||||
|
String unit = formatValue(bValue[i], *commonData).unit; // Unit of value
|
||||||
|
|
||||||
|
int xOffset = XOFFSET * (i - 1);
|
||||||
|
|
||||||
|
// Print name
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
|
getdisplay().setCursor(5 + xOffset, 213);
|
||||||
|
getdisplay().print(name); // name
|
||||||
|
|
||||||
|
// Print unit
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
getdisplay().setCursor(5 + xOffset, 229);
|
||||||
|
if (holdValues) {
|
||||||
|
getdisplay().print(unitOld[i]); // name
|
||||||
|
} else {
|
||||||
|
getdisplay().print(unit); // name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print value
|
||||||
|
int valueX = XOFFSET * i - 5;
|
||||||
|
int valueY = 275;
|
||||||
|
int DsegFontSize = 20;
|
||||||
|
if (!holdValues || useSimuData) {
|
||||||
|
printBoatValue(sValue, valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Real value as formated string
|
||||||
|
} else {
|
||||||
|
printBoatValue(sValueOld[i], valueX, valueY, RIGHT, DsegFontSize, smallDecimals); // Old value as formated string
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid) { // Save value for hold function
|
||||||
|
sValueOld[i] = sValue;
|
||||||
|
unitOld[i] = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print lines for data separation of bottom data values
|
||||||
|
getdisplay().fillRect(0, 191, 400, 2, commonData->fgcolor); // horizontal line
|
||||||
|
getdisplay().fillRect(133, 192, 2, 84, commonData->fgcolor); // vertical lines
|
||||||
|
getdisplay().fillRect(266, 192, 2, 84, commonData->fgcolor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
PageWeather(CommonData& common)
|
||||||
|
{
|
||||||
|
commonData = &common;
|
||||||
|
logger = commonData->logger;
|
||||||
|
LOG_DEBUG(GwLog::LOG, "Instantiate PageWeather");
|
||||||
|
|
||||||
|
width = getdisplay().width(); // Screen width
|
||||||
|
height = getdisplay().height(); // Screen height
|
||||||
|
|
||||||
|
// Get config data
|
||||||
|
useSimuData = commonData->config->getBool(commonData->config->useSimuData);
|
||||||
|
holdValues = commonData->config->getBool(commonData->config->holdvalues);
|
||||||
|
flashLED = commonData->config->getString(commonData->config->flashLED);
|
||||||
|
smallDecimals = commonData->config->getBool(commonData->config->smallDecimals);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setupKeys()
|
||||||
|
{
|
||||||
|
Page::setupKeys();
|
||||||
|
|
||||||
|
#if defined BOARD_OBP60S3
|
||||||
|
constexpr int ZOOM_KEY = 4;
|
||||||
|
#elif defined BOARD_OBP40S3
|
||||||
|
constexpr int ZOOM_KEY = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dataHstryBuf) { // show "Mode" key only if chart-supported boat data type is available
|
||||||
|
commonData->keydata[0].label = "MODE";
|
||||||
|
commonData->keydata[ZOOM_KEY].label = "ZOOM";
|
||||||
|
} else {
|
||||||
|
commonData->keydata[0].label = "";
|
||||||
|
commonData->keydata[ZOOM_KEY].label = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key functions
|
||||||
|
virtual int handleKey(int key)
|
||||||
|
{
|
||||||
|
if (dataHstryBuf) { // if boat data type supports charts
|
||||||
|
|
||||||
|
// Set page mode: value/half chart | full chart
|
||||||
|
if (key == 1) {
|
||||||
|
switch (pageMode) {
|
||||||
|
case VAL_CHART:
|
||||||
|
pageMode = CHART;
|
||||||
|
break;
|
||||||
|
case CHART:
|
||||||
|
pageMode = VAL_CHART;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setupKeys(); // Adjust key definition depending on <pageMode> and chart-supported boat data type
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set time frame to show for chart
|
||||||
|
#if defined BOARD_OBP60S3
|
||||||
|
if (key == 5) {
|
||||||
|
#elif defined BOARD_OBP40S3
|
||||||
|
if (key == 2) {
|
||||||
|
#endif
|
||||||
|
if (dataIntv == 1) {
|
||||||
|
dataIntv = 2;
|
||||||
|
} else if (dataIntv == 2) {
|
||||||
|
dataIntv = 4;
|
||||||
|
} else if (dataIntv == 4) {
|
||||||
|
dataIntv = 8;
|
||||||
|
} else if (dataIntv == 8) {
|
||||||
|
dataIntv = 12;
|
||||||
|
} else {
|
||||||
|
dataIntv = 1;
|
||||||
|
}
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keylock function
|
||||||
|
if (key == 11) { // Code for keylock
|
||||||
|
commonData->keylock = !commonData->keylock;
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void displayNew(PageData& pageData)
|
||||||
|
{
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
|
// Clear optical warning
|
||||||
|
if (flashLED == "Limit Violation") {
|
||||||
|
setBlinkingLED(false);
|
||||||
|
setFlashLED(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < NUMCHARTS; i++) {
|
||||||
|
if (!dataChart[i]) { // Create chart objects if they don't exist
|
||||||
|
|
||||||
|
GwApi::BoatValue* bValue = pageData.values[i]; // Page boat data element
|
||||||
|
String bValName = bValue->getName(); // Value name
|
||||||
|
String bValFormat = bValue->getFormat(); // Value format
|
||||||
|
|
||||||
|
dataHstryBuf[i] = pageData.hstryBuffers->getBuffer(bValName);
|
||||||
|
|
||||||
|
if (dataHstryBuf[i]) {
|
||||||
|
dataChart[i].reset(new Chart(*dataHstryBuf[i], *commonData, useSimuData));
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageWeather: Created chart object %d for %s, format: %s", i, bValName.c_str(), dataHstryBuf[i]->getFormat().c_str());
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageWeather: No chart object available for %s", bValName.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int displayPage(PageData& pageData)
|
||||||
|
{
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::LOG, "Display PageWeather");
|
||||||
|
|
||||||
|
// Get latest boat values for page
|
||||||
|
std::vector<GwApi::BoatValue*> bValue;
|
||||||
|
for (int i = 0; i < NUMVALUES; i++) {
|
||||||
|
bValue.push_back(pageData.values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bValue[0] == NULL && bValue[1] == NULL && bValue[2] == NULL && bValue[3] == NULL) {
|
||||||
|
return PAGE_OK; // no data, no page to display
|
||||||
|
}
|
||||||
|
if (dataHstryBuf == nullptr) { // no buffer for main boat data item, no page display
|
||||||
|
return PAGE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageWeather: printing #1: %s, %.3f, %s, #2: %s, %.3f, %s, #3: %s, %.3f, %s, #4: %s, %.3f, %s",
|
||||||
|
bValue[0]->getName().c_str(), bValue[0]->value, bValue[0]->getFormat().c_str(), bValue[1]->getName().c_str(), bValue[1]->value, bValue[1]->getFormat().c_str(),
|
||||||
|
bValue[2]->getName().c_str(), bValue[2]->value, bValue[2]->getFormat().c_str(), bValue[3]->getName().c_str(), bValue[3]->value, bValue[3]->getFormat().c_str());
|
||||||
|
|
||||||
|
// Draw page
|
||||||
|
//***********************************************************
|
||||||
|
|
||||||
|
displaySetPartialWindow(0, 0, width, height); // Set partial update
|
||||||
|
|
||||||
|
if (dataChart[0]) { // Check only if dataChart object exists at all
|
||||||
|
if (!dataChart[0]->isValid()) {
|
||||||
|
dataChart[0]->init(); // try late initialization if chart object could not be properly initialized earlier due to missing boat data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageMode == VAL_CHART) {
|
||||||
|
if (dataChart[0]) {
|
||||||
|
dataChart[0]->showChrt(Chart::HORIZONTAL, Chart::TWO_THIRD_TOP, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *bValue[0]);
|
||||||
|
}
|
||||||
|
showData(bValue);
|
||||||
|
|
||||||
|
} else if (pageMode == CHART && dataChart[0]) { // show only data chart, but that has to exist
|
||||||
|
dataChart[0]->showChrt(Chart::HORIZONTAL, Chart::FULL_SIZE, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *bValue[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PAGE_UPDATE;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static Page* createPage(CommonData& common)
|
||||||
|
{
|
||||||
|
return new PageWeather(common);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* with the code below we make this page known to the PageTask
|
||||||
|
* we give it a type (name) that can be selected in the config
|
||||||
|
* we define which function is to be called
|
||||||
|
* and we provide the number of user parameters we expect
|
||||||
|
* this will be number of BoatValue pointers in pageData.values
|
||||||
|
*/
|
||||||
|
PageDescription registerPageWeather(
|
||||||
|
"Weather", // Page name
|
||||||
|
createPage, // Action
|
||||||
|
4, // Number of bus values depends on selection in Web configuration (air baro pressure, air temperature, humidity, true wind speed)
|
||||||
|
{ }, // Bus values we need in the page
|
||||||
|
true // Show display header on/off
|
||||||
|
);
|
||||||
|
#endif
|
||||||
@@ -63,7 +63,7 @@ public:
|
|||||||
if (mode == 'W') {
|
if (mode == 'W') {
|
||||||
getdisplay().setFullWindow();
|
getdisplay().setFullWindow();
|
||||||
} else {
|
} else {
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == 'L') {
|
if (mode == 'L') {
|
||||||
|
|||||||
+12
-15
@@ -213,6 +213,10 @@ static unsigned char front_bits[] PROGMEM = {
|
|||||||
|
|
||||||
class PageWind : public Page
|
class PageWind : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
bool keylock = false; // Keylock
|
bool keylock = false; // Keylock
|
||||||
int8_t lp = 80; // Pointer length
|
int8_t lp = 80; // Pointer length
|
||||||
char mode = 'N'; // page mode (N)ormal | (L)ens | e(X)ample
|
char mode = 'N'; // page mode (N)ormal | (L)ens | e(X)ample
|
||||||
@@ -306,11 +310,10 @@ public:
|
|||||||
static String unit2old = "";
|
static String unit2old = "";
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
|
||||||
bool simulation = config->getBool(config->useSimuData);
|
bool simulation = config->getBool(config->useSimuData);
|
||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
GwApi::BoatValue *bvalue1; // Value 1 for speed on top
|
GwApi::BoatValue *bvalue1; // Value 1 for speed on top
|
||||||
GwApi::BoatValue *bvalue2; // Value 2 for angle on bottom
|
GwApi::BoatValue *bvalue2; // Value 2 for angle on bottom
|
||||||
@@ -358,7 +361,7 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
@@ -512,17 +515,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wind speed as decimal number
|
// Wind speed as decimal number
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
|
||||||
getdisplay().setCursor(150, 250);
|
|
||||||
if (holdvalues == false) {
|
if (holdvalues == false) {
|
||||||
getdisplay().print(svalue1);
|
printBoatValue(svalue1, 260, 250, RIGHT, 26, smallDecimals);
|
||||||
} else {
|
} else {
|
||||||
getdisplay().print(svalue1old);
|
printBoatValue(svalue1old, 260, 250, RIGHT, 26, smallDecimals);
|
||||||
}
|
}
|
||||||
// unit
|
// unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(220, 265);
|
drawTextRalign(260, 265, unit1);
|
||||||
getdisplay().print("kts");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Normal mode
|
// Normal mode
|
||||||
@@ -584,17 +584,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wind speed as decimal number
|
// Wind speed as decimal number
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
|
||||||
getdisplay().setCursor(150, 250);
|
|
||||||
if (holdvalues == false) {
|
if (holdvalues == false) {
|
||||||
getdisplay().print(svalue1);
|
printBoatValue(svalue1, 260, 250, RIGHT, 26, smallDecimals);
|
||||||
} else {
|
} else {
|
||||||
getdisplay().print(svalue1old);
|
printBoatValue(svalue1old, 260, 250, RIGHT, 26, smallDecimals);
|
||||||
}
|
}
|
||||||
// unit
|
// unit
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(220, 265);
|
drawTextRalign(260, 265, unit1);
|
||||||
getdisplay().print("kts");
|
|
||||||
|
|
||||||
// Wind pointer (angle)
|
// Wind pointer (angle)
|
||||||
if (bvalue2->valid or simulation) {
|
if (bvalue2->valid or simulation) {
|
||||||
|
|||||||
@@ -11,33 +11,22 @@ class PageWindPlot : public Page {
|
|||||||
private:
|
private:
|
||||||
GwLog* logger;
|
GwLog* logger;
|
||||||
|
|
||||||
enum ChartMode {
|
enum PageMode {
|
||||||
DIRECTION,
|
DIRECTION,
|
||||||
SPEED,
|
SPEED,
|
||||||
BOTH
|
BOTH
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr char HORIZONTAL = 'H';
|
|
||||||
static constexpr char VERTICAL = 'V';
|
|
||||||
static constexpr int8_t FULL_SIZE = 0;
|
|
||||||
static constexpr int8_t HALF_SIZE_LEFT = 1;
|
|
||||||
static constexpr int8_t HALF_SIZE_RIGHT = 2;
|
|
||||||
|
|
||||||
static constexpr bool PRNT_NAME = true;
|
|
||||||
static constexpr bool NO_PRNT_NAME = false;
|
|
||||||
static constexpr bool PRNT_VALUE = true;
|
|
||||||
static constexpr bool NO_PRNT_VALUE = false;
|
|
||||||
|
|
||||||
int width; // Screen width
|
int width; // Screen width
|
||||||
int height; // Screen height
|
int height; // Screen height
|
||||||
|
|
||||||
bool keylock = false; // Keylock
|
bool keylock = false; // Keylock
|
||||||
ChartMode chrtMode = DIRECTION;
|
PageMode chrtMode = DIRECTION;
|
||||||
bool showTruW = true; // Show true wind or apparent wind in chart area
|
bool showTruW = true; // Show true wind or apparent wind in chart area
|
||||||
bool oldShowTruW = false; // remember recent user selection of wind data type
|
bool oldShowTruW = false; // remember recent user selection of wind data type
|
||||||
|
|
||||||
int8_t dataIntv = 1; // Update interval for wind history chart:
|
int8_t dataIntv = 1; // Update interval for wind history chart:
|
||||||
// (1)|(2)|(3)|(4)|(8) x 240 seconds for 4, 8, 12, 16, 32 min. history chart
|
// (1)|(2)|(3)|(4)|(8) seconds for up to 32 min. history chart
|
||||||
bool useSimuData;
|
bool useSimuData;
|
||||||
// bool holdValues;
|
// bool holdValues;
|
||||||
String flashLED;
|
String flashLED;
|
||||||
@@ -168,10 +157,10 @@ public:
|
|||||||
twsHstry = pageData.hstryBuffers->getBuffer("TWS");
|
twsHstry = pageData.hstryBuffers->getBuffer("TWS");
|
||||||
|
|
||||||
if (twdHstry) {
|
if (twdHstry) {
|
||||||
twdChart.reset(new Chart(*twdHstry, Chart::dfltChrtDta["formatCourse"].range, *commonData, useSimuData));
|
twdChart.reset(new Chart(*twdHstry, *commonData, useSimuData));
|
||||||
}
|
}
|
||||||
if (twsHstry) {
|
if (twsHstry) {
|
||||||
twsChart.reset(new Chart(*twsHstry, Chart::dfltChrtDta["formatKnots"].range, *commonData, useSimuData));
|
twsChart.reset(new Chart(*twsHstry, *commonData, useSimuData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,10 +169,10 @@ public:
|
|||||||
awsHstry = pageData.hstryBuffers->getBuffer("AWS");
|
awsHstry = pageData.hstryBuffers->getBuffer("AWS");
|
||||||
|
|
||||||
if (awdHstry) {
|
if (awdHstry) {
|
||||||
awdChart.reset(new Chart(*awdHstry, Chart::dfltChrtDta["formatCourse"].range, *commonData, useSimuData));
|
awdChart.reset(new Chart(*awdHstry, *commonData, useSimuData));
|
||||||
}
|
}
|
||||||
if (awsHstry) {
|
if (awsHstry) {
|
||||||
awsChart.reset(new Chart(*awsHstry, Chart::dfltChrtDta["formatKnots"].range, *commonData, useSimuData));
|
awsChart.reset(new Chart(*awsHstry, *commonData, useSimuData));
|
||||||
}
|
}
|
||||||
if (twdHstry && twsHstry && awdHstry && awsHstry) {
|
if (twdHstry && twsHstry && awdHstry && awsHstry) {
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: Created wind charts");
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: Created wind charts");
|
||||||
@@ -221,25 +210,28 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
|
displaySetPartialWindow(0, 0, width, height); // Set partial update
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
if (chrtMode == DIRECTION) {
|
if (chrtMode == DIRECTION) {
|
||||||
if (wdChart) {
|
if (wdChart) {
|
||||||
wdChart->showChrt(VERTICAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *wdBVal);
|
wdChart->showChrt(Chart::VERTICAL, Chart::FULL_SIZE, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *wdBVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (chrtMode == SPEED) {
|
} else if (chrtMode == SPEED) {
|
||||||
if (wsChart) {
|
if (wsChart) {
|
||||||
wsChart->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *wsBVal);
|
if (dataIntv == 8) {
|
||||||
|
dataIntv = 1; // horizontal charts show max. 4 x 7 min. only; no factor 8 multiplier
|
||||||
|
}
|
||||||
|
wsChart->showChrt(Chart::HORIZONTAL, Chart::FULL_SIZE, dataIntv, Chart::PRNT_NAME, Chart::PRNT_VALUE, *wsBVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (chrtMode == BOTH) {
|
} else if (chrtMode == BOTH) {
|
||||||
if (wdChart) {
|
if (wdChart) {
|
||||||
wdChart->showChrt(VERTICAL, HALF_SIZE_LEFT, dataIntv, PRNT_NAME, PRNT_VALUE, *wdBVal);
|
wdChart->showChrt(Chart::VERTICAL, Chart::HALF_SIZE_LEFT_TOP, dataIntv, Chart::NO_PRNT_NAME, Chart::PRNT_VALUE, *wdBVal);
|
||||||
}
|
}
|
||||||
if (wsChart) {
|
if (wsChart) {
|
||||||
wsChart->showChrt(VERTICAL, HALF_SIZE_RIGHT, dataIntv, PRNT_NAME, PRNT_VALUE, *wsBVal);
|
wsChart->showChrt(Chart::VERTICAL, Chart::HALF_SIZE_RIGHT_BOTTOM, dataIntv, Chart::NO_PRNT_NAME, Chart::PRNT_VALUE, *wsBVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
|
|
||||||
class PageWindRose : public Page
|
class PageWindRose : public Page
|
||||||
{
|
{
|
||||||
int16_t lp = 80; // Pointer length
|
private:
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageWindRose(CommonData &common){
|
PageWindRose(CommonData &common){
|
||||||
@@ -27,6 +30,9 @@ public:
|
|||||||
GwConfigHandler *config = commonData->config;
|
GwConfigHandler *config = commonData->config;
|
||||||
GwLog *logger = commonData->logger;
|
GwLog *logger = commonData->logger;
|
||||||
|
|
||||||
|
int valueX, valueY;
|
||||||
|
int DsegFontSize;
|
||||||
|
|
||||||
static String svalue1old = "";
|
static String svalue1old = "";
|
||||||
static String unit1old = "";
|
static String unit1old = "";
|
||||||
static String svalue2old = "";
|
static String svalue2old = "";
|
||||||
@@ -41,19 +47,18 @@ public:
|
|||||||
static String unit6old = "";
|
static String unit6old = "";
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
|
||||||
bool simulation = config->getBool(config->useSimuData);
|
bool simulation = config->getBool(config->useSimuData);
|
||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
// Get boat value for AWA
|
// Get boat value for AWA
|
||||||
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
||||||
String name1 = xdrDelete(bvalue1->getName()); // Value name
|
String name1 = xdrDelete(bvalue1->getName()); // Value name
|
||||||
name1 = name1.substring(0, 6); // String length limit for value name
|
name1 = name1.substring(0, 6); // String length limit for value name
|
||||||
double value1 = bvalue1->value; // Value as double in SI unit
|
double value1 = bvalue1->value; // Value as double in SI unit
|
||||||
bool valid1 = bvalue1->valid; // Valid information
|
|
||||||
value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer
|
value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer
|
||||||
|
bool valid1 = bvalue1->valid; // Valid information
|
||||||
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||||
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
|
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
|
||||||
if(valid1 == true){
|
if(valid1 == true){
|
||||||
@@ -140,14 +145,11 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
// Show values AWA
|
// Show values AWA
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(svalue1, 101, 65, RIGHT, 20, smallDecimals); // value
|
||||||
getdisplay().setCursor(10, 65);
|
|
||||||
getdisplay().print(svalue1); // Value
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(10, 95);
|
getdisplay().setCursor(10, 95);
|
||||||
getdisplay().print(name1); // Name
|
getdisplay().print(name1); // Name
|
||||||
@@ -161,13 +163,11 @@ public:
|
|||||||
getdisplay().print(unit1old); // Unit
|
getdisplay().print(unit1old); // Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Horizintal separator left
|
// Horizontal separator left
|
||||||
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
|
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
|
||||||
|
|
||||||
// Show values AWS
|
// Show values AWS
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(svalue2, 101, 270, RIGHT, 20, smallDecimals); // value
|
||||||
getdisplay().setCursor(10, 270);
|
|
||||||
getdisplay().print(svalue2); // Value
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(10, 220);
|
getdisplay().setCursor(10, 220);
|
||||||
getdisplay().print(name2); // Name
|
getdisplay().print(name2); // Name
|
||||||
@@ -182,14 +182,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show values TWD
|
// Show values TWD
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(svalue3, 391, 65, RIGHT, 20, smallDecimals); // value
|
||||||
getdisplay().setCursor(295, 65);
|
|
||||||
if(valid3 == true){
|
|
||||||
getdisplay().print(abs(value3 * 180 / PI), 0); // Value
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print("---"); // Value
|
|
||||||
}
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(335, 95);
|
getdisplay().setCursor(335, 95);
|
||||||
getdisplay().print(name3); // Name
|
getdisplay().print(name3); // Name
|
||||||
@@ -207,9 +200,7 @@ public:
|
|||||||
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
|
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
|
||||||
|
|
||||||
// Show values TWS
|
// Show values TWS
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(svalue4, 391, 270, RIGHT, 20, smallDecimals); // value
|
||||||
getdisplay().setCursor(295, 270);
|
|
||||||
getdisplay().print(svalue4); // Value
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(335, 220);
|
getdisplay().setCursor(335, 220);
|
||||||
getdisplay().print(name4); // Name
|
getdisplay().print(name4); // Name
|
||||||
@@ -227,7 +218,7 @@ public:
|
|||||||
|
|
||||||
// Draw wind rose
|
// Draw wind rose
|
||||||
int rInstrument = 110; // Radius of grafic instrument
|
int rInstrument = 110; // Radius of grafic instrument
|
||||||
float pi = 3.141592;
|
String sDegree;
|
||||||
|
|
||||||
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
|
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
|
||||||
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
|
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
|
||||||
@@ -237,56 +228,38 @@ public:
|
|||||||
for(int i=0; i<360; i=i+10)
|
for(int i=0; i<360; i=i+10)
|
||||||
{
|
{
|
||||||
// Scaling values
|
// Scaling values
|
||||||
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots
|
float x = 200 + (rInstrument-30)*sin(i/180.0*M_PI); // x-coordinate dots
|
||||||
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots
|
float y = 150 - (rInstrument-30)*cos(i/180.0*M_PI); // y-coordinate cots
|
||||||
const char *ii = "";
|
|
||||||
switch (i)
|
|
||||||
{
|
|
||||||
case 0: ii="0"; break;
|
|
||||||
case 30 : ii="30"; break;
|
|
||||||
case 60 : ii="60"; break;
|
|
||||||
case 90 : ii="90"; break;
|
|
||||||
case 120 : ii="120"; break;
|
|
||||||
case 150 : ii="150"; break;
|
|
||||||
case 180 : ii="180"; break;
|
|
||||||
case 210 : ii="210"; break;
|
|
||||||
case 240 : ii="240"; break;
|
|
||||||
case 270 : ii="270"; break;
|
|
||||||
case 300 : ii="300"; break;
|
|
||||||
case 330 : ii="330"; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(i % 30 == 0){
|
||||||
// Print text centered on position x, y
|
// Print text centered on position x, y
|
||||||
int16_t x1, y1; // Return values of getTextBounds
|
int16_t x1, y1; // Return values of getTextBounds
|
||||||
uint16_t w, h; // Return values of getTextBounds
|
uint16_t w, h; // Return values of getTextBounds
|
||||||
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
sDegree = String(i);
|
||||||
getdisplay().setCursor(x-w/2, y+h/2);
|
|
||||||
if(i % 30 == 0){
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().print(ii);
|
displayGetTextBounds(sDegree, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
||||||
}
|
getdisplay().setCursor(x-w/2, y+h/2);
|
||||||
|
getdisplay().print(sDegree);
|
||||||
// Draw sub scale with dots
|
|
||||||
float x1c = 200 + rInstrument*sin(i/180.0*pi);
|
|
||||||
float y1c = 150 - rInstrument*cos(i/180.0*pi);
|
|
||||||
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
|
|
||||||
float sinx=sin(i/180.0*pi);
|
|
||||||
float cosx=cos(i/180.0*pi);
|
|
||||||
|
|
||||||
// Draw sub scale with lines (two triangles)
|
// Draw sub scale with lines (two triangles)
|
||||||
if(i % 30 == 0){
|
|
||||||
float dx=2; // Line thickness = 2*dx+1
|
float dx=2; // Line thickness = 2*dx+1
|
||||||
float xx1 = -dx;
|
float xx1 = -dx;
|
||||||
float xx2 = +dx;
|
float xx2 = +dx;
|
||||||
float yy1 = -(rInstrument-10);
|
float yy1 = -(rInstrument-10);
|
||||||
float yy2 = -(rInstrument+10);
|
float yy2 = -(rInstrument+10);
|
||||||
|
float sinx = sin(i/180.0*M_PI);
|
||||||
|
float cosx = cos(i/180.0*M_PI);
|
||||||
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
|
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
|
||||||
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
|
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
|
||||||
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
|
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
|
||||||
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
|
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
|
||||||
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
|
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
|
||||||
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
|
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
|
||||||
|
} else {
|
||||||
|
// Draw sub scale with dots
|
||||||
|
float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
|
||||||
|
float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
|
||||||
|
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,9 +296,7 @@ public:
|
|||||||
//*******************************************************************************************
|
//*******************************************************************************************
|
||||||
|
|
||||||
// Show values DBT
|
// Show values DBT
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
printBoatValue(svalue5, 200, 200, CENTER, 16, smallDecimals); // value
|
||||||
getdisplay().setCursor(160, 200);
|
|
||||||
getdisplay().print(svalue5); // Value
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(190, 215);
|
getdisplay().setCursor(190, 215);
|
||||||
getdisplay().print(" ");
|
getdisplay().print(" ");
|
||||||
@@ -337,9 +308,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show values STW
|
// Show values STW
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
printBoatValue(svalue6, 200, 130, CENTER, 16, smallDecimals); // value
|
||||||
getdisplay().setCursor(160, 130);
|
|
||||||
getdisplay().print(svalue6); // Value
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(190, 90);
|
getdisplay().setCursor(190, 90);
|
||||||
getdisplay().print(" ");
|
getdisplay().print(" ");
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
|
|
||||||
class PageWindRoseFlex : public Page
|
class PageWindRoseFlex : public Page
|
||||||
{
|
{
|
||||||
int16_t lp = 80; // Pointer length
|
private:
|
||||||
char source = 'A'; // data source (A)pparent | (T)rue
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
char source = 'A'; // data source (A)pparent | (T)rue
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageWindRoseFlex(CommonData &common){
|
PageWindRoseFlex(CommonData &common){
|
||||||
@@ -42,6 +45,9 @@ public:
|
|||||||
GwConfigHandler *config = commonData->config;
|
GwConfigHandler *config = commonData->config;
|
||||||
GwLog *logger = commonData->logger;
|
GwLog *logger = commonData->logger;
|
||||||
|
|
||||||
|
int valueX, valueY;
|
||||||
|
int DsegFontSize;
|
||||||
|
|
||||||
static String svalue1old = "";
|
static String svalue1old = "";
|
||||||
static String unit1old = "";
|
static String unit1old = "";
|
||||||
static String svalue2old = "";
|
static String svalue2old = "";
|
||||||
@@ -61,11 +67,10 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
|
||||||
bool simulation = config->getBool(config->useSimuData);
|
bool simulation = config->getBool(config->useSimuData);
|
||||||
bool holdvalues = config->getBool(config->holdvalues);
|
bool holdvalues = config->getBool(config->holdvalues);
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
bool smallDecimals = config->getBool(config->smallDecimals);
|
||||||
|
|
||||||
GwApi::BoatValue *bvalue1; // Value 1 for angle
|
GwApi::BoatValue *bvalue1; // Value 1 for angle
|
||||||
GwApi::BoatValue *bvalue2; // Value 2 for speed
|
GwApi::BoatValue *bvalue2; // Value 2 for speed
|
||||||
@@ -79,6 +84,7 @@ public:
|
|||||||
String name1 = bvalue1->getName().c_str(); // Value name
|
String name1 = bvalue1->getName().c_str(); // Value name
|
||||||
name1 = name1.substring(0, 6); // String length limit for value name
|
name1 = name1.substring(0, 6); // String length limit for value name
|
||||||
double value1 = bvalue1->value; // Value as double in SI unit
|
double value1 = bvalue1->value; // Value as double in SI unit
|
||||||
|
value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer
|
||||||
bool valid1 = bvalue1->valid; // Valid information
|
bool valid1 = bvalue1->valid; // Valid information
|
||||||
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||||
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
|
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
|
||||||
@@ -97,9 +103,6 @@ public:
|
|||||||
name2 = name2.substring(0, 6); // String length limit for value name
|
name2 = name2.substring(0, 6); // String length limit for value name
|
||||||
double value2 = bvalue2->value; // Value as double in SI unit
|
double value2 = bvalue2->value; // Value as double in SI unit
|
||||||
bool valid2 = bvalue2->valid; // Valid information
|
bool valid2 = bvalue2->valid; // Valid information
|
||||||
if (simulation) {
|
|
||||||
value2 = 0.62731; // some random value
|
|
||||||
}
|
|
||||||
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
|
||||||
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
|
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
|
||||||
if(valid2 == true){
|
if(valid2 == true){
|
||||||
@@ -185,7 +188,6 @@ public:
|
|||||||
unit6old = unit6; // Save old unit
|
unit6old = unit6; // Save old unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Optical warning by limit violation (unused)
|
// Optical warning by limit violation (unused)
|
||||||
if(String(flashLED) == "Limit Violation"){
|
if(String(flashLED) == "Limit Violation"){
|
||||||
setBlinkingLED(false);
|
setBlinkingLED(false);
|
||||||
@@ -200,14 +202,11 @@ public:
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
// Show AWS or TWS top left
|
// Show AWS or TWS top left
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(svalue2, 101, 65, RIGHT, 20, smallDecimals); // value
|
||||||
getdisplay().setCursor(10, 65);
|
|
||||||
getdisplay().print(svalue2); // Value
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
||||||
getdisplay().setCursor(10, 95);
|
getdisplay().setCursor(10, 95);
|
||||||
getdisplay().print(name2); // Name
|
getdisplay().print(name2); // Name
|
||||||
@@ -225,9 +224,7 @@ public:
|
|||||||
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
|
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
|
||||||
|
|
||||||
// Show value 3 (=first user-configured parameter) at bottom left
|
// Show value 3 (=first user-configured parameter) at bottom left
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(svalue3, 101, 270, RIGHT, 20, smallDecimals); // value
|
||||||
getdisplay().setCursor(10, 270);
|
|
||||||
getdisplay().print(svalue3); // Value
|
|
||||||
getdisplay().setFont(&name3font);
|
getdisplay().setFont(&name3font);
|
||||||
getdisplay().setCursor(10, 220);
|
getdisplay().setCursor(10, 220);
|
||||||
getdisplay().print(name3); // Name
|
getdisplay().print(name3); // Name
|
||||||
@@ -242,9 +239,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show value 4 (=second user-configured parameter) at top right
|
// Show value 4 (=second user-configured parameter) at top right
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(svalue4, 395, 65, RIGHT, 20, smallDecimals); // value
|
||||||
getdisplay().setCursor(295, 65);
|
|
||||||
getdisplay().print(svalue4); // Value
|
|
||||||
getdisplay().setFont(&name4font);
|
getdisplay().setFont(&name4font);
|
||||||
getdisplay().setCursor(325, 95);
|
getdisplay().setCursor(325, 95);
|
||||||
getdisplay().print(name4); // Name
|
getdisplay().print(name4); // Name
|
||||||
@@ -258,13 +253,11 @@ public:
|
|||||||
getdisplay().print(unit4old); // Unit
|
getdisplay().print(unit4old); // Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Horizintal separator right
|
// Horizontal separator right
|
||||||
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
|
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
|
||||||
|
|
||||||
// Show value 5 (=third user-configured parameter) at bottom right
|
// Show value 5 (=third user-configured parameter) at bottom right
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
printBoatValue(svalue5, 395, 270, RIGHT, 20, smallDecimals); // value
|
||||||
getdisplay().setCursor(295, 270);
|
|
||||||
getdisplay().print(svalue5); // Value
|
|
||||||
getdisplay().setFont(&name5font);
|
getdisplay().setFont(&name5font);
|
||||||
getdisplay().setCursor(325, 220);
|
getdisplay().setCursor(325, 220);
|
||||||
getdisplay().print(name5); // Name
|
getdisplay().print(name5); // Name
|
||||||
@@ -283,6 +276,7 @@ public:
|
|||||||
|
|
||||||
// Draw wind rose
|
// Draw wind rose
|
||||||
int rInstrument = 110; // Radius of grafic instrument
|
int rInstrument = 110; // Radius of grafic instrument
|
||||||
|
String sDegree;
|
||||||
|
|
||||||
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
|
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
|
||||||
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
|
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
|
||||||
@@ -294,53 +288,36 @@ public:
|
|||||||
// Scaling values
|
// Scaling values
|
||||||
float x = 200 + (rInstrument-30)*sin(i/180.0*M_PI); // x-coordinate dots
|
float x = 200 + (rInstrument-30)*sin(i/180.0*M_PI); // x-coordinate dots
|
||||||
float y = 150 - (rInstrument-30)*cos(i/180.0*M_PI); // y-coordinate dots
|
float y = 150 - (rInstrument-30)*cos(i/180.0*M_PI); // y-coordinate dots
|
||||||
const char *ii = "";
|
|
||||||
switch (i) {
|
|
||||||
case 0: ii="0"; break;
|
|
||||||
case 30 : ii="30"; break;
|
|
||||||
case 60 : ii="60"; break;
|
|
||||||
case 90 : ii="90"; break;
|
|
||||||
case 120 : ii="120"; break;
|
|
||||||
case 150 : ii="150"; break;
|
|
||||||
case 180 : ii="180"; break;
|
|
||||||
case 210 : ii="210"; break;
|
|
||||||
case 240 : ii="240"; break;
|
|
||||||
case 270 : ii="270"; break;
|
|
||||||
case 300 : ii="300"; break;
|
|
||||||
case 330 : ii="330"; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(i % 30 == 0){
|
||||||
// Print text centered on position x, y
|
// Print text centered on position x, y
|
||||||
int16_t x1, y1; // Return values of getTextBounds
|
int16_t x1, y1; // Return values of getTextBounds
|
||||||
uint16_t w, h; // Return values of getTextBounds
|
uint16_t w, h; // Return values of getTextBounds
|
||||||
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
sDegree = String(i);
|
||||||
getdisplay().setCursor(x-w/2, y+h/2);
|
|
||||||
if(i % 30 == 0){
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().print(ii);
|
displayGetTextBounds(sDegree, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
|
||||||
}
|
getdisplay().setCursor(x-w/2, y+h/2);
|
||||||
|
getdisplay().print(sDegree);
|
||||||
// Draw sub scale with dots
|
|
||||||
float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
|
|
||||||
float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
|
|
||||||
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
|
|
||||||
float sinx=sin(i/180.0*M_PI);
|
|
||||||
float cosx=cos(i/180.0*M_PI);
|
|
||||||
|
|
||||||
// Draw sub scale with lines (two triangles)
|
// Draw sub scale with lines (two triangles)
|
||||||
if(i % 30 == 0){
|
|
||||||
float dx=2; // Line thickness = 2*dx+1
|
float dx=2; // Line thickness = 2*dx+1
|
||||||
float xx1 = -dx;
|
float xx1 = -dx;
|
||||||
float xx2 = +dx;
|
float xx2 = +dx;
|
||||||
float yy1 = -(rInstrument-10);
|
float yy1 = -(rInstrument-10);
|
||||||
float yy2 = -(rInstrument+10);
|
float yy2 = -(rInstrument+10);
|
||||||
|
float sinx = sin(i/180.0*M_PI);
|
||||||
|
float cosx = cos(i/180.0*M_PI);
|
||||||
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
|
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
|
||||||
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
|
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
|
||||||
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
|
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
|
||||||
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
|
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
|
||||||
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
|
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
|
||||||
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
|
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
|
||||||
|
} else {
|
||||||
|
// Draw sub scale with dots
|
||||||
|
float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
|
||||||
|
float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
|
||||||
|
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,15 +360,12 @@ public:
|
|||||||
}
|
}
|
||||||
getdisplay().print({source});
|
getdisplay().print({source});
|
||||||
|
|
||||||
|
|
||||||
//*******************************************************************************************
|
//*******************************************************************************************
|
||||||
|
|
||||||
// Show value6 (=fourth user-configured parameter)
|
// Show value6 (=fourth user-configured parameter)
|
||||||
if ( cos(value1) > 0){
|
if ( cos(value1) > 0){
|
||||||
//pointer points upwards
|
//pointer points upwards
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
printBoatValue(svalue6, 200, 200, CENTER, 16, smallDecimals); // value
|
||||||
getdisplay().setCursor(160, 200);
|
|
||||||
getdisplay().print(svalue6); // Value
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(190, 215);
|
getdisplay().setCursor(190, 215);
|
||||||
getdisplay().print(" ");
|
getdisplay().print(" ");
|
||||||
@@ -401,12 +375,10 @@ if ( cos(value1) > 0){
|
|||||||
else{
|
else{
|
||||||
getdisplay().print(unit6old); // Unit
|
getdisplay().print(unit6old); // Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// pointer points downwards
|
// pointer points downwards
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
|
printBoatValue(svalue6, 200, 130, CENTER, 16, smallDecimals); // value
|
||||||
getdisplay().setCursor(160, 130);
|
|
||||||
getdisplay().print(svalue6); // Value
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(190, 90);
|
getdisplay().setCursor(190, 90);
|
||||||
getdisplay().print(" ");
|
getdisplay().print(" ");
|
||||||
@@ -416,7 +388,7 @@ else{
|
|||||||
else{
|
else{
|
||||||
getdisplay().print(unit6old); // Unit
|
getdisplay().print(unit6old); // Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,8 +28,13 @@ static unsigned char ship_bits[] PROGMEM = {
|
|||||||
|
|
||||||
class PageXTETrack : public Page
|
class PageXTETrack : public Page
|
||||||
{
|
{
|
||||||
|
static constexpr int8_t LEFT = 0;
|
||||||
|
static constexpr int8_t CENTER = 1;
|
||||||
|
static constexpr int8_t RIGHT = 2;
|
||||||
|
|
||||||
bool simulation = false;
|
bool simulation = false;
|
||||||
bool holdvalues = false;
|
bool holdvalues = false;
|
||||||
|
bool smallDecimals = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageXTETrack(CommonData &common){
|
PageXTETrack(CommonData &common){
|
||||||
@@ -37,6 +42,7 @@ class PageXTETrack : public Page
|
|||||||
common.logger->logDebug(GwLog::LOG,"Instantiate PageXTETrack");
|
common.logger->logDebug(GwLog::LOG,"Instantiate PageXTETrack");
|
||||||
simulation = common.config->getBool(common.config->useSimuData);
|
simulation = common.config->getBool(common.config->useSimuData);
|
||||||
holdvalues = common.config->getBool(common.config->holdvalues);
|
holdvalues = common.config->getBool(common.config->holdvalues);
|
||||||
|
smallDecimals = common.config->getBool(common.config->smallDecimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawSegment(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
|
void drawSegment(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
|
||||||
@@ -89,50 +95,39 @@ class PageXTETrack : public Page
|
|||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
// descriptions
|
// descriptions
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(50, 188);
|
getdisplay().setCursor(30, 188);
|
||||||
getdisplay().print("Cross-track error");
|
getdisplay().print("Cross-track error");
|
||||||
getdisplay().setCursor(270, 188);
|
getdisplay().setCursor(270, 188);
|
||||||
getdisplay().print("Track");
|
getdisplay().print("Track");
|
||||||
getdisplay().setCursor(45, 275);
|
getdisplay().setCursor(25, 275);
|
||||||
getdisplay().print("Distance to waypoint");
|
getdisplay().print("Distance to waypoint");
|
||||||
getdisplay().setCursor(260, 275);
|
getdisplay().setCursor(280, 275);
|
||||||
getdisplay().print("Bearing");
|
getdisplay().print("Bearing");
|
||||||
|
|
||||||
// values
|
// values
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
|
|
||||||
|
|
||||||
int16_t x, y;
|
|
||||||
uint16_t w, h;
|
|
||||||
|
|
||||||
GwApi::BoatValue *bv_xte = pageData.values[0]; // XTE
|
GwApi::BoatValue *bv_xte = pageData.values[0]; // XTE
|
||||||
String sval_xte = formatValue(bv_xte, *commonData).svalue;
|
String sval_xte = formatValue(bv_xte, *commonData).svalue;
|
||||||
getdisplay().getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h);
|
// printBoatValue(sval_xte, 160, 170, RIGHT, 30, smallDecimals);
|
||||||
getdisplay().setCursor(160-w, 170);
|
printBoatValue(sval_xte, 180, 170, RIGHT, 30, smallDecimals);
|
||||||
getdisplay().print(sval_xte);
|
|
||||||
|
|
||||||
GwApi::BoatValue *bv_cog = pageData.values[1]; // COG
|
GwApi::BoatValue *bv_cog = pageData.values[1]; // COG
|
||||||
String sval_cog = formatValue(bv_cog, *commonData).svalue;
|
String sval_cog = formatValue(bv_cog, *commonData).svalue;
|
||||||
getdisplay().getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h);
|
printBoatValue(sval_cog, 360, 170, RIGHT, 30, smallDecimals);
|
||||||
getdisplay().setCursor(360-w, 170);
|
|
||||||
getdisplay().print(sval_cog);
|
|
||||||
|
|
||||||
GwApi::BoatValue *bv_dtw = pageData.values[2]; // DTW
|
GwApi::BoatValue *bv_dtw = pageData.values[2]; // DTW
|
||||||
String sval_dtw = formatValue(bv_dtw, *commonData).svalue;
|
String sval_dtw = formatValue(bv_dtw, *commonData).svalue;
|
||||||
getdisplay().getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h);
|
// printBoatValue(sval_dtw, 160, 257, RIGHT, 30, smallDecimals);
|
||||||
getdisplay().setCursor(160-w, 257);
|
printBoatValue(sval_dtw, 160, 257, RIGHT, 30, smallDecimals);
|
||||||
getdisplay().print(sval_dtw);
|
|
||||||
|
|
||||||
GwApi::BoatValue *bv_btw = pageData.values[3]; // BTW
|
GwApi::BoatValue *bv_btw = pageData.values[3]; // BTW
|
||||||
String sval_btw = formatValue(bv_btw, *commonData).svalue;
|
String sval_btw = formatValue(bv_btw, *commonData).svalue;
|
||||||
getdisplay().getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h);
|
printBoatValue(sval_btw, 368, 257, RIGHT, 30, smallDecimals);
|
||||||
getdisplay().setCursor(360-w, 257);
|
|
||||||
getdisplay().print(sval_btw);
|
|
||||||
|
|
||||||
bool valid = bv_cog->valid && bv_btw->valid;
|
bool valid = bv_cog->valid && bv_btw->valid;
|
||||||
|
|
||||||
@@ -148,8 +143,11 @@ class PageXTETrack : public Page
|
|||||||
sval_wpname = "Tonne 122";
|
sval_wpname = "Tonne 122";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t x, y;
|
||||||
|
uint16_t w, h;
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
getdisplay().setFont(&Ubuntu_Bold10pt8b);
|
||||||
getdisplay().getTextBounds(sval_wpname, 0, 150, &x, &y, &w, &h);
|
displayGetTextBounds(sval_wpname, 0, 150, &x, &y, &w, &h);
|
||||||
// TODO if text don't fix use smaller font size.
|
// TODO if text don't fix use smaller font size.
|
||||||
// if smallest size does not fit use 2 lines
|
// if smallest size does not fit use 2 lines
|
||||||
// last resort: clip with ellipsis
|
// last resort: clip with ellipsis
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ typedef struct{
|
|||||||
AlarmData alarm;
|
AlarmData alarm;
|
||||||
GwApi::BoatValue *time = nullptr;
|
GwApi::BoatValue *time = nullptr;
|
||||||
GwApi::BoatValue *date = nullptr;
|
GwApi::BoatValue *date = nullptr;
|
||||||
float tz = 0.0; // timezone from config
|
|
||||||
uint16_t fgcolor;
|
uint16_t fgcolor;
|
||||||
uint16_t bgcolor;
|
uint16_t bgcolor;
|
||||||
bool keylock = false;
|
bool keylock = false;
|
||||||
@@ -127,7 +126,7 @@ class Page{
|
|||||||
virtual void displayNew(PageData &pageData){}
|
virtual void displayNew(PageData &pageData){}
|
||||||
virtual void leavePage(PageData &pageData){}
|
virtual void leavePage(PageData &pageData){}
|
||||||
virtual void setupKeys() {
|
virtual void setupKeys() {
|
||||||
#ifdef HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
commonData->keydata[0].label = "";
|
commonData->keydata[0].label = "";
|
||||||
commonData->keydata[1].label = "";
|
commonData->keydata[1].label = "";
|
||||||
commonData->keydata[2].label = "#LEFT";
|
commonData->keydata[2].label = "#LEFT";
|
||||||
@@ -206,6 +205,8 @@ typedef struct{
|
|||||||
// Formatter for boat values
|
// Formatter for boat values
|
||||||
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
|
||||||
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting);
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting);
|
||||||
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, String setPrecision);
|
||||||
|
String formatValue(const double &value, const String &vFormat, CommonData &commondata);
|
||||||
|
|
||||||
// Helper method for conversion of any data value from SI to user defined format (defined in OBP60Formatter)
|
// Helper method for conversion of any data value from SI to user defined format (defined in OBP60Formatter)
|
||||||
double convertValue(const double &value, const String &format, CommonData &commondata);
|
double convertValue(const double &value, const String &format, CommonData &commondata);
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
param(
|
||||||
|
[string]$dir = "."
|
||||||
|
)
|
||||||
|
|
||||||
|
$total = 0
|
||||||
|
|
||||||
|
Get-ChildItem -Path $dir -Recurse -File | Where-Object {
|
||||||
|
$_.Extension -in ".c", ".cpp", ".h"
|
||||||
|
} | ForEach-Object {
|
||||||
|
$lines = [System.Linq.Enumerable]::Count([System.IO.File]::ReadLines($_.FullName))
|
||||||
|
Write-Output "$($_.FullName) : $lines"
|
||||||
|
$total += $lines
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "-----------------------------"
|
||||||
|
Write-Output "Over all files: $total"
|
||||||
+144
-57
@@ -76,19 +76,43 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chainLength",
|
"name": "leeKStd",
|
||||||
"label": "Anchor Chain Length [m]",
|
"label": "Leeway Coefficient",
|
||||||
"type": "number",
|
"type": "list",
|
||||||
"default": "0",
|
"default": "---",
|
||||||
"check": "checkMinMax",
|
"description": "Boat coefficient K for leeway",
|
||||||
"min": 0,
|
"list": [
|
||||||
"max": 255,
|
"---",
|
||||||
"description": "The length of the anchor chain [0...255m]",
|
"Racer [2]",
|
||||||
|
"Multihull Draggerboard [4]",
|
||||||
|
"Performance Cruiser [6]",
|
||||||
|
"Family Cruiser [9]",
|
||||||
|
"Heavy Displacement [13]",
|
||||||
|
"Multihull Fixed Keel [16]",
|
||||||
|
"Individual value"
|
||||||
|
],
|
||||||
"category": "OBP40 Settings",
|
"category": "OBP40 Settings",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40": "true"
|
"obp40": "true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "leeK",
|
||||||
|
"label": "Leeway Coefficient",
|
||||||
|
"type": "number",
|
||||||
|
"default": "0.00",
|
||||||
|
"check": "checkMinMax",
|
||||||
|
"min": 0,
|
||||||
|
"max": 20,
|
||||||
|
"description": "Boat coefficient K for leeway - individual setting [0..20]",
|
||||||
|
"category": "OBP40 Settings",
|
||||||
|
"capabilities": {
|
||||||
|
"obp40":"true"
|
||||||
|
},
|
||||||
|
"condition": [
|
||||||
|
{ "leeKStd": ["Individual value"] }
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "fuelTank",
|
"name": "fuelTank",
|
||||||
"label": "Fuel Tank [l]",
|
"label": "Fuel Tank [l]",
|
||||||
@@ -244,6 +268,17 @@
|
|||||||
"obp40": "true"
|
"obp40": "true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "smoothCharts",
|
||||||
|
"label": "Smooth chart lines",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": "false",
|
||||||
|
"description": "Print chart lines with a smoothed gradient",
|
||||||
|
"category": "OBP40 Settings",
|
||||||
|
"capabilities": {
|
||||||
|
"obp40": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "lengthFormat",
|
"name": "lengthFormat",
|
||||||
"label": "Length Format",
|
"label": "Length Format",
|
||||||
@@ -692,7 +727,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text1",
|
"default": "text1",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP40 IO-Module1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -703,7 +738,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text2",
|
"default": "text2",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP40 IO-Module1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -714,7 +749,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text3",
|
"default": "text3",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP40 IO-Module1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -725,7 +760,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text4",
|
"default": "text4",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP40 IO-Module1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -736,7 +771,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text5",
|
"default": "text5",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP40 IO-Module1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -1081,8 +1116,7 @@
|
|||||||
"description": "Type of map source, cloud service or local service",
|
"description": "Type of map source, cloud service or local service",
|
||||||
"list": [
|
"list": [
|
||||||
"OBP Service",
|
"OBP Service",
|
||||||
"Local Service",
|
"Local Service"
|
||||||
"Remote Service"
|
|
||||||
],
|
],
|
||||||
"category": "OBP40 Navigation",
|
"category": "OBP40 Navigation",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
@@ -1119,34 +1153,6 @@
|
|||||||
{ "mapsource": ["Local Service"] }
|
{ "mapsource": ["Local Service"] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "mapServer",
|
|
||||||
"label": "map server",
|
|
||||||
"type": "string",
|
|
||||||
"default": "",
|
|
||||||
"description": "Server for converting map tiles. Use only one hostname or IP address",
|
|
||||||
"category": "OBP40 Navigation",
|
|
||||||
"capabilities": {
|
|
||||||
"obp40": "true"
|
|
||||||
},
|
|
||||||
"condition": [
|
|
||||||
{ "mapsource": ["Remote Service"] }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mapTilePath",
|
|
||||||
"label": "map tile path",
|
|
||||||
"type": "string",
|
|
||||||
"default": "map.php",
|
|
||||||
"description": "Path to converter access e.g. index.php or map.php",
|
|
||||||
"category": "OBP40 Navigation",
|
|
||||||
"capabilities": {
|
|
||||||
"obp40": "true"
|
|
||||||
},
|
|
||||||
"condition": [
|
|
||||||
{ "mapsource": ["Remote Service"] }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "maptype",
|
"name": "maptype",
|
||||||
"label": "Map Type",
|
"label": "Map Type",
|
||||||
@@ -1288,8 +1294,8 @@
|
|||||||
"name": "timeSource",
|
"name": "timeSource",
|
||||||
"label": "Status Time Source",
|
"label": "Status Time Source",
|
||||||
"type": "list",
|
"type": "list",
|
||||||
"default": "GPS",
|
"default": "iRTC",
|
||||||
"description": "Data source for date and time display in status line [RTC|iRTC|GPS]",
|
"description": "Data source for date and time display in status line [iRTC|RTC|GPS]",
|
||||||
"list": [
|
"list": [
|
||||||
{"l":"Internal real time clock (iRTC)","v":"iRTC"},
|
{"l":"Internal real time clock (iRTC)","v":"iRTC"},
|
||||||
{"l":"External real time clock (RTC)","v":"RTC"},
|
{"l":"External real time clock (RTC)","v":"RTC"},
|
||||||
@@ -1349,10 +1355,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "valueprecision",
|
"name": "valueprecision",
|
||||||
"label": "Display value precision",
|
"label": "Value precision",
|
||||||
"type": "list",
|
"type": "list",
|
||||||
"default": "2",
|
"default": "2",
|
||||||
"description": "Maximum number of decimal places to display [1|2]",
|
"description": "Number of decimal precision on display: [1|2] decimals",
|
||||||
"list": [
|
"list": [
|
||||||
"1",
|
"1",
|
||||||
"2"
|
"2"
|
||||||
@@ -1362,6 +1368,17 @@
|
|||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "smallDecimals",
|
||||||
|
"label": "Display small decimals",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": "true",
|
||||||
|
"description": "Show decimals of values in smaller size [on|off]",
|
||||||
|
"category": "OBP40 Display",
|
||||||
|
"capabilities": {
|
||||||
|
"obp40": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "backlight",
|
"name": "backlight",
|
||||||
"label": "Backlight Mode",
|
"label": "Backlight Mode",
|
||||||
@@ -1552,6 +1569,7 @@
|
|||||||
"obp40": "true"
|
"obp40": "true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "page1type",
|
"name": "page1type",
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
@@ -1559,12 +1577,13 @@
|
|||||||
"default": "Voltage",
|
"default": "Voltage",
|
||||||
"description": "Type of page for page 1",
|
"description": "Type of page for page 1",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -1582,6 +1601,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -1620,6 +1640,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page1type": [
|
"page1type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -1628,6 +1649,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1662,6 +1684,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1694,6 +1717,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1725,6 +1749,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1890,12 +1915,13 @@
|
|||||||
"default": "WindRose",
|
"default": "WindRose",
|
||||||
"description": "Type of page for page 2",
|
"description": "Type of page for page 2",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -1913,6 +1939,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -1950,6 +1977,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page2type": [
|
"page2type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -1958,6 +1986,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1991,6 +2020,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2022,6 +2052,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2052,6 +2083,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2212,12 +2244,13 @@
|
|||||||
"default": "OneValue",
|
"default": "OneValue",
|
||||||
"description": "Type of page for page 3",
|
"description": "Type of page for page 3",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -2235,6 +2268,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -2271,6 +2305,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page3type": [
|
"page3type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -2279,6 +2314,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2311,6 +2347,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2341,6 +2378,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2370,6 +2408,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2525,12 +2564,13 @@
|
|||||||
"default": "TwoValues",
|
"default": "TwoValues",
|
||||||
"description": "Type of page for page 4",
|
"description": "Type of page for page 4",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -2548,6 +2588,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -2583,6 +2624,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page4type": [
|
"page4type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -2591,6 +2633,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2622,6 +2665,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2651,6 +2695,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2679,6 +2724,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2829,12 +2875,13 @@
|
|||||||
"default": "ThreeValues",
|
"default": "ThreeValues",
|
||||||
"description": "Type of page for page 5",
|
"description": "Type of page for page 5",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -2852,6 +2899,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -2886,6 +2934,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page5type": [
|
"page5type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -2894,6 +2943,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2924,6 +2974,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2979,6 +3030,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3124,12 +3176,13 @@
|
|||||||
"default": "FourValues",
|
"default": "FourValues",
|
||||||
"description": "Type of page for page 6",
|
"description": "Type of page for page 6",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -3147,6 +3200,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -3180,6 +3234,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page6type": [
|
"page6type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -3188,6 +3243,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3217,6 +3273,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3244,6 +3301,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3270,6 +3328,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3410,12 +3469,13 @@
|
|||||||
"default": "FourValues2",
|
"default": "FourValues2",
|
||||||
"description": "Type of page for page 7",
|
"description": "Type of page for page 7",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -3433,6 +3493,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -3465,6 +3526,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page7type": [
|
"page7type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -3473,6 +3535,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3501,6 +3564,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3527,6 +3591,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3552,6 +3617,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3687,12 +3753,13 @@
|
|||||||
"default": "Clock",
|
"default": "Clock",
|
||||||
"description": "Type of page for page 8",
|
"description": "Type of page for page 8",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -3710,6 +3777,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -3741,6 +3809,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page8type": [
|
"page8type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -3749,6 +3818,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3776,6 +3846,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3801,6 +3872,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3825,6 +3897,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3955,12 +4028,13 @@
|
|||||||
"default": "RollPitch",
|
"default": "RollPitch",
|
||||||
"description": "Type of page for page 9",
|
"description": "Type of page for page 9",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -3978,6 +4052,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -4008,6 +4083,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page9type": [
|
"page9type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -4016,6 +4092,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4042,6 +4119,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4066,6 +4144,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4089,6 +4168,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4214,12 +4294,13 @@
|
|||||||
"default": "Battery2",
|
"default": "Battery2",
|
||||||
"description": "Type of page for page 10",
|
"description": "Type of page for page 10",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -4237,6 +4318,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -4266,6 +4348,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page10type": [
|
"page10type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -4274,6 +4357,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4299,6 +4383,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4322,6 +4407,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4344,6 +4430,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
|
|||||||
+137
-47
@@ -76,18 +76,42 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chainLength",
|
"name": "leeKStd",
|
||||||
"label": "Anchor Chain Length [m]",
|
"label": "Leeway Coefficient",
|
||||||
|
"type": "list",
|
||||||
|
"default": "---",
|
||||||
|
"description": "Boat coefficient K for leeway",
|
||||||
|
"list": [
|
||||||
|
"---",
|
||||||
|
"Racer [2]",
|
||||||
|
"Multihull Draggerboard [4]",
|
||||||
|
"Performance Cruiser [6]",
|
||||||
|
"Family Cruiser [9]",
|
||||||
|
"Heavy Displacement [13]",
|
||||||
|
"Multihull Fixed Keel [16]",
|
||||||
|
"Individual value"
|
||||||
|
],
|
||||||
|
"category": "OBP60 Settings",
|
||||||
|
"capabilities": {
|
||||||
|
"obp60": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "leeK",
|
||||||
|
"label": "Leeway Coefficient",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"default": "0",
|
"default": "0.00",
|
||||||
"check": "checkMinMax",
|
"check": "checkMinMax",
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 255,
|
"max": 20,
|
||||||
"description": "The length of the anchor chain [0...255m]",
|
"description": "Boat coefficient K for leeway - individual setting [0..20]",
|
||||||
"category": "OBP60 Settings",
|
"category": "OBP60 Settings",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp60":"true"
|
"obp60":"true"
|
||||||
}
|
},
|
||||||
|
"condition": [
|
||||||
|
{ "leeKStd": ["Individual value"] }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fuelTank",
|
"name": "fuelTank",
|
||||||
@@ -244,6 +268,17 @@
|
|||||||
"obp60": "true"
|
"obp60": "true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "smoothCharts",
|
||||||
|
"label": "Smooth chart lines",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": "false",
|
||||||
|
"description": "Print chart lines with a smoothed gradient",
|
||||||
|
"category": "OBP60 Settings",
|
||||||
|
"capabilities": {
|
||||||
|
"obp60": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "lengthFormat",
|
"name": "lengthFormat",
|
||||||
"label": "Length Format",
|
"label": "Length Format",
|
||||||
@@ -1107,34 +1142,6 @@
|
|||||||
{ "mapsource": ["Local Service"] }
|
{ "mapsource": ["Local Service"] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "mapServer",
|
|
||||||
"label": "map server",
|
|
||||||
"type": "string",
|
|
||||||
"default": "",
|
|
||||||
"description": "Server for converting map tiles. Use only one hostname or IP address",
|
|
||||||
"category": "OBP60 Navigation",
|
|
||||||
"capabilities": {
|
|
||||||
"obp60": "true"
|
|
||||||
},
|
|
||||||
"condition": [
|
|
||||||
{ "mapsource": ["Remote Service"] }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mapTilePath",
|
|
||||||
"label": "map tile path",
|
|
||||||
"type": "string",
|
|
||||||
"default": "map.php",
|
|
||||||
"description": "Path to converter access e.g. index.php or map.php",
|
|
||||||
"category": "OBP40 Navigation",
|
|
||||||
"capabilities": {
|
|
||||||
"obp40": "true"
|
|
||||||
},
|
|
||||||
"condition": [
|
|
||||||
{ "mapsource": ["Remote Service"] }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "maptype",
|
"name": "maptype",
|
||||||
"label": "Map Type",
|
"label": "Map Type",
|
||||||
@@ -1277,8 +1284,9 @@
|
|||||||
"label": "Status Time Source",
|
"label": "Status Time Source",
|
||||||
"type": "list",
|
"type": "list",
|
||||||
"default": "GPS",
|
"default": "GPS",
|
||||||
"description": "Data source for date and time display in status line [RTC|GPS]",
|
"description": "Data source for date and time display in status line [iRTC|RTC|GPS]",
|
||||||
"list": [
|
"list": [
|
||||||
|
{"l":"Internal real time clock (iRTC)","v":"iRTC"},
|
||||||
{"l":"Real time clock (RTC)","v":"RTC"},
|
{"l":"Real time clock (RTC)","v":"RTC"},
|
||||||
{"l":"Time via bus (GPS)","v":"GPS"}
|
{"l":"Time via bus (GPS)","v":"GPS"}
|
||||||
],
|
],
|
||||||
@@ -1336,10 +1344,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "valueprecision",
|
"name": "valueprecision",
|
||||||
"label": "Display value precision",
|
"label": "Value precision",
|
||||||
"type": "list",
|
"type": "list",
|
||||||
"default": "2",
|
"default": "2",
|
||||||
"description": "Maximum number of decimal places to display [1|2]",
|
"description": "Number of decimal precision on display: [1|2] decimals",
|
||||||
"list": [
|
"list": [
|
||||||
"1",
|
"1",
|
||||||
"2"
|
"2"
|
||||||
@@ -1349,6 +1357,17 @@
|
|||||||
"obp60":"true"
|
"obp60":"true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "smallDecimals",
|
||||||
|
"label": "Display small decimals",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": "true",
|
||||||
|
"description": "Show decimals of values in smaller size [on|off]",
|
||||||
|
"category": "OBP60 Display",
|
||||||
|
"capabilities": {
|
||||||
|
"obp60": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "backlight",
|
"name": "backlight",
|
||||||
"label": "Backlight Mode",
|
"label": "Backlight Mode",
|
||||||
@@ -1528,6 +1547,7 @@
|
|||||||
"obp60":"true"
|
"obp60":"true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "page1type",
|
"name": "page1type",
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
@@ -1535,12 +1555,13 @@
|
|||||||
"default": "Voltage",
|
"default": "Voltage",
|
||||||
"description": "Type of page for page 1",
|
"description": "Type of page for page 1",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -1558,6 +1579,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -1596,6 +1618,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page1type": [
|
"page1type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -1604,6 +1627,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1638,6 +1662,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1670,6 +1695,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1701,6 +1727,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1836,12 +1863,13 @@
|
|||||||
"default": "WindRose",
|
"default": "WindRose",
|
||||||
"description": "Type of page for page 2",
|
"description": "Type of page for page 2",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -1859,6 +1887,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -1896,6 +1925,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page2type": [
|
"page2type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -1904,6 +1934,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1937,6 +1968,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1968,6 +2000,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -1998,6 +2031,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2129,12 +2163,13 @@
|
|||||||
"default": "OneValue",
|
"default": "OneValue",
|
||||||
"description": "Type of page for page 3",
|
"description": "Type of page for page 3",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -2152,6 +2187,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -2188,6 +2224,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page3type": [
|
"page3type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -2196,6 +2233,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2228,6 +2266,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2258,6 +2297,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2287,6 +2327,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2414,12 +2455,13 @@
|
|||||||
"default": "TwoValues",
|
"default": "TwoValues",
|
||||||
"description": "Type of page for page 4",
|
"description": "Type of page for page 4",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -2437,6 +2479,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -2472,6 +2515,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page4type": [
|
"page4type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -2480,6 +2524,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2511,6 +2556,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2540,6 +2586,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2568,6 +2615,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2691,12 +2739,13 @@
|
|||||||
"default": "ThreeValues",
|
"default": "ThreeValues",
|
||||||
"description": "Type of page for page 5",
|
"description": "Type of page for page 5",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -2714,6 +2763,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -2748,6 +2798,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page5type": [
|
"page5type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -2756,6 +2807,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2786,6 +2838,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2814,6 +2867,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2841,6 +2895,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -2960,12 +3015,13 @@
|
|||||||
"default": "FourValues",
|
"default": "FourValues",
|
||||||
"description": "Type of page for page 6",
|
"description": "Type of page for page 6",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -2983,6 +3039,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -3016,6 +3073,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page6type": [
|
"page6type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -3024,6 +3082,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3053,6 +3112,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3080,6 +3140,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3106,6 +3167,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3221,12 +3283,13 @@
|
|||||||
"default": "FourValues2",
|
"default": "FourValues2",
|
||||||
"description": "Type of page for page 7",
|
"description": "Type of page for page 7",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -3244,6 +3307,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -3276,6 +3340,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page7type": [
|
"page7type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -3284,6 +3349,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3312,6 +3378,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3338,6 +3405,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3363,6 +3431,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3474,12 +3543,13 @@
|
|||||||
"default": "Clock",
|
"default": "Clock",
|
||||||
"description": "Type of page for page 8",
|
"description": "Type of page for page 8",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -3497,6 +3567,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -3528,6 +3599,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page8type": [
|
"page8type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -3536,6 +3608,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3563,6 +3636,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3588,6 +3662,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3612,6 +3687,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3719,12 +3795,13 @@
|
|||||||
"default": "RollPitch",
|
"default": "RollPitch",
|
||||||
"description": "Type of page for page 9",
|
"description": "Type of page for page 9",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -3742,6 +3819,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -3772,6 +3850,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page9type": [
|
"page9type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -3780,6 +3859,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3806,6 +3886,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3830,6 +3911,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3853,6 +3935,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -3956,12 +4039,13 @@
|
|||||||
"default": "Battery2",
|
"default": "Battery2",
|
||||||
"description": "Type of page for page 10",
|
"description": "Type of page for page 10",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
"Clock",
|
"Clock",
|
||||||
"Compass",
|
"Compass",
|
||||||
|
"Current",
|
||||||
"DigitalOut",
|
"DigitalOut",
|
||||||
"DST810",
|
"DST810",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
@@ -3979,6 +4063,7 @@
|
|||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
"Voltage",
|
"Voltage",
|
||||||
|
"Weather",
|
||||||
"WhitePage",
|
"WhitePage",
|
||||||
"Wind",
|
"Wind",
|
||||||
"WindPlot",
|
"WindPlot",
|
||||||
@@ -4008,6 +4093,7 @@
|
|||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"page10type": [
|
"page10type": [
|
||||||
|
"Current",
|
||||||
"Fluid",
|
"Fluid",
|
||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
@@ -4016,6 +4102,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4041,6 +4128,7 @@
|
|||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
"TwoValues",
|
"TwoValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4064,6 +4152,7 @@
|
|||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
"ThreeValues",
|
"ThreeValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
@@ -4086,6 +4175,7 @@
|
|||||||
"FourValues",
|
"FourValues",
|
||||||
"FourValues2",
|
"FourValues2",
|
||||||
"SixValues",
|
"SixValues",
|
||||||
|
"Weather",
|
||||||
"WindRoseFlex"
|
"WindRoseFlex"
|
||||||
],
|
],
|
||||||
"visiblePages": [
|
"visiblePages": [
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,4 +3,7 @@ Debugging tool
|
|||||||
|
|
||||||
log.txt = text file with error messages from terminal console
|
log.txt = text file with error messages from terminal console
|
||||||
|
|
||||||
|
cd /home/norbert/esp32-nmea2000-obp60
|
||||||
tools/decoder.py -p ESP32S3 -t ~/.platformio/packages/toolchain-xtensa-esp32s3/ -e .pio/build/obp60_s3/firmware.elf log.txt
|
tools/decoder.py -p ESP32S3 -t ~/.platformio/packages/toolchain-xtensa-esp32s3/ -e .pio/build/obp60_s3/firmware.elf log.txt
|
||||||
|
|
||||||
|
tools/decoder.py -p ESP32S3 -t ~/.platformio/packages/toolchain-xtensa-esp32s3/ -e .pio/build/obp70_s3/firmware.elf /home/norbert/Dokumente/Multifunktionsdisplay_OBP60/Crashes/20260313/log.txt
|
||||||
@@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
def cleanup_patches(source, target, env):
|
||||||
|
for p in patchfiles:
|
||||||
|
patch = os.path.join(patchdir, p)
|
||||||
|
print(f"removing {patch}")
|
||||||
|
res = subprocess.run(["git", "apply", "-R", patch], capture_output=True, text=True)
|
||||||
|
if res.returncode != 0:
|
||||||
|
print(res.stderr)
|
||||||
|
|
||||||
patching = False
|
patching = False
|
||||||
|
|
||||||
epdtype = "unknown"
|
epdtype = "unknown"
|
||||||
@@ -46,11 +54,13 @@ if patching:
|
|||||||
print("patchdir not found, no patches applied")
|
print("patchdir not found, no patches applied")
|
||||||
else:
|
else:
|
||||||
patchfiles = [f for f in os.listdir(patchdir)]
|
patchfiles = [f for f in os.listdir(patchdir)]
|
||||||
|
if len(patchfiles) > 0:
|
||||||
for p in patchfiles:
|
for p in patchfiles:
|
||||||
patch = os.path.join(patchdir, p)
|
patch = os.path.join(patchdir, p)
|
||||||
print(f"applying {patch}")
|
print(f"applying {patch}")
|
||||||
res = subprocess.run(["git", "apply", patch], capture_output=True, text=True)
|
res = subprocess.run(["git", "apply", patch], capture_output=True, text=True)
|
||||||
if res.returncode != 0:
|
if res.returncode != 0:
|
||||||
print(res.stderr)
|
print(res.stderr)
|
||||||
|
env.AddPostAction("$PROGPATH", cleanup_patches)
|
||||||
else:
|
else:
|
||||||
print("no patches found")
|
print("no patches found")
|
||||||
|
|||||||
@@ -0,0 +1,278 @@
|
|||||||
|
const uint8_t DSEG7Classic_BoldItalic10pt7bBitmaps[] PROGMEM = {
|
||||||
|
0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE,
|
||||||
|
0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18,
|
||||||
|
0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61,
|
||||||
|
0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86,
|
||||||
|
0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18,
|
||||||
|
0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
|
||||||
|
0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86,
|
||||||
|
0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18,
|
||||||
|
0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61,
|
||||||
|
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87,
|
||||||
|
0xF0, 0xFF, 0x7F, 0x80, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86,
|
||||||
|
0x18, 0x61, 0x87, 0xF0, 0x1F, 0xF8, 0xBF, 0xD3, 0x00, 0xCC, 0x07, 0x70,
|
||||||
|
0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x07, 0x70, 0x19, 0x00, 0x24, 0x00, 0x98,
|
||||||
|
0x06, 0x60, 0x19, 0x80, 0x66, 0x03, 0xB8, 0x0E, 0xE0, 0x3B, 0x80, 0xEB,
|
||||||
|
0xFF, 0x9F, 0xF8, 0x13, 0x77, 0x77, 0x76, 0x22, 0x66, 0x6E, 0xEE, 0xE6,
|
||||||
|
0x1F, 0xF8, 0x3F, 0xD0, 0x00, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01,
|
||||||
|
0xC0, 0x07, 0x00, 0x18, 0x7F, 0xA5, 0xFE, 0x18, 0x00, 0x60, 0x01, 0x80,
|
||||||
|
0x06, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80, 0x0B, 0xFE, 0x1F, 0xF8, 0x3F,
|
||||||
|
0xF0, 0xFF, 0x40, 0x06, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07,
|
||||||
|
0x00, 0x31, 0xFE, 0x8F, 0xF4, 0x00, 0x60, 0x03, 0x00, 0x18, 0x01, 0xC0,
|
||||||
|
0x0E, 0x00, 0x70, 0x03, 0x9F, 0xFD, 0xFF, 0x80, 0x40, 0x0B, 0x00, 0xD8,
|
||||||
|
0x0F, 0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x06, 0xBF, 0xD1, 0xFE,
|
||||||
|
0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70,
|
||||||
|
0x01, 0x80, 0x3F, 0xF5, 0xFE, 0x60, 0x06, 0x00, 0xE0, 0x0E, 0x00, 0xE0,
|
||||||
|
0x0E, 0x00, 0xE0, 0x0B, 0xFC, 0x3F, 0xD0, 0x03, 0x00, 0x30, 0x03, 0x00,
|
||||||
|
0x70, 0x07, 0x00, 0x70, 0x07, 0x7F, 0xFF, 0xFC, 0x1F, 0xF9, 0x7F, 0x8C,
|
||||||
|
0x00, 0x60, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70, 0x02, 0xFF,
|
||||||
|
0x17, 0xFA, 0xC0, 0x36, 0x01, 0xB0, 0x0D, 0x80, 0xFC, 0x07, 0xE0, 0x3F,
|
||||||
|
0x01, 0xEF, 0xFE, 0xFF, 0xC0, 0x3F, 0xF2, 0xFF, 0x58, 0x06, 0xC0, 0x7E,
|
||||||
|
0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x34, 0x00, 0x80, 0x04, 0x00,
|
||||||
|
0x60, 0x03, 0x00, 0x18, 0x01, 0xC0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x0C,
|
||||||
|
0x1F, 0xF8, 0xBF, 0xD3, 0x00, 0xCC, 0x07, 0x70, 0x1D, 0xC0, 0x77, 0x01,
|
||||||
|
0xDC, 0x07, 0x70, 0x19, 0x7F, 0xA5, 0xFE, 0x98, 0x06, 0x60, 0x19, 0x80,
|
||||||
|
0x66, 0x03, 0xB8, 0x0E, 0xE0, 0x3B, 0x80, 0xEB, 0xFF, 0x9F, 0xF8, 0x3F,
|
||||||
|
0xF2, 0xFF, 0x58, 0x06, 0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07,
|
||||||
|
0xE0, 0x35, 0xFE, 0x8F, 0xF4, 0x00, 0x60, 0x03, 0x00, 0x18, 0x01, 0xC0,
|
||||||
|
0x0E, 0x00, 0x70, 0x03, 0x9F, 0xFD, 0xFF, 0x80, 0x67, 0x00, 0x00, 0x00,
|
||||||
|
0x4E, 0x40, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87,
|
||||||
|
0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0,
|
||||||
|
0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE,
|
||||||
|
0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18,
|
||||||
|
0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61,
|
||||||
|
0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0x1F, 0xF8, 0xBF, 0xD3,
|
||||||
|
0x00, 0xCC, 0x07, 0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x07, 0x70, 0x19,
|
||||||
|
0x7F, 0xA5, 0xFE, 0x98, 0x06, 0x60, 0x19, 0x80, 0x66, 0x03, 0xB8, 0x0E,
|
||||||
|
0xE0, 0x3B, 0x80, 0xE8, 0x01, 0x80, 0x20, 0x01, 0x80, 0x0C, 0x00, 0xE0,
|
||||||
|
0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x5F, 0xE2, 0xFF, 0x58, 0x06,
|
||||||
|
0xC0, 0x36, 0x01, 0xB0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3D, 0xFF, 0xDF,
|
||||||
|
0xF8, 0x1F, 0xE5, 0xFE, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0xE0, 0x0E,
|
||||||
|
0x00, 0xE0, 0x0B, 0xFE, 0x7F, 0xE0, 0x00, 0x04, 0x00, 0x30, 0x01, 0xC0,
|
||||||
|
0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x06, 0x1F, 0xE9, 0x7F, 0xA6,
|
||||||
|
0x01, 0x98, 0x06, 0x60, 0x19, 0x80, 0xEE, 0x03, 0xB8, 0x0E, 0xE0, 0x3A,
|
||||||
|
0xFF, 0xE7, 0xFE, 0x00, 0x1F, 0xF9, 0x7F, 0x8C, 0x00, 0x60, 0x07, 0x00,
|
||||||
|
0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70, 0x02, 0xFF, 0x17, 0xF8, 0xC0, 0x06,
|
||||||
|
0x00, 0x30, 0x01, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x2F, 0xF8, 0xFF,
|
||||||
|
0xC0, 0x1F, 0xF9, 0x7F, 0x8C, 0x00, 0x60, 0x07, 0x00, 0x38, 0x01, 0xC0,
|
||||||
|
0x0E, 0x00, 0x70, 0x02, 0xFF, 0x17, 0xF8, 0xC0, 0x06, 0x00, 0x30, 0x01,
|
||||||
|
0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x20, 0x00, 0x1F, 0xF9, 0x7F, 0x8C,
|
||||||
|
0x00, 0x60, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70, 0x02, 0x00,
|
||||||
|
0x10, 0x02, 0xC0, 0x36, 0x01, 0xB0, 0x0D, 0x80, 0xFC, 0x07, 0xE0, 0x3F,
|
||||||
|
0x01, 0xEF, 0xFE, 0xFF, 0xC0, 0x20, 0x01, 0x80, 0x0C, 0x00, 0xE0, 0x07,
|
||||||
|
0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x5F, 0xE2, 0xFF, 0x58, 0x06, 0xC0,
|
||||||
|
0x36, 0x01, 0xB0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3C, 0x00, 0xC0, 0x2D,
|
||||||
|
0xBF, 0xFF, 0x60, 0x00, 0x04, 0x00, 0x30, 0x01, 0xC0, 0x07, 0x00, 0x1C,
|
||||||
|
0x00, 0x70, 0x01, 0xC0, 0x06, 0x00, 0x09, 0x00, 0x26, 0x01, 0x98, 0x06,
|
||||||
|
0x60, 0x19, 0x80, 0xEE, 0x03, 0xB8, 0x0E, 0xE0, 0x3A, 0xFF, 0xE7, 0xFE,
|
||||||
|
0x00, 0x1F, 0xF9, 0x7F, 0x8C, 0x00, 0x60, 0x07, 0x00, 0x38, 0x01, 0xC0,
|
||||||
|
0x0E, 0x00, 0x70, 0x02, 0xFF, 0x17, 0xFA, 0xC0, 0x36, 0x01, 0xB0, 0x0D,
|
||||||
|
0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xE0, 0x06, 0x20, 0x03, 0x00, 0x30,
|
||||||
|
0x07, 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, 0x00, 0x40, 0x04, 0x00, 0x60,
|
||||||
|
0x06, 0x00, 0x60, 0x06, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0B, 0xFE, 0x7F,
|
||||||
|
0xE0, 0x1F, 0xF8, 0xBF, 0xD3, 0x00, 0xCC, 0x07, 0x70, 0x1D, 0xC0, 0x77,
|
||||||
|
0x01, 0xDC, 0x07, 0x70, 0x19, 0x00, 0x24, 0x00, 0x98, 0x06, 0x60, 0x19,
|
||||||
|
0x80, 0x66, 0x03, 0xB8, 0x0E, 0xE0, 0x3B, 0x80, 0xE8, 0x01, 0x80, 0x1F,
|
||||||
|
0xE2, 0xFF, 0x58, 0x06, 0xC0, 0x36, 0x01, 0xB0, 0x1F, 0x80, 0xFC, 0x07,
|
||||||
|
0xE0, 0x3C, 0x00, 0xC0, 0x1F, 0xE2, 0xFF, 0x58, 0x06, 0xC0, 0x36, 0x01,
|
||||||
|
0xB0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3D, 0xFF, 0xDF, 0xF8, 0x1F, 0xF8,
|
||||||
|
0xBF, 0xD3, 0x00, 0xCC, 0x07, 0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x07,
|
||||||
|
0x70, 0x19, 0x7F, 0xA5, 0xFE, 0x18, 0x00, 0x60, 0x01, 0x80, 0x06, 0x00,
|
||||||
|
0x38, 0x00, 0xE0, 0x03, 0x80, 0x08, 0x00, 0x00, 0x3F, 0xF2, 0xFF, 0x58,
|
||||||
|
0x06, 0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x35, 0xFE,
|
||||||
|
0x8F, 0xF4, 0x00, 0x60, 0x03, 0x00, 0x18, 0x01, 0xC0, 0x0E, 0x00, 0x70,
|
||||||
|
0x03, 0x80, 0x0C, 0x1F, 0xE5, 0xFE, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
|
||||||
|
0xE0, 0x0E, 0x00, 0xE0, 0x08, 0x00, 0x40, 0x06, 0x00, 0x60, 0x0E, 0x00,
|
||||||
|
0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xBF, 0xC3, 0xFD, 0x00, 0x30, 0x03,
|
||||||
|
0x00, 0x30, 0x07, 0x00, 0x70, 0x07, 0x00, 0x77, 0xFF, 0xFF, 0xC0, 0x20,
|
||||||
|
0x03, 0x00, 0x30, 0x07, 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, 0x00, 0x5F,
|
||||||
|
0xE5, 0xFE, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0xE0, 0x0E, 0x00, 0xE0,
|
||||||
|
0x0B, 0xFE, 0x7F, 0xE0, 0x40, 0x0B, 0x00, 0xD8, 0x06, 0xC0, 0x36, 0x03,
|
||||||
|
0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xBF, 0xFB, 0xFF, 0x00, 0x20, 0x04, 0xC0,
|
||||||
|
0x33, 0x01, 0xDC, 0x07, 0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x06, 0x40,
|
||||||
|
0x09, 0x00, 0x26, 0x01, 0x98, 0x06, 0x60, 0x19, 0x80, 0xEE, 0x03, 0xB8,
|
||||||
|
0x0E, 0xE0, 0x3A, 0xFF, 0xE7, 0xFE, 0x00, 0x20, 0x04, 0xC0, 0x33, 0x01,
|
||||||
|
0xDC, 0x07, 0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x06, 0x5F, 0xE9, 0x7F,
|
||||||
|
0xA6, 0x01, 0x98, 0x06, 0x60, 0x19, 0x80, 0xEE, 0x03, 0xB8, 0x0E, 0xE0,
|
||||||
|
0x3A, 0xFF, 0xE7, 0xFE, 0x00, 0x20, 0x04, 0xC0, 0x33, 0x01, 0xDC, 0x07,
|
||||||
|
0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x06, 0x5F, 0xE9, 0x7F, 0xA6, 0x01,
|
||||||
|
0x98, 0x06, 0x60, 0x19, 0x80, 0xEE, 0x03, 0xB8, 0x0E, 0xE0, 0x3A, 0x00,
|
||||||
|
0x60, 0x40, 0x0B, 0x00, 0xD8, 0x0F, 0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0x80,
|
||||||
|
0xFC, 0x06, 0xBF, 0xD1, 0xFE, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x38,
|
||||||
|
0x01, 0xC0, 0x0E, 0x00, 0x73, 0xFF, 0xBF, 0xF0, 0x1F, 0xF8, 0x3F, 0xD0,
|
||||||
|
0x00, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x18,
|
||||||
|
0x00, 0x24, 0x00, 0x18, 0x00, 0x60, 0x01, 0x80, 0x06, 0x00, 0x38, 0x00,
|
||||||
|
0xE0, 0x03, 0x80, 0x0B, 0xFE, 0x1F, 0xF8, 0xFE, 0x18, 0x61, 0x86, 0x18,
|
||||||
|
0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
|
||||||
|
0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86,
|
||||||
|
0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18,
|
||||||
|
0x61, 0x87, 0xF0, 0x7F, 0xDF, 0xF8, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
|
||||||
|
0x86, 0x18, 0x61, 0x87, 0xF0, 0x1F, 0xF8, 0xBF, 0xD3, 0x00, 0xCC, 0x07,
|
||||||
|
0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x07, 0x70, 0x19, 0x7F, 0xA5, 0xFE,
|
||||||
|
0x98, 0x06, 0x60, 0x19, 0x80, 0x66, 0x03, 0xB8, 0x0E, 0xE0, 0x3B, 0x80,
|
||||||
|
0xE8, 0x01, 0x80, 0x20, 0x01, 0x80, 0x0C, 0x00, 0xE0, 0x07, 0x00, 0x38,
|
||||||
|
0x01, 0xC0, 0x0E, 0x00, 0x5F, 0xE2, 0xFF, 0x58, 0x06, 0xC0, 0x36, 0x01,
|
||||||
|
0xB0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3D, 0xFF, 0xDF, 0xF8, 0x1F, 0xE5,
|
||||||
|
0xFE, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0B,
|
||||||
|
0xFE, 0x7F, 0xE0, 0x00, 0x04, 0x00, 0x30, 0x01, 0xC0, 0x07, 0x00, 0x1C,
|
||||||
|
0x00, 0x70, 0x01, 0xC0, 0x06, 0x1F, 0xE9, 0x7F, 0xA6, 0x01, 0x98, 0x06,
|
||||||
|
0x60, 0x19, 0x80, 0xEE, 0x03, 0xB8, 0x0E, 0xE0, 0x3A, 0xFF, 0xE7, 0xFE,
|
||||||
|
0x00, 0x1F, 0xF9, 0x7F, 0x8C, 0x00, 0x60, 0x07, 0x00, 0x38, 0x01, 0xC0,
|
||||||
|
0x0E, 0x00, 0x70, 0x02, 0xFF, 0x17, 0xF8, 0xC0, 0x06, 0x00, 0x30, 0x01,
|
||||||
|
0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x2F, 0xF8, 0xFF, 0xC0, 0x1F, 0xF9,
|
||||||
|
0x7F, 0x8C, 0x00, 0x60, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70,
|
||||||
|
0x02, 0xFF, 0x17, 0xF8, 0xC0, 0x06, 0x00, 0x30, 0x01, 0x80, 0x1C, 0x00,
|
||||||
|
0xE0, 0x07, 0x00, 0x20, 0x00, 0x1F, 0xF9, 0x7F, 0x8C, 0x00, 0x60, 0x07,
|
||||||
|
0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70, 0x02, 0x00, 0x10, 0x02, 0xC0,
|
||||||
|
0x36, 0x01, 0xB0, 0x0D, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xEF, 0xFE,
|
||||||
|
0xFF, 0xC0, 0x20, 0x01, 0x80, 0x0C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01,
|
||||||
|
0xC0, 0x0E, 0x00, 0x5F, 0xE2, 0xFF, 0x58, 0x06, 0xC0, 0x36, 0x01, 0xB0,
|
||||||
|
0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3C, 0x00, 0xC0, 0x2D, 0xBF, 0xFF, 0x60,
|
||||||
|
0x00, 0x04, 0x00, 0x30, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01,
|
||||||
|
0xC0, 0x06, 0x00, 0x09, 0x00, 0x26, 0x01, 0x98, 0x06, 0x60, 0x19, 0x80,
|
||||||
|
0xEE, 0x03, 0xB8, 0x0E, 0xE0, 0x3A, 0xFF, 0xE7, 0xFE, 0x00, 0x1F, 0xF9,
|
||||||
|
0x7F, 0x8C, 0x00, 0x60, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70,
|
||||||
|
0x02, 0xFF, 0x17, 0xFA, 0xC0, 0x36, 0x01, 0xB0, 0x0D, 0x80, 0xFC, 0x07,
|
||||||
|
0xE0, 0x3F, 0x01, 0xE0, 0x06, 0x20, 0x03, 0x00, 0x30, 0x07, 0x00, 0x70,
|
||||||
|
0x07, 0x00, 0x70, 0x07, 0x00, 0x40, 0x04, 0x00, 0x60, 0x06, 0x00, 0x60,
|
||||||
|
0x06, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0B, 0xFE, 0x7F, 0xE0, 0x1F, 0xF8,
|
||||||
|
0xBF, 0xD3, 0x00, 0xCC, 0x07, 0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x07,
|
||||||
|
0x70, 0x19, 0x00, 0x24, 0x00, 0x98, 0x06, 0x60, 0x19, 0x80, 0x66, 0x03,
|
||||||
|
0xB8, 0x0E, 0xE0, 0x3B, 0x80, 0xE8, 0x01, 0x80, 0x1F, 0xE2, 0xFF, 0x58,
|
||||||
|
0x06, 0xC0, 0x36, 0x01, 0xB0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3C, 0x00,
|
||||||
|
0xC0, 0x1F, 0xE2, 0xFF, 0x58, 0x06, 0xC0, 0x36, 0x01, 0xB0, 0x1F, 0x80,
|
||||||
|
0xFC, 0x07, 0xE0, 0x3D, 0xFF, 0xDF, 0xF8, 0x1F, 0xF8, 0xBF, 0xD3, 0x00,
|
||||||
|
0xCC, 0x07, 0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x07, 0x70, 0x19, 0x7F,
|
||||||
|
0xA5, 0xFE, 0x18, 0x00, 0x60, 0x01, 0x80, 0x06, 0x00, 0x38, 0x00, 0xE0,
|
||||||
|
0x03, 0x80, 0x08, 0x00, 0x00, 0x3F, 0xF2, 0xFF, 0x58, 0x06, 0xC0, 0x7E,
|
||||||
|
0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x35, 0xFE, 0x8F, 0xF4, 0x00,
|
||||||
|
0x60, 0x03, 0x00, 0x18, 0x01, 0xC0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x0C,
|
||||||
|
0x1F, 0xE5, 0xFE, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0xE0, 0x0E, 0x00,
|
||||||
|
0xE0, 0x08, 0x00, 0x40, 0x06, 0x00, 0x60, 0x0E, 0x00, 0xE0, 0x0E, 0x00,
|
||||||
|
0xE0, 0x0E, 0x00, 0xBF, 0xC3, 0xFD, 0x00, 0x30, 0x03, 0x00, 0x30, 0x07,
|
||||||
|
0x00, 0x70, 0x07, 0x00, 0x77, 0xFF, 0xFF, 0xC0, 0x20, 0x03, 0x00, 0x30,
|
||||||
|
0x07, 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, 0x00, 0x5F, 0xE5, 0xFE, 0x60,
|
||||||
|
0x06, 0x00, 0x60, 0x06, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0B, 0xFE, 0x7F,
|
||||||
|
0xE0, 0x40, 0x0B, 0x00, 0xD8, 0x06, 0xC0, 0x36, 0x03, 0xF0, 0x1F, 0x80,
|
||||||
|
0xFC, 0x07, 0xBF, 0xFB, 0xFF, 0x00, 0x20, 0x04, 0xC0, 0x33, 0x01, 0xDC,
|
||||||
|
0x07, 0x70, 0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x06, 0x40, 0x09, 0x00, 0x26,
|
||||||
|
0x01, 0x98, 0x06, 0x60, 0x19, 0x80, 0xEE, 0x03, 0xB8, 0x0E, 0xE0, 0x3A,
|
||||||
|
0xFF, 0xE7, 0xFE, 0x00, 0x20, 0x04, 0xC0, 0x33, 0x01, 0xDC, 0x07, 0x70,
|
||||||
|
0x1D, 0xC0, 0x77, 0x01, 0xDC, 0x06, 0x5F, 0xE9, 0x7F, 0xA6, 0x01, 0x98,
|
||||||
|
0x06, 0x60, 0x19, 0x80, 0xEE, 0x03, 0xB8, 0x0E, 0xE0, 0x3A, 0xFF, 0xE7,
|
||||||
|
0xFE, 0x00, 0x20, 0x04, 0xC0, 0x33, 0x01, 0xDC, 0x07, 0x70, 0x1D, 0xC0,
|
||||||
|
0x77, 0x01, 0xDC, 0x06, 0x5F, 0xE9, 0x7F, 0xA6, 0x01, 0x98, 0x06, 0x60,
|
||||||
|
0x19, 0x80, 0xEE, 0x03, 0xB8, 0x0E, 0xE0, 0x3A, 0x00, 0x60, 0x40, 0x0B,
|
||||||
|
0x00, 0xD8, 0x0F, 0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x06, 0xBF,
|
||||||
|
0xD1, 0xFE, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x38, 0x01, 0xC0, 0x0E,
|
||||||
|
0x00, 0x73, 0xFF, 0xBF, 0xF0, 0x1F, 0xF8, 0x3F, 0xD0, 0x00, 0xC0, 0x07,
|
||||||
|
0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x18, 0x00, 0x24, 0x00,
|
||||||
|
0x18, 0x00, 0x60, 0x01, 0x80, 0x06, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80,
|
||||||
|
0x0B, 0xFE, 0x1F, 0xF8, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18,
|
||||||
|
0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61,
|
||||||
|
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87,
|
||||||
|
0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0 };
|
||||||
|
|
||||||
|
const GFXglyph DSEG7Classic_BoldItalic10pt7bGlyphs[] PROGMEM = {
|
||||||
|
{ 0, 0, 0, 4, 0, 1 }, // 0x20 ' '
|
||||||
|
{ 0, 0, 0, 16, 0, 1 }, // 0x21 '!'
|
||||||
|
{ 0, 6, 14, 7, 1, -13 }, // 0x22 '"'
|
||||||
|
{ 11, 6, 14, 7, 1, -13 }, // 0x23 '#'
|
||||||
|
{ 22, 6, 14, 7, 1, -13 }, // 0x24 '$'
|
||||||
|
{ 33, 6, 14, 7, 1, -13 }, // 0x25 '%'
|
||||||
|
{ 44, 6, 14, 7, 1, -13 }, // 0x26 '&'
|
||||||
|
{ 55, 6, 14, 7, 1, -13 }, // 0x27 '''
|
||||||
|
{ 66, 6, 14, 7, 1, -13 }, // 0x28 '('
|
||||||
|
{ 77, 6, 14, 7, 1, -13 }, // 0x29 ')'
|
||||||
|
{ 88, 6, 14, 7, 1, -13 }, // 0x2A '*'
|
||||||
|
{ 99, 6, 14, 7, 1, -13 }, // 0x2B '+'
|
||||||
|
{ 110, 6, 14, 7, 1, -13 }, // 0x2C ','
|
||||||
|
{ 121, 9, 2, 16, 4, -10 }, // 0x2D '-'
|
||||||
|
{ 124, 2, 2, 0, -2, -1 }, // 0x2E '.'
|
||||||
|
{ 125, 6, 14, 7, 1, -13 }, // 0x2F '/'
|
||||||
|
{ 136, 14, 20, 16, 1, -19 }, // 0x30 '0'
|
||||||
|
{ 171, 4, 18, 16, 11, -18 }, // 0x31 '1'
|
||||||
|
{ 180, 14, 20, 16, 1, -19 }, // 0x32 '2'
|
||||||
|
{ 215, 13, 20, 16, 2, -19 }, // 0x33 '3'
|
||||||
|
{ 248, 13, 18, 16, 2, -18 }, // 0x34 '4'
|
||||||
|
{ 278, 12, 20, 16, 2, -19 }, // 0x35 '5'
|
||||||
|
{ 308, 13, 20, 16, 1, -19 }, // 0x36 '6'
|
||||||
|
{ 341, 13, 19, 16, 2, -19 }, // 0x37 '7'
|
||||||
|
{ 372, 14, 20, 16, 1, -19 }, // 0x38 '8'
|
||||||
|
{ 407, 13, 20, 16, 2, -19 }, // 0x39 '9'
|
||||||
|
{ 440, 4, 11, 4, 0, -14 }, // 0x3A ':'
|
||||||
|
{ 446, 6, 14, 7, 1, -13 }, // 0x3B ';'
|
||||||
|
{ 457, 6, 14, 7, 1, -13 }, // 0x3C '<'
|
||||||
|
{ 468, 6, 14, 7, 1, -13 }, // 0x3D '='
|
||||||
|
{ 479, 6, 14, 7, 1, -13 }, // 0x3E '>'
|
||||||
|
{ 490, 6, 14, 7, 1, -13 }, // 0x3F '?'
|
||||||
|
{ 501, 6, 14, 7, 1, -13 }, // 0x40 '@'
|
||||||
|
{ 512, 14, 19, 16, 1, -19 }, // 0x41 'A'
|
||||||
|
{ 546, 13, 19, 16, 1, -18 }, // 0x42 'B'
|
||||||
|
{ 577, 12, 11, 16, 1, -10 }, // 0x43 'C'
|
||||||
|
{ 594, 14, 19, 16, 1, -18 }, // 0x44 'D'
|
||||||
|
{ 628, 13, 20, 16, 1, -19 }, // 0x45 'E'
|
||||||
|
{ 661, 13, 19, 16, 1, -19 }, // 0x46 'F'
|
||||||
|
{ 692, 13, 20, 16, 1, -19 }, // 0x47 'G'
|
||||||
|
{ 725, 13, 18, 16, 1, -18 }, // 0x48 'H'
|
||||||
|
{ 755, 3, 9, 16, 11, -9 }, // 0x49 'I'
|
||||||
|
{ 759, 14, 19, 16, 1, -18 }, // 0x4A 'J'
|
||||||
|
{ 793, 13, 19, 16, 1, -19 }, // 0x4B 'K'
|
||||||
|
{ 824, 12, 19, 16, 1, -18 }, // 0x4C 'L'
|
||||||
|
{ 853, 14, 19, 16, 1, -19 }, // 0x4D 'M'
|
||||||
|
{ 887, 13, 10, 16, 1, -10 }, // 0x4E 'N'
|
||||||
|
{ 904, 13, 11, 16, 1, -10 }, // 0x4F 'O'
|
||||||
|
{ 922, 14, 19, 16, 1, -19 }, // 0x50 'P'
|
||||||
|
{ 956, 13, 19, 16, 2, -19 }, // 0x51 'Q'
|
||||||
|
{ 987, 12, 10, 16, 1, -10 }, // 0x52 'R'
|
||||||
|
{ 1002, 12, 19, 16, 2, -18 }, // 0x53 'S'
|
||||||
|
{ 1031, 12, 19, 16, 1, -18 }, // 0x54 'T'
|
||||||
|
{ 1060, 13, 10, 16, 1, -9 }, // 0x55 'U'
|
||||||
|
{ 1077, 14, 19, 16, 1, -18 }, // 0x56 'V'
|
||||||
|
{ 1111, 14, 19, 16, 1, -18 }, // 0x57 'W'
|
||||||
|
{ 1145, 14, 18, 16, 1, -18 }, // 0x58 'X'
|
||||||
|
{ 1177, 13, 19, 16, 2, -18 }, // 0x59 'Y'
|
||||||
|
{ 1208, 14, 20, 16, 1, -19 }, // 0x5A 'Z'
|
||||||
|
{ 1243, 6, 14, 7, 1, -13 }, // 0x5B '['
|
||||||
|
{ 1254, 6, 14, 7, 1, -13 }, // 0x5C '\'
|
||||||
|
{ 1265, 6, 14, 7, 1, -13 }, // 0x5D ']'
|
||||||
|
{ 1276, 6, 14, 7, 1, -13 }, // 0x5E '^'
|
||||||
|
{ 1287, 11, 2, 16, 2, -1 }, // 0x5F '_'
|
||||||
|
{ 1290, 6, 14, 7, 1, -13 }, // 0x60 '`'
|
||||||
|
{ 1301, 14, 19, 16, 1, -19 }, // 0x61 'a'
|
||||||
|
{ 1335, 13, 19, 16, 1, -18 }, // 0x62 'b'
|
||||||
|
{ 1366, 12, 11, 16, 1, -10 }, // 0x63 'c'
|
||||||
|
{ 1383, 14, 19, 16, 1, -18 }, // 0x64 'd'
|
||||||
|
{ 1417, 13, 20, 16, 1, -19 }, // 0x65 'e'
|
||||||
|
{ 1450, 13, 19, 16, 1, -19 }, // 0x66 'f'
|
||||||
|
{ 1481, 13, 20, 16, 1, -19 }, // 0x67 'g'
|
||||||
|
{ 1514, 13, 18, 16, 1, -18 }, // 0x68 'h'
|
||||||
|
{ 1544, 3, 9, 16, 11, -9 }, // 0x69 'i'
|
||||||
|
{ 1548, 14, 19, 16, 1, -18 }, // 0x6A 'j'
|
||||||
|
{ 1582, 13, 19, 16, 1, -19 }, // 0x6B 'k'
|
||||||
|
{ 1613, 12, 19, 16, 1, -18 }, // 0x6C 'l'
|
||||||
|
{ 1642, 14, 19, 16, 1, -19 }, // 0x6D 'm'
|
||||||
|
{ 1676, 13, 10, 16, 1, -10 }, // 0x6E 'n'
|
||||||
|
{ 1693, 13, 11, 16, 1, -10 }, // 0x6F 'o'
|
||||||
|
{ 1711, 14, 19, 16, 1, -19 }, // 0x70 'p'
|
||||||
|
{ 1745, 13, 19, 16, 2, -19 }, // 0x71 'q'
|
||||||
|
{ 1776, 12, 10, 16, 1, -10 }, // 0x72 'r'
|
||||||
|
{ 1791, 12, 19, 16, 2, -18 }, // 0x73 's'
|
||||||
|
{ 1820, 12, 19, 16, 1, -18 }, // 0x74 't'
|
||||||
|
{ 1849, 13, 10, 16, 1, -9 }, // 0x75 'u'
|
||||||
|
{ 1866, 14, 19, 16, 1, -18 }, // 0x76 'v'
|
||||||
|
{ 1900, 14, 19, 16, 1, -18 }, // 0x77 'w'
|
||||||
|
{ 1934, 14, 18, 16, 1, -18 }, // 0x78 'x'
|
||||||
|
{ 1966, 13, 19, 16, 2, -18 }, // 0x79 'y'
|
||||||
|
{ 1997, 14, 20, 16, 1, -19 }, // 0x7A 'z'
|
||||||
|
{ 2032, 6, 14, 7, 1, -13 }, // 0x7B '{'
|
||||||
|
{ 2043, 6, 14, 7, 1, -13 }, // 0x7C '|'
|
||||||
|
{ 2054, 6, 14, 7, 1, -13 }, // 0x7D '}'
|
||||||
|
{ 2065, 6, 14, 7, 1, -13 } }; // 0x7E '~'
|
||||||
|
|
||||||
|
const GFXfont DSEG7Classic_BoldItalic10pt7b PROGMEM = {
|
||||||
|
(uint8_t *)DSEG7Classic_BoldItalic10pt7bBitmaps,
|
||||||
|
(GFXglyph *)DSEG7Classic_BoldItalic10pt7bGlyphs,
|
||||||
|
0x20, 0x7E, 22 };
|
||||||
|
|
||||||
|
// Approx. 2748 bytes
|
||||||
@@ -0,0 +1,352 @@
|
|||||||
|
const uint8_t DSEG7Classic_BoldItalic12pt7bBitmaps[] PROGMEM = {
|
||||||
|
0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30,
|
||||||
|
0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C,
|
||||||
|
0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83,
|
||||||
|
0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60,
|
||||||
|
0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18,
|
||||||
|
0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06,
|
||||||
|
0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF,
|
||||||
|
0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30,
|
||||||
|
0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C,
|
||||||
|
0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83,
|
||||||
|
0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60,
|
||||||
|
0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18,
|
||||||
|
0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0x7F, 0xEF,
|
||||||
|
0xFC, 0x7D, 0x80, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06,
|
||||||
|
0x0C, 0x18, 0x30, 0x60, 0xFF, 0x0F, 0xFF, 0x07, 0xFF, 0x4D, 0xFF, 0x67,
|
||||||
|
0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03,
|
||||||
|
0x9C, 0x03, 0x9C, 0x00, 0xCC, 0x00, 0x26, 0x00, 0x13, 0x80, 0x39, 0xC0,
|
||||||
|
0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x00, 0xE7,
|
||||||
|
0x00, 0xE3, 0xFF, 0xF1, 0xFF, 0xE8, 0x7F, 0xF8, 0x08, 0xCE, 0x73, 0x9C,
|
||||||
|
0xE7, 0x71, 0x84, 0x27, 0x39, 0xCE, 0x73, 0x9D, 0xC6, 0x10, 0x0F, 0xFF,
|
||||||
|
0x07, 0xFF, 0x41, 0xFF, 0x60, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00,
|
||||||
|
0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0xC1, 0xFF, 0xA6,
|
||||||
|
0xFF, 0xC3, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00,
|
||||||
|
0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0xFF, 0xC1, 0xFF, 0xE0, 0x7F,
|
||||||
|
0xF8, 0x1F, 0xFE, 0x1F, 0xFD, 0x0F, 0xFB, 0x00, 0x07, 0x00, 0x07, 0x00,
|
||||||
|
0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x06, 0x1F,
|
||||||
|
0xFA, 0x1F, 0xFA, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00,
|
||||||
|
0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x1C, 0x3F, 0xFC, 0x7F, 0xF4, 0x7F,
|
||||||
|
0xF8, 0x00, 0x01, 0x60, 0x03, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0xE0, 0x06, 0xDF, 0xFA, 0x1F,
|
||||||
|
0xFA, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00,
|
||||||
|
0x0E, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x04, 0x1F, 0xFE, 0x3F,
|
||||||
|
0xF9, 0xBF, 0xE3, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00,
|
||||||
|
0x70, 0x00, 0xE0, 0x03, 0x80, 0x06, 0xFF, 0xC1, 0xFF, 0xA0, 0x01, 0xC0,
|
||||||
|
0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x01,
|
||||||
|
0xC7, 0xFF, 0x9F, 0xFD, 0x3F, 0xFC, 0x0F, 0xFF, 0x0F, 0xFE, 0x37, 0xFC,
|
||||||
|
0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00,
|
||||||
|
0x38, 0x00, 0x70, 0x00, 0x6F, 0xFC, 0x6F, 0xFD, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E,
|
||||||
|
0x7F, 0xFE, 0x7F, 0xFA, 0x3F, 0xFC, 0x1F, 0xFE, 0x1F, 0xFD, 0x6F, 0xFB,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x0E, 0xE0, 0x06, 0xC0, 0x02, 0x00, 0x02, 0x00, 0x0E, 0x00, 0x0E,
|
||||||
|
0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x1C,
|
||||||
|
0x00, 0x0C, 0x00, 0x04, 0x0F, 0xFF, 0x07, 0xFF, 0x4D, 0xFF, 0x67, 0x00,
|
||||||
|
0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C,
|
||||||
|
0x03, 0x9C, 0x00, 0xCD, 0xFF, 0xA6, 0xFF, 0xD3, 0x80, 0x39, 0xC0, 0x1C,
|
||||||
|
0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x00, 0xE7, 0x00,
|
||||||
|
0xE3, 0xFF, 0xF1, 0xFF, 0xE8, 0x7F, 0xF8, 0x1F, 0xFE, 0x1F, 0xFD, 0x6F,
|
||||||
|
0xFB, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x0E, 0xE0, 0x06, 0xDF, 0xFA, 0x1F, 0xFA, 0x00, 0x0E, 0x00,
|
||||||
|
0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00,
|
||||||
|
0x1C, 0x3F, 0xFC, 0x7F, 0xF4, 0x7F, 0xF8, 0x37, 0x30, 0x00, 0x00, 0x00,
|
||||||
|
0x6E, 0x60, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C,
|
||||||
|
0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83,
|
||||||
|
0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60,
|
||||||
|
0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18,
|
||||||
|
0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06,
|
||||||
|
0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF,
|
||||||
|
0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30,
|
||||||
|
0x60, 0xFF, 0x0F, 0xFF, 0x07, 0xFF, 0x4D, 0xFF, 0x67, 0x00, 0x73, 0x80,
|
||||||
|
0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x03, 0x9C,
|
||||||
|
0x00, 0xCD, 0xFF, 0xA6, 0xFF, 0xD3, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E,
|
||||||
|
0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x00, 0xE7, 0x00, 0xE3, 0x00,
|
||||||
|
0x31, 0x00, 0x08, 0x00, 0x00, 0x30, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x6F,
|
||||||
|
0xFC, 0x6F, 0xFD, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFA, 0x3F,
|
||||||
|
0xFC, 0x0F, 0xFD, 0xBF, 0xF7, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07,
|
||||||
|
0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0xFF, 0x9F, 0xFE, 0x3F, 0xFC,
|
||||||
|
0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00,
|
||||||
|
0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x07, 0x00, 0x01, 0x83, 0xFF, 0x4D,
|
||||||
|
0xFF, 0xA7, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07,
|
||||||
|
0x38, 0x03, 0x9C, 0x01, 0xCE, 0x01, 0xC7, 0xFF, 0xE3, 0xFF, 0xD0, 0xFF,
|
||||||
|
0xF0, 0x0F, 0xFF, 0x0F, 0xFE, 0x37, 0xFC, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x6F,
|
||||||
|
0xFC, 0x6F, 0xFC, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
|
||||||
|
0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x7F, 0xF8, 0x7F, 0xF8, 0x3F,
|
||||||
|
0xFC, 0x0F, 0xFF, 0x0F, 0xFE, 0x37, 0xFC, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x6F,
|
||||||
|
0xFC, 0x6F, 0xFC, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
|
||||||
|
0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x60, 0x00, 0x40, 0x00, 0x0F,
|
||||||
|
0xFF, 0x0F, 0xFE, 0x37, 0xFC, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x60, 0x00, 0x60,
|
||||||
|
0x01, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFA, 0x3F, 0xFC, 0x00,
|
||||||
|
0x00, 0x30, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x6F, 0xFC, 0x6F, 0xFD, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x0E, 0x60, 0x06, 0x40, 0x02, 0x17, 0x77, 0x77, 0x77, 0xE6,
|
||||||
|
0x20, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38,
|
||||||
|
0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x07, 0x00, 0x01, 0x80, 0x00,
|
||||||
|
0x4C, 0x00, 0x27, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70,
|
||||||
|
0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x01, 0xC7, 0xFF, 0xE3, 0xFF, 0xD0,
|
||||||
|
0xFF, 0xF0, 0x0F, 0xFF, 0x0F, 0xFE, 0x37, 0xFC, 0x38, 0x00, 0x38, 0x00,
|
||||||
|
0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00,
|
||||||
|
0x6F, 0xFC, 0x6F, 0xFD, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0x60, 0x06, 0x40, 0x02,
|
||||||
|
0x00, 0x00, 0xC0, 0x03, 0x80, 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80,
|
||||||
|
0x0E, 0x00, 0x38, 0x01, 0xC0, 0x06, 0x00, 0x18, 0x00, 0x70, 0x01, 0xC0,
|
||||||
|
0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x7F,
|
||||||
|
0xF9, 0xFF, 0xE3, 0xFF, 0xC0, 0x0F, 0xFF, 0x07, 0xFF, 0x4D, 0xFF, 0x67,
|
||||||
|
0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03,
|
||||||
|
0x9C, 0x03, 0x9C, 0x00, 0xCC, 0x00, 0x26, 0x00, 0x13, 0x80, 0x39, 0xC0,
|
||||||
|
0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x00, 0xE7,
|
||||||
|
0x00, 0xE3, 0x00, 0x31, 0x00, 0x08, 0x0F, 0xFC, 0x6F, 0xFD, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x0E, 0x60, 0x06, 0x40, 0x02, 0x0F, 0xFC, 0x6F, 0xFD, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFA, 0x3F, 0xFC, 0x0F, 0xFF, 0x07, 0xFF,
|
||||||
|
0x4D, 0xFF, 0x67, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70,
|
||||||
|
0x07, 0x38, 0x03, 0x9C, 0x03, 0x9C, 0x00, 0xCD, 0xFF, 0xA6, 0xFF, 0xC3,
|
||||||
|
0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00,
|
||||||
|
0x0E, 0x00, 0x07, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x1F, 0xFE, 0x1F,
|
||||||
|
0xFD, 0x6F, 0xFB, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x0E, 0xE0, 0x06, 0xDF, 0xFA, 0x1F, 0xFA, 0x00,
|
||||||
|
0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00,
|
||||||
|
0x0E, 0x00, 0x1C, 0x00, 0x0C, 0x00, 0x04, 0x0F, 0xFD, 0xBF, 0xF7, 0x00,
|
||||||
|
0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0,
|
||||||
|
0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0x03, 0x80, 0x07,
|
||||||
|
0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38, 0x00, 0x70, 0x01, 0xC0, 0x03, 0x7F,
|
||||||
|
0xE0, 0xFF, 0xD0, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E,
|
||||||
|
0x00, 0x1C, 0x00, 0x38, 0x00, 0xE3, 0xFF, 0xCF, 0xFE, 0x9F, 0xFE, 0x00,
|
||||||
|
0x00, 0x00, 0xC0, 0x03, 0x80, 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80,
|
||||||
|
0x0E, 0x00, 0x38, 0x01, 0xC0, 0x06, 0xFF, 0xDB, 0xFF, 0x70, 0x01, 0xC0,
|
||||||
|
0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x7F,
|
||||||
|
0xF9, 0xFF, 0xE3, 0xFF, 0xC0, 0x60, 0x01, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0x7F,
|
||||||
|
0xFE, 0x7F, 0xFA, 0x3F, 0xFC, 0x00, 0x00, 0x98, 0x00, 0xCE, 0x00, 0xE7,
|
||||||
|
0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x07,
|
||||||
|
0x38, 0x01, 0x98, 0x00, 0x4C, 0x00, 0x27, 0x00, 0x73, 0x80, 0x39, 0xC0,
|
||||||
|
0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x01, 0xC7,
|
||||||
|
0xFF, 0xE3, 0xFF, 0xD0, 0xFF, 0xF0, 0x00, 0x00, 0x98, 0x00, 0xCE, 0x00,
|
||||||
|
0xE7, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38,
|
||||||
|
0x07, 0x38, 0x01, 0x9B, 0xFF, 0x4D, 0xFF, 0xA7, 0x00, 0x73, 0x80, 0x39,
|
||||||
|
0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x01,
|
||||||
|
0xC7, 0xFF, 0xE3, 0xFF, 0xD0, 0xFF, 0xF0, 0x00, 0x00, 0x98, 0x00, 0xCE,
|
||||||
|
0x00, 0xE7, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07,
|
||||||
|
0x38, 0x07, 0x38, 0x01, 0x9B, 0xFF, 0x4D, 0xFF, 0xA7, 0x00, 0x73, 0x80,
|
||||||
|
0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE,
|
||||||
|
0x01, 0xC6, 0x00, 0x62, 0x00, 0x10, 0x00, 0x01, 0x60, 0x03, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E,
|
||||||
|
0xE0, 0x06, 0xDF, 0xFA, 0x1F, 0xFA, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E,
|
||||||
|
0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x1C, 0x3F, 0xFC,
|
||||||
|
0x7F, 0xF4, 0x7F, 0xF8, 0x0F, 0xFF, 0x07, 0xFF, 0x41, 0xFF, 0x60, 0x00,
|
||||||
|
0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80,
|
||||||
|
0x03, 0x80, 0x00, 0xC0, 0x00, 0x26, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00,
|
||||||
|
0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00,
|
||||||
|
0x03, 0xFF, 0xC1, 0xFF, 0xE0, 0x7F, 0xF8, 0xFF, 0x06, 0x0C, 0x18, 0x30,
|
||||||
|
0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C,
|
||||||
|
0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF,
|
||||||
|
0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60,
|
||||||
|
0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18,
|
||||||
|
0x30, 0x60, 0xFF, 0x3F, 0xF3, 0xFF, 0x9F, 0xFE, 0xFF, 0x06, 0x0C, 0x18,
|
||||||
|
0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0x0F, 0xFF,
|
||||||
|
0x07, 0xFF, 0x4D, 0xFF, 0x67, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0,
|
||||||
|
0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x03, 0x9C, 0x00, 0xCD, 0xFF, 0xA6,
|
||||||
|
0xFF, 0xD3, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03,
|
||||||
|
0x9C, 0x01, 0xCE, 0x00, 0xE7, 0x00, 0xE3, 0x00, 0x31, 0x00, 0x08, 0x00,
|
||||||
|
0x00, 0x30, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x6F, 0xFC, 0x6F, 0xFD, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFA, 0x3F, 0xFC, 0x0F, 0xFD, 0xBF,
|
||||||
|
0xF7, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70,
|
||||||
|
0x01, 0xC0, 0x07, 0xFF, 0x9F, 0xFE, 0x3F, 0xFC, 0x00, 0x00, 0x80, 0x00,
|
||||||
|
0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00,
|
||||||
|
0x07, 0x00, 0x07, 0x00, 0x01, 0x83, 0xFF, 0x4D, 0xFF, 0xA7, 0x00, 0x73,
|
||||||
|
0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01,
|
||||||
|
0xCE, 0x01, 0xC7, 0xFF, 0xE3, 0xFF, 0xD0, 0xFF, 0xF0, 0x0F, 0xFF, 0x0F,
|
||||||
|
0xFE, 0x37, 0xFC, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x6F, 0xFC, 0x6F, 0xFC, 0x70,
|
||||||
|
0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
|
||||||
|
0x00, 0x70, 0x00, 0x7F, 0xF8, 0x7F, 0xF8, 0x3F, 0xFC, 0x0F, 0xFF, 0x0F,
|
||||||
|
0xFE, 0x37, 0xFC, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x6F, 0xFC, 0x6F, 0xFC, 0x70,
|
||||||
|
0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
|
||||||
|
0x00, 0x70, 0x00, 0x60, 0x00, 0x40, 0x00, 0x0F, 0xFF, 0x0F, 0xFE, 0x37,
|
||||||
|
0xFC, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x70, 0x00, 0x60, 0x00, 0x60, 0x01, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x0E, 0x7F, 0xFE, 0x7F, 0xFA, 0x3F, 0xFC, 0x00, 0x00, 0x30, 0x00, 0x38,
|
||||||
|
0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38,
|
||||||
|
0x00, 0x70, 0x00, 0x6F, 0xFC, 0x6F, 0xFD, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0x60,
|
||||||
|
0x06, 0x40, 0x02, 0x17, 0x77, 0x77, 0x77, 0xE6, 0x20, 0x00, 0x00, 0x80,
|
||||||
|
0x00, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E,
|
||||||
|
0x00, 0x07, 0x00, 0x07, 0x00, 0x01, 0x80, 0x00, 0x4C, 0x00, 0x27, 0x00,
|
||||||
|
0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C,
|
||||||
|
0x01, 0xCE, 0x01, 0xC7, 0xFF, 0xE3, 0xFF, 0xD0, 0xFF, 0xF0, 0x0F, 0xFF,
|
||||||
|
0x0F, 0xFE, 0x37, 0xFC, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00,
|
||||||
|
0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x6F, 0xFC, 0x6F, 0xFD,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x0E, 0x60, 0x06, 0x40, 0x02, 0x00, 0x00, 0xC0, 0x03,
|
||||||
|
0x80, 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x38, 0x01,
|
||||||
|
0xC0, 0x06, 0x00, 0x18, 0x00, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00,
|
||||||
|
0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x7F, 0xF9, 0xFF, 0xE3, 0xFF,
|
||||||
|
0xC0, 0x0F, 0xFF, 0x07, 0xFF, 0x4D, 0xFF, 0x67, 0x00, 0x73, 0x80, 0x39,
|
||||||
|
0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x03, 0x9C, 0x00,
|
||||||
|
0xCC, 0x00, 0x26, 0x00, 0x13, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70,
|
||||||
|
0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x00, 0xE7, 0x00, 0xE3, 0x00, 0x31,
|
||||||
|
0x00, 0x08, 0x0F, 0xFC, 0x6F, 0xFD, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0x60, 0x06,
|
||||||
|
0x40, 0x02, 0x0F, 0xFC, 0x6F, 0xFD, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0x7F, 0xFE,
|
||||||
|
0x7F, 0xFA, 0x3F, 0xFC, 0x0F, 0xFF, 0x07, 0xFF, 0x4D, 0xFF, 0x67, 0x00,
|
||||||
|
0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C,
|
||||||
|
0x03, 0x9C, 0x00, 0xCD, 0xFF, 0xA6, 0xFF, 0xC3, 0x80, 0x01, 0xC0, 0x00,
|
||||||
|
0xE0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00,
|
||||||
|
0x03, 0x00, 0x01, 0x00, 0x00, 0x1F, 0xFE, 0x1F, 0xFD, 0x6F, 0xFB, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x0E, 0xE0, 0x06, 0xDF, 0xFA, 0x1F, 0xFA, 0x00, 0x0E, 0x00, 0x0E, 0x00,
|
||||||
|
0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x1C, 0x00,
|
||||||
|
0x0C, 0x00, 0x04, 0x0F, 0xFD, 0xBF, 0xF7, 0x00, 0x1C, 0x00, 0x70, 0x01,
|
||||||
|
0xC0, 0x07, 0x00, 0x1C, 0x00, 0x70, 0x01, 0xC0, 0x06, 0x00, 0x10, 0x00,
|
||||||
|
0x00, 0x00, 0xC0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1C,
|
||||||
|
0x00, 0x38, 0x00, 0x70, 0x01, 0xC0, 0x03, 0x7F, 0xE0, 0xFF, 0xD0, 0x00,
|
||||||
|
0xE0, 0x01, 0xC0, 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1C, 0x00, 0x38,
|
||||||
|
0x00, 0xE3, 0xFF, 0xCF, 0xFE, 0x9F, 0xFE, 0x00, 0x00, 0x00, 0xC0, 0x03,
|
||||||
|
0x80, 0x0E, 0x00, 0x38, 0x00, 0xE0, 0x03, 0x80, 0x0E, 0x00, 0x38, 0x01,
|
||||||
|
0xC0, 0x06, 0xFF, 0xDB, 0xFF, 0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00,
|
||||||
|
0x70, 0x01, 0xC0, 0x07, 0x00, 0x1C, 0x00, 0x7F, 0xF9, 0xFF, 0xE3, 0xFF,
|
||||||
|
0xC0, 0x60, 0x01, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70,
|
||||||
|
0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFA, 0x3F,
|
||||||
|
0xFC, 0x00, 0x00, 0x98, 0x00, 0xCE, 0x00, 0xE7, 0x00, 0x73, 0x80, 0x39,
|
||||||
|
0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x07, 0x38, 0x01, 0x98, 0x00,
|
||||||
|
0x4C, 0x00, 0x27, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70,
|
||||||
|
0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x01, 0xC7, 0xFF, 0xE3, 0xFF, 0xD0,
|
||||||
|
0xFF, 0xF0, 0x00, 0x00, 0x98, 0x00, 0xCE, 0x00, 0xE7, 0x00, 0x73, 0x80,
|
||||||
|
0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x07, 0x38, 0x01, 0x9B,
|
||||||
|
0xFF, 0x4D, 0xFF, 0xA7, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E,
|
||||||
|
0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x01, 0xC7, 0xFF, 0xE3, 0xFF,
|
||||||
|
0xD0, 0xFF, 0xF0, 0x00, 0x00, 0x98, 0x00, 0xCE, 0x00, 0xE7, 0x00, 0x73,
|
||||||
|
0x80, 0x39, 0xC0, 0x1C, 0xE0, 0x0E, 0x70, 0x07, 0x38, 0x07, 0x38, 0x01,
|
||||||
|
0x9B, 0xFF, 0x4D, 0xFF, 0xA7, 0x00, 0x73, 0x80, 0x39, 0xC0, 0x1C, 0xE0,
|
||||||
|
0x0E, 0x70, 0x07, 0x38, 0x03, 0x9C, 0x01, 0xCE, 0x01, 0xC6, 0x00, 0x62,
|
||||||
|
0x00, 0x10, 0x00, 0x01, 0x60, 0x03, 0x70, 0x07, 0x70, 0x07, 0x70, 0x07,
|
||||||
|
0x70, 0x07, 0x70, 0x07, 0x70, 0x07, 0x70, 0x0E, 0xE0, 0x06, 0xDF, 0xFA,
|
||||||
|
0x1F, 0xFA, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E,
|
||||||
|
0x00, 0x0E, 0x00, 0x0E, 0x00, 0x1C, 0x3F, 0xFC, 0x7F, 0xF4, 0x7F, 0xF8,
|
||||||
|
0x0F, 0xFF, 0x07, 0xFF, 0x41, 0xFF, 0x60, 0x00, 0x70, 0x00, 0x38, 0x00,
|
||||||
|
0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0xC0,
|
||||||
|
0x00, 0x26, 0x00, 0x03, 0x80, 0x01, 0xC0, 0x00, 0xE0, 0x00, 0x70, 0x00,
|
||||||
|
0x38, 0x00, 0x1C, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0xFF, 0xC1, 0xFF,
|
||||||
|
0xE0, 0x7F, 0xF8, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06,
|
||||||
|
0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1,
|
||||||
|
0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30,
|
||||||
|
0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, 0x06, 0x0C,
|
||||||
|
0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF };
|
||||||
|
|
||||||
|
const GFXglyph DSEG7Classic_BoldItalic12pt7bGlyphs[] PROGMEM = {
|
||||||
|
{ 0, 0, 0, 5, 0, 1 }, // 0x20 ' '
|
||||||
|
{ 0, 0, 0, 20, 0, 1 }, // 0x21 '!'
|
||||||
|
{ 0, 7, 16, 9, 1, -15 }, // 0x22 '"'
|
||||||
|
{ 14, 7, 16, 9, 1, -15 }, // 0x23 '#'
|
||||||
|
{ 28, 7, 16, 9, 1, -15 }, // 0x24 '$'
|
||||||
|
{ 42, 7, 16, 9, 1, -15 }, // 0x25 '%'
|
||||||
|
{ 56, 7, 16, 9, 1, -15 }, // 0x26 '&'
|
||||||
|
{ 70, 7, 16, 9, 1, -15 }, // 0x27 '''
|
||||||
|
{ 84, 7, 16, 9, 1, -15 }, // 0x28 '('
|
||||||
|
{ 98, 7, 16, 9, 1, -15 }, // 0x29 ')'
|
||||||
|
{ 112, 7, 16, 9, 1, -15 }, // 0x2A '*'
|
||||||
|
{ 126, 7, 16, 9, 1, -15 }, // 0x2B '+'
|
||||||
|
{ 140, 7, 16, 9, 1, -15 }, // 0x2C ','
|
||||||
|
{ 154, 11, 2, 20, 4, -12 }, // 0x2D '-'
|
||||||
|
{ 157, 3, 3, 0, -3, -2 }, // 0x2E '.'
|
||||||
|
{ 159, 7, 16, 9, 1, -15 }, // 0x2F '/'
|
||||||
|
{ 173, 17, 24, 20, 1, -23 }, // 0x30 '0'
|
||||||
|
{ 224, 5, 22, 20, 13, -22 }, // 0x31 '1'
|
||||||
|
{ 238, 17, 24, 20, 1, -23 }, // 0x32 '2'
|
||||||
|
{ 289, 16, 24, 20, 2, -23 }, // 0x33 '3'
|
||||||
|
{ 337, 16, 22, 20, 2, -22 }, // 0x34 '4'
|
||||||
|
{ 381, 15, 24, 20, 2, -23 }, // 0x35 '5'
|
||||||
|
{ 426, 16, 24, 20, 1, -23 }, // 0x36 '6'
|
||||||
|
{ 474, 16, 23, 20, 2, -23 }, // 0x37 '7'
|
||||||
|
{ 520, 17, 24, 20, 1, -23 }, // 0x38 '8'
|
||||||
|
{ 571, 16, 24, 20, 2, -23 }, // 0x39 '9'
|
||||||
|
{ 619, 4, 13, 5, 0, -17 }, // 0x3A ':'
|
||||||
|
{ 626, 7, 16, 9, 1, -15 }, // 0x3B ';'
|
||||||
|
{ 640, 7, 16, 9, 1, -15 }, // 0x3C '<'
|
||||||
|
{ 654, 7, 16, 9, 1, -15 }, // 0x3D '='
|
||||||
|
{ 668, 7, 16, 9, 1, -15 }, // 0x3E '>'
|
||||||
|
{ 682, 7, 16, 9, 1, -15 }, // 0x3F '?'
|
||||||
|
{ 696, 7, 16, 9, 1, -15 }, // 0x40 '@'
|
||||||
|
{ 710, 17, 23, 20, 1, -23 }, // 0x41 'A'
|
||||||
|
{ 759, 16, 23, 20, 1, -22 }, // 0x42 'B'
|
||||||
|
{ 805, 14, 13, 20, 1, -12 }, // 0x43 'C'
|
||||||
|
{ 828, 17, 23, 20, 1, -22 }, // 0x44 'D'
|
||||||
|
{ 877, 16, 24, 20, 1, -23 }, // 0x45 'E'
|
||||||
|
{ 925, 16, 23, 20, 1, -23 }, // 0x46 'F'
|
||||||
|
{ 971, 16, 24, 20, 1, -23 }, // 0x47 'G'
|
||||||
|
{ 1019, 16, 22, 20, 1, -22 }, // 0x48 'H'
|
||||||
|
{ 1063, 4, 11, 20, 13, -11 }, // 0x49 'I'
|
||||||
|
{ 1069, 17, 23, 20, 1, -22 }, // 0x4A 'J'
|
||||||
|
{ 1118, 16, 23, 20, 1, -23 }, // 0x4B 'K'
|
||||||
|
{ 1164, 14, 23, 20, 1, -22 }, // 0x4C 'L'
|
||||||
|
{ 1205, 17, 23, 20, 1, -23 }, // 0x4D 'M'
|
||||||
|
{ 1254, 16, 12, 20, 1, -12 }, // 0x4E 'N'
|
||||||
|
{ 1278, 16, 13, 20, 1, -12 }, // 0x4F 'O'
|
||||||
|
{ 1304, 17, 23, 20, 1, -23 }, // 0x50 'P'
|
||||||
|
{ 1353, 16, 23, 20, 2, -23 }, // 0x51 'Q'
|
||||||
|
{ 1399, 14, 12, 20, 1, -12 }, // 0x52 'R'
|
||||||
|
{ 1420, 15, 23, 20, 2, -22 }, // 0x53 'S'
|
||||||
|
{ 1464, 14, 23, 20, 1, -22 }, // 0x54 'T'
|
||||||
|
{ 1505, 16, 12, 20, 1, -11 }, // 0x55 'U'
|
||||||
|
{ 1529, 17, 23, 20, 1, -22 }, // 0x56 'V'
|
||||||
|
{ 1578, 17, 23, 20, 1, -22 }, // 0x57 'W'
|
||||||
|
{ 1627, 17, 22, 20, 1, -22 }, // 0x58 'X'
|
||||||
|
{ 1674, 16, 23, 20, 2, -22 }, // 0x59 'Y'
|
||||||
|
{ 1720, 17, 24, 20, 1, -23 }, // 0x5A 'Z'
|
||||||
|
{ 1771, 7, 16, 9, 1, -15 }, // 0x5B '['
|
||||||
|
{ 1785, 7, 16, 9, 1, -15 }, // 0x5C '\'
|
||||||
|
{ 1799, 7, 16, 9, 1, -15 }, // 0x5D ']'
|
||||||
|
{ 1813, 7, 16, 9, 1, -15 }, // 0x5E '^'
|
||||||
|
{ 1827, 13, 3, 20, 2, -2 }, // 0x5F '_'
|
||||||
|
{ 1832, 7, 16, 9, 1, -15 }, // 0x60 '`'
|
||||||
|
{ 1846, 17, 23, 20, 1, -23 }, // 0x61 'a'
|
||||||
|
{ 1895, 16, 23, 20, 1, -22 }, // 0x62 'b'
|
||||||
|
{ 1941, 14, 13, 20, 1, -12 }, // 0x63 'c'
|
||||||
|
{ 1964, 17, 23, 20, 1, -22 }, // 0x64 'd'
|
||||||
|
{ 2013, 16, 24, 20, 1, -23 }, // 0x65 'e'
|
||||||
|
{ 2061, 16, 23, 20, 1, -23 }, // 0x66 'f'
|
||||||
|
{ 2107, 16, 24, 20, 1, -23 }, // 0x67 'g'
|
||||||
|
{ 2155, 16, 22, 20, 1, -22 }, // 0x68 'h'
|
||||||
|
{ 2199, 4, 11, 20, 13, -11 }, // 0x69 'i'
|
||||||
|
{ 2205, 17, 23, 20, 1, -22 }, // 0x6A 'j'
|
||||||
|
{ 2254, 16, 23, 20, 1, -23 }, // 0x6B 'k'
|
||||||
|
{ 2300, 14, 23, 20, 1, -22 }, // 0x6C 'l'
|
||||||
|
{ 2341, 17, 23, 20, 1, -23 }, // 0x6D 'm'
|
||||||
|
{ 2390, 16, 12, 20, 1, -12 }, // 0x6E 'n'
|
||||||
|
{ 2414, 16, 13, 20, 1, -12 }, // 0x6F 'o'
|
||||||
|
{ 2440, 17, 23, 20, 1, -23 }, // 0x70 'p'
|
||||||
|
{ 2489, 16, 23, 20, 2, -23 }, // 0x71 'q'
|
||||||
|
{ 2535, 14, 12, 20, 1, -12 }, // 0x72 'r'
|
||||||
|
{ 2556, 15, 23, 20, 2, -22 }, // 0x73 's'
|
||||||
|
{ 2600, 14, 23, 20, 1, -22 }, // 0x74 't'
|
||||||
|
{ 2641, 16, 12, 20, 1, -11 }, // 0x75 'u'
|
||||||
|
{ 2665, 17, 23, 20, 1, -22 }, // 0x76 'v'
|
||||||
|
{ 2714, 17, 23, 20, 1, -22 }, // 0x77 'w'
|
||||||
|
{ 2763, 17, 22, 20, 1, -22 }, // 0x78 'x'
|
||||||
|
{ 2810, 16, 23, 20, 2, -22 }, // 0x79 'y'
|
||||||
|
{ 2856, 17, 24, 20, 1, -23 }, // 0x7A 'z'
|
||||||
|
{ 2907, 7, 16, 9, 1, -15 }, // 0x7B '{'
|
||||||
|
{ 2921, 7, 16, 9, 1, -15 }, // 0x7C '|'
|
||||||
|
{ 2935, 7, 16, 9, 1, -15 }, // 0x7D '}'
|
||||||
|
{ 2949, 7, 16, 9, 1, -15 } }; // 0x7E '~'
|
||||||
|
|
||||||
|
const GFXfont DSEG7Classic_BoldItalic12pt7b PROGMEM = {
|
||||||
|
(uint8_t *)DSEG7Classic_BoldItalic12pt7bBitmaps,
|
||||||
|
(GFXglyph *)DSEG7Classic_BoldItalic12pt7bGlyphs,
|
||||||
|
0x20, 0x7E, 26 };
|
||||||
|
|
||||||
|
// Approx. 3635 bytes
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
// Arduino Moving Average Library
|
|
||||||
// https://github.com/JChristensen/movingAvg
|
|
||||||
// Copyright (C) 2018 by Jack Christensen and licensed under
|
|
||||||
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
|
|
||||||
|
|
||||||
#include <movingAvg.h>
|
|
||||||
|
|
||||||
// initialize - allocate the interval array
|
|
||||||
void movingAvg::begin()
|
|
||||||
{
|
|
||||||
m_readings = new int[m_interval];
|
|
||||||
}
|
|
||||||
|
|
||||||
// add a new reading and return the new moving average
|
|
||||||
int movingAvg::reading(int newReading)
|
|
||||||
{
|
|
||||||
// add each new data point to the sum until the m_readings array is filled
|
|
||||||
if (m_nbrReadings < m_interval) {
|
|
||||||
++m_nbrReadings;
|
|
||||||
m_sum += newReading;
|
|
||||||
}
|
|
||||||
// once the array is filled, subtract the oldest data point and add the new one
|
|
||||||
else {
|
|
||||||
m_sum = m_sum - m_readings[m_next] + newReading;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_readings[m_next] = newReading;
|
|
||||||
if (++m_next >= m_interval) m_next = 0;
|
|
||||||
return (m_sum + m_nbrReadings / 2) / m_nbrReadings;
|
|
||||||
}
|
|
||||||
|
|
||||||
// just return the current moving average
|
|
||||||
int movingAvg::getAvg()
|
|
||||||
{
|
|
||||||
return (m_sum + m_nbrReadings / 2) / m_nbrReadings;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the average for a subset of the data, the most recent nPoints readings.
|
|
||||||
// for invalid values of nPoints, return zero.
|
|
||||||
int movingAvg::getAvg(int nPoints)
|
|
||||||
{
|
|
||||||
if (nPoints < 1 || nPoints > m_interval || nPoints > m_nbrReadings) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
long sum{0};
|
|
||||||
int i = m_next;
|
|
||||||
for (int n=0; n<nPoints; ++n) {
|
|
||||||
if (i == 0) {
|
|
||||||
i = m_interval - 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
--i;
|
|
||||||
}
|
|
||||||
sum += m_readings[i];
|
|
||||||
}
|
|
||||||
return (sum + nPoints / 2) / nPoints;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// start the moving average over again
|
|
||||||
void movingAvg::reset()
|
|
||||||
{
|
|
||||||
m_nbrReadings = 0;
|
|
||||||
m_sum = 0;
|
|
||||||
m_next = 0;
|
|
||||||
}
|
|
||||||
@@ -3,27 +3,64 @@
|
|||||||
// Copyright (C) 2018 by Jack Christensen and licensed under
|
// Copyright (C) 2018 by Jack Christensen and licensed under
|
||||||
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
|
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
|
||||||
|
|
||||||
|
// Extended to template class for handling of multiple data types
|
||||||
|
|
||||||
#ifndef MOVINGAVG_H_INCLUDED
|
#ifndef MOVINGAVG_H_INCLUDED
|
||||||
#define MOVINGAVG_H_INCLUDED
|
#define MOVINGAVG_H_INCLUDED
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
class movingAvg
|
class movingAvg
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
movingAvg(int interval)
|
movingAvg(int interval)
|
||||||
: m_interval{interval}, m_nbrReadings{0}, m_sum{0}, m_next{0} {}
|
: m_interval{interval}, m_nbrReadings{0}, m_sum{0}, m_next{0}, m_readings{nullptr} {}
|
||||||
|
~movingAvg() { delete[] m_readings; }
|
||||||
void begin();
|
void begin();
|
||||||
int reading(int newReading);
|
T reading(T newReading);
|
||||||
int getAvg();
|
T getAvg();
|
||||||
int getAvg(int nPoints);
|
T getAvg(int nPoints);
|
||||||
int getCount() {return m_nbrReadings;}
|
int getCount() { return m_nbrReadings; }
|
||||||
void reset();
|
void reset();
|
||||||
int* getReadings() {return m_readings;}
|
T* getReadings() { return m_readings; }
|
||||||
|
T to2PI(T a);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_interval; // number of data points for the moving average
|
int m_interval; // number of data points for the moving average
|
||||||
int m_nbrReadings; // number of readings
|
int m_nbrReadings; // number of readings
|
||||||
long m_sum; // sum of the m_readings array
|
// Sum type adapts to T: long for integers, T for floating point
|
||||||
|
using SumType = typename std::conditional<std::is_floating_point<T>::value, T, long>::type;
|
||||||
|
SumType m_sum;
|
||||||
int m_next; // index to the next reading
|
int m_next; // index to the next reading
|
||||||
int* m_readings; // pointer to the dynamically allocated interval array
|
T* m_readings; // pointer to the dynamically allocated interval array
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// moving average for angle type of data (wind, course, rotation)
|
||||||
|
// makes sense for data types double and float; angle in radians [0..2pi]
|
||||||
|
template <typename T>
|
||||||
|
class movingAvgAngle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
movingAvgAngle(int interval)
|
||||||
|
: m_interval{interval}, m_nbrReadings{0}, m_sumSin{0}, m_sumCos{0}, m_next{0}, m_buffer{nullptr} {}
|
||||||
|
~movingAvgAngle() { delete[] m_buffer; }
|
||||||
|
void begin();
|
||||||
|
T reading(T newReading);
|
||||||
|
T getAvg();
|
||||||
|
T getAvg(int nPoints);
|
||||||
|
int getCount() { return m_nbrReadings; }
|
||||||
|
void reset();
|
||||||
|
T* getReadings() { return m_buffer; }
|
||||||
|
T to2PI(T a);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_interval; // number of data points for the moving average
|
||||||
|
int m_nbrReadings; // number of readings
|
||||||
|
double m_sumSin, m_sumCos; // sum for angle values should always be double for precision reasons, regardless of class type
|
||||||
|
int m_next; // index to the next reading
|
||||||
|
T* m_buffer; // pointer to the dynamically allocated interval array
|
||||||
|
};
|
||||||
|
|
||||||
|
// Include the implementation to satisfy template instantiation requirements
|
||||||
|
#include "movingAvg.tpp"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// Arduino Moving Average Library
|
||||||
|
// https://github.com/JChristensen/movingAvg
|
||||||
|
// Copyright (C) 2018 by Jack Christensen and licensed under
|
||||||
|
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
|
||||||
|
|
||||||
|
// Extended to template class for handling of multiple data types
|
||||||
|
|
||||||
|
// initialize - allocate the interval array
|
||||||
|
template <typename T>
|
||||||
|
void movingAvg<T>::begin()
|
||||||
|
{
|
||||||
|
m_readings = new T[m_interval];
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a new reading and return the new moving average
|
||||||
|
template <typename T>
|
||||||
|
T movingAvg<T>::reading(T newReading)
|
||||||
|
{
|
||||||
|
// add each new data point to the sum until the m_readings array is filled
|
||||||
|
if (m_nbrReadings < m_interval) {
|
||||||
|
++m_nbrReadings;
|
||||||
|
m_sum += newReading;
|
||||||
|
}
|
||||||
|
// once the array is filled, subtract the oldest data point and add the new one
|
||||||
|
else {
|
||||||
|
m_sum = m_sum - m_readings[m_next] + newReading;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_readings[m_next] = newReading;
|
||||||
|
if (++m_next >= m_interval) m_next = 0;
|
||||||
|
return getAvg();
|
||||||
|
}
|
||||||
|
|
||||||
|
// just return the current moving average
|
||||||
|
template <typename T>
|
||||||
|
T movingAvg<T>::getAvg()
|
||||||
|
{
|
||||||
|
if (m_nbrReadings == 0) return 0;
|
||||||
|
|
||||||
|
// Apply rounding for integers only
|
||||||
|
if (std::is_floating_point<T>::value) {
|
||||||
|
return m_sum / m_nbrReadings;
|
||||||
|
} else {
|
||||||
|
return (m_sum + (SumType)m_nbrReadings / 2) / m_nbrReadings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the average for a subset of the data, the most recent nPoints readings.
|
||||||
|
// for invalid values of nPoints, return zero.
|
||||||
|
template <typename T>
|
||||||
|
T movingAvg<T>::getAvg(int nPoints)
|
||||||
|
{
|
||||||
|
if (nPoints < 1 || nPoints > m_interval || nPoints > m_nbrReadings) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SumType sum{0};
|
||||||
|
int i = m_next;
|
||||||
|
for (int n=0; n<nPoints; ++n) {
|
||||||
|
if (i == 0) {
|
||||||
|
i = m_interval - 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
sum += m_readings[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::is_floating_point<T>::value) {
|
||||||
|
return sum / nPoints;
|
||||||
|
} else {
|
||||||
|
return (sum + (SumType)nPoints / 2) / nPoints; //
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the moving average over again
|
||||||
|
template <typename T>
|
||||||
|
void movingAvg<T>::reset()
|
||||||
|
{
|
||||||
|
m_nbrReadings = 0;
|
||||||
|
m_sum = 0;
|
||||||
|
m_next = 0;
|
||||||
|
}
|
||||||
|
// --- End Class movingAvg ---------------
|
||||||
|
|
||||||
|
// --- Class MovingAvgAngle ---------------
|
||||||
|
template <typename T>
|
||||||
|
void movingAvgAngle<T>::begin()
|
||||||
|
{
|
||||||
|
m_buffer = new T[m_interval];
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a new reading and return the new moving average
|
||||||
|
template <typename T>
|
||||||
|
T movingAvgAngle<T>::reading(T newReading)
|
||||||
|
{
|
||||||
|
double s = std::sin(newReading);
|
||||||
|
double c = std::cos(newReading);
|
||||||
|
|
||||||
|
// add each new data point to the sum until the m_readings array is filled
|
||||||
|
if (m_nbrReadings < m_interval) {
|
||||||
|
++m_nbrReadings;
|
||||||
|
m_sumSin += s;
|
||||||
|
m_sumCos += c;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// array is filled; subtract the oldest data point and add the new one
|
||||||
|
m_sumSin = m_sumSin - sin(m_buffer[m_next]) + s;
|
||||||
|
m_sumCos = m_sumCos - cos(m_buffer[m_next]) + c;
|
||||||
|
}
|
||||||
|
m_buffer[m_next] = newReading;
|
||||||
|
|
||||||
|
if (++m_next >= m_interval)
|
||||||
|
m_next = 0;
|
||||||
|
|
||||||
|
return getAvg();
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the current moving average
|
||||||
|
template <typename T>
|
||||||
|
T movingAvgAngle<T>::getAvg()
|
||||||
|
{
|
||||||
|
if (m_nbrReadings == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// check size of vector; if near 0, set it to 0
|
||||||
|
const double len = m_sumSin * m_sumSin + m_sumCos * m_sumCos;
|
||||||
|
if (len < 1e-24)
|
||||||
|
return 0.0; // average direction undefined
|
||||||
|
|
||||||
|
return static_cast<T>(to2PI(std::atan2(m_sumSin, m_sumCos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the current moving average for a subset of the data, the most recent nPoints readings.
|
||||||
|
// for invalid values of nPoints, return zero.
|
||||||
|
template <typename T>
|
||||||
|
T movingAvgAngle<T>::getAvg(int nPoints)
|
||||||
|
{
|
||||||
|
if (nPoints < 1 || nPoints > m_interval || nPoints > m_nbrReadings)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
double sumSin = 0.0;
|
||||||
|
double sumCos = 0.0;
|
||||||
|
|
||||||
|
int i = m_next;
|
||||||
|
for (int n = 0; n < nPoints; ++n) {
|
||||||
|
if (i == 0)
|
||||||
|
i = m_interval - 1;
|
||||||
|
else
|
||||||
|
--i;
|
||||||
|
|
||||||
|
sumSin += std::sin(m_buffer[i]);
|
||||||
|
sumCos += std::cos(m_buffer[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check size of vector; if near 0, set it to 0
|
||||||
|
const double len = m_sumSin * m_sumSin + m_sumCos * m_sumCos;
|
||||||
|
if (len < 1e-24)
|
||||||
|
return 0.0; // average direction undefined
|
||||||
|
|
||||||
|
return static_cast<T>(to2PI(std::atan2(sumSin, sumCos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the moving average over again
|
||||||
|
template <typename T>
|
||||||
|
void movingAvgAngle<T>::reset()
|
||||||
|
{
|
||||||
|
m_nbrReadings = 0;
|
||||||
|
m_sumSin = 0;
|
||||||
|
m_sumCos = 0;
|
||||||
|
m_next = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T movingAvgAngle<T>::to2PI(T a)
|
||||||
|
{
|
||||||
|
a = fmod(a, M_TWOPI);
|
||||||
|
if (a < 0.0) {
|
||||||
|
a += M_TWOPI;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
// --- End class MovingAvgAngle ---------------
|
||||||
+94
-55
@@ -9,7 +9,6 @@
|
|||||||
#include <NMEA0183.h> // NMEA0183
|
#include <NMEA0183.h> // NMEA0183
|
||||||
#include <NMEA0183Msg.h>
|
#include <NMEA0183Msg.h>
|
||||||
#include <NMEA0183Messages.h>
|
#include <NMEA0183Messages.h>
|
||||||
#include <GxEPD2_BW.h> // GxEPD2 lib for b/w E-Ink displays
|
|
||||||
#include "OBP60Extensions.h" // Functions lib for extension board
|
#include "OBP60Extensions.h" // Functions lib for extension board
|
||||||
#include "OBP60Keypad.h" // Functions for keypad
|
#include "OBP60Keypad.h" // Functions for keypad
|
||||||
#include "OBPDataOperations.h" // Functions lib for data operations such as true wind calculation
|
#include "OBPDataOperations.h" // Functions lib for data operations such as true wind calculation
|
||||||
@@ -234,6 +233,8 @@ void registerAllPages(PageList &list){
|
|||||||
list.add(®isterPageClock);
|
list.add(®isterPageClock);
|
||||||
extern PageDescription registerPageCompass;
|
extern PageDescription registerPageCompass;
|
||||||
list.add(®isterPageCompass);
|
list.add(®isterPageCompass);
|
||||||
|
extern PageDescription registerPageCurrent;
|
||||||
|
list.add(®isterPageCurrent);
|
||||||
extern PageDescription registerPageWhite;
|
extern PageDescription registerPageWhite;
|
||||||
list.add(®isterPageWhite);
|
list.add(®isterPageWhite);
|
||||||
extern PageDescription registerPageBME280;
|
extern PageDescription registerPageBME280;
|
||||||
@@ -264,8 +265,8 @@ void registerAllPages(PageList &list){
|
|||||||
list.add(®isterPageDigitalOut);
|
list.add(®isterPageDigitalOut);
|
||||||
extern PageDescription registerPageAutopilot;
|
extern PageDescription registerPageAutopilot;
|
||||||
list.add(®isterPageAutopilot);
|
list.add(®isterPageAutopilot);
|
||||||
extern PageDescription registerPageAnchor;
|
extern PageDescription registerPageWeather;
|
||||||
list.add(®isterPageAnchor);
|
list.add(®isterPageWeather);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Undervoltage detection for shutdown display
|
// Undervoltage detection for shutdown display
|
||||||
@@ -286,8 +287,12 @@ void underVoltageError(CommonData &common) {
|
|||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(65, 175);
|
getdisplay().setCursor(65, 175);
|
||||||
getdisplay().print("Charge battery and restart system");
|
getdisplay().print("Charge battery and restart system");
|
||||||
getdisplay().nextPage(); // Partial update
|
displayNextPage(); // Partial update
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
getpaneldisplay().powerSave(true); // Display power save
|
||||||
|
#else
|
||||||
getdisplay().powerOff(); // Display power off
|
getdisplay().powerOff(); // Display power off
|
||||||
|
#endif
|
||||||
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
|
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
|
||||||
setPortPin(OBP_POWER_SD, false); // Power off SD card
|
setPortPin(OBP_POWER_SD, false); // Power off SD card
|
||||||
#else
|
#else
|
||||||
@@ -297,7 +302,7 @@ void underVoltageError(CommonData &common) {
|
|||||||
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
|
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
|
||||||
setPortPin(OBP_POWER_50, false); // Power rail 5.0V Off
|
setPortPin(OBP_POWER_50, false); // Power rail 5.0V Off
|
||||||
// Shutdown EInk display
|
// Shutdown EInk display
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
getdisplay().fillScreen(common.bgcolor);// Clear screen
|
getdisplay().fillScreen(common.bgcolor);// Clear screen
|
||||||
getdisplay().setTextColor(common.fgcolor);
|
getdisplay().setTextColor(common.fgcolor);
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
||||||
@@ -306,8 +311,12 @@ void underVoltageError(CommonData &common) {
|
|||||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
getdisplay().setCursor(65, 175);
|
getdisplay().setCursor(65, 175);
|
||||||
getdisplay().print("To wake up repower system");
|
getdisplay().print("To wake up repower system");
|
||||||
getdisplay().nextPage(); // Partial update
|
displayNextPage(); // Partial update
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
getpaneldisplay().powerSave(true); // Display power save
|
||||||
|
#else
|
||||||
getdisplay().powerOff(); // Display power off
|
getdisplay().powerOff(); // Display power off
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
while (true) {
|
while (true) {
|
||||||
esp_deep_sleep_start(); // Deep Sleep without wakeup. Wakeup only after power cycle (restart).
|
esp_deep_sleep_start(); // Deep Sleep without wakeup. Wakeup only after power cycle (restart).
|
||||||
@@ -318,7 +327,7 @@ inline bool underVoltageDetection(float voffset, float vslope) {
|
|||||||
// Read supply voltage
|
// Read supply voltage
|
||||||
#if defined VOLTAGE_SENSOR && defined LIPO_ACCU_1200
|
#if defined VOLTAGE_SENSOR && defined LIPO_ACCU_1200
|
||||||
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
|
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
|
||||||
float minVoltage = 3.65; // Absolut minimum volatge for 3,7V LiPo accu
|
float minVoltage = 3.65; // Absolut minimum voltage for 3,7V LiPo accu
|
||||||
#else
|
#else
|
||||||
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
|
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
|
||||||
float minVoltage = MIN_VOLTAGE;
|
float minVoltage = MIN_VOLTAGE;
|
||||||
@@ -327,6 +336,7 @@ inline bool underVoltageDetection(float voffset, float vslope) {
|
|||||||
return (calVoltage < minVoltage);
|
return (calVoltage < minVoltage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// OBP60 Task
|
// OBP60 Task
|
||||||
//####################################################################################
|
//####################################################################################
|
||||||
void OBP60Task(GwApi *api){
|
void OBP60Task(GwApi *api){
|
||||||
@@ -334,7 +344,7 @@ void OBP60Task(GwApi *api){
|
|||||||
// return;
|
// return;
|
||||||
GwLog *logger=api->getLogger();
|
GwLog *logger=api->getLogger();
|
||||||
GwConfigHandler *config=api->getConfig();
|
GwConfigHandler *config=api->getConfig();
|
||||||
#if defined HARDWARE_V20 || HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
startLedTask(api);
|
startLedTask(api);
|
||||||
#endif
|
#endif
|
||||||
PageList allPages;
|
PageList allPages;
|
||||||
@@ -343,7 +353,7 @@ void OBP60Task(GwApi *api){
|
|||||||
commonData.logger=logger;
|
commonData.logger=logger;
|
||||||
commonData.config=config;
|
commonData.config=config;
|
||||||
|
|
||||||
#if defined HARDWARE_V20 || HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
// Keyboard coordinates for page footer
|
// Keyboard coordinates for page footer
|
||||||
initKeys(commonData);
|
initKeys(commonData);
|
||||||
#endif
|
#endif
|
||||||
@@ -377,36 +387,47 @@ void OBP60Task(GwApi *api){
|
|||||||
|
|
||||||
#ifdef DISPLAY_GDEY042T81
|
#ifdef DISPLAY_GDEY042T81
|
||||||
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
|
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
|
||||||
|
#elif defined(TFT_DISPLAY)
|
||||||
|
getpaneldisplay().init(); // Init for TFT LCD panel
|
||||||
#else
|
#else
|
||||||
getdisplay().init(115200); // Init for normal displays
|
getdisplay().init(115200); // Init for normal displays
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
getpaneldisplay().setRotation(0); // Set display orientation (horizontal)
|
||||||
|
getpaneldisplay().setPanelOffset(0, 0); // Use full native framebuffer coordinates
|
||||||
|
getpaneldisplay().fillScreen(0x0000); // Initialize full TFT screen to black (native RGB565)
|
||||||
|
getpaneldisplay().setPanelOffset(OBP_TFT_OFFSET_X, OBP_TFT_OFFSET_Y); // Restore configured operating panel offset
|
||||||
|
#else
|
||||||
getdisplay().setRotation(0); // Set display orientation (horizontal)
|
getdisplay().setRotation(0); // Set display orientation (horizontal)
|
||||||
getdisplay().setFullWindow(); // Set full Refresh
|
#endif
|
||||||
getdisplay().firstPage(); // set first page
|
displaySetFullWindow(); // Set full Refresh (E-Ink only)
|
||||||
|
displayFirstPage(); // set first page
|
||||||
getdisplay().fillScreen(commonData.bgcolor);
|
getdisplay().fillScreen(commonData.bgcolor);
|
||||||
getdisplay().setTextColor(commonData.fgcolor);
|
getdisplay().setTextColor(commonData.fgcolor);
|
||||||
getdisplay().nextPage(); // Full Refresh
|
displayNextPage(); // Full Refresh
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update (E-Ink only)
|
||||||
getdisplay().fillScreen(commonData.bgcolor);
|
getdisplay().fillScreen(commonData.bgcolor);
|
||||||
getdisplay().nextPage(); // Fast Refresh
|
displayNextPage(); // Fast Refresh
|
||||||
getdisplay().nextPage(); // Fast Refresh
|
displayNextPage(); // Fast Refresh
|
||||||
if(String(displaymode) == "Logo + QR Code" || String(displaymode) == "Logo"){
|
if(String(displaymode) == "Logo + QR Code" || String(displaymode) == "Logo"){
|
||||||
getdisplay().fillScreen(commonData.bgcolor);
|
getdisplay().fillScreen(commonData.bgcolor);
|
||||||
getdisplay().drawBitmap(0, 0, gImage_Logo_OBP_400x300_sw, getdisplay().width(), getdisplay().height(), commonData.fgcolor); // Draw start logo
|
// draw the fixed-size logo via generic display wrapper
|
||||||
getdisplay().nextPage(); // Fast Refresh
|
displayDrawBitmap(0, 0, gImage_Logo_OBP_400x300_sw,
|
||||||
getdisplay().nextPage(); // Fast Refresh
|
400, 300, commonData.fgcolor);
|
||||||
|
displayNextPage(); // Fast Refresh
|
||||||
|
displayNextPage(); // Fast Refresh
|
||||||
delay(SHOW_TIME); // Logo show time
|
delay(SHOW_TIME); // Logo show time
|
||||||
if(String(displaymode) == "Logo + QR Code"){
|
if(String(displaymode) == "Logo + QR Code"){
|
||||||
getdisplay().fillScreen(commonData.bgcolor);
|
getdisplay().fillScreen(commonData.bgcolor);
|
||||||
qrWiFi(systemname, wifipass, commonData.fgcolor, commonData.bgcolor); // Show QR code for WiFi connection
|
qrWiFi(systemname, wifipass, commonData.fgcolor, commonData.bgcolor); // Show QR code for WiFi connection
|
||||||
getdisplay().nextPage(); // Fast Refresh
|
displayNextPage(); // Fast Refresh
|
||||||
getdisplay().nextPage(); // Fast Refresh
|
displayNextPage(); // Fast Refresh
|
||||||
delay(SHOW_TIME); // QR code show time
|
delay(SHOW_TIME); // QR code show time
|
||||||
}
|
}
|
||||||
getdisplay().fillScreen(commonData.bgcolor);
|
getdisplay().fillScreen(commonData.bgcolor);
|
||||||
getdisplay().nextPage(); // Fast Refresh
|
displayNextPage(); // Fast Refresh
|
||||||
getdisplay().nextPage(); // Fast Refresh
|
displayNextPage(); // Fast Refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init pages
|
// Init pages
|
||||||
@@ -434,13 +455,20 @@ void OBP60Task(GwApi *api){
|
|||||||
#endif
|
#endif
|
||||||
LOG_DEBUG(GwLog::LOG,"...done");
|
LOG_DEBUG(GwLog::LOG,"...done");
|
||||||
|
|
||||||
int lastPage=-1; // initialize with an impiossible value, so we can detect wether we are during startup and no page has been displayed yet
|
int lastPage=-1; // initialize with an impossible value, so we can detect wether we are during startup and no page has been displayed yet
|
||||||
|
|
||||||
BoatValueList boatValues; //all the boat values for the api query
|
BoatValueList boatValues; //all the boat values for the api query
|
||||||
HstryBuffers hstryBufferList(1920, &boatValues, logger); // Create empty list of boat data history buffers (1.920 values = seconds = 32 min.)
|
HstryBuffers hstryBufferList(1920, &boatValues, logger); // Create empty list of boat data history buffers (1.920 values = seconds = 32 min.)
|
||||||
WindUtils trueWind(&boatValues, logger); // Create helper object for true wind calculation
|
WindUtils trueWind(&boatValues, logger); // Create helper object for true wind calculation
|
||||||
CalibrationData calibrationDataList(logger); // all boat data types which are supposed to be calibrated
|
CalibrationData calibrationDataList(logger); // all boat data types which are supposed to be calibrated
|
||||||
|
|
||||||
|
// Read user settings from config file
|
||||||
|
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
|
||||||
|
bool smoothCharts = api->getConfig()->getBool(api->getConfig()->smoothCharts, false);
|
||||||
|
bool useSimuData = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
|
||||||
|
// Read user calibration data settings from config file
|
||||||
|
calibrationDataList.readConfig(config);
|
||||||
|
|
||||||
//fill the page data from config
|
//fill the page data from config
|
||||||
numPages=config->getInt(config->visiblePages,1);
|
numPages=config->getInt(config->visiblePages,1);
|
||||||
if (numPages < 1) numPages=1;
|
if (numPages < 1) numPages=1;
|
||||||
@@ -481,26 +509,21 @@ void OBP60Task(GwApi *api){
|
|||||||
pages[i].parameters.values.push_back(value);
|
pages[i].parameters.values.push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the specified boat data types of relevant pages and create a history buffer for each type
|
// Read the specified boat data types of relevant pages and create a history buffer for each type for later use in charts
|
||||||
if (pages[i].parameters.pageName == "OneValue" || pages[i].parameters.pageName == "TwoValues" || pages[i].parameters.pageName == "WindPlot") {
|
// applies only to pages that uses charts
|
||||||
|
if (pages[i].parameters.pageName == "OneValue" || pages[i].parameters.pageName == "TwoValues"
|
||||||
|
|| pages[i].parameters.pageName == "WindPlot" || pages[i].parameters.pageName == "Weather") {
|
||||||
for (auto pVal : pages[i].parameters.values) {
|
for (auto pVal : pages[i].parameters.values) {
|
||||||
hstryBufferList.addBuffer(pVal->getName());
|
hstryBufferList.addBuffer(pVal->getName(), smoothCharts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add list of history buffers to page parameters
|
// Add list of history buffers to page parameters
|
||||||
pages[i].parameters.hstryBuffers = &hstryBufferList;
|
pages[i].parameters.hstryBuffers = &hstryBufferList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add out of band system page (always available)
|
// add out of band system page (always available)
|
||||||
Page *syspage = allPages.pages[0]->creator(commonData);
|
Page *syspage = allPages.pages[0]->creator(commonData);
|
||||||
|
|
||||||
// Read user settings from config file
|
|
||||||
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
|
|
||||||
bool useSimuData = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
|
|
||||||
// Read user calibration data settings from config file
|
|
||||||
calibrationDataList.readConfig(config);
|
|
||||||
|
|
||||||
// Display screenshot handler for HTTP request
|
// Display screenshot handler for HTTP request
|
||||||
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||||
api->registerRequestHandler("screenshot", [api, &pageNumber, pages](AsyncWebServerRequest *request) {
|
api->registerRequestHandler("screenshot", [api, &pageNumber, pages](AsyncWebServerRequest *request) {
|
||||||
@@ -527,8 +550,8 @@ void OBP60Task(GwApi *api){
|
|||||||
// Configuration values for main loop
|
// Configuration values for main loop
|
||||||
String gpsFix = api->getConfig()->getConfigItem(api->getConfig()->flashLED,true)->asString();
|
String gpsFix = api->getConfig()->getConfigItem(api->getConfig()->flashLED,true)->asString();
|
||||||
String gpsOn=api->getConfig()->getConfigItem(api->getConfig()->useGPS,true)->asString();
|
String gpsOn=api->getConfig()->getConfigItem(api->getConfig()->useGPS,true)->asString();
|
||||||
|
float tz = api->getConfig()->getConfigItem(api->getConfig()->timeZone,true)->asFloat();
|
||||||
|
|
||||||
commonData.tz = api->getConfig()->getConfigItem(api->getConfig()->timeZone,true)->asFloat();
|
|
||||||
commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight,true)->asString());
|
commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight,true)->asString());
|
||||||
commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor,true)->asString());
|
commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor,true)->asString());
|
||||||
commonData.backlight.brightness = uint(config->getConfigItem(config->blBrightness,true)->asInt());
|
commonData.backlight.brightness = uint(config->getConfigItem(config->blBrightness,true)->asInt());
|
||||||
@@ -703,7 +726,7 @@ void OBP60Task(GwApi *api){
|
|||||||
starttime5 = millis();
|
starttime5 = millis();
|
||||||
if(time->valid == true && date->valid == true && lat->valid == true && lon->valid == true){
|
if(time->valid == true && date->valid == true && lat->valid == true && lon->valid == true){
|
||||||
// Provide sundata to all pages
|
// Provide sundata to all pages
|
||||||
commonData.sundata = calcSunsetSunrise(time->value , date->value, lat->value, lon->value, commonData.tz);
|
commonData.sundata = calcSunsetSunrise(time->value , date->value, lat->value, lon->value, tz);
|
||||||
// Backlight with sun control
|
// Backlight with sun control
|
||||||
if (commonData.backlight.mode == BacklightMode::SUN) {
|
if (commonData.backlight.mode == BacklightMode::SUN) {
|
||||||
// if(String(backlight) == "Control by Sun"){
|
// if(String(backlight) == "Control by Sun"){
|
||||||
@@ -716,7 +739,7 @@ void OBP60Task(GwApi *api){
|
|||||||
}
|
}
|
||||||
} else if (homevalid and commonData.data.rtcValid) {
|
} else if (homevalid and commonData.data.rtcValid) {
|
||||||
// No gps fix but valid home location and time configured
|
// No gps fix but valid home location and time configured
|
||||||
commonData.sundata = calcSunsetSunriseRTC(&commonData.data.rtcTime, homelat, homelon, commonData.tz);
|
commonData.sundata = calcSunsetSunriseRTC(&commonData.data.rtcTime, homelat, homelon, tz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,25 +747,29 @@ void OBP60Task(GwApi *api){
|
|||||||
if(millis() > starttime4 + 8000 && delayedDisplayUpdate == true){
|
if(millis() > starttime4 + 8000 && delayedDisplayUpdate == true){
|
||||||
starttime1 = millis();
|
starttime1 = millis();
|
||||||
starttime2 = millis();
|
starttime2 = millis();
|
||||||
getdisplay().setFullWindow(); // Set full update
|
displaySetFullWindow(); // Set full update
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// TFT LCD doesn't need refresh operations
|
||||||
|
#else
|
||||||
if(fastrefresh == "true"){
|
if(fastrefresh == "true"){
|
||||||
getdisplay().nextPage(); // Full update
|
displayNextPage(); // Full update
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().fillScreen(commonData.fgcolor); // Clear display
|
getdisplay().fillScreen(commonData.fgcolor); // Clear display
|
||||||
#ifdef DISPLAY_GDEY042T81
|
#ifdef DISPLAY_GDEY042T81
|
||||||
getdisplay().hibernate(); // Set display in hybenate mode
|
getdisplay().hibernate(); // Set display in hibenate mode
|
||||||
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
|
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
|
||||||
#else
|
#else
|
||||||
getdisplay().init(115200); // Init for normal displays
|
getdisplay().init(115200); // Init for normal displays
|
||||||
#endif
|
#endif
|
||||||
getdisplay().firstPage(); // Full update
|
displayFirstPage(); // Full update
|
||||||
getdisplay().nextPage(); // Full update
|
displayNextPage(); // Full update
|
||||||
// getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
// getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
// getdisplay().fillScreen(commonData.bgcolor); // Clear display
|
// getdisplay().fillScreen(commonData.bgcolor); // Clear display
|
||||||
// getdisplay().nextPage(); // Partial update
|
// getdisplay().nextPage(); // Partial update
|
||||||
// getdisplay().nextPage(); // Partial update
|
// getdisplay().nextPage(); // Partial update
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
delayedDisplayUpdate = false;
|
delayedDisplayUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,31 +779,38 @@ void OBP60Task(GwApi *api){
|
|||||||
starttime1 = millis();
|
starttime1 = millis();
|
||||||
starttime2 = millis();
|
starttime2 = millis();
|
||||||
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh first 5 min");
|
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh first 5 min");
|
||||||
getdisplay().setFullWindow(); // Set full update
|
displaySetFullWindow(); // Set full update
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// TFT LCD doesn't need refresh operations
|
||||||
|
#else
|
||||||
if(fastrefresh == "true"){
|
if(fastrefresh == "true"){
|
||||||
getdisplay().nextPage(); // Full update
|
displayNextPage(); // Full update
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().fillScreen(commonData.fgcolor); // Clear display
|
getdisplay().fillScreen(commonData.fgcolor); // Clear display
|
||||||
#ifdef DISPLAY_GDEY042T81
|
#ifdef DISPLAY_GDEY042T81
|
||||||
getdisplay().hibernate(); // Set display in hybenate mode
|
getdisplay().hibernate(); // Set display in hibernate mode
|
||||||
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
|
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
|
||||||
#else
|
#else
|
||||||
getdisplay().init(115200); // Init for normal displays
|
getdisplay().init(115200); // Init for normal displays
|
||||||
#endif
|
#endif
|
||||||
getdisplay().firstPage(); // Full update
|
displayFirstPage(); // Full update
|
||||||
getdisplay().nextPage(); // Full update
|
displayNextPage(); // Full update
|
||||||
// getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
// getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
// getdisplay().fillScreen(commonData.bgcolor); // Clear display
|
// getdisplay().fillScreen(commonData.bgcolor); // Clear display
|
||||||
// getdisplay().nextPage(); // Partial update
|
// getdisplay().nextPage(); // Partial update
|
||||||
// getdisplay().nextPage(); // Partial update
|
// getdisplay().nextPage(); // Partial update
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtask E-Ink full refresh
|
// Subtask E-Ink full refresh
|
||||||
if(millis() > starttime2 + fullrefreshtime * 60 * 1000){
|
if(millis() > starttime2 + fullrefreshtime * 60 * 1000){
|
||||||
starttime2 = millis();
|
starttime2 = millis();
|
||||||
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh");
|
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh");
|
||||||
|
#ifdef TFT_DISPLAY
|
||||||
|
// TFT LCD: no special refresh
|
||||||
|
#else
|
||||||
getdisplay().setFullWindow(); // Set full update
|
getdisplay().setFullWindow(); // Set full update
|
||||||
if(fastrefresh == "true"){
|
if(fastrefresh == "true"){
|
||||||
getdisplay().nextPage(); // Full update
|
getdisplay().nextPage(); // Full update
|
||||||
@@ -784,7 +818,7 @@ void OBP60Task(GwApi *api){
|
|||||||
else{
|
else{
|
||||||
getdisplay().fillScreen(commonData.fgcolor); // Clear display
|
getdisplay().fillScreen(commonData.fgcolor); // Clear display
|
||||||
#ifdef DISPLAY_GDEY042T81
|
#ifdef DISPLAY_GDEY042T81
|
||||||
getdisplay().hibernate(); // Set display in hybenate mode
|
getdisplay().hibernate(); // Set display in hibernate mode
|
||||||
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
|
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
|
||||||
#else
|
#else
|
||||||
getdisplay().init(115200); // Init for normal displays
|
getdisplay().init(115200); // Init for normal displays
|
||||||
@@ -796,6 +830,7 @@ void OBP60Task(GwApi *api){
|
|||||||
// getdisplay().nextPage(); // Partial update
|
// getdisplay().nextPage(); // Partial update
|
||||||
// getdisplay().nextPage(); // Partial update
|
// getdisplay().nextPage(); // Partial update
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh display data, default all 1s
|
// Refresh display data, default all 1s
|
||||||
@@ -808,16 +843,18 @@ void OBP60Task(GwApi *api){
|
|||||||
if(millis() > starttime3 + pagetime){
|
if(millis() > starttime3 + pagetime){
|
||||||
LOG_DEBUG(GwLog::DEBUG,"Page with refreshtime=%d", pagetime);
|
LOG_DEBUG(GwLog::DEBUG,"Page with refreshtime=%d", pagetime);
|
||||||
starttime3 = millis();
|
starttime3 = millis();
|
||||||
|
bool pageChanged = (lastPage != pageNumber);
|
||||||
|
|
||||||
//refresh data from api
|
//refresh data from api
|
||||||
api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues);
|
api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues);
|
||||||
api->getStatus(commonData.status);
|
api->getStatus(commonData.status);
|
||||||
|
|
||||||
if (calcTrueWnds) {
|
// ulong startHandl = millis();
|
||||||
trueWind.addWinds(); // calculate true wind data from apparent wind values
|
trueWind.handleWinds(calcTrueWnds); // calculate true wind data from apparent wind values
|
||||||
}
|
trueWind.setMaxWs(); // maintain MaxTWS value in any case; invalid TWS value is considered automatically; MaxAWS is provided by core gateway if AWS is available
|
||||||
calibrationDataList.handleCalibration(&boatValues); // Process calibration for all boat data in <calibrationDataList>
|
calibrationDataList.handleCalibration(&boatValues); // Process calibration for all boat data in <calibrationDataList>
|
||||||
hstryBufferList.handleHstryBufs(useSimuData, commonData); // Handle history buffers for certain boat data for windplot page and other usage
|
hstryBufferList.handleHstryBufs(useSimuData, commonData); // Handle history buffers for certain boat data for charts and other usage
|
||||||
|
// LOG_DEBUG(GwLog::DEBUG, "obp60task: data handling: %d ms", millis() - startHandl);
|
||||||
|
|
||||||
// Clear display
|
// Clear display
|
||||||
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
|
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
|
||||||
@@ -844,16 +881,16 @@ void OBP60Task(GwApi *api){
|
|||||||
if (currentPage == NULL){
|
if (currentPage == NULL){
|
||||||
LOG_DEBUG(GwLog::ERROR,"page number %d not found", pageNumber);
|
LOG_DEBUG(GwLog::ERROR,"page number %d not found", pageNumber);
|
||||||
// Error handling for missing page
|
// Error handling for missing page
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
getdisplay().fillScreen(commonData.bgcolor); // Clear display
|
getdisplay().fillScreen(commonData.bgcolor); // Clear display
|
||||||
getdisplay().drawXBitmap(200 - unknown_width / 2, 150 - unknown_height / 2, unknown_bits, unknown_width, unknown_height, commonData.fgcolor);
|
getdisplay().drawXBitmap(200 - unknown_width / 2, 150 - unknown_height / 2, unknown_bits, unknown_width, unknown_height, commonData.fgcolor);
|
||||||
getdisplay().setCursor(140, 250);
|
getdisplay().setCursor(140, 250);
|
||||||
getdisplay().setFont(&Atari16px);
|
getdisplay().setFont(&Atari16px);
|
||||||
getdisplay().print("Here be dragons!");
|
getdisplay().print("Here be dragons!");
|
||||||
getdisplay().nextPage(); // Partial update (fast)
|
displayNextPage(); // Partial update (fast)
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if (lastPage != pageNumber){
|
if (pageChanged){
|
||||||
if (lastPage != -1){ // skip cleanup if we are during startup, and no page has been displayed yet.
|
if (lastPage != -1){ // skip cleanup if we are during startup, and no page has been displayed yet.
|
||||||
pages[lastPage].page->leavePage(pages[lastPage].parameters); // call page cleanup code
|
pages[lastPage].page->leavePage(pages[lastPage].parameters); // call page cleanup code
|
||||||
if (hasFRAM) fram.write(FRAM_PAGE_NO, pageNumber); // remember new page for device restart
|
if (hasFRAM) fram.write(FRAM_PAGE_NO, pageNumber); // remember new page for device restart
|
||||||
@@ -873,10 +910,12 @@ void OBP60Task(GwApi *api){
|
|||||||
displayAlarm(commonData);
|
displayAlarm(commonData);
|
||||||
}
|
}
|
||||||
if (ret & PAGE_UPDATE) {
|
if (ret & PAGE_UPDATE) {
|
||||||
getdisplay().nextPage(); // Partial update (fast)
|
displayNextPage(); // Partial update (fast)
|
||||||
}
|
}
|
||||||
if (ret & PAGE_HIBERNATE) {
|
if (ret & PAGE_HIBERNATE) {
|
||||||
|
#ifndef TFT_DISPLAY
|
||||||
getdisplay().hibernate();
|
getdisplay().hibernate();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//we only compile for some boards
|
//we only compile for some boards
|
||||||
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
|
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
|
||||||
#define USBSerial Serial
|
#define USBSerial Serial
|
||||||
#ifdef HARDWARE_V21
|
#ifdef BOARD_OBP60S3
|
||||||
// CAN NMEA2000
|
// CAN NMEA2000
|
||||||
#define ESP32_CAN_TX_PIN 46
|
#define ESP32_CAN_TX_PIN 46
|
||||||
#define ESP32_CAN_RX_PIN 3
|
#define ESP32_CAN_RX_PIN 3
|
||||||
@@ -35,13 +35,19 @@
|
|||||||
// OBP60 Task
|
// OBP60 Task
|
||||||
void OBP60Task(GwApi *param);
|
void OBP60Task(GwApi *param);
|
||||||
DECLARE_USERTASK_PARAM(OBP60Task, 35000); // Need 35k RAM as stack size
|
DECLARE_USERTASK_PARAM(OBP60Task, 35000); // Need 35k RAM as stack size
|
||||||
#ifdef HARDWARE_V21
|
#if defined(BOARD_OBP60S3) && defined(TFT_DISPLAY)
|
||||||
|
DECLARE_CAPABILITY(obp70,true);
|
||||||
|
#endif
|
||||||
|
#if defined(BOARD_OBP60S3) && !defined(TFT_DISPLAY)
|
||||||
DECLARE_CAPABILITY(obp60,true);
|
DECLARE_CAPABILITY(obp60,true);
|
||||||
#endif
|
#endif
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
DECLARE_CAPABILITY(obp40,true)
|
DECLARE_CAPABILITY(obp40,true)
|
||||||
#endif
|
#endif
|
||||||
#ifdef BOARD_OBP60S3
|
#if defined(BOARD_OBP60S3) && defined(TFT_DISPLAY)
|
||||||
|
DECLARE_STRING_CAPABILITY(HELP_URL, "https://obp60-v2-docu.readthedocs.io/en/latest/"); // Link to help pages
|
||||||
|
#endif
|
||||||
|
#if defined(BOARD_OBP60S3) && !defined(TFT_DISPLAY)
|
||||||
DECLARE_STRING_CAPABILITY(HELP_URL, "https://obp60-v2-docu.readthedocs.io/en/latest/"); // Link to help pages
|
DECLARE_STRING_CAPABILITY(HELP_URL, "https://obp60-v2-docu.readthedocs.io/en/latest/"); // Link to help pages
|
||||||
#endif
|
#endif
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h
|
||||||
|
index 88f9690..9663a65 100644
|
||||||
|
--- a/lib/api/GwApi.h
|
||||||
|
+++ b/lib/api/GwApi.h
|
||||||
|
@@ -2,6 +2,8 @@
|
||||||
|
#define _GWAPI_H
|
||||||
|
#include "GwMessage.h"
|
||||||
|
#include "N2kMsg.h"
|
||||||
|
+#include "Nmea2kTwai.h"
|
||||||
|
+#include "N2kDeviceList.h"
|
||||||
|
#include "NMEA0183Msg.h"
|
||||||
|
#include "GWConfig.h"
|
||||||
|
#include "GwBoatData.h"
|
||||||
|
@@ -222,6 +224,8 @@ class GwApi{
|
||||||
|
* accessing boat data must only be executed from within the main thread
|
||||||
|
* you need to use the request pattern as shown in GwExampleTask.cpp
|
||||||
|
*/
|
||||||
|
+ virtual Nmea2kTwai *getNMEA2000()=0;
|
||||||
|
+ virtual tN2kDeviceList *getN2kDeviceList()=0;
|
||||||
|
virtual GwBoatData *getBoatData()=0;
|
||||||
|
virtual ~GwApi(){}
|
||||||
|
};
|
||||||
|
diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h
|
||||||
|
index 9e2bc99..34eaeac 100644
|
||||||
|
--- a/lib/obp60task/OBP60Extensions.h
|
||||||
|
+++ b/lib/obp60task/OBP60Extensions.h
|
||||||
|
@@ -29,6 +29,9 @@
|
||||||
|
#define MOUNT_POINT "/sdcard"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+// Patches to apply to gateway code
|
||||||
|
+#define PATCH_N2K
|
||||||
|
+
|
||||||
|
// FRAM address reservations 32kB: 0x0000 - 0x7FFF
|
||||||
|
// 0x0000 - 0x03ff: single variables
|
||||||
|
#define FRAM_PAGE_NO 0x0002
|
||||||
|
diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp
|
||||||
|
index 1b007f8..90087d4 100644
|
||||||
|
--- a/lib/usercode/GwUserCode.cpp
|
||||||
|
+++ b/lib/usercode/GwUserCode.cpp
|
||||||
|
@@ -216,6 +216,14 @@ public:
|
||||||
|
{
|
||||||
|
return api->getLogger();
|
||||||
|
}
|
||||||
|
+ virtual Nmea2kTwai *getNMEA2000()
|
||||||
|
+ {
|
||||||
|
+ return api->getNMEA2000();
|
||||||
|
+ }
|
||||||
|
+ virtual tN2kDeviceList *getN2kDeviceList()
|
||||||
|
+ {
|
||||||
|
+ return api->getN2kDeviceList();
|
||||||
|
+ }
|
||||||
|
virtual GwBoatData *getBoatData()
|
||||||
|
{
|
||||||
|
return api->getBoatData();
|
||||||
|
@@ -428,4 +436,4 @@ void GwUserCode::handleWebRequest(const String &url,AsyncWebServerRequest *req){
|
||||||
|
}
|
||||||
|
LOG_DEBUG(GwLog::DEBUG,"no task found for web request %s[%s]",url.c_str(),tname.c_str());
|
||||||
|
req->send(404, "text/plain", "not found");
|
||||||
|
-}
|
||||||
|
\ No newline at end of file
|
||||||
|
+}
|
||||||
|
diff --git a/src/main.cpp b/src/main.cpp
|
||||||
|
index 95d5d1e..01fcd32 100644
|
||||||
|
--- a/src/main.cpp
|
||||||
|
+++ b/src/main.cpp
|
||||||
|
@@ -100,6 +100,7 @@ GwLog logger(LOGLEVEL,NULL);
|
||||||
|
GwConfigHandler config(&logger);
|
||||||
|
|
||||||
|
#include "Nmea2kTwai.h"
|
||||||
|
+#include <N2kDeviceList.h>
|
||||||
|
static const unsigned long CAN_RECOVERY_PERIOD=3000; //ms
|
||||||
|
static const unsigned long NMEA2000_HEARTBEAT_INTERVAL=5000;
|
||||||
|
class Nmea2kTwaiLog : public Nmea2kTwai{
|
||||||
|
@@ -126,6 +127,7 @@ class Nmea2kTwaiLog : public Nmea2kTwai{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog((gpio_num_t)ESP32_CAN_TX_PIN,(gpio_num_t)ESP32_CAN_RX_PIN,CAN_RECOVERY_PERIOD,&logger));
|
||||||
|
+tN2kDeviceList *pN2kDeviceList;
|
||||||
|
|
||||||
|
#ifdef GWBUTTON_PIN
|
||||||
|
bool fixedApPass=false;
|
||||||
|
@@ -333,6 +335,12 @@ public:
|
||||||
|
status.n2kTx=countNMEA2KOut.getGlobal();
|
||||||
|
channels.fillStatus(status);
|
||||||
|
}
|
||||||
|
+ virtual Nmea2kTwai *getNMEA2000(){
|
||||||
|
+ return &NMEA2000;
|
||||||
|
+ }
|
||||||
|
+ virtual tN2kDeviceList *getN2kDeviceList(){
|
||||||
|
+ return pN2kDeviceList;
|
||||||
|
+ }
|
||||||
|
virtual GwBoatData *getBoatData(){
|
||||||
|
return &boatData;
|
||||||
|
}
|
||||||
|
@@ -935,6 +943,7 @@ void setup() {
|
||||||
|
NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){
|
||||||
|
handleN2kMessage(n2kMsg,N2K_CHANNEL_ID);
|
||||||
|
});
|
||||||
|
+ pN2kDeviceList = new tN2kDeviceList(&NMEA2000);
|
||||||
|
NMEA2000.Open();
|
||||||
|
logger.logDebug(GwLog::LOG,"starting addon tasks");
|
||||||
|
logger.flush();
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
diff --git a/lib/boatData/GwBoatData.cpp b/lib/boatData/GwBoatData.cpp
|
||||||
|
index 424ebe4..b717097 100644
|
||||||
|
--- a/lib/boatData/GwBoatData.cpp
|
||||||
|
+++ b/lib/boatData/GwBoatData.cpp
|
||||||
|
@@ -6,6 +6,7 @@
|
||||||
|
#define GWTYPE_UINT32 2
|
||||||
|
#define GWTYPE_UINT16 3
|
||||||
|
#define GWTYPE_INT16 4
|
||||||
|
+#define GWTYPE_STRING 5
|
||||||
|
#define GWTYPE_USER 100
|
||||||
|
|
||||||
|
class GwBoatItemTypes
|
||||||
|
@@ -15,6 +16,7 @@ public:
|
||||||
|
static int getType(const uint16_t &x) { return GWTYPE_UINT16; }
|
||||||
|
static int getType(const int16_t &x) { return GWTYPE_INT16; }
|
||||||
|
static int getType(const double &x) { return GWTYPE_DOUBLE; }
|
||||||
|
+ static int getType(const String &x) { return GWTYPE_STRING; }
|
||||||
|
static int getType(const GwSatInfoList &x) { return GWTYPE_USER + 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -252,6 +254,10 @@ static void writeToString(GwTextWriter *writer, const int16_t &value)
|
||||||
|
{
|
||||||
|
writer->writeInteger(value);
|
||||||
|
}
|
||||||
|
+static void writeToString(GwTextWriter *writer, String value)
|
||||||
|
+{
|
||||||
|
+ writer->writeString(value.c_str());
|
||||||
|
+}
|
||||||
|
static void writeToString(GwTextWriter *writer, GwSatInfoList &value)
|
||||||
|
{
|
||||||
|
writer->writeInteger(value.getNumSats());
|
||||||
|
@@ -288,6 +294,8 @@ template class GwBoatItem<double>;
|
||||||
|
template class GwBoatItem<uint32_t>;
|
||||||
|
template class GwBoatItem<uint16_t>;
|
||||||
|
template class GwBoatItem<int16_t>;
|
||||||
|
+template class GwBoatItem<String>;
|
||||||
|
+
|
||||||
|
void GwSatInfoList::houseKeeping(unsigned long ts)
|
||||||
|
{
|
||||||
|
if (ts == 0)
|
||||||
|
diff --git a/lib/boatData/GwBoatData.h b/lib/boatData/GwBoatData.h
|
||||||
|
index ae50b3f..5b32f70 100644
|
||||||
|
--- a/lib/boatData/GwBoatData.h
|
||||||
|
+++ b/lib/boatData/GwBoatData.h
|
||||||
|
@@ -58,6 +58,7 @@ class GwBoatItemBase{
|
||||||
|
GWSC(formatRot);
|
||||||
|
GWSC(formatDate);
|
||||||
|
GWSC(formatTime);
|
||||||
|
+ GWSC(formatName);
|
||||||
|
protected:
|
||||||
|
int type;
|
||||||
|
unsigned long lastSet=0;
|
||||||
|
@@ -92,6 +93,7 @@ class GwBoatItemBase{
|
||||||
|
virtual int getLastSource(){return lastUpdateSource;}
|
||||||
|
virtual void refresh(unsigned long ts=0){uls(ts);}
|
||||||
|
virtual double getDoubleValue()=0;
|
||||||
|
+ virtual String getStringValue()=0;
|
||||||
|
String getName(){return name;}
|
||||||
|
const String & getFormat() const{return format;}
|
||||||
|
virtual void setInvalidTime(GwConfigHandler *cfg);
|
||||||
|
@@ -120,7 +122,19 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
|
||||||
|
if (! isValid(millis())) return defaultv;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
- virtual double getDoubleValue(){return (double)data;}
|
||||||
|
+ virtual double getDoubleValue(){
|
||||||
|
+ if constexpr (std::is_same<T, String>::value) {
|
||||||
|
+ return 0.0;
|
||||||
|
+ // TODO any better ideas?
|
||||||
|
+ // perhaps return std::numeric_limits<double>::quiet_NaN();
|
||||||
|
+ } else {
|
||||||
|
+ return (double)data;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ virtual String getStringValue(){
|
||||||
|
+ return (String)data;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
virtual void fillString();
|
||||||
|
virtual void toJsonDoc(GwJsonDocument *doc, unsigned long minTime);
|
||||||
|
virtual int getLastSource(){return lastUpdateSource;}
|
||||||
|
@@ -235,6 +249,7 @@ class GwBoatData{
|
||||||
|
GWBOATDATA(double,XTE,formatXte) // cross track error
|
||||||
|
GWBOATDATA(double,WPLat,formatLatitude) // waypoint latitude
|
||||||
|
GWBOATDATA(double,WPLon,formatLongitude) // waypoint longitude
|
||||||
|
+ GWBOATDATA(String,WPName,formatName) // waypoint name
|
||||||
|
GWSPECBOATDATA(GwBoatDataSatList,SatInfo,GwSatInfoList::toType,formatFixed0);
|
||||||
|
public:
|
||||||
|
GwBoatData(GwLog *logger, GwConfigHandler *cfg);
|
||||||
|
diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp
|
||||||
|
index 6989570..238af38 100644
|
||||||
|
--- a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp
|
||||||
|
+++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp
|
||||||
|
@@ -355,6 +355,7 @@ private:
|
||||||
|
AppendN2kRouteWPInfo(n2kMsg,destinationId,rmb.destID,rmb.latitude,rmb.longitude);
|
||||||
|
send(n2kMsg,msg.sourceId);
|
||||||
|
}
|
||||||
|
+ boatData->WPName->update(String(rmb.destID), msg.sourceId);
|
||||||
|
}
|
||||||
|
void convertRMC(const SNMEA0183Msg &msg)
|
||||||
|
{
|
||||||
|
diff --git a/platformio.ini b/platformio.ini
|
||||||
|
index b738131..e131a6e 100644
|
||||||
|
--- a/platformio.ini
|
||||||
|
+++ b/platformio.ini
|
||||||
|
@@ -68,6 +68,9 @@ lib_ldf_mode = off
|
||||||
|
monitor_speed = 115200
|
||||||
|
build_flags =
|
||||||
|
-D PIO_ENV_BUILD=$PIOENV
|
||||||
|
+ -std=gnu++17
|
||||||
|
+build_unflags =
|
||||||
|
+ -std=gnu++11
|
||||||
|
|
||||||
|
[sensors]
|
||||||
|
; collect the libraries for sensors here
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h
|
||||||
|
index 88f9690..bde7670 100644
|
||||||
|
--- a/lib/api/GwApi.h
|
||||||
|
+++ b/lib/api/GwApi.h
|
||||||
|
@@ -137,6 +137,7 @@ class GwApi{
|
||||||
|
* thread safe methods - can directly be called from a user task
|
||||||
|
*/
|
||||||
|
virtual GwRequestQueue *getQueue()=0;
|
||||||
|
+ virtual QueueHandle_t getKbQueue()=0;
|
||||||
|
virtual void sendN2kMessage(const tN2kMsg &msg, bool convert=true)=0;
|
||||||
|
/**
|
||||||
|
* deprecated - sourceId will be ignored
|
||||||
|
diff --git a/lib/config/GwConverterConfig.h b/lib/config/GwConverterConfig.h
|
||||||
|
index 4a93a71..34ed7bf 100644
|
||||||
|
--- a/lib/config/GwConverterConfig.h
|
||||||
|
+++ b/lib/config/GwConverterConfig.h
|
||||||
|
@@ -71,6 +71,7 @@ class GwConverterConfig{
|
||||||
|
int rmcInterval=1000;
|
||||||
|
int rmcCheckTime=4000;
|
||||||
|
int winst312=256;
|
||||||
|
+ int swBankInstance=0;
|
||||||
|
bool unmappedXdr=false;
|
||||||
|
unsigned long xdrTimeout=60000;
|
||||||
|
std::vector<WindMapping> windMappings;
|
||||||
|
@@ -113,4 +114,4 @@ class GwConverterConfig{
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
-#endif
|
||||||
|
\ No newline at end of file
|
||||||
|
+#endif
|
||||||
|
diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp
|
||||||
|
index dbf6af5..9fe405b 100644
|
||||||
|
--- a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp
|
||||||
|
+++ b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp
|
||||||
|
@@ -1572,6 +1572,29 @@ private:
|
||||||
|
finalizeXdr();
|
||||||
|
}
|
||||||
|
|
||||||
|
+ void Handle127502(const tN2kMsg &msg){
|
||||||
|
+ // switch bank control / receive remote key strokes
|
||||||
|
+ unsigned char instance=-1;
|
||||||
|
+ tN2kBinaryStatus bankstatus;
|
||||||
|
+ LOG_DEBUG(GwLog::LOG,"received switch bank control");
|
||||||
|
+ // check if we are addressed and our configured instance is used
|
||||||
|
+ if (! ParseN2kPGN127502(msg,instance,bankstatus)) {
|
||||||
|
+ LOG_DEBUG(GwLog::DEBUG,"unable to parse PGN %d",msg.PGN);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ if (! (instance == config.swBankInstance)) {
|
||||||
|
+ LOG_DEBUG(GwLog::DEBUG,"switch bank instance #%d ignored",instance);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ for (uint8_t i=1; i<=28; i++) {
|
||||||
|
+ tN2kOnOff keystatus = N2kGetStatusOnBinaryStatus(bankstatus, i);
|
||||||
|
+ if (keystatus == 1) {
|
||||||
|
+ // key pressed: send key to queue
|
||||||
|
+ xQueueSend(keyboardQueue, &i, 0);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
void registerConverters()
|
||||||
|
{
|
||||||
|
//register all converter functions
|
||||||
|
@@ -1607,6 +1630,7 @@ private:
|
||||||
|
converters.registerConverter(127488UL, &N2kToNMEA0183Functions::Handle127488);
|
||||||
|
converters.registerConverter(130316UL, &N2kToNMEA0183Functions::Handle130316);
|
||||||
|
converters.registerConverter(127257UL, &N2kToNMEA0183Functions::Handle127257);
|
||||||
|
+ converters.registerConverter(127502UL, &N2kToNMEA0183Functions::Handle127502);
|
||||||
|
#define HANDLE_AIS
|
||||||
|
#ifdef HANDLE_AIS
|
||||||
|
converters.registerConverter(129038UL, &N2kToNMEA0183Functions::HandleAISClassAPosReport); // AIS Class A Position Report, Message Type 1
|
||||||
|
@@ -1620,13 +1644,15 @@ private:
|
||||||
|
public:
|
||||||
|
N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData,
|
||||||
|
SendNMEA0183MessageCallback callback,
|
||||||
|
- String talkerId, GwXDRMappings *xdrMappings, const GwConverterConfig &cfg)
|
||||||
|
+ String talkerId, GwXDRMappings *xdrMappings, const GwConverterConfig &cfg,
|
||||||
|
+ QueueHandle_t kbQueue)
|
||||||
|
: N2kDataToNMEA0183(logger, boatData, callback,talkerId)
|
||||||
|
{
|
||||||
|
this->logger = logger;
|
||||||
|
this->boatData = boatData;
|
||||||
|
this->xdrMappings=xdrMappings;
|
||||||
|
this->config=cfg;
|
||||||
|
+ this->keyboardQueue=kbQueue;
|
||||||
|
registerConverters();
|
||||||
|
}
|
||||||
|
virtual void loop(unsigned long lastExtRmc) override
|
||||||
|
@@ -1642,8 +1668,8 @@ private:
|
||||||
|
|
||||||
|
N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData,
|
||||||
|
SendNMEA0183MessageCallback callback, String talkerId, GwXDRMappings *xdrMappings,
|
||||||
|
- const GwConverterConfig &cfg){
|
||||||
|
- LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183");
|
||||||
|
- return new N2kToNMEA0183Functions(logger,boatData,callback, talkerId,xdrMappings,cfg);
|
||||||
|
+ const GwConverterConfig &cfg, QueueHandle_t kbQueue){
|
||||||
|
+ LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183");
|
||||||
|
+ return new N2kToNMEA0183Functions(logger,boatData,callback, talkerId,xdrMappings,cfg,kbQueue);
|
||||||
|
}
|
||||||
|
//*****************************************************************************
|
||||||
|
diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.h b/lib/nmea2kto0183/N2kDataToNMEA0183.h
|
||||||
|
index 61e8ee2..f7d8c75 100644
|
||||||
|
--- a/lib/nmea2kto0183/N2kDataToNMEA0183.h
|
||||||
|
+++ b/lib/nmea2kto0183/N2kDataToNMEA0183.h
|
||||||
|
@@ -43,6 +43,7 @@ protected:
|
||||||
|
GwBoatData *boatData;
|
||||||
|
int sourceId=0;
|
||||||
|
char talkerId[3];
|
||||||
|
+ QueueHandle_t keyboardQueue;
|
||||||
|
SendNMEA0183MessageCallback sendNMEA0183MessageCallback;
|
||||||
|
void SendMessage(const tNMEA0183Msg &NMEA0183Msg);
|
||||||
|
N2kDataToNMEA0183(GwLog *logger, GwBoatData *boatData,
|
||||||
|
@@ -50,7 +51,8 @@ protected:
|
||||||
|
|
||||||
|
public:
|
||||||
|
static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback,
|
||||||
|
- String talkerId, GwXDRMappings *xdrMappings,const GwConverterConfig &cfg);
|
||||||
|
+ String talkerId, GwXDRMappings *xdrMappings,const GwConverterConfig &cfg,
|
||||||
|
+ QueueHandle_t kbQueue);
|
||||||
|
virtual void HandleMsg(const tN2kMsg &N2kMsg, int sourceId) = 0;
|
||||||
|
virtual void loop(unsigned long lastRmc);
|
||||||
|
virtual ~N2kDataToNMEA0183(){}
|
||||||
|
@@ -59,4 +61,4 @@ public:
|
||||||
|
virtual void toJson(GwJsonDocument *json)=0;
|
||||||
|
virtual String handledKeys()=0;
|
||||||
|
};
|
||||||
|
-#endif
|
||||||
|
\ No newline at end of file
|
||||||
|
+#endif
|
||||||
|
diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp
|
||||||
|
index 1058ae0..ae11a81 100644
|
||||||
|
--- a/lib/obp60task/obp60task.cpp
|
||||||
|
+++ b/lib/obp60task/obp60task.cpp
|
||||||
|
@@ -348,6 +348,8 @@ void OBP60Task(GwApi *api){
|
||||||
|
|
||||||
|
tN2kMsg N2kMsg;
|
||||||
|
|
||||||
|
+ QueueHandle_t keyboardQueue = api->getKbQueue();
|
||||||
|
+
|
||||||
|
LOG_DEBUG(GwLog::LOG,"obp60task started");
|
||||||
|
for (auto it=allPages.pages.begin();it != allPages.pages.end();it++){
|
||||||
|
LOG_DEBUG(GwLog::LOG,"found registered page %s",(*it)->pageName.c_str());
|
||||||
|
@@ -622,7 +624,16 @@ void OBP60Task(GwApi *api){
|
||||||
|
if((String(gpsFix) == "GPS Fix Lost" && hdop->value > hdopAccuracy && hdop->valid == true) || (String(gpsFix) == "GPS Fix Lost" && hdop->valid == false)){
|
||||||
|
setFlashLED(true);
|
||||||
|
}
|
||||||
|
-
|
||||||
|
+
|
||||||
|
+ // Keyboard messages from remote
|
||||||
|
+ uint8_t remotekey = 0;
|
||||||
|
+ if (xQueueReceive(keyboardQueue, &remotekey, 0) == pdPASS) {
|
||||||
|
+ LOG_DEBUG(GwLog::LOG, "OBP received remote key: %d", remotekey);
|
||||||
|
+ // inject into internal keyboard queue
|
||||||
|
+ int injectkey = static_cast<int>(remotekey);
|
||||||
|
+ xQueueSend(allParameters.queue, &injectkey, 0);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
// Check the keyboard message
|
||||||
|
int keyboardMessage=0;
|
||||||
|
while (xQueueReceive(allParameters.queue,&keyboardMessage,0)){
|
||||||
|
diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp
|
||||||
|
index 1b007f8..b891422 100644
|
||||||
|
--- a/lib/usercode/GwUserCode.cpp
|
||||||
|
+++ b/lib/usercode/GwUserCode.cpp
|
||||||
|
@@ -189,6 +189,10 @@ public:
|
||||||
|
{
|
||||||
|
return api->getQueue();
|
||||||
|
}
|
||||||
|
+ virtual QueueHandle_t getKbQueue()
|
||||||
|
+ {
|
||||||
|
+ return api->getKbQueue();
|
||||||
|
+ }
|
||||||
|
virtual void sendN2kMessage(const tN2kMsg &msg,bool convert)
|
||||||
|
{
|
||||||
|
GWSYNCHRONIZED(mainLock);
|
||||||
|
@@ -428,4 +432,4 @@ void GwUserCode::handleWebRequest(const String &url,AsyncWebServerRequest *req){
|
||||||
|
}
|
||||||
|
LOG_DEBUG(GwLog::DEBUG,"no task found for web request %s[%s]",url.c_str(),tname.c_str());
|
||||||
|
req->send(404, "text/plain", "not found");
|
||||||
|
-}
|
||||||
|
\ No newline at end of file
|
||||||
|
+}
|
||||||
|
diff --git a/src/main.cpp b/src/main.cpp
|
||||||
|
index 6daaa51..50625d8 100644
|
||||||
|
--- a/src/main.cpp
|
||||||
|
+++ b/src/main.cpp
|
||||||
|
@@ -158,6 +158,8 @@ GwCounter<unsigned long> countNMEA2KIn("countNMEA2000in");
|
||||||
|
GwCounter<unsigned long> countNMEA2KOut("countNMEA2000out");
|
||||||
|
GwIntervalRunner timers;
|
||||||
|
|
||||||
|
+QueueHandle_t keyboardQueue = NULL;
|
||||||
|
+
|
||||||
|
bool checkPass(String hash){
|
||||||
|
return config.checkPass(hash);
|
||||||
|
}
|
||||||
|
@@ -269,6 +271,10 @@ public:
|
||||||
|
{
|
||||||
|
return &mainQueue;
|
||||||
|
}
|
||||||
|
+ virtual QueueHandle_t getKbQueue()
|
||||||
|
+ {
|
||||||
|
+ return keyboardQueue;
|
||||||
|
+ }
|
||||||
|
virtual void sendN2kMessage(const tN2kMsg &msg,bool convert)
|
||||||
|
{
|
||||||
|
handleN2kMessage(msg,sourceId,!convert);
|
||||||
|
@@ -860,6 +866,8 @@ void setup() {
|
||||||
|
webserver.begin();
|
||||||
|
xdrMappings.begin();
|
||||||
|
logger.flush();
|
||||||
|
+ // remote keyboard support
|
||||||
|
+ keyboardQueue = xQueueCreate(10, sizeof(uint8_t));
|
||||||
|
GwConverterConfig converterConfig;
|
||||||
|
converterConfig.init(&config,&logger);
|
||||||
|
nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,
|
||||||
|
@@ -869,7 +877,8 @@ void setup() {
|
||||||
|
,
|
||||||
|
config.getString(config.talkerId,String("GP")),
|
||||||
|
&xdrMappings,
|
||||||
|
- converterConfig
|
||||||
|
+ converterConfig,
|
||||||
|
+ keyboardQueue
|
||||||
|
);
|
||||||
|
|
||||||
|
toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg, int sourceId)->bool{
|
||||||
|
diff --git a/web/config.json b/web/config.json
|
||||||
|
index 26d3166..bef7790 100644
|
||||||
|
--- a/web/config.json
|
||||||
|
+++ b/web/config.json
|
||||||
|
@@ -88,6 +88,16 @@
|
||||||
|
"description":"the brightness of the led (0..255)",
|
||||||
|
"category":"system"
|
||||||
|
},
|
||||||
|
+ {
|
||||||
|
+ "name": "swBankInstance",
|
||||||
|
+ "type": "number",
|
||||||
|
+ "default": 0,
|
||||||
|
+ "min": 0,
|
||||||
|
+ "max": 252,
|
||||||
|
+ "check": "checkMinMax",
|
||||||
|
+ "description": "the instance of switch bank to receive data for (0..252)",
|
||||||
|
+ "category": "system"
|
||||||
|
+ },
|
||||||
|
{
|
||||||
|
"name": "talkerId",
|
||||||
|
"label": "NMEA0183 ID",
|
||||||
|
@@ -1287,4 +1297,4 @@
|
||||||
|
"check": "checkXDR",
|
||||||
|
"category": "xdr30"
|
||||||
|
}
|
||||||
|
-]
|
||||||
|
\ No newline at end of file
|
||||||
|
+]
|
||||||
@@ -4,9 +4,66 @@
|
|||||||
#by uncommenting the next line
|
#by uncommenting the next line
|
||||||
|
|
||||||
default_envs =
|
default_envs =
|
||||||
|
obp70_s3
|
||||||
obp60_s3
|
obp60_s3
|
||||||
obp40_s3
|
obp40_s3
|
||||||
|
|
||||||
|
[env:obp70_s3]
|
||||||
|
platform = espressif32@6.8.1
|
||||||
|
board_build.variants_dir = variants
|
||||||
|
board = obp70_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production series
|
||||||
|
board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
|
||||||
|
custom_config = lib/obp60task/config_obp70.json
|
||||||
|
custom_script = lib/obp60task/extra_task.py
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
${basedeps.lib_deps}
|
||||||
|
Wire
|
||||||
|
SPI
|
||||||
|
ESP32time
|
||||||
|
HTTPClient
|
||||||
|
WiFiClientSecure
|
||||||
|
esphome/AsyncTCP-esphome@2.1.1
|
||||||
|
robtillaart/PCF8574@0.3.9
|
||||||
|
adafruit/Adafruit Unified Sensor @ 1.1.13
|
||||||
|
blemasle/MCP23017@2.0.0
|
||||||
|
adafruit/Adafruit BusIO@1.5.0
|
||||||
|
adafruit/Adafruit GFX Library@1.11.9
|
||||||
|
#zinggjm/GxEPD2@1.5.8
|
||||||
|
#https://github.com/ZinggJM/GxEPD2
|
||||||
|
https://github.com/thooge/GxEPD2
|
||||||
|
sstaub/Ticker@4.4.0
|
||||||
|
adafruit/Adafruit BMP280 Library@2.6.2
|
||||||
|
adafruit/Adafruit BME280 Library@2.2.2
|
||||||
|
adafruit/Adafruit BMP085 Library@1.2.1
|
||||||
|
enjoyneering/HTU21D@1.2.1
|
||||||
|
robtillaart/INA226@0.2.0
|
||||||
|
paulstoffregen/OneWire@2.3.8
|
||||||
|
milesburton/DallasTemperature@3.11.0
|
||||||
|
signetica/SunRise@2.0.2
|
||||||
|
adafruit/Adafruit FRAM I2C@2.0.3
|
||||||
|
lovyan03/LovyanGFX@1.2.19
|
||||||
|
build_flags=
|
||||||
|
#https://thingpulse.com/usb-settings-for-logging-with-the-esp32-s3-in-platformio/?srsltid=AfmBOopGskbkr4GoeVkNlFaZXe_zXkLceKF6Rn-tmoXABCeAR2vWsdHL
|
||||||
|
# -D CORE_DEBUG_LEVEL=1 #Debug level for CPU core via CDC (serial device)
|
||||||
|
# -D TIME=$UNIX_TIME #Set PC time for RTC (only settable via VSC)
|
||||||
|
-D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib
|
||||||
|
-D BOARD_OBP60S3 #Board OBP60 V2.1 with ESP32S3
|
||||||
|
# -D HARDWARE_V20 #OBP60 hardware revision V2.0
|
||||||
|
-D HARDWARE_V21 #OBP60 hardware revision V2.1
|
||||||
|
-D TFT_DISPLAY #Enable TFT LCD display path (instead of E-Ink)
|
||||||
|
# -D TFT_320x480_ST7796 #TFT panel type: ST7796 (320x480, 80 MHz), best performance
|
||||||
|
-D TFT_320x480_ILI9488 #TFT panel type: ILI9488 (320x480, 40 MHz), lower performance
|
||||||
|
-D OBP_TFT_ENABLE_SCALING=1 #TFT scaling on/off (1=scale to max Y=320, 0=no scaling)
|
||||||
|
-D OBP_TFT_SCALE_ANTIALIAS=1 #Antialiasing for TFT scaling (1=on, 0=off)
|
||||||
|
# -D ENABLE_PATCHES #enable patching of gateway code
|
||||||
|
${env.build_flags}
|
||||||
|
#CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default
|
||||||
|
upload_port = /dev/ttyACM0 #OBP60 download via USB-C direct
|
||||||
|
upload_protocol = esptool #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27
|
||||||
|
upload_speed = 230400
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
[env:obp60_s3]
|
[env:obp60_s3]
|
||||||
platform = espressif32@6.8.1
|
platform = espressif32@6.8.1
|
||||||
board_build.variants_dir = variants
|
board_build.variants_dir = variants
|
||||||
@@ -26,7 +83,7 @@ lib_deps =
|
|||||||
ESP32time
|
ESP32time
|
||||||
HTTPClient
|
HTTPClient
|
||||||
WiFiClientSecure
|
WiFiClientSecure
|
||||||
esphome/AsyncTCP-esphome@2.0.1
|
esphome/AsyncTCP-esphome@2.1.1
|
||||||
robtillaart/PCF8574@0.3.9
|
robtillaart/PCF8574@0.3.9
|
||||||
adafruit/Adafruit Unified Sensor @ 1.1.13
|
adafruit/Adafruit Unified Sensor @ 1.1.13
|
||||||
blemasle/MCP23017@2.0.0
|
blemasle/MCP23017@2.0.0
|
||||||
@@ -82,7 +139,7 @@ lib_deps =
|
|||||||
ESP32time
|
ESP32time
|
||||||
HTTPClient
|
HTTPClient
|
||||||
WiFiClientSecure
|
WiFiClientSecure
|
||||||
esphome/AsyncTCP-esphome@2.0.1
|
esphome/AsyncTCP-esphome@2.1.1
|
||||||
robtillaart/PCF8574@0.3.9
|
robtillaart/PCF8574@0.3.9
|
||||||
adafruit/Adafruit Unified Sensor @ 1.1.13
|
adafruit/Adafruit Unified Sensor @ 1.1.13
|
||||||
blemasle/MCP23017@2.0.0
|
blemasle/MCP23017@2.0.0
|
||||||
@@ -107,8 +164,8 @@ build_flags=
|
|||||||
-D HARDWARE_V10 #OBP40 hardware revision V1.0 SKU:DIE07300S V1.1 (CrowPanel 4.2)
|
-D HARDWARE_V10 #OBP40 hardware revision V1.0 SKU:DIE07300S V1.1 (CrowPanel 4.2)
|
||||||
-D DISPLAY_GDEY042T81 #new E-Ink display from Good Display (Waveshare), R10 2.2 ohm - good (contast lost by shunshine)
|
-D DISPLAY_GDEY042T81 #new E-Ink display from Good Display (Waveshare), R10 2.2 ohm - good (contast lost by shunshine)
|
||||||
#-D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good
|
#-D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good
|
||||||
#-D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh
|
-D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh
|
||||||
#-D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors
|
-D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors
|
||||||
#-D ENABLE_PATCHES #enable patching of gateway code
|
#-D ENABLE_PATCHES #enable patching of gateway code
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
upload_port = /dev/ttyUSB0 #OBP40 download via external USB/Serail converter
|
upload_port = /dev/ttyUSB0 #OBP40 download via external USB/Serail converter
|
||||||
|
|||||||
Regular → Executable
Regular → Executable
Regular → Executable
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script compile the software für OBP60-S3
|
||||||
|
|
||||||
|
# Attention! Start this cript only in the Gitpod Docker container.
|
||||||
|
# Start the script with: bash run
|
||||||
|
|
||||||
|
# Compile the firmware
|
||||||
|
echo "Compiling Firmware"
|
||||||
|
platformio run -e obp70_s3
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
static inline int64_t gwMonotonicUs(){
|
||||||
|
TickType_t ticks=xTaskGetTickCount();
|
||||||
|
return ((int64_t)ticks) * ((int64_t)portTICK_PERIOD_MS) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
class TimeAverage{
|
class TimeAverage{
|
||||||
double factor=0.3;
|
double factor=0.3;
|
||||||
double current=0;
|
double current=0;
|
||||||
@@ -70,7 +78,7 @@ class TimeMonitor{
|
|||||||
}
|
}
|
||||||
void reset(){
|
void reset(){
|
||||||
if (last != 0 && start != 0) loop->add(last-start);
|
if (last != 0 && start != 0) loop->add(last-start);
|
||||||
start=esp_timer_get_time();
|
start=gwMonotonicUs();
|
||||||
for (size_t i=0;i<len;i++) current[i]=0;
|
for (size_t i=0;i<len;i++) current[i]=0;
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
@@ -114,7 +122,7 @@ class TimeMonitor{
|
|||||||
for (size_t i=0;i<index;i++){
|
for (size_t i=0;i<index;i++){
|
||||||
if (current[i] != 0) sv=current[i];
|
if (current[i] != 0) sv=current[i];
|
||||||
}
|
}
|
||||||
int64_t now=esp_timer_get_time();
|
int64_t now=gwMonotonicUs();
|
||||||
last=now;
|
last=now;
|
||||||
current[index]=now;
|
current[index]=now;
|
||||||
int64_t currentv=now-sv;
|
int64_t currentv=now-sv;
|
||||||
|
|||||||
+4
-4
@@ -21,8 +21,8 @@ lib_deps =
|
|||||||
ttlappalainen_NMEA2000=https://github.com/wellenvogel/NMEA2000.git#20251126
|
ttlappalainen_NMEA2000=https://github.com/wellenvogel/NMEA2000.git#20251126
|
||||||
ttlappalainen/NMEA0183 @ 1.10.1
|
ttlappalainen/NMEA0183 @ 1.10.1
|
||||||
ArduinoJson @ 6.18.5
|
ArduinoJson @ 6.18.5
|
||||||
AsyncTCP-esphome @ 2.0.1
|
AsyncTCP-esphome @ 2.1.1
|
||||||
ottowinter/ESPAsyncWebServer-esphome@2.0.1
|
ottowinter/ESPAsyncWebServer-esphome@3.4.0
|
||||||
FS
|
FS
|
||||||
Preferences
|
Preferences
|
||||||
ESPmDNS
|
ESPmDNS
|
||||||
@@ -34,8 +34,8 @@ lib_deps=
|
|||||||
ttlappalainen_NMEA2000=symlink://../NMEA2000
|
ttlappalainen_NMEA2000=symlink://../NMEA2000
|
||||||
ttlappalainen/NMEA0183 @ 1.10.1
|
ttlappalainen/NMEA0183 @ 1.10.1
|
||||||
ArduinoJson @ 6.18.5
|
ArduinoJson @ 6.18.5
|
||||||
AsyncTCP-esphome @ 2.0.1
|
AsyncTCP-esphome @ 2.1.1
|
||||||
ottowinter/ESPAsyncWebServer-esphome@2.0.1
|
ottowinter/ESPAsyncWebServer-esphome@3.4.0
|
||||||
FS
|
FS
|
||||||
Preferences
|
Preferences
|
||||||
ESPmDNS
|
ESPmDNS
|
||||||
|
|||||||
+1
-1
@@ -767,7 +767,7 @@ void loopFunction(void *){
|
|||||||
//if(Serial1.available()) {}
|
//if(Serial1.available()) {}
|
||||||
//if(Serial.available()) {}
|
//if(Serial.available()) {}
|
||||||
//if(Serial2.available()) {}
|
//if(Serial2.available()) {}
|
||||||
//delay(1);
|
vTaskDelay(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const String USERPREFIX="/api/user/";
|
const String USERPREFIX="/api/user/";
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#ifndef Pins_Arduino_h
|
||||||
|
#define Pins_Arduino_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
#define USB_VID 0x303a
|
||||||
|
#define USB_PID 0x1001
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define EXTERNAL_NUM_INTERRUPTS 46
|
||||||
|
#define NUM_DIGITAL_PINS 48
|
||||||
|
#define NUM_ANALOG_INPUTS 20
|
||||||
|
|
||||||
|
// Multi Function Display OBP60 V2.0
|
||||||
|
static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT+48;
|
||||||
|
#define BUILTIN_LED LED_BUILTIN // backward compatibility
|
||||||
|
#define LED_BUILTIN LED_BUILTIN
|
||||||
|
#define RGB_BUILTIN LED_BUILTIN
|
||||||
|
#define RGB_BRIGHTNESS 64
|
||||||
|
|
||||||
|
#define analogInputToDigitalPin(p) (((p)<20)?(analogChannelToDigitalPin(p)):-1)
|
||||||
|
#define digitalPinToInterrupt(p) (((p)<48)?(p):-1)
|
||||||
|
#define digitalPinHasPWM(p) (p < 46)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint8_t TX = 43;
|
||||||
|
static const uint8_t RX = 44;
|
||||||
|
|
||||||
|
static const uint8_t SDA = 47;
|
||||||
|
static const uint8_t SCL = 21;
|
||||||
|
|
||||||
|
static const uint8_t SS = 39;
|
||||||
|
static const uint8_t MOSI = 48;
|
||||||
|
static const uint8_t MISO = 11;
|
||||||
|
static const uint8_t SCK = 38;
|
||||||
|
|
||||||
|
static const uint8_t A0 = 1;
|
||||||
|
static const uint8_t A1 = 2;
|
||||||
|
static const uint8_t A2 = 3;
|
||||||
|
static const uint8_t A3 = 4;
|
||||||
|
static const uint8_t A4 = 5;
|
||||||
|
static const uint8_t A5 = 6;
|
||||||
|
static const uint8_t A6 = 7;
|
||||||
|
static const uint8_t A7 = 8;
|
||||||
|
static const uint8_t A8 = 9;
|
||||||
|
static const uint8_t A9 = 10;
|
||||||
|
static const uint8_t A10 = 11;
|
||||||
|
static const uint8_t A11 = 12;
|
||||||
|
static const uint8_t A12 = 13;
|
||||||
|
static const uint8_t A13 = 14;
|
||||||
|
static const uint8_t A14 = 15;
|
||||||
|
static const uint8_t A15 = 16;
|
||||||
|
static const uint8_t A16 = 17;
|
||||||
|
static const uint8_t A17 = 18;
|
||||||
|
static const uint8_t A18 = 19;
|
||||||
|
static const uint8_t A19 = 20;
|
||||||
|
|
||||||
|
static const uint8_t T1 = 1;
|
||||||
|
static const uint8_t T2 = 2;
|
||||||
|
static const uint8_t T3 = 3;
|
||||||
|
static const uint8_t T4 = 4;
|
||||||
|
static const uint8_t T5 = 5;
|
||||||
|
static const uint8_t T6 = 6;
|
||||||
|
static const uint8_t T7 = 7;
|
||||||
|
static const uint8_t T8 = 8;
|
||||||
|
static const uint8_t T9 = 9;
|
||||||
|
static const uint8_t T10 = 10;
|
||||||
|
static const uint8_t T11 = 11;
|
||||||
|
static const uint8_t T12 = 12;
|
||||||
|
static const uint8_t T13 = 13;
|
||||||
|
static const uint8_t T14 = 14;
|
||||||
|
|
||||||
|
#endif /* Pins_Arduino_h */
|
||||||
Reference in New Issue
Block a user