From 9b9bf76e4d8bec7014db4ce8e5c028659ad11f30 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Mon, 3 Nov 2025 20:04:31 +0100 Subject: [PATCH 1/5] Added page anchor with background map --- lib/obp60task/PageAnchor.cpp | 402 ++++++++++++++++++++++++++++++++ lib/obp60task/config.json | 47 +++- lib/obp60task/config_obp40.json | 46 ++++ lib/obp60task/obp60task.cpp | 2 + lib/obp60task/platformio.ini | 4 + 5 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 lib/obp60task/PageAnchor.cpp diff --git a/lib/obp60task/PageAnchor.cpp b/lib/obp60task/PageAnchor.cpp new file mode 100644 index 0000000..11d3425 --- /dev/null +++ b/lib/obp60task/PageAnchor.cpp @@ -0,0 +1,402 @@ +// 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 + 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 + + Drop / raise function in device OBP40 has to be done inside + config mode because of limited number of buttons. + + TODO + gzip for data transfer, + 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) + +*/ + +#include +#include +#include "Pagedata.h" +#include "OBP60Extensions.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 + + //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; + double map_lat = 0; // current center of valid map + double map_lon = 0; + String server_name; // server with map service + 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; + + bool anchor_set = false; + double anchor_lat; + double anchor_lon; + double anchor_depth; + int anchor_ts; // time stamp anchor dropped + + void displayModeNormal(PageData &pageData) { + + // Boatvalues: DBS, HDT, AWS, AWD, LAT, LON, HDOP + GwApi::BoatValue *bv_dbs = pageData.values[0]; // DBS + String sval_dbs = formatValue(bv_dbs, *commonData).svalue; + String sunit_dbs = formatValue(bv_dbs, *commonData).unit; + GwApi::BoatValue *bv_hdt = pageData.values[1]; // HDT + String sval_hdt = formatValue(bv_hdt, *commonData).svalue; + GwApi::BoatValue *bv_aws = pageData.values[2]; // AWS + String sval_aws = formatValue(bv_aws, *commonData).svalue; + String sunit_aws = formatValue(bv_aws, *commonData).unit; + GwApi::BoatValue *bv_awd = pageData.values[3]; // AWD + String sval_awd = formatValue(bv_awd, *commonData).svalue; + GwApi::BoatValue *bv_lat = pageData.values[4]; // LAT + String sval_lat = formatValue(bv_lat, *commonData).svalue; + GwApi::BoatValue *bv_lon = pageData.values[5]; // LON + String sval_lon = formatValue(bv_lon, *commonData).svalue; + GwApi::BoatValue *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 + if (map_valid) { + 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 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 und dann Linien zeichnen + // TODO rotate boat according to current heading + if (bv_hdt->valid) { + drawPoly(rotatePoints(c, pts_boat, RadToDeg(bv_hdt->value)), commonData->fgcolor); + } else { + // no heading available draw north oriented + drawPoly(pts_boat, commonData->fgcolor); + } + + // Draw wind arrow + const std::vector 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("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(rhumb(map_lat, map_lon, bv_lat->value, bv_lon->value))); + } 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) + "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() { + + getdisplay().setTextColor(commonData->fgcolor); + getdisplay().setFont(&Ubuntu_Bold12pt8b); + getdisplay().setCursor(8, 48); + getdisplay().print("Anchor configuration"); + + } + +public: + PageAnchor(CommonData &common) + { + commonData = &common; + common.logger->logDebug(GwLog::LOG,"Instantiate PageAnchor"); + + server_name = common.config->getString(common.config->mapServer); + tile_path = common.config->getString(common.config->mapTilePath); + + lengthformat = common.config->getString(common.config->lengthFormat); + chain_length = common.config->getInt(common.config->chainLength); + + canvas = new GFXcanvas1(264, 260); // Byte aligned, no padding! + } + + void setupKeys(){ + Page::setupKeys(); + commonData->keydata[0].label = "MODE"; +#ifdef BOARD_OBP40S3 + commonData->keydata[1].label = "DROP"; +#endif +#ifdef BOARD_OBP60S3 + commonData->keydata[4].label = "DROP"; +#endif + } + + int handleKey(int key) { + if (key == 1) { // Switch between normal and config mode + if (mode == 'N') { + mode = 'C'; + commonData->keydata[1].label = "EDIT"; + } else { + mode = 'N'; + commonData->keydata[1].label = "ALARM"; + } + 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; + String url = "http://" + server_name + "/" + tile_path; + String parameter = "?lat=" + String(lat, 6) + "&lon=" + String(lon, 6)+ "&zoom=" + String(zoom) + + "&w=" + String(map_width) + "&h=" + String(map_height); + commonData->logger->logDebug(GwLog::LOG, "HTTP query: %s", String(url + parameter).c_str()); + http.begin(url + parameter); + int httpCode = http.GET(); + if (httpCode > 0) { + if (httpCode == HTTP_CODE_OK) { + WiFiClient* stream = http.getStreamPtr(); + int size = http.getSize(); + commonData->logger->logDebug(GwLog::LOG, "HTTP get size: %d", size); + // header: P4 (e.g. 11 byte) + uint8_t header[14]; // max: P4wwww wwww + bool header_read = false; + int header_size = 0; + uint8_t* buf = canvas->getBuffer(); + int n = 0; + int ix = 0; + 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); + } + 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; + } + + map_lat = bv_lat->value; // save for later comparison + map_lon = bv_lon->value; + map_valid = getBackgroundMap(map_lat, map_lon, zoom); + }; + + int displayPage(PageData &pageData) { + GwLog *logger = commonData->logger; + + // Logging boat values + 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(); + } + + 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 diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index f633762..5534391 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -19,6 +19,28 @@ "obp60": "true" } }, + { + "name": "mapServer", + "label": "map server", + "type": "string", + "default": "", + "description": "Server for converting map tiles. Use only one hostname or IP address", + "category": "wifi client", + "capabilities": { + "obp40": "true" + } + }, + { + "name": "mapTilePath", + "label": "map tile path", + "type": "string", + "default": "map.php", + "description": "Path to converter access e.g. index.php or map.php", + "category": "wifi client", + "capabilities": { + "obp40": "true" + } + }, { "name": "timeZone", "label": "Time Zone", @@ -75,6 +97,20 @@ "obp60":"true" } }, + { + "name": "chainLength", + "label": "Anchor Chain Length [m]", + "type": "number", + "default": "0", + "check": "checkMinMax", + "min": 0, + "max": 255, + "description": "The length of the anchor chain [0...255m]", + "category": "OBP60 Settings", + "capabilities": { + "obp60":"true" + } + }, { "name": "fuelTank", "label": "Fuel Tank [l]", @@ -1212,7 +1248,6 @@ "obp60":"true" } }, - { "name": "page1type", "label": "Type", @@ -1220,6 +1255,7 @@ "default": "Voltage", "description": "Type of page for page 1", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -1518,6 +1554,7 @@ "default": "WindRose", "description": "Type of page for page 2", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -1808,6 +1845,7 @@ "default": "OneValue", "description": "Type of page for page 3", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -2090,6 +2128,7 @@ "default": "TwoValues", "description": "Type of page for page 4", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -2364,6 +2403,7 @@ "default": "ThreeValues", "description": "Type of page for page 5", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -2630,6 +2670,7 @@ "default": "FourValues", "description": "Type of page for page 6", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -2888,6 +2929,7 @@ "default": "FourValues2", "description": "Type of page for page 7", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -3138,6 +3180,7 @@ "default": "Clock", "description": "Type of page for page 8", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -3380,6 +3423,7 @@ "default": "RollPitch", "description": "Type of page for page 9", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -3614,6 +3658,7 @@ "default": "Battery2", "description": "Type of page for page 10", "list": [ + "Anchor", "BME280", "Battery", "Battery2", diff --git a/lib/obp60task/config_obp40.json b/lib/obp60task/config_obp40.json index 9addd67..bd92fb5 100644 --- a/lib/obp60task/config_obp40.json +++ b/lib/obp60task/config_obp40.json @@ -19,6 +19,28 @@ "obp40": "true" } }, + { + "name": "mapServer", + "label": "map server", + "type": "string", + "default": "", + "description": "Server for converting map tiles. Use only one hostname or IP address", + "category": "wifi client", + "capabilities": { + "obp40": "true" + } + }, + { + "name": "mapTilePath", + "label": "map tile path", + "type": "string", + "default": "map.php", + "description": "Path to converter access e.g. index.php or map.php", + "category": "wifi client", + "capabilities": { + "obp40": "true" + } + }, { "name": "timeZone", "label": "Time Zone", @@ -75,6 +97,20 @@ "obp40": "true" } }, + { + "name": "chainLength", + "label": "Anchor Chain Length [m]", + "type": "number", + "default": "0", + "check": "checkMinMax", + "min": 0, + "max": 255, + "description": "The length of the anchor chain [0...255m]", + "category": "OBP40 Settings", + "capabilities": { + "obp40":"true" + } + }, { "name": "fuelTank", "label": "Fuel Tank [l]", @@ -1243,6 +1279,7 @@ "default": "Voltage", "description": "Type of page for page 1", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -1571,6 +1608,7 @@ "default": "WindRose", "description": "Type of page for page 2", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -1890,6 +1928,7 @@ "default": "OneValue", "description": "Type of page for page 3", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -2200,6 +2239,7 @@ "default": "TwoValues", "description": "Type of page for page 4", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -2501,6 +2541,7 @@ "default": "ThreeValues", "description": "Type of page for page 5", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -2793,6 +2834,7 @@ "default": "FourValues", "description": "Type of page for page 6", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -3076,6 +3118,7 @@ "default": "FourValues2", "description": "Type of page for page 7", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -3350,6 +3393,7 @@ "default": "Clock", "description": "Type of page for page 8", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -3615,6 +3659,7 @@ "default": "RollPitch", "description": "Type of page for page 9", "list": [ + "Anchor", "BME280", "Battery", "Battery2", @@ -3871,6 +3916,7 @@ "default": "Battery2", "description": "Type of page for page 10", "list": [ + "Anchor", "BME280", "Battery", "Battery2", diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index b1c0e01..f6d1695 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -260,6 +260,8 @@ void registerAllPages(PageList &list){ list.add(®isterPageFluid); extern PageDescription registerPageSkyView; list.add(®isterPageSkyView); + extern PageDescription registerPageAnchor; + list.add(®isterPageAnchor); } // Undervoltage detection for shutdown display diff --git a/lib/obp60task/platformio.ini b/lib/obp60task/platformio.ini index 16cd23c..7b8e2e1 100644 --- a/lib/obp60task/platformio.ini +++ b/lib/obp60task/platformio.ini @@ -41,6 +41,8 @@ lib_deps = milesburton/DallasTemperature@3.11.0 signetica/SunRise@2.0.2 adafruit/Adafruit FRAM I2C@2.0.3 + WifiClientSecure + HTTPClient 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) @@ -93,6 +95,8 @@ lib_deps = milesburton/DallasTemperature@3.11.0 signetica/SunRise@2.0.2 adafruit/Adafruit FRAM I2C@2.0.3 + WifiClientSecure + HTTPClient build_flags= -D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib -D BOARD_OBP40S3 #Board OBP40 with ESP32S3 From 5b477331de4e65f82a2d548c73782c7e3df4368a Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Wed, 5 Nov 2025 20:02:36 +0100 Subject: [PATCH 2/5] More work on anchor page --- lib/obp60task/PageAnchor.cpp | 55 +++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/lib/obp60task/PageAnchor.cpp b/lib/obp60task/PageAnchor.cpp index 11d3425..cd28179 100644 --- a/lib/obp60task/PageAnchor.cpp +++ b/lib/obp60task/PageAnchor.cpp @@ -68,7 +68,7 @@ private: uint8_t alarm_range; uint8_t chain_length; - uint8_t chain; + uint8_t chain = 0; bool anchor_set = false; double anchor_lat; @@ -100,7 +100,22 @@ private: 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; + 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); } @@ -123,9 +138,16 @@ private: //rotatePoints und dann Linien zeichnen // TODO rotate boat according to current heading if (bv_hdt->valid) { + if (map_valid) { + Point b1 = rotatePoint(c, {b.x, b.y - 8}, RadToDeg(bv_hdt->value)); + getdisplay().fillCircle(b1.x, b1.y, 10, commonData->bgcolor); + } drawPoly(rotatePoints(c, pts_boat, RadToDeg(bv_hdt->value)), 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); } @@ -161,9 +183,9 @@ private: // Corner values getdisplay().setFont(&Ubuntu_Bold8pt8b); getdisplay().setCursor(8, 54); - getdisplay().print("Ready"); // Anchor state + getdisplay().print(anchor_set ? "Dropped" : "Ready"); // Anchor state getdisplay().setCursor(8, 72); - getdisplay().print("Alarm: "); // Alarm state + getdisplay().print("Alarm: "); // Alarm state getdisplay().print(alarm_enabled ? "on" : "off"); getdisplay().setCursor(8, 120); @@ -175,7 +197,7 @@ private: getdisplay().print("diff"); getdisplay().setCursor(8, 176); if (map_valid and bv_lat->valid and bv_lon->valid) { - getdisplay().print(String(rhumb(map_lat, map_lon, bv_lat->value, bv_lon->value))); + getdisplay().print(String(posdiff)); } else { getdisplay().print("n/a"); } @@ -216,7 +238,7 @@ private: 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) + "m"); + 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, @@ -259,6 +281,7 @@ public: #endif } + // TODO OBP40 / OBP60 different handling int handleKey(int key) { if (key == 1) { // Switch between normal and config mode if (mode == 'N') { @@ -266,10 +289,20 @@ public: commonData->keydata[1].label = "EDIT"; } else { mode = 'N'; - commonData->keydata[1].label = "ALARM"; +#ifdef BOARD_OBP40S3 + 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) { + anchor_set = !anchor_set; + commonData->keydata[1].label = anchor_set ? "RAISE": "DROP"; + return 0; + } // Code for keylock if (key == 11){ commonData->keylock = !commonData->keylock; @@ -301,9 +334,11 @@ public: HTTPClient http; String url = "http://" + server_name + "/" + tile_path; String parameter = "?lat=" + String(lat, 6) + "&lon=" + String(lon, 6)+ "&zoom=" + String(zoom) - + "&w=" + String(map_width) + "&h=" + String(map_height); + + "&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.SetAcceptEncoding("gzip"); + // TODO miniz.c from ROM int httpCode = http.GET(); if (httpCode > 0) { if (httpCode == HTTP_CODE_OK) { @@ -342,6 +377,7 @@ public: } else { commonData->logger->logDebug(GwLog::ERROR, "HTTP error #%d", httpCode); } + http.end(); return valid; } @@ -359,6 +395,11 @@ public: 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); + } }; int displayPage(PageData &pageData) { From 04dc09e44ab08e2ef477224ab7fcc2d6816b3979 Mon Sep 17 00:00:00 2001 From: Norbert Walter Date: Mon, 9 Feb 2026 15:25:27 +0100 Subject: [PATCH 3/5] Fix directory path for tool installation --- lib/obp60task/run_install_tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/obp60task/run_install_tools b/lib/obp60task/run_install_tools index 9c4667e..c46c10c 100644 --- a/lib/obp60task/run_install_tools +++ b/lib/obp60task/run_install_tools @@ -8,6 +8,6 @@ # Install tools echo "Installing tools" -cd /workspace/esp32-nmea2000 +cd /workspaces/esp32-nmea2000 pip3 install -U esptool pip3 install platformio From fc5daaba3788382ba3f608420a76fd5d8c5750ab Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Mon, 9 Feb 2026 22:31:07 +0100 Subject: [PATCH 4/5] - change control of key settings on PageOneValue + PageTwoValues - added taskYIELD() to chart loop to be nice to other tasks - fix typo in config_obp40.json - fine tune chart labels - disable debug messages --- lib/obp60task/OBPDataOperations.cpp | 22 +++++++++++----------- lib/obp60task/OBPcharts.cpp | 24 +++++++++++++----------- lib/obp60task/PageOneValue.cpp | 17 +++++++++++------ lib/obp60task/PageTwoValues.cpp | 21 +++++++++++++-------- lib/obp60task/PageWindPlot.cpp | 4 ++-- lib/obp60task/config_obp40.json | 20 ++++++++++---------- 6 files changed, 60 insertions(+), 48 deletions(-) diff --git a/lib/obp60task/OBPDataOperations.cpp b/lib/obp60task/OBPDataOperations.cpp index 562b1f6..c968e11 100644 --- a/lib/obp60task/OBPDataOperations.cpp +++ b/lib/obp60task/OBPDataOperations.cpp @@ -135,7 +135,7 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue) double dataValue = 0; std::string format = ""; - // we test this earlier, but for safety reason ... + // we test this earlier, but for safety reasons ... if (calibrationMap.find(instance) == calibrationMap.end()) { LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s not in calibration list", instance.c_str()); return false; @@ -151,7 +151,7 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue) slope = calibrationMap[instance].slope; dataValue = boatDataValue->value; format = boatDataValue->getFormat().c_str(); - LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: value: %f, format: %s", instance.c_str(), dataValue, format.c_str()); + // LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: value: %f, format: %s", instance.c_str(), dataValue, format.c_str()); if (format == "formatWind") { // instance is of type angle dataValue = (dataValue * slope) + offset; @@ -174,7 +174,7 @@ bool CalibrationData::calibrateInstance(GwApi::BoatValue* boatDataValue) calibrationMap[instance].value = dataValue; // store the calibrated value in the list calibrationMap[instance].isCalibrated = true; - LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: Offset: %f, Slope: %f, Result: %f", instance.c_str(), offset, slope, calibrationMap[instance].value); + // LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: Offset: %f, Slope: %f, Result: %f", instance.c_str(), offset, slope, calibrationMap[instance].value); return true; } @@ -189,7 +189,7 @@ bool CalibrationData::smoothInstance(GwApi::BoatValue* boatDataValue) // we test this earlier, but for safety reason ... if (calibrationMap.find(instance) == calibrationMap.end()) { - LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s not in calibration list", instance.c_str()); + // LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s not in calibration list", instance.c_str()); return false; } @@ -211,7 +211,7 @@ bool CalibrationData::smoothInstance(GwApi::BoatValue* boatDataValue) calibrationMap[instance].value = dataValue; // store the smoothed value in the list calibrationMap[instance].isCalibrated = true; - LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: smooth: %f, oldValue: %f, result: %f", instance.c_str(), smoothFactor, oldValue, calibrationMap[instance].value); + // LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: smooth: %f, oldValue: %f, result: %f", instance.c_str(), smoothFactor, oldValue, calibrationMap[instance].value); return true; } @@ -241,7 +241,7 @@ void HstryBuf::add(double value) { if (value >= hstryMin && value <= hstryMax) { hstryBuf.add(value); - LOG_DEBUG(GwLog::DEBUG, "HstryBuf::add: name: %s, value: %.3f", hstryBuf.getName(), value); + // LOG_DEBUG(GwLog::DEBUG, "HstryBuf::add: name: %s, value: %.3f", hstryBuf.getName(), value); } } @@ -405,7 +405,7 @@ void WindUtils::calcTwdSA(const double* AWA, const double* AWS, double stw = -*STW; addPolar(AWD, AWS, CTW, &stw, TWD, TWS); - // Normalize TWD and TWA to 0-360°/2PI + // Normalize TWD to [0..360°] (2PI) and TWA to [-180..180] (PI) *TWD = to2PI(*TWD); *TWA = toPI(*TWD - *HDT); } @@ -487,8 +487,8 @@ bool WindUtils::addWinds() double hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX; double hdmVal = hdmBVal->valid ? hdmBVal->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, - 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); + //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, + // 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); // Check if TWD can be calculated from TWA and HDT/HDM if (twaBVal->valid) { @@ -528,8 +528,8 @@ bool WindUtils::addWinds() } } } - 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); + // 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; } diff --git a/lib/obp60task/OBPcharts.cpp b/lib/obp60task/OBPcharts.cpp index e15fc83..8ae38ec 100644 --- a/lib/obp60task/OBPcharts.cpp +++ b/lib/obp60task/OBPcharts.cpp @@ -161,8 +161,8 @@ bool Chart::setChartDimensions(const char direction, const int8_t size) break; } } - LOG_DEBUG(GwLog::ERROR, "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", - dataBuf.getName(), direction, size, dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, top, bottom, hGap, vGap); + //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", + // dataBuf.getName(), direction, size, dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, top, bottom, hGap, vGap); return true; } @@ -176,7 +176,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); calcChrtBorders(chrtMin, chrtMid, chrtMax, chrtRng); 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", chrtMin, chrtMid, chrtMax, chrtRng); // Do we have valid buffer data? if (dataBuf.getMax() == dbMAX_VAL) { // only values in buffer -> no valid wind data available @@ -261,8 +261,8 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub } recalcRngMid = false; // Reset flag for determination - LOG_DEBUG(GwLog::DEBUG, "calcChrtRange: rngMin: %.1f°, rngMid: %.1f°, rngMax: %.1f°, rng: %.1f°, rngStep: %.1f°", rngMin * RAD_TO_DEG, rngMid * RAD_TO_DEG, rngMax * RAD_TO_DEG, - rng * RAD_TO_DEG, rngStep * RAD_TO_DEG); + // LOG_DEBUG(GwLog::DEBUG, "calcChrtRange: rngMin: %.1f°, rngMid: %.1f°, rngMax: %.1f°, rng: %.1f°, rngStep: %.1f°", rngMin * RAD_TO_DEG, rngMid * RAD_TO_DEG, rngMax * RAD_TO_DEG, + // rng * RAD_TO_DEG, rngStep * RAD_TO_DEG); } } @@ -287,8 +287,8 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub 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, - tmpRng * RAD_TO_DEG, rng * RAD_TO_DEG, rngStep * 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); } else { // chart data is of any other type @@ -320,8 +320,8 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub rngMid = (rngMin + rngMax) / 2.0; 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", - currMinVal, currMaxVal, rngMin, rngMid, rngMax, rng, rngStep, zeroValue, dbMIN_VAL); + // 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); } } @@ -397,6 +397,8 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do } break; } + + taskYIELD(); // we run for 50-150ms; be polite to other tasks with same priority } } @@ -656,7 +658,7 @@ void Chart::prntHorizChartThreeValueAxisLabel(const GFXfont* font) if (font == &Ubuntu_Bold10pt8b) { xOffset = 39; - yOffset = 15; + yOffset = 16; } else if (font == &Ubuntu_Bold12pt8b) { xOffset = 51; yOffset = 18; @@ -718,7 +720,7 @@ void Chart::prntHorizChartMultiValueAxisLabel(const GFXfont* font) axSlots = valAxis / static_cast(VALAXIS_STEP); // number of axis labels (and we want to have a double calculation, no integer) axIntv = chrtRng / axSlots; axLabel = chrtMin + axIntv; - 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); + // 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); int loopStrt, loopEnd, loopStp; if (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE || chrtDataFmt == OTHER) { diff --git a/lib/obp60task/PageOneValue.cpp b/lib/obp60task/PageOneValue.cpp index a5c0f9c..78cc6e6 100644 --- a/lib/obp60task/PageOneValue.cpp +++ b/lib/obp60task/PageOneValue.cpp @@ -162,9 +162,13 @@ public: constexpr int ZOOM_KEY = 1; #endif - if (dataHstryBuf) { // show "Mode" key only if chart supported boat data type is available + 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"; + if (pageMode != VALUE) { // show "ZOOM" key only if chart is visible + commonData->keydata[ZOOM_KEY].label = "ZOOM"; + } else { + commonData->keydata[ZOOM_KEY].label = ""; + } } else { commonData->keydata[0].label = ""; commonData->keydata[ZOOM_KEY].label = ""; @@ -189,14 +193,15 @@ public: pageMode = VALUE; break; } + setupKeys(); // Adjust key definition depending on and chart-supported boat data type return 0; // Commit the key } - // Set time frame to show for history chart + // Set time frame to show for chart #if defined BOARD_OBP60S3 - if (key == 5) { + if (key == 5 && pageMode != VALUE) { #elif defined BOARD_OBP40S3 - if (key == 2) { + if (key == 2 && pageMode != VALUE) { #endif if (dataIntv == 1) { dataIntv = 2; @@ -247,7 +252,7 @@ public: } } - setupKeys(); // adjust key depending on chart supported boat data type + setupKeys(); // Adjust key definition depending on and chart-supported boat data type } int displayPage(PageData& pageData) diff --git a/lib/obp60task/PageTwoValues.cpp b/lib/obp60task/PageTwoValues.cpp index 323cbcb..5990656 100644 --- a/lib/obp60task/PageTwoValues.cpp +++ b/lib/obp60task/PageTwoValues.cpp @@ -114,7 +114,7 @@ private: } if (numValues == 2 && mode == FULL) { // print line only, if we want to show 2 data values - getdisplay().fillRect(0, 145, width, 3, commonData->fgcolor); // Horizontal line 3 pix + getdisplay().fillRect(0, 145, width, 3, commonData->fgcolor); // Horizontal line 3 pix } } @@ -149,7 +149,11 @@ public: if (dataHstryBuf[0] || dataHstryBuf[1]) { // show "Mode" key only if at least 1 chart supported boat data type is available commonData->keydata[0].label = "MODE"; - commonData->keydata[ZOOM_KEY].label = "ZOOM"; + if (pageMode != VALUES) { // show "ZOOM" key only if chart is visible + commonData->keydata[ZOOM_KEY].label = "ZOOM"; + } else { + commonData->keydata[ZOOM_KEY].label = ""; + } } else { commonData->keydata[0].label = ""; commonData->keydata[ZOOM_KEY].label = ""; @@ -191,14 +195,15 @@ public: pageMode = VALUES; break; } + setupKeys(); // Adjust key definition depending on and chart-supported boat data type return 0; // Commit the key } - // Set time frame to show for history chart + // Set time frame to show for chart #if defined BOARD_OBP60S3 - if (key == 5) { + if (key == 5 && pageMode != VALUES) { #elif defined BOARD_OBP40S3 - if (key == 2) { + if (key == 2 && pageMode != VALUES) { #endif if (dataIntv == 1) { dataIntv = 2; @@ -251,7 +256,7 @@ public: } } - setupKeys(); // adjust key depending on chart supported boat data type + setupKeys(); // Adjust key definition depending on and chart-supported boat data type } int displayPage(PageData& pageData) @@ -285,13 +290,13 @@ public: showData(bValue, FULL); } else if (pageMode == VAL1_CHART) { // show data value 1 and chart - showData({bValue[0]}, HALF); + showData({ bValue[0] }, HALF); if (dataChart[0]) { dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[0]); } } else if (pageMode == VAL2_CHART) { // show data value 2 and chart - showData({bValue[1]}, HALF); + showData({ bValue[1] }, HALF); if (dataChart[1]) { dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[1]); } diff --git a/lib/obp60task/PageWindPlot.cpp b/lib/obp60task/PageWindPlot.cpp index f5745f7..da948c2 100644 --- a/lib/obp60task/PageWindPlot.cpp +++ b/lib/obp60task/PageWindPlot.cpp @@ -196,7 +196,7 @@ public: int displayPage(PageData& pageData) { LOG_DEBUG(GwLog::LOG, "Display PageWindPlot"); - ulong pageTime = millis(); + // ulong pageTime = millis(); if (showTruW != oldShowTruW) { @@ -243,7 +243,7 @@ public: } } - LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: page time %ldms", millis() - pageTime); + // LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: page time %ldms", millis() - pageTime); return PAGE_UPDATE; } }; diff --git a/lib/obp60task/config_obp40.json b/lib/obp60task/config_obp40.json index 54a5409..4e75006 100644 --- a/lib/obp60task/config_obp40.json +++ b/lib/obp60task/config_obp40.json @@ -1818,7 +1818,7 @@ "description": "Wind source for page 1: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 1", "capabilities": { @@ -2140,7 +2140,7 @@ "description": "Wind source for page 2: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 2", "capabilities": { @@ -2453,7 +2453,7 @@ "description": "Wind source for page 3: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 3", "capabilities": { @@ -2757,7 +2757,7 @@ "description": "Wind source for page 4: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 4", "capabilities": { @@ -3052,7 +3052,7 @@ "description": "Wind source for page 5: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 5", "capabilities": { @@ -3338,7 +3338,7 @@ "description": "Wind source for page 6: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 6", "capabilities": { @@ -3615,7 +3615,7 @@ "description": "Wind source for page 7: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 7", "capabilities": { @@ -3883,7 +3883,7 @@ "description": "Wind source for page 8: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 8", "capabilities": { @@ -4142,7 +4142,7 @@ "description": "Wind source for page 9: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 9", "capabilities": { @@ -4392,7 +4392,7 @@ "description": "Wind source for page 10: [true|apparent]", "list": [ "True wind", - "apparent wind" + "Apparent wind" ], "category": "OBP40 Page 10", "capabilities": { From 0363ba4379d4eb2d11acca34b076fc9555f49911 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Sun, 15 Feb 2026 13:13:19 +0100 Subject: [PATCH 5/5] Add feature to optionally apply patches to gateway code --- lib/obp60task/extra_task.py | 30 ++++++++++++++++++++++++++++-- lib/obp60task/platformio.ini | 2 ++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/obp60task/extra_task.py b/lib/obp60task/extra_task.py index 28e9980..66ee3b1 100644 --- a/lib/obp60task/extra_task.py +++ b/lib/obp60task/extra_task.py @@ -1,12 +1,21 @@ # PlatformIO extra script for obp60task +import subprocess + +patching = False + epdtype = "unknown" pcbvers = "unknown" for x in env["BUILD_FLAGS"]: - if x.startswith("-D HARDWARE_"): + if not x.startswith('-D'): + continue + opt = x[2:].strip() + if opt.startswith("HARDWARE_"): pcbvers = x.split('_')[1] - if x.startswith("-D DISPLAY_"): + elif opt.startswith("DISPLAY_"): epdtype = x.split('_')[1] + elif opt == 'ENABLE_PATCHES': + patching = True propfilename = os.path.join(env["PROJECT_LIBDEPS_DIR"], env["PIOENV"], "GxEPD2/library.properties") properties = {} @@ -28,3 +37,20 @@ except: env["CPPDEFINES"].extend([("BOARD", env["BOARD"]), ("EPDTYPE", epdtype), ("PCBVERS", pcbvers), ("GXEPD2VERS", gxepd2vers)]) print("added hardware info to CPPDEFINES") + +if patching: + # apply patches to gateway code + print("applying gateway patches") + patchdir = os.path.join(os.path.dirname(script), "patches") + if not os.path.isdir(patchdir): + print("patchdir not found, no patches applied") + else: + patchfiles = [f for f in os.listdir(patchdir)] + for p in patchfiles: + patch = os.path.join(patchdir, p) + print(f"applying {patch}") + res = subprocess.run(["git", "apply", patch], capture_output=True, text=True) + if res.returncode != 0: + print(res.stderr) + else: + print("no patches found") diff --git a/lib/obp60task/platformio.ini b/lib/obp60task/platformio.ini index 63468c3..03a5463 100644 --- a/lib/obp60task/platformio.ini +++ b/lib/obp60task/platformio.ini @@ -58,6 +58,7 @@ build_flags= # -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm - medium # -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm - bad (burn in effects) # -D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good +# -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 @@ -108,6 +109,7 @@ build_flags= #-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 VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors + #-D ENABLE_PATCHES #enable patching of gateway code ${env.build_flags} upload_port = /dev/ttyUSB0 #OBP40 download via external USB/Serail converter 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