mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-02-25 05:03:07 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97fcebdcb7 | |||
|
|
66e71acac3 | ||
|
|
b85504bf50 | ||
|
|
3043be8e1d | ||
|
|
02b2c888ee | ||
|
|
00b06f458b | ||
|
|
6c7997e369 | ||
|
|
7f747e9b35 | ||
|
|
71512e7262 | ||
|
|
4468c0555b | ||
|
|
99404991a3 | ||
|
|
ee5077e0a5 |
@@ -2,6 +2,9 @@
|
|||||||
#define _GWWIFI_H
|
#define _GWWIFI_H
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <GWConfig.h>
|
#include <GWConfig.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
class GwWifi{
|
class GwWifi{
|
||||||
private:
|
private:
|
||||||
const GwConfigHandler *config;
|
const GwConfigHandler *config;
|
||||||
@@ -16,13 +19,19 @@ class GwWifi{
|
|||||||
bool apActive=false;
|
bool apActive=false;
|
||||||
bool fixedApPass=true;
|
bool fixedApPass=true;
|
||||||
bool clientIsConnected=false;
|
bool clientIsConnected=false;
|
||||||
|
SemaphoreHandle_t wifiMutex=nullptr;
|
||||||
|
static const TickType_t WIFI_MUTEX_TIMEOUT=pdMS_TO_TICKS(1000);
|
||||||
|
bool acquireMutex();
|
||||||
|
void releaseMutex();
|
||||||
public:
|
public:
|
||||||
const char *AP_password = "esp32nmea2k";
|
const char *AP_password = "esp32nmea2k";
|
||||||
GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true);
|
GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass=true);
|
||||||
|
~GwWifi();
|
||||||
void setup();
|
void setup();
|
||||||
void loop();
|
void loop();
|
||||||
bool clientConnected();
|
bool clientConnected();
|
||||||
bool connectClient();
|
bool connectClient(); // Blocking version
|
||||||
|
bool connectClientAsync(); // Non-blocking version for other tasks
|
||||||
String apIP();
|
String apIP();
|
||||||
bool isApActive(){return apActive;}
|
bool isApActive(){return apActive;}
|
||||||
bool isClientActive(){return wifiClient->asBoolean();}
|
bool isClientActive(){return wifiClient->asBoolean();}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include <esp_wifi.h>
|
#include <esp_wifi.h>
|
||||||
#include "GWWifi.h"
|
#include "GWWifi.h"
|
||||||
|
|
||||||
|
|
||||||
GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
|
GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
|
||||||
this->config=config;
|
this->config=config;
|
||||||
this->logger=log;
|
this->logger=log;
|
||||||
@@ -9,6 +8,28 @@ GwWifi::GwWifi(const GwConfigHandler *config,GwLog *log, bool fixedApPass){
|
|||||||
wifiSSID=config->getConfigItem(config->wifiSSID,true);
|
wifiSSID=config->getConfigItem(config->wifiSSID,true);
|
||||||
wifiPass=config->getConfigItem(config->wifiPass,true);
|
wifiPass=config->getConfigItem(config->wifiPass,true);
|
||||||
this->fixedApPass=fixedApPass;
|
this->fixedApPass=fixedApPass;
|
||||||
|
wifiMutex=xSemaphoreCreateMutex();
|
||||||
|
if (wifiMutex==nullptr){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: unable to create mutex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GwWifi::~GwWifi(){
|
||||||
|
if (wifiMutex!=nullptr){
|
||||||
|
vSemaphoreDelete(wifiMutex);
|
||||||
|
wifiMutex=nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GwWifi::acquireMutex(){
|
||||||
|
if (wifiMutex==nullptr) return false;
|
||||||
|
return xSemaphoreTake(wifiMutex,WIFI_MUTEX_TIMEOUT)==pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GwWifi::releaseMutex(){
|
||||||
|
if (wifiMutex!=nullptr){
|
||||||
|
xSemaphoreGive(wifiMutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void GwWifi::setup(){
|
void GwWifi::setup(){
|
||||||
LOG_DEBUG(GwLog::LOG,"Wifi setup");
|
LOG_DEBUG(GwLog::LOG,"Wifi setup");
|
||||||
@@ -85,8 +106,14 @@ bool GwWifi::connectInternal(){
|
|||||||
if (wifiClient->asBoolean()){
|
if (wifiClient->asBoolean()){
|
||||||
clientIsConnected=false;
|
clientIsConnected=false;
|
||||||
LOG_DEBUG(GwLog::LOG,"creating wifiClient ssid=%s",wifiSSID->asString().c_str());
|
LOG_DEBUG(GwLog::LOG,"creating wifiClient ssid=%s",wifiSSID->asString().c_str());
|
||||||
|
// CRITICAL SECTION: WiFi-Operationen müssen serialisiert werden
|
||||||
|
if (!acquireMutex()){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in connectInternal");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
WiFi.setAutoReconnect(false); //#102
|
WiFi.setAutoReconnect(false); //#102
|
||||||
wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString());
|
wl_status_t rt=WiFi.begin(wifiSSID->asCString(),wifiPass->asCString());
|
||||||
|
releaseMutex();
|
||||||
LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt);
|
LOG_DEBUG(GwLog::LOG,"wifiClient connect returns %d",(int)rt);
|
||||||
lastConnectStart=millis();
|
lastConnectStart=millis();
|
||||||
return true;
|
return true;
|
||||||
@@ -104,9 +131,21 @@ void GwWifi::loop(){
|
|||||||
if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now)
|
if (lastConnectStart > now || (lastConnectStart + RETRY_MILLIS) < now)
|
||||||
{
|
{
|
||||||
LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString());
|
LOG_DEBUG(GwLog::LOG,"wifiClient: retry connect to %s", wifiSSID->asCString());
|
||||||
WiFi.disconnect();
|
|
||||||
|
// 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();
|
connectInternal();
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in loop");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if (! clientIsConnected){
|
if (! clientIsConnected){
|
||||||
@@ -126,11 +165,42 @@ void GwWifi::loop(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GwWifi::clientConnected(){
|
bool GwWifi::clientConnected(){
|
||||||
return WiFi.status() == WL_CONNECTED;
|
// CRITICAL SECTION: WiFi.status() muss geschützt werden
|
||||||
|
if (!acquireMutex()){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in clientConnected");
|
||||||
|
return false; // Conservative: nehme an, nicht verbunden
|
||||||
|
}
|
||||||
|
bool result = WiFi.status() == WL_CONNECTED;
|
||||||
|
releaseMutex();
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool GwWifi::connectClient(){
|
bool GwWifi::connectClient(){
|
||||||
|
// CRITICAL SECTION: Disconnect und Connect müssen atomar sein
|
||||||
|
if (!acquireMutex()){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex timeout in connectClient");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
|
releaseMutex();
|
||||||
|
return connectInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GwWifi::connectClientAsync(){
|
||||||
|
// Non-blocking version: Versuche Mutex zu nehmen, gib aber sofort auf
|
||||||
|
// Ideal für Tasks, die nicht blockieren dürfen
|
||||||
|
if (wifiMutex==nullptr){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"GwWifi: mutex not initialized in connectClientAsync");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (xSemaphoreTake(wifiMutex, 0)!=pdTRUE){
|
||||||
|
LOG_DEBUG(GwLog::LOG,"GwWifi: connectClientAsync skipped - WiFi busy");
|
||||||
|
return false; // WiFi ist aktuell busy, versuche es später nochmal
|
||||||
|
}
|
||||||
|
WiFi.disconnect();
|
||||||
|
xSemaphoreGive(wifiMutex);
|
||||||
return connectInternal();
|
return connectInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#include "NetworkClient.h"
|
#include "NetworkClient.h"
|
||||||
|
#include "GWWifi.h" // WiFi management (thread-safe)
|
||||||
|
|
||||||
|
extern GwWifi gwWifi; // Extern declaration of global WiFi instance
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "puff.h"
|
#include "puff.h"
|
||||||
@@ -51,8 +54,13 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
const size_t capacity = READLIMIT; // Read limit for data (can be adjusted in NetworkClient.h)
|
const size_t capacity = READLIMIT; // Read limit for data (can be adjusted in NetworkClient.h)
|
||||||
uint8_t* buffer = (uint8_t*)malloc(capacity);
|
uint8_t* buffer = (uint8_t*)malloc(capacity);
|
||||||
|
|
||||||
|
if (!gwWifi.clientConnected()) {
|
||||||
|
if (DEBUGING) {Serial.println("No WiFi connection");}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
if (DEBUG) {Serial.println("Malloc failed (buffer");}
|
if (DEBUGING) {Serial.println("Malloc failed buffer");}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +114,7 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
len += read;
|
len += read;
|
||||||
lastData = millis();
|
lastData = millis();
|
||||||
|
|
||||||
if (DEBUG) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
if (DEBUGING) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
||||||
|
|
||||||
if (len < 20) continue; // Not enough data for header
|
if (len < 20) continue; // Not enough data for header
|
||||||
|
|
||||||
@@ -122,7 +130,7 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
|
|
||||||
int res = puff(test, &testLen, buffer + headerOffset, &srcLen);
|
int res = puff(test, &testLen, buffer + headerOffset, &srcLen);
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
if (DEBUG) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
if (DEBUGING) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
||||||
outData = test;
|
outData = test;
|
||||||
outLen = testLen;
|
outLen = testLen;
|
||||||
complete = true;
|
complete = true;
|
||||||
@@ -167,7 +175,7 @@ bool NetworkClient::fetchAndDecompressJson(const String& url) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) {Serial.println("JSON OK!");}
|
if (DEBUGING) {Serial.println("JSON OK!");}
|
||||||
_valid = true;
|
_valid = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
#define DEBUG false // Debug flag for NetworkClient for more live information
|
#define DEBUGING false // Debug flag for NetworkClient for more live information
|
||||||
#define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted)
|
#define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted)
|
||||||
#define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection
|
#define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection
|
||||||
#define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack
|
#define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack
|
||||||
|
|||||||
@@ -923,7 +923,7 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor){
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generator graphic with fill level
|
// Generator graphic
|
||||||
void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
||||||
// Show battery
|
// Show battery
|
||||||
int xb = x; // X position
|
int xb = x; // X position
|
||||||
@@ -940,6 +940,74 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
|||||||
getdisplay().print("G");
|
getdisplay().print("G");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display rudder position as horizontal bargraph with configurable +/- range (degrees)
|
||||||
|
void displayRudderPosition(int rudderPosition, uint8_t rangeDeg, uint16_t cx, uint16_t cy, uint16_t fg, uint16_t bg){
|
||||||
|
const int w = 360;
|
||||||
|
const int h = 20;
|
||||||
|
const int t = 3; // Line thickness
|
||||||
|
const int halfw = w/2;
|
||||||
|
const int halfh = h/2;
|
||||||
|
// Calculate top-left of bar (cx,cy are center of 0°)
|
||||||
|
int left = int(cx) - halfw;
|
||||||
|
int top = int(cy) - halfh;
|
||||||
|
|
||||||
|
// clamp provided range to allowed bounds [10,45]
|
||||||
|
if (rangeDeg < 10) rangeDeg = 10;
|
||||||
|
if (rangeDeg > 45) rangeDeg = 45;
|
||||||
|
|
||||||
|
// Pixels per degree for +/-rangeDeg -> total span = 2*rangeDeg
|
||||||
|
const float pxPerDeg = float(w) / (2.0f * float(rangeDeg));
|
||||||
|
|
||||||
|
// Draw outer border (thickness t)
|
||||||
|
for (int i = 0; i < t; i++) {
|
||||||
|
getdisplay().drawRect(left + i, top + i, w - 2 * i, h - 2 * i, fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill inner area with background
|
||||||
|
getdisplay().fillRect(left + t, top + t, w - 2 * t, h - 2 * t, bg);
|
||||||
|
|
||||||
|
// Draw center line
|
||||||
|
getdisplay().drawRect(cx - 1, top + 1, 3 , h - 2, fg);
|
||||||
|
|
||||||
|
// Clamp rudder position to -rangeDeg..rangeDeg
|
||||||
|
if (rudderPosition > (int)rangeDeg) rudderPosition = (int)rangeDeg;
|
||||||
|
if (rudderPosition < -((int)rangeDeg)) rudderPosition = -((int)rangeDeg);
|
||||||
|
|
||||||
|
// Compute fill width in pixels
|
||||||
|
int fillPx = int(round(rudderPosition * pxPerDeg)); // positive -> right
|
||||||
|
|
||||||
|
// Fill area from center to position (if non-zero)
|
||||||
|
int centerx = cx;
|
||||||
|
int innerTop = top + t;
|
||||||
|
int innerH = h - 2 * t;
|
||||||
|
if (fillPx > 0) {
|
||||||
|
// Right side
|
||||||
|
getdisplay().fillRect(centerx, innerTop, fillPx, innerH, fg);
|
||||||
|
} else if (fillPx < 0) {
|
||||||
|
// Left side
|
||||||
|
getdisplay().fillRect(centerx + fillPx, innerTop, -fillPx, innerH, fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Draw tick marks every 5° and labels outside the bar
|
||||||
|
getdisplay().setTextColor(fg);
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt8b);
|
||||||
|
for (int angle = -((int)rangeDeg); angle <= (int)rangeDeg; angle += 5) {
|
||||||
|
int xpos = int(round(centerx + angle * pxPerDeg));
|
||||||
|
// Vertical tick inside bar
|
||||||
|
getdisplay().drawLine(xpos, top, xpos, top + h + 2, fg);
|
||||||
|
// Label outside: below the bar
|
||||||
|
String lbl = String(angle);
|
||||||
|
int16_t bx, by;
|
||||||
|
uint16_t bw, bh;
|
||||||
|
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
|
// Function to handle HTTP image request
|
||||||
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||||
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request) {
|
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request) {
|
||||||
|
|||||||
@@ -128,6 +128,10 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor); // S
|
|||||||
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic
|
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic
|
||||||
void startLedTask(GwApi *api);
|
void startLedTask(GwApi *api);
|
||||||
|
|
||||||
|
// Display rudder position as horizontal bargraph with configurable +/- range (degrees)
|
||||||
|
// 'rangeDeg' is unsigned and will be clamped to [10,45]
|
||||||
|
void displayRudderPosition(int rudderPosition, uint8_t rangeDeg, uint16_t cx, uint16_t cy, uint16_t fg, uint16_t bg);
|
||||||
|
|
||||||
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
|
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
|
|||||||
@@ -835,7 +835,7 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool
|
|||||||
result.cvalue = dplace;
|
result.cvalue = dplace;
|
||||||
}
|
}
|
||||||
//########################################################
|
//########################################################
|
||||||
else if (value->getFormat() == "formatXdr:A:D"){
|
else if ((value->getFormat() == "formatXdr:A:D") || ((value->getFormat() == "formatXdr:A:rd"))){
|
||||||
double angle = 0;
|
double angle = 0;
|
||||||
if (usesimudata == false) {
|
if (usesimudata == false) {
|
||||||
angle = value->value;
|
angle = value->value;
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ void sensorTask(void *param){
|
|||||||
GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP);
|
GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP);
|
||||||
GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop};
|
GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop};
|
||||||
|
|
||||||
// Internal RTC with NTP init
|
// Internal iRTC with NTP init
|
||||||
ESP32Time rtc(0);
|
ESP32Time rtc(0);
|
||||||
if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") {
|
if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") {
|
||||||
GwApi::Status status;
|
GwApi::Status status;
|
||||||
@@ -432,13 +432,13 @@ void sensorTask(void *param){
|
|||||||
|
|
||||||
iRTC RTC GPS N2K
|
iRTC RTC GPS N2K
|
||||||
0 0 0 (1)
|
0 0 0 (1)
|
||||||
0 0 (1) (X)
|
0 0 (1) X
|
||||||
0 (1) 0 (X)
|
0 (1) 0 X
|
||||||
0 1 <-(1) (X)
|
0 1 <-(1) X
|
||||||
(1) 0 0 (X)
|
(1) 0 0 X
|
||||||
1 0 (1) (X)
|
1 0 (1) X
|
||||||
1 ->(1) 0 (X)
|
1 ->(1) 0 X
|
||||||
1 1 <-(1) (X)
|
1 1 <-(1) X
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -524,7 +524,7 @@ void sensorTask(void *param){
|
|||||||
// N2K sysTime is double in n2klib
|
// N2K sysTime is double in n2klib
|
||||||
double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second();
|
double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second();
|
||||||
if(!isnan(daysAt1970) && !isnan(sysTime)){
|
if(!isnan(daysAt1970) && !isnan(sysTime)){
|
||||||
//api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",sensors.rtcTime.tm_year+1900,sensors.rtcTime.tm_mon, sensors.rtcTime.tm_mday, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
|
//api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",sensors.rtcTime.tm_year+1900,sensors.rtcTime.tm_mon+1, sensors.rtcTime.tm_mday, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
|
||||||
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
||||||
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
|
SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
|
||||||
api->sendN2kMessage(N2kMsg);
|
api->sendN2kMessage(N2kMsg);
|
||||||
@@ -533,25 +533,26 @@ void sensorTask(void *param){
|
|||||||
}
|
}
|
||||||
// Send date and time from software RTC (iRTC)
|
// Send date and time from software RTC (iRTC)
|
||||||
if (iRTC_ready == true && RTC_ready == false && GPS_ready == false) {
|
if (iRTC_ready == true && RTC_ready == false && GPS_ready == false) {
|
||||||
// Use internal RTC feature
|
sensors.rtcTime = rtc.getTimeStruct();
|
||||||
sensors.rtcTime = rtc.getTimeStruct(); // Save software RTC values in SensorData
|
|
||||||
// TODO implement daysAt1970 and sysTime as methods of DateTime
|
|
||||||
const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
|
const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
|
||||||
uint16_t switchYear = ((sensors.rtcTime.tm_year-1)-1968)/4 - ((sensors.rtcTime.tm_year-1)-1900)/100 + ((sensors.rtcTime.tm_year-1)-1600)/400;
|
int year = sensors.rtcTime.tm_year + 1900;
|
||||||
long daysAt1970 = (sensors.rtcTime.tm_year-1970)*365 + switchYear + daysOfYear[sensors.rtcTime.tm_mon-1] + sensors.rtcTime.tm_mday-1;
|
int month = sensors.rtcTime.tm_mon;
|
||||||
// If switch year then add one day
|
int day = sensors.rtcTime.tm_mday;
|
||||||
if ((sensors.rtcTime.tm_mon > 2) && (sensors.rtcTime.tm_year % 4 == 0 && (sensors.rtcTime.tm_year % 100 != 0 || sensors.rtcTime.tm_year % 400 == 0))) {
|
uint16_t switchYear = ((year - 1) - 1968) / 4 - ((year - 1) - 1900) / 100 + ((year - 1) - 1600) / 400;
|
||||||
|
long daysAt1970 = (year - 1970) * 365L + switchYear + daysOfYear[month] + day - 1;
|
||||||
|
|
||||||
|
// Leap day add if date is after Feb (i.e. month >= March)
|
||||||
|
if (month >= 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) {
|
||||||
daysAt1970 += 1;
|
daysAt1970 += 1;
|
||||||
}
|
}
|
||||||
// N2K sysTime is double in n2klib
|
double sysTime = sensors.rtcTime.tm_hour * 3600.0 + sensors.rtcTime.tm_min * 60.0 + sensors.rtcTime.tm_sec;
|
||||||
double sysTime = (sensors.rtcTime.tm_hour * 3600) + (sensors.rtcTime.tm_min * 60) + sensors.rtcTime.tm_sec;
|
//api->getLogger()->logDebug(GwLog::LOG, "iRTC time: %04d/%02d/%02d %02d:%02d:%02d", year, month + 1, day, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
|
||||||
if(!isnan(daysAt1970) && !isnan(sysTime)){
|
|
||||||
//api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",sensors.rtcTime.tm_year+1900,sensors.rtcTime.tm_mon, sensors.rtcTime.tm_mday, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
|
|
||||||
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
//api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
|
||||||
SetN2kPGN126992(N2kMsg, 0, daysAt1970, sysTime, N2ktimes_LocalCrystalClock);
|
SetN2kPGN126992(N2kMsg, 0, daysAt1970, sysTime, N2ktimes_LocalCrystalClock);
|
||||||
api->sendN2kMessage(N2kMsg);
|
api->sendN2kMessage(N2kMsg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send 1Wire data for all temperature sensors to N2K all 2s
|
// Send 1Wire data for all temperature sensors to N2K all 2s
|
||||||
|
|||||||
@@ -1,497 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
|
|
||||||
|
|
||||||
/*
|
|
||||||
This page is in experimental stage so be warned!
|
|
||||||
North is up.
|
|
||||||
Anchor page with background map from mapservice
|
|
||||||
|
|
||||||
Boatdata used
|
|
||||||
DBS - Water depth
|
|
||||||
HDT - Boat heading
|
|
||||||
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,8 +5,9 @@
|
|||||||
|
|
||||||
// These constants have to match the declaration below in :
|
// These constants have to match the declaration below in :
|
||||||
// PageDescription registerPageAutopilot(
|
// PageDescription registerPageAutopilot(
|
||||||
// {"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW"}, // Bus values we need in the page
|
// {"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW", "RPOS", "ROT"}, // Bus values we need in the page
|
||||||
const int HowManyValues = 9;
|
|
||||||
|
const int HowManyValues = 11;
|
||||||
|
|
||||||
const int AverageValues = 4;
|
const int AverageValues = 4;
|
||||||
|
|
||||||
@@ -19,10 +20,13 @@ const int ShowDBT = 5;
|
|||||||
const int ShowXTE = 6;
|
const int ShowXTE = 6;
|
||||||
const int ShowDTW = 7;
|
const int ShowDTW = 7;
|
||||||
const int ShowBTW = 8;
|
const int ShowBTW = 8;
|
||||||
|
const int ShowRPOS = 9;
|
||||||
|
const int ShowROT = 10;
|
||||||
|
|
||||||
const int Compass_X0 = 200; // X center point of compass band
|
const int Compass_X0 = 200; // X center point of compass band
|
||||||
const int Compass_Y0 = 220; // Y position of compass lines
|
const int Compass_Y0 = 90; // Y position of compass lines
|
||||||
const int Compass_LineLength = 22; // Length of compass lines
|
//const int Compass_LineLength = 22; // Length of compass lines
|
||||||
|
const int Compass_LineLength = 15; // Length of compass lines
|
||||||
const float Compass_LineDelta = 8.0;// Compass band: 1deg = 5 Pixels, 10deg = 50 Pixels
|
const float Compass_LineDelta = 8.0;// Compass band: 1deg = 5 Pixels, 10deg = 50 Pixels
|
||||||
|
|
||||||
class PageAutopilot : public Page
|
class PageAutopilot : public Page
|
||||||
@@ -38,8 +42,11 @@ class PageAutopilot : public Page
|
|||||||
|
|
||||||
virtual void setupKeys(){
|
virtual void setupKeys(){
|
||||||
Page::setupKeys();
|
Page::setupKeys();
|
||||||
commonData->keydata[0].label = "CMP";
|
commonData->keydata[0].label = "-10";
|
||||||
commonData->keydata[1].label = "SRC";
|
commonData->keydata[1].label = "-1";
|
||||||
|
commonData->keydata[2].label = "Auto";
|
||||||
|
commonData->keydata[3].label = "+1";
|
||||||
|
commonData->keydata[4].label = "+10";
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int handleKey(int key){
|
virtual int handleKey(int key){
|
||||||
@@ -69,8 +76,8 @@ class PageAutopilot : public Page
|
|||||||
GwLog *logger = commonData->logger;
|
GwLog *logger = commonData->logger;
|
||||||
|
|
||||||
// Old values for hold function
|
// Old values for hold function
|
||||||
static String OldDataText[HowManyValues] = {"", "", "","", "", "","", "", ""};
|
static String OldDataText[HowManyValues] = {"", "", "", "", "", "","", "", "", "", ""};
|
||||||
static String OldDataUnits[HowManyValues] = {"", "", "","", "", "","", "", ""};
|
static String OldDataUnits[HowManyValues] = {"", "", "", "", "", "","", "", "", "", ""};
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String lengthformat = config->getString(config->lengthFormat);
|
String lengthformat = config->getString(config->lengthFormat);
|
||||||
@@ -107,14 +114,12 @@ class PageAutopilot : public Page
|
|||||||
setFlashLED(false);
|
setFlashLED(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
|
|
||||||
|
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
/*
|
||||||
// Horizontal line 2 pix top & bottom
|
// Horizontal line 2 pix top & bottom
|
||||||
// Print data on top half
|
// Print data on top half
|
||||||
getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor);
|
getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor);
|
||||||
@@ -138,7 +143,7 @@ class PageAutopilot : public Page
|
|||||||
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
|
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
|
||||||
OldDataUnits[WhichDataDisplay] = DataUnits[WhichDataDisplay]; // Save the old unit
|
OldDataUnits[WhichDataDisplay] = DataUnits[WhichDataDisplay]; // Save the old unit
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// Now draw compass band
|
// Now draw compass band
|
||||||
// Get the data
|
// Get the data
|
||||||
double TheAngle = DataValue[WhichDataCompass];
|
double TheAngle = DataValue[WhichDataCompass];
|
||||||
@@ -152,13 +157,13 @@ class PageAutopilot : public Page
|
|||||||
buffer[0]=0;
|
buffer[0]=0;
|
||||||
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
getdisplay().setFont(&Ubuntu_Bold16pt8b);
|
||||||
getdisplay().setCursor(10, Compass_Y0-60);
|
getdisplay().setCursor(10, Compass_Y0-40);
|
||||||
getdisplay().print(DataName[WhichDataCompass]); // Page name
|
getdisplay().print(DataName[WhichDataCompass]); // Page name
|
||||||
|
|
||||||
|
|
||||||
// Draw compass base line and pointer
|
// Draw compass base line and pointer
|
||||||
getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
|
getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
|
||||||
getdisplay().fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,commonData->fgcolor);
|
//getdisplay().fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,commonData->fgcolor);
|
||||||
|
getdisplay().fillTriangle(Compass_X0,Compass_Y0-30,Compass_X0-10,Compass_Y0-60,Compass_X0+10,Compass_Y0-60,commonData->fgcolor);
|
||||||
// Draw trendlines
|
// Draw trendlines
|
||||||
for ( int i = 1; i < abs(TheTrend) / 2; i++){
|
for ( int i = 1; i < abs(TheTrend) / 2; i++){
|
||||||
int x1;
|
int x1;
|
||||||
@@ -238,6 +243,8 @@ class PageAutopilot : public Page
|
|||||||
// if ( x_test > 390)
|
// if ( x_test > 390)
|
||||||
// x_test = 320;
|
// x_test = 320;
|
||||||
|
|
||||||
|
displayRudderPosition(DataValue[ShowSOG], 20, 200, 160, commonData->fgcolor, commonData->bgcolor);
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -256,7 +263,7 @@ PageDescription registerPageAutopilot(
|
|||||||
"Autopilot", // Page name
|
"Autopilot", // Page name
|
||||||
createPage, // Action
|
createPage, // Action
|
||||||
0, // Number of bus values depends on selection in Web configuration
|
0, // Number of bus values depends on selection in Web configuration
|
||||||
{"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW"}, // Bus values we need in the page
|
{"HDM","HDT", "COG", "STW", "SOG", "DBT","XTE", "DTW", "BTW", "RPOS", "ROT"}, // Bus values we need in the page
|
||||||
true // Show display header on/off
|
true // Show display header on/off
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -19,28 +19,6 @@
|
|||||||
"obp40": "true"
|
"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",
|
"name": "timeZone",
|
||||||
"label": "Time Zone",
|
"label": "Time Zone",
|
||||||
@@ -97,20 +75,6 @@
|
|||||||
"obp40": "true"
|
"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",
|
"name": "fuelTank",
|
||||||
"label": "Fuel Tank [l]",
|
"label": "Fuel Tank [l]",
|
||||||
@@ -714,7 +678,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text1",
|
"default": "text1",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP60 IO-Modul1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -725,7 +689,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text2",
|
"default": "text2",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP60 IO-Modul1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -736,7 +700,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text3",
|
"default": "text3",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP60 IO-Modul1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -747,7 +711,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text4",
|
"default": "text4",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP60 IO-Modul1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -758,7 +722,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "text5",
|
"default": "text5",
|
||||||
"description": "Button name",
|
"description": "Button name",
|
||||||
"category": "OBP40 IO-Modul1",
|
"category": "OBP60 IO-Modul1",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"obp40":"true"
|
"obp40":"true"
|
||||||
}
|
}
|
||||||
@@ -1103,8 +1067,7 @@
|
|||||||
"description": "Type of map source, cloud service or local service",
|
"description": "Type of map source, cloud service or local service",
|
||||||
"list": [
|
"list": [
|
||||||
"OBP Service",
|
"OBP Service",
|
||||||
"Local Service",
|
"Local Service"
|
||||||
"Remote Service"
|
|
||||||
],
|
],
|
||||||
"category": "OBP40 Navigation",
|
"category": "OBP40 Navigation",
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
@@ -1141,34 +1104,6 @@
|
|||||||
{ "mapsource": ["Local Service"] }
|
{ "mapsource": ["Local Service"] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "mapServer",
|
|
||||||
"label": "map server",
|
|
||||||
"type": "string",
|
|
||||||
"default": "",
|
|
||||||
"description": "Server for converting map tiles. Use only one hostname or IP address",
|
|
||||||
"category": "OBP40 Navigation",
|
|
||||||
"capabilities": {
|
|
||||||
"obp40": "true"
|
|
||||||
},
|
|
||||||
"condition": [
|
|
||||||
{ "mapsource": ["Remote Service"] }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mapTilePath",
|
|
||||||
"label": "map tile path",
|
|
||||||
"type": "string",
|
|
||||||
"default": "map.php",
|
|
||||||
"description": "Path to converter access e.g. index.php or map.php",
|
|
||||||
"category": "OBP40 Navigation",
|
|
||||||
"capabilities": {
|
|
||||||
"obp40": "true"
|
|
||||||
},
|
|
||||||
"condition": [
|
|
||||||
{ "mapsource": ["Remote Service"] }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "maptype",
|
"name": "maptype",
|
||||||
"label": "Map Type",
|
"label": "Map Type",
|
||||||
@@ -1310,8 +1245,8 @@
|
|||||||
"name": "timeSource",
|
"name": "timeSource",
|
||||||
"label": "Status Time Source",
|
"label": "Status Time Source",
|
||||||
"type": "list",
|
"type": "list",
|
||||||
"default": "GPS",
|
"default": "iRTC",
|
||||||
"description": "Data source for date and time display in status line [RTC|iRTC|GPS]",
|
"description": "Data source for date and time display in status line [iRTC|RTC|GPS]",
|
||||||
"list": [
|
"list": [
|
||||||
{"l":"Internal real time clock (iRTC)","v":"iRTC"},
|
{"l":"Internal real time clock (iRTC)","v":"iRTC"},
|
||||||
{"l":"External real time clock (RTC)","v":"RTC"},
|
{"l":"External real time clock (RTC)","v":"RTC"},
|
||||||
@@ -1582,7 +1517,7 @@
|
|||||||
"default": "Voltage",
|
"default": "Voltage",
|
||||||
"description": "Type of page for page 1",
|
"description": "Type of page for page 1",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -1913,7 +1848,7 @@
|
|||||||
"default": "WindRose",
|
"default": "WindRose",
|
||||||
"description": "Type of page for page 2",
|
"description": "Type of page for page 2",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -2235,7 +2170,7 @@
|
|||||||
"default": "OneValue",
|
"default": "OneValue",
|
||||||
"description": "Type of page for page 3",
|
"description": "Type of page for page 3",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -2548,7 +2483,7 @@
|
|||||||
"default": "TwoValues",
|
"default": "TwoValues",
|
||||||
"description": "Type of page for page 4",
|
"description": "Type of page for page 4",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -2852,7 +2787,7 @@
|
|||||||
"default": "ThreeValues",
|
"default": "ThreeValues",
|
||||||
"description": "Type of page for page 5",
|
"description": "Type of page for page 5",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -3147,7 +3082,7 @@
|
|||||||
"default": "FourValues",
|
"default": "FourValues",
|
||||||
"description": "Type of page for page 6",
|
"description": "Type of page for page 6",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -3433,7 +3368,7 @@
|
|||||||
"default": "FourValues2",
|
"default": "FourValues2",
|
||||||
"description": "Type of page for page 7",
|
"description": "Type of page for page 7",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -3710,7 +3645,7 @@
|
|||||||
"default": "Clock",
|
"default": "Clock",
|
||||||
"description": "Type of page for page 8",
|
"description": "Type of page for page 8",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -3978,7 +3913,7 @@
|
|||||||
"default": "RollPitch",
|
"default": "RollPitch",
|
||||||
"description": "Type of page for page 9",
|
"description": "Type of page for page 9",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -4237,7 +4172,7 @@
|
|||||||
"default": "Battery2",
|
"default": "Battery2",
|
||||||
"description": "Type of page for page 10",
|
"description": "Type of page for page 10",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
|
|||||||
@@ -19,28 +19,6 @@
|
|||||||
"obp60": "true"
|
"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",
|
"name": "timeZone",
|
||||||
"label": "Time Zone",
|
"label": "Time Zone",
|
||||||
@@ -97,20 +75,6 @@
|
|||||||
"obp60":"true"
|
"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",
|
"name": "fuelTank",
|
||||||
"label": "Fuel Tank [l]",
|
"label": "Fuel Tank [l]",
|
||||||
@@ -1271,8 +1235,9 @@
|
|||||||
"label": "Status Time Source",
|
"label": "Status Time Source",
|
||||||
"type": "list",
|
"type": "list",
|
||||||
"default": "GPS",
|
"default": "GPS",
|
||||||
"description": "Data source for date and time display in status line [RTC|GPS]",
|
"description": "Data source for date and time display in status line [iRTC|RTC|GPS]",
|
||||||
"list": [
|
"list": [
|
||||||
|
{"l":"Internal real time clock (iRTC)","v":"iRTC"},
|
||||||
{"l":"Real time clock (RTC)","v":"RTC"},
|
{"l":"Real time clock (RTC)","v":"RTC"},
|
||||||
{"l":"Time via bus (GPS)","v":"GPS"}
|
{"l":"Time via bus (GPS)","v":"GPS"}
|
||||||
],
|
],
|
||||||
@@ -1522,6 +1487,7 @@
|
|||||||
"obp60":"true"
|
"obp60":"true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "page1type",
|
"name": "page1type",
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
@@ -1529,7 +1495,7 @@
|
|||||||
"default": "Voltage",
|
"default": "Voltage",
|
||||||
"description": "Type of page for page 1",
|
"description": "Type of page for page 1",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -1830,7 +1796,7 @@
|
|||||||
"default": "WindRose",
|
"default": "WindRose",
|
||||||
"description": "Type of page for page 2",
|
"description": "Type of page for page 2",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -2123,7 +2089,7 @@
|
|||||||
"default": "OneValue",
|
"default": "OneValue",
|
||||||
"description": "Type of page for page 3",
|
"description": "Type of page for page 3",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -2408,7 +2374,7 @@
|
|||||||
"default": "TwoValues",
|
"default": "TwoValues",
|
||||||
"description": "Type of page for page 4",
|
"description": "Type of page for page 4",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -2685,7 +2651,7 @@
|
|||||||
"default": "ThreeValues",
|
"default": "ThreeValues",
|
||||||
"description": "Type of page for page 5",
|
"description": "Type of page for page 5",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -2954,7 +2920,7 @@
|
|||||||
"default": "FourValues",
|
"default": "FourValues",
|
||||||
"description": "Type of page for page 6",
|
"description": "Type of page for page 6",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -3215,7 +3181,7 @@
|
|||||||
"default": "FourValues2",
|
"default": "FourValues2",
|
||||||
"description": "Type of page for page 7",
|
"description": "Type of page for page 7",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -3468,7 +3434,7 @@
|
|||||||
"default": "Clock",
|
"default": "Clock",
|
||||||
"description": "Type of page for page 8",
|
"description": "Type of page for page 8",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -3713,7 +3679,7 @@
|
|||||||
"default": "RollPitch",
|
"default": "RollPitch",
|
||||||
"description": "Type of page for page 9",
|
"description": "Type of page for page 9",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
@@ -3950,7 +3916,7 @@
|
|||||||
"default": "Battery2",
|
"default": "Battery2",
|
||||||
"description": "Type of page for page 10",
|
"description": "Type of page for page 10",
|
||||||
"list": [
|
"list": [
|
||||||
"Anchor",
|
"Autopilot",
|
||||||
"BME280",
|
"BME280",
|
||||||
"Battery",
|
"Battery",
|
||||||
"Battery2",
|
"Battery2",
|
||||||
|
|||||||
@@ -264,8 +264,6 @@ void registerAllPages(PageList &list){
|
|||||||
list.add(®isterPageDigitalOut);
|
list.add(®isterPageDigitalOut);
|
||||||
extern PageDescription registerPageAutopilot;
|
extern PageDescription registerPageAutopilot;
|
||||||
list.add(®isterPageAutopilot);
|
list.add(®isterPageAutopilot);
|
||||||
extern PageDescription registerPageAnchor;
|
|
||||||
list.add(®isterPageAnchor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Undervoltage detection for shutdown display
|
// Undervoltage detection for shutdown display
|
||||||
|
|||||||
@@ -45,8 +45,6 @@ lib_deps =
|
|||||||
milesburton/DallasTemperature@3.11.0
|
milesburton/DallasTemperature@3.11.0
|
||||||
signetica/SunRise@2.0.2
|
signetica/SunRise@2.0.2
|
||||||
adafruit/Adafruit FRAM I2C@2.0.3
|
adafruit/Adafruit FRAM I2C@2.0.3
|
||||||
WifiClientSecure
|
|
||||||
HTTPClient
|
|
||||||
build_flags=
|
build_flags=
|
||||||
#https://thingpulse.com/usb-settings-for-logging-with-the-esp32-s3-in-platformio/?srsltid=AfmBOopGskbkr4GoeVkNlFaZXe_zXkLceKF6Rn-tmoXABCeAR2vWsdHL
|
#https://thingpulse.com/usb-settings-for-logging-with-the-esp32-s3-in-platformio/?srsltid=AfmBOopGskbkr4GoeVkNlFaZXe_zXkLceKF6Rn-tmoXABCeAR2vWsdHL
|
||||||
# -D CORE_DEBUG_LEVEL=1 #Debug level for CPU core via CDC (serial device)
|
# -D CORE_DEBUG_LEVEL=1 #Debug level for CPU core via CDC (serial device)
|
||||||
@@ -103,8 +101,6 @@ lib_deps =
|
|||||||
milesburton/DallasTemperature@3.11.0
|
milesburton/DallasTemperature@3.11.0
|
||||||
signetica/SunRise@2.0.2
|
signetica/SunRise@2.0.2
|
||||||
adafruit/Adafruit FRAM I2C@2.0.3
|
adafruit/Adafruit FRAM I2C@2.0.3
|
||||||
WifiClientSecure
|
|
||||||
HTTPClient
|
|
||||||
build_flags=
|
build_flags=
|
||||||
-D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib
|
-D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib
|
||||||
-D BOARD_OBP40S3 #Board OBP40 with ESP32S3
|
-D BOARD_OBP40S3 #Board OBP40 with ESP32S3
|
||||||
|
|||||||
Reference in New Issue
Block a user