mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-02-24 20:53:07 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 608b782b43 | |||
| 4ab3749028 | |||
| 47d9d9f5ca | |||
| 0b02e5b54c | |||
| 48b9380724 | |||
| 5b477331de | |||
| 9b9bf76e4d |
@@ -2,9 +2,6 @@
|
||||
#define _GWWIFI_H
|
||||
#include <WiFi.h>
|
||||
#include <GWConfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
class GwWifi{
|
||||
private:
|
||||
const GwConfigHandler *config;
|
||||
@@ -19,19 +16,13 @@ class GwWifi{
|
||||
bool apActive=false;
|
||||
bool fixedApPass=true;
|
||||
bool clientIsConnected=false;
|
||||
SemaphoreHandle_t wifiMutex=nullptr;
|
||||
static const TickType_t WIFI_MUTEX_TIMEOUT=pdMS_TO_TICKS(1000);
|
||||
bool acquireMutex();
|
||||
void releaseMutex();
|
||||
public:
|
||||
const char *AP_password = "esp32nmea2k";
|
||||
GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true);
|
||||
~GwWifi();
|
||||
void setup();
|
||||
void loop();
|
||||
bool clientConnected();
|
||||
bool connectClient(); // Blocking version
|
||||
bool connectClientAsync(); // Non-blocking version for other tasks
|
||||
bool connectClient();
|
||||
String apIP();
|
||||
bool isApActive(){return apActive;}
|
||||
bool isClientActive(){return wifiClient->asBoolean();}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <esp_wifi.h>
|
||||
#include "GWWifi.h"
|
||||
|
||||
|
||||
GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
|
||||
this->config=config;
|
||||
this->logger=log;
|
||||
@@ -8,28 +9,6 @@ GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
|
||||
wifiSSID=config->getConfigItem(config->wifiSSID,true);
|
||||
wifiPass=config->getConfigItem(config->wifiPass,true);
|
||||
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(){
|
||||
LOG_DEBUG(GwLog::LOG,"Wifi setup");
|
||||
@@ -106,14 +85,8 @@ bool GwWifi::connectInternal(){
|
||||
if (wifiClient->asBoolean()){
|
||||
clientIsConnected=false;
|
||||
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
|
||||
wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString());
|
||||
releaseMutex();
|
||||
LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt);
|
||||
lastConnectStart=millis();
|
||||
return true;
|
||||
@@ -131,20 +104,8 @@ void GwWifi::loop(){
|
||||
if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now)
|
||||
{
|
||||
LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString());
|
||||
|
||||
// CRITICAL SECTION: WiFi-Operationen müssen serialisiert werden
|
||||
if (acquireMutex()){
|
||||
WiFi.disconnect(true);
|
||||
delay(300);
|
||||
esp_wifi_stop();
|
||||
delay(100);
|
||||
esp_wifi_start();
|
||||
releaseMutex();
|
||||
connectInternal();
|
||||
}
|
||||
else{
|
||||
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop");
|
||||
}
|
||||
WiFi.disconnect();
|
||||
connectInternal();
|
||||
}
|
||||
}
|
||||
else{
|
||||
@@ -165,42 +126,11 @@ void GwWifi::loop(){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GwWifi::clientConnected(){
|
||||
// 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;
|
||||
return WiFi.status() == WL_CONNECTED;
|
||||
};
|
||||
|
||||
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();
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#include "NetworkClient.h"
|
||||
#include "GWWifi.h" // WiFi management (thread-safe)
|
||||
|
||||
extern GwWifi gwWifi; // Extern declaration of global WiFi instance
|
||||
|
||||
extern "C" {
|
||||
#include "puff.h"
|
||||
@@ -54,13 +51,8 @@ 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)
|
||||
uint8_t* buffer = (uint8_t*)malloc(capacity);
|
||||
|
||||
if (!gwWifi.clientConnected()) {
|
||||
if (DEBUGING) {Serial.println("No WiFi connection");}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
if (DEBUGING) {Serial.println("Malloc failed buffer");}
|
||||
if (DEBUG) {Serial.println("Malloc failed (buffer");}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,7 +106,7 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
||||
len += read;
|
||||
lastData = millis();
|
||||
|
||||
if (DEBUGING) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
||||
if (DEBUG) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
||||
|
||||
if (len < 20) continue; // Not enough data for header
|
||||
|
||||
@@ -130,7 +122,7 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
||||
|
||||
int res = puff(test, &testLen, buffer + headerOffset, &srcLen);
|
||||
if (res == 0) {
|
||||
if (DEBUGING) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
||||
if (DEBUG) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
||||
outData = test;
|
||||
outLen = testLen;
|
||||
complete = true;
|
||||
@@ -175,7 +167,7 @@ bool NetworkClient::fetchAndDecompressJson(const String& url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DEBUGING) {Serial.println("JSON OK!");}
|
||||
if (DEBUG) {Serial.println("JSON OK!");}
|
||||
_valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#define DEBUGING false // Debug flag for NetworkClient for more live information
|
||||
#define DEBUG false // Debug flag for NetworkClient for more live information
|
||||
#define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted)
|
||||
#define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection
|
||||
#define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack
|
||||
|
||||
@@ -923,7 +923,7 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor){
|
||||
|
||||
}
|
||||
|
||||
// Generator graphic
|
||||
// Generator graphic with fill level
|
||||
void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
||||
// Show battery
|
||||
int xb = x; // X position
|
||||
@@ -940,74 +940,6 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
||||
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;
|
||||
getdisplay().getTextBounds(lbl, 0, 0, &bx, &by, &bw, &bh);
|
||||
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
|
||||
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request) {
|
||||
|
||||
@@ -128,10 +128,6 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor); // S
|
||||
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic
|
||||
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);
|
||||
|
||||
// Icons
|
||||
|
||||
@@ -371,7 +371,7 @@ void sensorTask(void *param){
|
||||
GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP);
|
||||
GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop};
|
||||
|
||||
// Internal iRTC with NTP init
|
||||
// Internal RTC with NTP init
|
||||
ESP32Time rtc(0);
|
||||
if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") {
|
||||
GwApi::Status status;
|
||||
@@ -432,17 +432,17 @@ void sensorTask(void *param){
|
||||
|
||||
iRTC RTC GPS N2K
|
||||
0 0 0 (1)
|
||||
0 0 (1) X
|
||||
0 (1) 0 X
|
||||
0 1 <-(1) X
|
||||
(1) 0 0 X
|
||||
1 0 (1) X
|
||||
1 ->(1) 0 X
|
||||
1 1 <-(1) X
|
||||
0 0 (1) (X)
|
||||
0 (1) 0 (X)
|
||||
0 1 <-(1) (X)
|
||||
(1) 0 0 (X)
|
||||
1 0 (1) (X)
|
||||
1 ->(1) 0 (X)
|
||||
1 1 <-(1) (X)
|
||||
|
||||
*/
|
||||
|
||||
// If RTC DS1388 ready, then copy iRTC and GPS data to RTC all 1 min
|
||||
// If RTC DS1388 ready, then copy iRTC and GPS data to RTC all 1min
|
||||
if(millis() > starttime11 + 1*60*1000){
|
||||
starttime11 = millis();
|
||||
// Set RTC chip via iRTC (NTP)
|
||||
@@ -475,7 +475,7 @@ void sensorTask(void *param){
|
||||
// Adjust RTC time as unix time value
|
||||
ds1388.adjust(adjusttime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set RTC chip via N2K or 183 in case the internal GPS is off (only one time)
|
||||
@@ -524,7 +524,7 @@ void sensorTask(void *param){
|
||||
// N2K sysTime is double in n2klib
|
||||
double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second();
|
||||
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+1, 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, 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);
|
||||
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
|
||||
api->sendN2kMessage(N2kMsg);
|
||||
@@ -533,26 +533,25 @@ void sensorTask(void *param){
|
||||
}
|
||||
// Send date and time from software RTC (iRTC)
|
||||
if (iRTC_ready == true && RTC_ready == false && GPS_ready == false) {
|
||||
sensors.rtcTime = rtc.getTimeStruct();
|
||||
|
||||
// Use internal RTC feature
|
||||
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};
|
||||
int year = sensors.rtcTime.tm_year + 1900;
|
||||
int month = sensors.rtcTime.tm_mon;
|
||||
int day = sensors.rtcTime.tm_mday;
|
||||
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))) {
|
||||
uint16_t switchYear = ((sensors.rtcTime.tm_year-1)-1968)/4 - ((sensors.rtcTime.tm_year-1)-1900)/100 + ((sensors.rtcTime.tm_year-1)-1600)/400;
|
||||
long daysAt1970 = (sensors.rtcTime.tm_year-1970)*365 + switchYear + daysOfYear[sensors.rtcTime.tm_mon-1] + sensors.rtcTime.tm_mday-1;
|
||||
// If switch year then add one day
|
||||
if ((sensors.rtcTime.tm_mon > 2) && (sensors.rtcTime.tm_year % 4 == 0 && (sensors.rtcTime.tm_year % 100 != 0 || sensors.rtcTime.tm_year % 400 == 0))) {
|
||||
daysAt1970 += 1;
|
||||
}
|
||||
double sysTime = sensors.rtcTime.tm_hour * 3600.0 + sensors.rtcTime.tm_min * 60.0 + 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);
|
||||
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
||||
SetN2kPGN126992(N2kMsg, 0, daysAt1970, sysTime, N2ktimes_LocalCrystalClock);
|
||||
api->sendN2kMessage(N2kMsg);
|
||||
// N2K sysTime is double in n2klib
|
||||
double sysTime = (sensors.rtcTime.tm_hour * 3600) + (sensors.rtcTime.tm_min * 60) + 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);
|
||||
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
|
||||
api->sendN2kMessage(N2kMsg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Send 1Wire data for all temperature sensors to N2K all 2s
|
||||
|
||||
497
lib/obp60task/PageAnchor.cpp
Normal file
497
lib/obp60task/PageAnchor.cpp
Normal file
@@ -0,0 +1,497 @@
|
||||
// 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)
|
||||
|
||||
Map service options / URL parameters
|
||||
- mandatory
|
||||
lat: latitude
|
||||
lon: longitude
|
||||
width: image width in px
|
||||
height: image height in px
|
||||
- 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
|
||||
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"
|
||||
|
||||
#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;
|
||||
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
|
||||
|
||||
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
|
||||
// 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 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}, 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, 250);
|
||||
getdisplay().print("Press MODE 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());
|
||||
|
||||
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, 128);
|
||||
getdisplay().printf("No valid position: background map disabled");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// TODO OBP40 / OBP60 different handling
|
||||
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';
|
||||
#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;
|
||||
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)
|
||||
+ "&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) {
|
||||
WiFiClient* stream = http.getStreamPtr();
|
||||
int size = http.getSize();
|
||||
commonData->logger->logDebug(GwLog::LOG, "HTTP get size: %d", size);
|
||||
// header: P4<LF><width> <height><LF> (e.g. 11 byte)
|
||||
uint8_t header[14]; // max: P4<LF>wwww wwww<LF>
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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(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,9 +5,8 @@
|
||||
|
||||
// These constants have to match the declaration below in :
|
||||
// PageDescription registerPageAutopilot(
|
||||
// {"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW", "RPOS", "ROT"}, // Bus values we need in the page
|
||||
|
||||
const int HowManyValues = 11;
|
||||
// {"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW"}, // Bus values we need in the page
|
||||
const int HowManyValues = 9;
|
||||
|
||||
const int AverageValues = 4;
|
||||
|
||||
@@ -20,13 +19,10 @@ const int ShowDBT = 5;
|
||||
const int ShowXTE = 6;
|
||||
const int ShowDTW = 7;
|
||||
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_Y0 = 90; // Y position of compass lines
|
||||
//const int Compass_LineLength = 22; // Length of compass lines
|
||||
const int Compass_LineLength = 15; // Length of compass lines
|
||||
const int Compass_Y0 = 220; // Y position of compass lines
|
||||
const int Compass_LineLength = 22; // Length of compass lines
|
||||
const float Compass_LineDelta = 8.0;// Compass band: 1deg = 5 Pixels, 10deg = 50 Pixels
|
||||
|
||||
class PageAutopilot : public Page
|
||||
@@ -42,11 +38,8 @@ class PageAutopilot : public Page
|
||||
|
||||
virtual void setupKeys(){
|
||||
Page::setupKeys();
|
||||
commonData->keydata[0].label = "-10";
|
||||
commonData->keydata[1].label = "-1";
|
||||
commonData->keydata[2].label = "Auto";
|
||||
commonData->keydata[3].label = "+1";
|
||||
commonData->keydata[4].label = "+10";
|
||||
commonData->keydata[0].label = "CMP";
|
||||
commonData->keydata[1].label = "SRC";
|
||||
}
|
||||
|
||||
virtual int handleKey(int key){
|
||||
@@ -76,8 +69,8 @@ class PageAutopilot : public Page
|
||||
GwLog *logger = commonData->logger;
|
||||
|
||||
// Old values for hold function
|
||||
static String OldDataText[HowManyValues] = {"", "", "", "", "", "","", "", "", "", ""};
|
||||
static String OldDataUnits[HowManyValues] = {"", "", "", "", "", "","", "", "", "", ""};
|
||||
static String OldDataText[HowManyValues] = {"", "", "","", "", "","", "", ""};
|
||||
static String OldDataUnits[HowManyValues] = {"", "", "","", "", "","", "", ""};
|
||||
|
||||
// Get config data
|
||||
String lengthformat = config->getString(config->lengthFormat);
|
||||
@@ -113,13 +106,15 @@ class PageAutopilot : public Page
|
||||
setBlinkingLED(false);
|
||||
setFlashLED(false);
|
||||
}
|
||||
|
||||
if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
|
||||
|
||||
//***********************************************************
|
||||
|
||||
// Set display in partial refresh mode
|
||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||
getdisplay().setTextColor(commonData->fgcolor);
|
||||
/*
|
||||
|
||||
// Horizontal line 2 pix top & bottom
|
||||
// Print data on top half
|
||||
getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor);
|
||||
@@ -143,7 +138,7 @@ class PageAutopilot : public Page
|
||||
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
|
||||
OldDataUnits[WhichDataDisplay] = DataUnits[WhichDataDisplay]; // Save the old unit
|
||||
}
|
||||
*/
|
||||
|
||||
// Now draw compass band
|
||||
// Get the data
|
||||
double TheAngle = DataValue[WhichDataCompass];
|
||||
@@ -157,13 +152,13 @@ class PageAutopilot : public Page
|
||||
buffer[0]=0;
|
||||
|
||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||
getdisplay().setCursor(10, Compass_Y0-40);
|
||||
getdisplay().setCursor(10, Compass_Y0-60);
|
||||
getdisplay().print(DataName[WhichDataCompass]); // Page name
|
||||
|
||||
|
||||
// Draw compass base line and pointer
|
||||
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-30,Compass_X0-10,Compass_Y0-60,Compass_X0+10,Compass_Y0-60,commonData->fgcolor);
|
||||
getdisplay().fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,commonData->fgcolor);
|
||||
// Draw trendlines
|
||||
for ( int i = 1; i < abs(TheTrend) / 2; i++){
|
||||
int x1;
|
||||
@@ -243,8 +238,6 @@ class PageAutopilot : public Page
|
||||
// if ( x_test > 390)
|
||||
// x_test = 320;
|
||||
|
||||
displayRudderPosition(DataValue[ShowSOG], 20, 200, 160, commonData->fgcolor, commonData->bgcolor);
|
||||
|
||||
return PAGE_UPDATE;
|
||||
};
|
||||
|
||||
@@ -263,7 +256,7 @@ PageDescription registerPageAutopilot(
|
||||
"Autopilot", // Page name
|
||||
createPage, // Action
|
||||
0, // Number of bus values depends on selection in Web configuration
|
||||
{"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW", "RPOS", "ROT"}, // Bus values we need in the page
|
||||
{"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW"}, // Bus values we need in the page
|
||||
true // Show display header on/off
|
||||
);
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "images/logo64.xbm"
|
||||
#include <esp32/clk.h>
|
||||
#include "qrcode.h"
|
||||
#include <vector>
|
||||
|
||||
#ifdef BOARD_OBP40S3
|
||||
#include "dirent.h"
|
||||
@@ -41,7 +40,6 @@ private:
|
||||
String rtc_module;
|
||||
String gps_module;
|
||||
String env_module;
|
||||
String flashLED;
|
||||
|
||||
String batt_sensor;
|
||||
String solar_sensor;
|
||||
@@ -52,17 +50,6 @@ private:
|
||||
|
||||
char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard
|
||||
|
||||
#ifdef PATCH_N2K
|
||||
struct device {
|
||||
uint64_t NAME;
|
||||
uint8_t id;
|
||||
char hex_name[17];
|
||||
uint16_t manuf_code;
|
||||
const char *model;
|
||||
};
|
||||
std::vector<device> devicelist;
|
||||
#endif
|
||||
|
||||
public:
|
||||
PageSystem(CommonData &common){
|
||||
commonData = &common;
|
||||
@@ -89,7 +76,6 @@ public:
|
||||
rot_sensor = common.config->getString(common.config->useRotSensor);
|
||||
homelat = common.config->getString(common.config->homeLAT).toDouble();
|
||||
homelon = common.config->getString(common.config->homeLON).toDouble();
|
||||
flashLED = common.config->getString(common.config->flashLED);
|
||||
}
|
||||
|
||||
void setupKeys() {
|
||||
@@ -182,43 +168,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
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);
|
||||
|
||||
@@ -499,54 +461,12 @@ public:
|
||||
getdisplay().print("NMEA2000 device list");
|
||||
|
||||
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||
getdisplay().setCursor(20, 70);
|
||||
getdisplay().setCursor(20, 80);
|
||||
getdisplay().print("RxD: ");
|
||||
getdisplay().print(String(commonData->status.n2kRx));
|
||||
getdisplay().setCursor(120, 70);
|
||||
getdisplay().setCursor(20, 100);
|
||||
getdisplay().print("TxD: ");
|
||||
getdisplay().print(String(commonData->status.n2kTx));
|
||||
|
||||
#ifdef PATCH_N2K
|
||||
x0 = 20;
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
// Update display
|
||||
|
||||
@@ -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]",
|
||||
@@ -678,7 +714,7 @@
|
||||
"type": "string",
|
||||
"default": "text1",
|
||||
"description": "Button name",
|
||||
"category": "OBP60 IO-Modul1",
|
||||
"category": "OBP40 IO-Modul1",
|
||||
"capabilities": {
|
||||
"obp40":"true"
|
||||
}
|
||||
@@ -689,7 +725,7 @@
|
||||
"type": "string",
|
||||
"default": "text2",
|
||||
"description": "Button name",
|
||||
"category": "OBP60 IO-Modul1",
|
||||
"category": "OBP40 IO-Modul1",
|
||||
"capabilities": {
|
||||
"obp40":"true"
|
||||
}
|
||||
@@ -700,7 +736,7 @@
|
||||
"type": "string",
|
||||
"default": "text3",
|
||||
"description": "Button name",
|
||||
"category": "OBP60 IO-Modul1",
|
||||
"category": "OBP40 IO-Modul1",
|
||||
"capabilities": {
|
||||
"obp40":"true"
|
||||
}
|
||||
@@ -711,7 +747,7 @@
|
||||
"type": "string",
|
||||
"default": "text4",
|
||||
"description": "Button name",
|
||||
"category": "OBP60 IO-Modul1",
|
||||
"category": "OBP40 IO-Modul1",
|
||||
"capabilities": {
|
||||
"obp40":"true"
|
||||
}
|
||||
@@ -722,7 +758,7 @@
|
||||
"type": "string",
|
||||
"default": "text5",
|
||||
"description": "Button name",
|
||||
"category": "OBP60 IO-Modul1",
|
||||
"category": "OBP40 IO-Modul1",
|
||||
"capabilities": {
|
||||
"obp40":"true"
|
||||
}
|
||||
@@ -1067,7 +1103,8 @@
|
||||
"description": "Type of map source, cloud service or local service",
|
||||
"list": [
|
||||
"OBP Service",
|
||||
"Local Service"
|
||||
"Local Service",
|
||||
"Remote Service"
|
||||
],
|
||||
"category": "OBP40 Navigation",
|
||||
"capabilities": {
|
||||
@@ -1104,6 +1141,34 @@
|
||||
{ "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",
|
||||
"label": "Map Type",
|
||||
@@ -1245,8 +1310,8 @@
|
||||
"name": "timeSource",
|
||||
"label": "Status Time Source",
|
||||
"type": "list",
|
||||
"default": "iRTC",
|
||||
"description": "Data source for date and time display in status line [iRTC|RTC|GPS]",
|
||||
"default": "GPS",
|
||||
"description": "Data source for date and time display in status line [RTC|iRTC|GPS]",
|
||||
"list": [
|
||||
{"l":"Internal real time clock (iRTC)","v":"iRTC"},
|
||||
{"l":"External real time clock (RTC)","v":"RTC"},
|
||||
@@ -1517,7 +1582,7 @@
|
||||
"default": "Voltage",
|
||||
"description": "Type of page for page 1",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -1848,7 +1913,7 @@
|
||||
"default": "WindRose",
|
||||
"description": "Type of page for page 2",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -2170,7 +2235,7 @@
|
||||
"default": "OneValue",
|
||||
"description": "Type of page for page 3",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -2483,7 +2548,7 @@
|
||||
"default": "TwoValues",
|
||||
"description": "Type of page for page 4",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -2787,7 +2852,7 @@
|
||||
"default": "ThreeValues",
|
||||
"description": "Type of page for page 5",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -3082,7 +3147,7 @@
|
||||
"default": "FourValues",
|
||||
"description": "Type of page for page 6",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -3368,7 +3433,7 @@
|
||||
"default": "FourValues2",
|
||||
"description": "Type of page for page 7",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -3645,7 +3710,7 @@
|
||||
"default": "Clock",
|
||||
"description": "Type of page for page 8",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -3913,7 +3978,7 @@
|
||||
"default": "RollPitch",
|
||||
"description": "Type of page for page 9",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -4172,7 +4237,7 @@
|
||||
"default": "Battery2",
|
||||
"description": "Type of page for page 10",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
|
||||
@@ -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]",
|
||||
@@ -1235,9 +1271,8 @@
|
||||
"label": "Status Time Source",
|
||||
"type": "list",
|
||||
"default": "GPS",
|
||||
"description": "Data source for date and time display in status line [iRTC|RTC|GPS]",
|
||||
"description": "Data source for date and time display in status line [RTC|GPS]",
|
||||
"list": [
|
||||
{"l":"Internal real time clock (iRTC)","v":"iRTC"},
|
||||
{"l":"Real time clock (RTC)","v":"RTC"},
|
||||
{"l":"Time via bus (GPS)","v":"GPS"}
|
||||
],
|
||||
@@ -1487,7 +1522,6 @@
|
||||
"obp60":"true"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "page1type",
|
||||
"label": "Type",
|
||||
@@ -1495,7 +1529,7 @@
|
||||
"default": "Voltage",
|
||||
"description": "Type of page for page 1",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -1796,7 +1830,7 @@
|
||||
"default": "WindRose",
|
||||
"description": "Type of page for page 2",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -2089,7 +2123,7 @@
|
||||
"default": "OneValue",
|
||||
"description": "Type of page for page 3",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -2374,7 +2408,7 @@
|
||||
"default": "TwoValues",
|
||||
"description": "Type of page for page 4",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -2651,7 +2685,7 @@
|
||||
"default": "ThreeValues",
|
||||
"description": "Type of page for page 5",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -2920,7 +2954,7 @@
|
||||
"default": "FourValues",
|
||||
"description": "Type of page for page 6",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -3181,7 +3215,7 @@
|
||||
"default": "FourValues2",
|
||||
"description": "Type of page for page 7",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -3434,7 +3468,7 @@
|
||||
"default": "Clock",
|
||||
"description": "Type of page for page 8",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -3679,7 +3713,7 @@
|
||||
"default": "RollPitch",
|
||||
"description": "Type of page for page 9",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
@@ -3916,7 +3950,7 @@
|
||||
"default": "Battery2",
|
||||
"description": "Type of page for page 10",
|
||||
"list": [
|
||||
"Autopilot",
|
||||
"Anchor",
|
||||
"BME280",
|
||||
"Battery",
|
||||
"Battery2",
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
|
||||
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
|
||||
|
||||
epdtype = "unknown"
|
||||
@@ -54,13 +46,11 @@ if patching:
|
||||
print("patchdir not found, no patches applied")
|
||||
else:
|
||||
patchfiles = [f for f in os.listdir(patchdir)]
|
||||
if len(patchfiles) > 0:
|
||||
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)
|
||||
env.AddPostAction("$PROGPATH", cleanup_patches)
|
||||
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")
|
||||
|
||||
@@ -264,6 +264,8 @@ void registerAllPages(PageList &list){
|
||||
list.add(®isterPageDigitalOut);
|
||||
extern PageDescription registerPageAutopilot;
|
||||
list.add(®isterPageAutopilot);
|
||||
extern PageDescription registerPageAnchor;
|
||||
list.add(®isterPageAnchor);
|
||||
}
|
||||
|
||||
// Undervoltage detection for shutdown display
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
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 604c356..2fe4496 100644
|
||||
--- a/lib/obp60task/OBP60Extensions.h
|
||||
+++ b/lib/obp60task/OBP60Extensions.h
|
||||
@@ -15,6 +15,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 44c715f..fdb0366 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();
|
||||
@@ -45,6 +45,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)
|
||||
@@ -101,6 +103,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
|
||||
|
||||
Reference in New Issue
Block a user