1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2025-12-28 21:23:07 +01:00

13 Commits

Author SHA1 Message Date
d5b0af3b19 Integrated config menu into system page 2025-08-03 21:18:08 +02:00
aed389f3b2 Added code for configuration menu handling 2025-08-01 17:08:01 +02:00
32ba4a64ed Add friendly color names to color class for system page display 2025-07-31 14:23:11 +02:00
thooge
7edd201daa Merge branch 'norbert-walter:master' into system 2025-07-31 13:20:39 +02:00
norbert-walter
fb3af0bf83 Fix for page RollPitch 2025-07-31 11:58:04 +02:00
Norbert Walter
539500e088 Merge pull request #190 from thooge/master
Fix typo: Formated -> Formatted
2025-07-31 11:52:13 +02:00
Norbert Walter
229106b04c Merge pull request #189 from thooge/cleanup
Minor code cleanup: fixing comments and formatting
2025-07-31 11:51:54 +02:00
Norbert Walter
eefa59a7c2 Merge pull request #188 from Scorgan01/PageWindPlot
Page WindPlot: small fixes for TWS name display and data availabity checks for true wind calculation
2025-07-31 11:51:36 +02:00
f4d88f1b8b Code rework at page system. Preparation for config menu. 2025-07-31 11:39:23 +02:00
588008e370 Fix typo: Formated -> Formatted 2025-07-29 19:33:31 +02:00
caf5572459 Minor code cleanup: fixing comments and formatting 2025-07-28 09:54:20 +02:00
Ulrich Meine
05f8b3ec65 fix TWS name not displayed; improve check for chart center adjustment; debug code changes 2025-07-27 20:54:35 +02:00
Ulrich Meine
351ef5d9fe fix true wind input check; fix TWS not calculated with SOG only or w/o COG 2025-07-27 20:51:11 +02:00
29 changed files with 650 additions and 956 deletions

View File

@@ -192,3 +192,13 @@ Rect ConfigMenu::getItemRect(int8_t index) {
return {static_cast<double>(x), static_cast<double>(y + index * h),
static_cast<double>(w), static_cast<double>(h)};
}
void ConfigMenu::setCallback(void (*callback)()) {
fptrCallback = callback;
}
void ConfigMenu::storeValues() {
if (fptrCallback) {
fptrCallback();
}
}

View File

@@ -44,6 +44,7 @@ private:
uint16_t y;
uint16_t w;
uint16_t h;
void (*fptrCallback)();
public:
ConfigMenu(String title, uint16_t menu_x, uint16_t menu_y);
@@ -60,4 +61,6 @@ public:
Point getXY();
Rect getRect();
Rect getItemRect(int8_t index);
void setCallback(void (*callback)());
void storeValues();
};

View File

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

View File

@@ -22,6 +22,8 @@ class Color{
bool operator != (const Color &other) const{
return ! equal(other);
}
String toHex();
String toName();
};
static Color COLOR_GREEN=Color(0,255,0);

View File

@@ -107,6 +107,27 @@ void hardwareInit(GwApi *api)
}
}
void powerInit(String powermode) {
// Max Power | Only 5.0V | Min Power
if (powermode == "Max Power" || powermode == "Only 5.0V") {
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, true);// Power on ePaper display
setPortPin(OBP_POWER_SD, true); // Power on SD card
#endif
} else { // Min Power
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, false);// Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
#endif
}
}
void setPortPin(uint pin, bool value){
pinMode(pin, OUTPUT);
digitalWrite(pin, value);

View File

@@ -71,6 +71,7 @@ void deepSleep(CommonData &common);
uint8_t getLastPage();
void hardwareInit(GwApi *api);
void powerInit(String powermode);
void setPortPin(uint pin, bool value); // Set port pin for extension port

View File

@@ -49,9 +49,9 @@ String formatLongitude(double lon) {
return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lon > 0) ? "E" : "W");
}
FormatedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
GwLog *logger = commondata.logger;
FormatedData result;
FormattedData result;
static int dayoffset = 0;
double rawvalue = 0;

View File

@@ -94,7 +94,7 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
} else if (*cogVal != DBL_MIN) {
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available
} else {
return false; // Cannot calculate without valid HDT or HDM
return false; // Cannot calculate without valid HDT or HDM+VAR or COG
}
}
@@ -102,8 +102,7 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
ctw = *cogVal; // Use COG as CTW if available
// ctw = *cogVal + ((*cogVal - hdt) / 2); // Estimate CTW from COG
} else {
ctw = hdt; // 2nd approximation for CTW;
return false;
ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code
}
if (*stwVal != DBL_MIN) {
@@ -115,11 +114,11 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
return false;
}
if ((*awaVal == DBL_MIN) || (*awsVal == DBL_MIN) || (*cogVal == DBL_MIN) || (*stwVal == DBL_MIN)) {
// Cannot calculate true wind without valid AWA, AWS, COG, or STW
if ((*awaVal == DBL_MIN) || (*awsVal == DBL_MIN)) {
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
return false;
} else {
calcTwdSA(awaVal, awsVal, &ctw, stwVal, &hdt, &twd, &tws, &twa);
calcTwdSA(awaVal, awsVal, &ctw, &stw, &hdt, &twd, &tws, &twa);
*twdVal = twd;
*twsVal = tws;
*twaVal = twa;

View File

@@ -1,433 +0,0 @@
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "ConfigMenu.h"
/*
Anchor overview with additional associated data
This page is in experimental stage so be warned!
North is up.
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
This is the fist page to contain a configuration page with
data entry option.
Also it will make use of the new alarm function.
Data
Anchor position lat/lon
Depth at anchor position
Chain length used
Boat position current
Depth at boat position
Boat heading
Wind direction
Wind strength
Alarm j/n
Alarm radius
GPS position error
Timestamp while dropping anchor
Drop / raise function in device OBP40 has to be done inside
config mode because of limited number of buttons.
*/
#define anchor_width 16
#define anchor_height 16
static unsigned char anchor_bits[] = {
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:
GwConfigHandler *config;
GwLog *logger;
bool simulation = false;
bool holdvalues = false;
String flashLED;
String backlightMode;
String lengthformat;
int scale = 50; // Radius of display circle in meter
bool alarm = false;
bool alarm_enabled = false;
uint8_t alarm_range;
uint8_t chain_length;
uint8_t chain;
bool anchor_set = false;
double anchor_lat;
double anchor_lon;
double anchor_depth;
int anchor_ts; // time stamp anchor dropped
char mode = 'N'; // (N)ormal, (C)onfig
int8_t editmode = -1; // marker for menu/edit/set function
ConfigMenu *menu;
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;
LOG_DEBUG(GwLog::DEBUG,"Drawing at PageAnchor; DBS=%f, HDT=%f, AWS=%f", bv_dbs->value, bv_hdt->value, bv_aws->value);
Point c = {200, 150}; // center = anchor position
uint16_t r = 125;
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
//drawPoly(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor);
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_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("Anchor");
getdisplay().setFont(&Ubuntu_Bold10pt8b);
getdisplay().setCursor(8, 200);
getdisplay().print("Depth");
drawTextRalign(392, 38, "Chain");
drawTextRalign(392, 200, "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, 70);
getdisplay().print("Alarm: ");
getdisplay().print(alarm_enabled ? "On" : "Off");
getdisplay().setCursor(8, 90);
getdisplay().print("HDOP");
getdisplay().setCursor(8, 106);
if (bv_hdop->valid) {
getdisplay().print(round(bv_hdop->value), 0);
getdisplay().print(sunit_hdop);
} else {
getdisplay().print("n/a");
}
// Values
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
// Current chain used
getdisplay().setCursor(328, 85);
getdisplay().print("27");
// Depth
getdisplay().setCursor(8, 250);
getdisplay().print(sval_dbs);
// Wind
getdisplay().setCursor(328, 250);
getdisplay().print(sval_aws);
getdisplay().drawCircle(c.x, c.y, r, commonData->fgcolor);
getdisplay().drawCircle(c.x, c.y, r + 1, commonData->fgcolor);
// zoom scale
getdisplay().drawLine(c.x + 10, c.y, c.x + r - 4, c.y, commonData->fgcolor);
// arrow left
getdisplay().drawLine(c.x + 10, c.y, c.x + 16, c.y - 4, commonData->fgcolor);
getdisplay().drawLine(c.x + 10, c.y, c.x + 16, c.y + 4, commonData->fgcolor);
// arrow right
getdisplay().drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y - 4, commonData->fgcolor);
getdisplay().drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y + 4, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(c.x + r / 2, c.y + 8, String(scale) + "m");
// alarm range circle
if (alarm_enabled) {
// alarm range in meter has to be smaller than the scale in meter
// r and r_range are pixel values
uint16_t r_range = int(alarm_range * r / scale);
LOG_DEBUG(GwLog::LOG,"Drawing at PageAnchor; Alarm range = %d", r_range);
getdisplay().drawCircle(c.x, c.y, r_range, commonData->fgcolor);
}
// draw anchor symbol (as bitmap)
getdisplay().drawXBitmap(c.x - anchor_width / 2, c.y - anchor_height / 2,
anchor_bits, anchor_width, anchor_height, commonData->fgcolor);
}
void displayModeConfig() {
getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("Anchor configuration");
// TODO
// show lat/lon for anchor pos
// show lat/lon for boat pos
// show distance anchor <-> boat
getdisplay().setFont(&Ubuntu_Bold8pt8b);
for (int i = 0 ; i < menu->getItemCount(); i++) {
ConfigMenuItem *itm = menu->getItemByIndex(i);
if (!itm) {
LOG_DEBUG(GwLog::ERROR, "Menu item not found: %d", i);
} else {
Rect r = menu->getItemRect(i);
bool inverted = (i == menu->getActiveIndex());
drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false);
if (inverted and editmode > 0) {
// triangle as edit marker
getdisplay().fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor);
}
getdisplay().setCursor(r.x + r.w + 40, r.y + r.h - 4);
if (itm->getType() == "int") {
getdisplay().print(itm->getValue());
getdisplay().print(itm->getUnit());
} else {
getdisplay().print(itm->getValue() == 0 ? "No" : "Yes");
}
}
}
}
public:
PageAnchor(CommonData &common)
{
commonData = &common;
config = commonData->config;
logger = commonData->logger;
logger->logDebug(GwLog::LOG,"Instantiate PageAnchor");
// preload configuration data
simulation = config->getBool(config->useSimuData);
holdvalues = config->getBool(config->holdvalues);
flashLED = config->getString(config->flashLED);
backlightMode = config->getString(config->backlight);
lengthformat = config->getString(config->lengthFormat);
chain_length = config->getInt(config->chainLength);
chain = 0;
anchor_set = false;
alarm_range = 30;
// Initialize config menu
menu = new ConfigMenu("Options", 40, 80);
menu->setItemDimension(150, 20);
ConfigMenuItem *newitem;
newitem = menu->addItem("chain", "Chain out", "int", 0, "m");
if (! newitem) {
// Demo: in case of failure exit here, should never be happen
logger->logDebug(GwLog::ERROR,"Menu item creation failed");
return;
}
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("chainmax", "Chain max", "int", chain_length, "m");
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("zoom", "Zoom", "int", 50, "m");
newitem->setRange(0, 200, {1, });
newitem = menu->addItem("range", "Alarm range", "int", 40, "m");
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("alat", "Adjust anchor lat.", "int", 0, "m");
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("alon", "Adjust anchor lon.", "int", 0, "m");
newitem->setRange(0, 200, {1, 5, 10});
#ifdef BOARD_OBP40S3
// Intodruced here because of missing keys for OBP40
newitem = menu->addItem("anchor", "Anchor down", "bool", 0, "");
#endif
menu->setItemActive("zoom");
}
void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "MODE";
commonData->keydata[1].label = "ALARM";
}
#ifdef BOARD_OBP60S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (mode == 'N') {
if (key == 2) { // Toggle alarm
alarm_enabled = !alarm_enabled;
return 0;
}
} else { // Config mode
if (key == 3) {
// menu down
menu->goNext();
return 0;
}
if (key == 4) {
// menu up
menu->goPrev();
return 0;
}
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
#ifdef BOARD_OBP40S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
commonData->keydata[1].label = "EDIT";
} else {
mode = 'N';
commonData->keydata[1].label = "ALARM";
}
return 0;
}
if (mode == 'N') {
if (key == 2) { // Toggle alarm
alarm_enabled = !alarm_enabled;
return 0;
}
} else { // Config mode
// TODO different code for OBP40 / OBP60
if (key == 9) {
// menu down
if (editmode > 0) {
// decrease item value
menu->getActiveItem()->decValue();
} else {
menu->goNext();
}
return 0;
}
if (key == 10) {
// menu up or value up
if (editmode > 0) {
// increase item value
menu->getActiveItem()->incValue();
} else {
menu->goPrev();
}
return 0;
}
if (key == 2) {
// enter / leave edit mode for current menu item
if (editmode > 0) {
commonData->keydata[1].label = "EDIT";
editmode = 0;
} else {
commonData->keydata[1].label = "SET";
editmode = 1;
}
return 0;
}
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
void displayNew(PageData &pageData){
};
int displayPage(PageData &pageData){
// Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageAnchor; Mode=%c", mode);
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageAnchor(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageAnchor(
"Anchor", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"DBS", "HDT", "AWS", "AWD", "LAT", "LON", "HDOP"}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off
);
#endif

View File

@@ -120,7 +120,7 @@ bool homevalid = false; // homelat and homelon are valid
}
else{
value1 = simtime++; // Simulation data for time value 11:36 in seconds
} // Other simulation data see OBP60Formater.cpp
} // Other simulation data see OBP60Formatter.cpp
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value

View File

@@ -83,7 +83,7 @@ class PageCompass : public Page
String DataText[HowManyValues];
String DataUnits[HowManyValues];
String DataFormat[HowManyValues];
FormatedData TheFormattedData;
FormattedData TheFormattedData;
for (int i = 0; i < HowManyValues; i++){
bvalue = pageData.values[i];

View File

@@ -53,7 +53,7 @@ class PageFourValues : public Page
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue)
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
@@ -63,7 +63,7 @@ class PageFourValues : public Page
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
@@ -73,7 +73,7 @@ class PageFourValues : public Page
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue)
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated

View File

@@ -86,8 +86,7 @@ public:
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots
const char *ii = " ";
switch (i)
{
switch (i) {
case 0: ii=" "; break; // Use a blank for a empty scale value
case 30 : ii=" "; break;
case 60 : ii=" "; break;

View File

@@ -41,9 +41,9 @@ public:
String backlightMode = config->getString(config->backlight);
int rolllimit = config->getInt(config->rollLimit);
String roffset = config->getString(config->rollOffset);
double rolloffset = roffset.toFloat()/360*(2*PI);
double rolloffset = roffset.toFloat()/360*(2*M_PI);
String poffset = config->getString(config->pitchOffset);
double pitchoffset = poffset.toFloat()/360*(2*PI);
double pitchoffset = poffset.toFloat()/360*(2*M_PI);
// Get boat values for roll
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll)
@@ -55,17 +55,17 @@ public:
}
else{
if(simulation == true){
value1 = (20 + float(random(0, 50)) / 10.0)/360*2*PI;
value1 = (20 + float(random(0, 50)) / 10.0)/360*2*M_PI;
}
else{
value1 = 0;
}
}
if(value1/(2*PI)*360 > -10 && value1/(2*PI)*360 < 10){
svalue1 = String(value1/(2*PI)*360,1); // Convert raw value to string
if(value1/(2*M_PI)*360 > -10 && value1/(2*M_PI)*360 < 10){
svalue1 = String(value1/(2*M_PI)*360,1); // Convert raw value to string
}
else{
svalue1 = String(value1/(2*PI)*360,0);
svalue1 = String(value1/(2*M_PI)*360,0);
}
if(valid1 == true){
svalue1old = svalue1; // Save the old value
@@ -80,17 +80,17 @@ public:
}
else{
if(simulation == true){
value2 = (float(random(-5, 5)))/360*2*PI;
value2 = (float(random(-5, 5)))/360*2*M_PI;
}
else{
value2 = 0;
}
}
if(value2/(2*PI)*360 > -10 && value2/(2*PI)*360 < 10){
svalue2 = String(value2/(2*PI)*360,1); // Convert raw value to string
if(value2/(2*PI)*360 > -10 && value2/(2*M_PI)*360 < 10){
svalue2 = String(value2/(2*M_PI)*360,1); // Convert raw value to string
}
else{
svalue2 = String(value2/(2*PI)*360,0);
svalue2 = String(value2/(2*M_PI)*360,0);
}
if(valid2 == true){
svalue2old = svalue2; // Save the old value
@@ -99,7 +99,7 @@ public:
// Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){
// Limits for roll
if(value1*360/(2*PI) >= -1*rolllimit && value1*360/(2*PI) <= rolllimit){
if(value1*360/(2*M_PI) >= -1*rolllimit && value1*360/(2*M_PI) <= rolllimit){
setBlinkingLED(false);
setFlashLED(false);
}
@@ -177,11 +177,10 @@ public:
// Only scaling +/- 60 degrees
if((i >= 0 && i <= 60) || (i >= 300 && i <= 360)){
// Scaling values
float x = 200 + (rInstrument+25)*sin(i/180.0*pi); // x-coordinate dots
float y = 150 - (rInstrument+25)*cos(i/180.0*pi); // y-coordinate cots
float x = 200 + (rInstrument+25)*sin(i/180.0*M_PI); // x-coordinate dots
float y = 150 - (rInstrument+25)*cos(i/180.0*M_PI); // y-coordinate cots
const char *ii = "";
switch (i)
{
switch (i) {
case 0: ii="0"; break;
case 20 : ii="20"; break;
case 40 : ii="40"; break;
@@ -203,11 +202,11 @@ public:
}
// Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi);
float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi);
float cosx=cos(i/180.0*pi);
float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*M_PI);
// Draw sub scale with lines (two triangles)
if(i % 20 == 0){
@@ -229,11 +228,11 @@ public:
// Draw mast position pointer
float startwidth = 8; // Start width of pointer
// value1 = (2 * pi ) - value1; // Mirror coordiante system for pointer, keel and boat
// value1 = (2 * M_PI ) - value1; // Mirror coordiante system for pointer, keel and boat
if(valid1 == true || holdvalues == true || simulation == true){
float sinx=sin(value1 + pi);
float cosx=cos(value1 + pi);
float sinx=sin(value1 + M_PI);
float cosx=cos(value1 + M_PI);
// Normal pointer
// Pointer as triangle with center base 2*width
float xx1 = -startwidth;

View File

@@ -2,6 +2,7 @@
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "ConfigMenu.h"
#include "images/logo64.xbm"
#include <esp32/clk.h>
#include "qrcode.h"
@@ -25,11 +26,33 @@
* Consists of some sub-pages with following content:
* 1. Hard and software information
* 2. System settings
* 3. NMEA2000 device list
* 3. System configuration: running and NVRAM
* 4. NMEA2000 device list
* 5. SD Card information if available
*
* TODO
* - setCpuFrequencyMhz(80|160|240);
* - Accesspoint / ! Änderung im Gatewaycode erforderlich?
* if (! isApActive()) {
* wifiSSID = config->getString(config->wifiSSID);
* wifiPass = config->getString(config->wifiPass);
* wifiSoftAP(wifiSSID, wifiPass);
* }
* - Power mode
* powerInit(powermode);
*/
class PageSystem : public Page
{
private:
GwConfigHandler *config;
GwLog *logger;
// NVRAM config options
String flashLED;
// Generic data access
uint64_t chipid;
bool simulation;
bool sdcard;
@@ -47,158 +70,59 @@ String rot_sensor;
double homelat;
double homelon;
char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard
char mode = 'N'; // (N)ormal, (S)ettings, (C)onfiguration, (D)evice list, c(A)rd
int8_t editmode = -1; // marker for menu/edit/set function
public:
PageSystem(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSystem");
if (hasFRAM) {
mode = fram.read(FRAM_SYSTEM_MODE);
}
chipid = ESP.getEfuseMac();
simulation = common.config->getBool(common.config->useSimuData);
#ifdef BOARD_OBP40S3
sdcard = common.config->getBool(common.config->useSDCard);
#endif
buzzer_mode = common.config->getString(common.config->buzzerMode);
buzzer_mode.toLowerCase();
buzzer_power = common.config->getInt(common.config->buzzerPower);
cpuspeed = common.config->getString(common.config->cpuSpeed);
env_module = common.config->getString(common.config->useEnvSensor);
rtc_module = common.config->getString(common.config->useRTC);
gps_module = common.config->getString(common.config->useGPS);
batt_sensor = common.config->getString(common.config->usePowSensor1);
solar_sensor = common.config->getString(common.config->usePowSensor2);
gen_sensor = common.config->getString(common.config->usePowSensor3);
rot_sensor = common.config->getString(common.config->useRotSensor);
homelat = common.config->getString(common.config->homeLAT).toDouble();
homelon = common.config->getString(common.config->homeLON).toDouble();
}
ConfigMenu *menu;
virtual void setupKeys(){
commonData->keydata[0].label = "EXIT";
commonData->keydata[1].label = "MODE";
commonData->keydata[2].label = "";
commonData->keydata[3].label = "RST";
commonData->keydata[4].label = "STBY";
commonData->keydata[5].label = "ILUM";
}
virtual int handleKey(int key){
// do *NOT* handle key #1 this handled by obp60task as exit
// Switch display mode
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler");
if (key == 2) {
if (mode == 'N') {
void incMode() {
if (mode == 'N') { // Normal
mode = 'S';
} else if (mode == 'S') {
} else if (mode == 'S') { // Settings
mode = 'C';
} else if (mode == 'C') { // Config
mode = 'D';
} else if (mode == 'D') {
} else if (mode == 'D') { // Device list
if (sdcard) {
mode = 'A'; // SD-Card
} else {
mode = 'N';
}
} else {
mode = 'N';
}
}
void decMode() {
if (mode == 'N') {
if (sdcard) {
mode = 'A';
} else {
mode = 'D';
}
} else if (mode == 'S') { // Settings
mode = 'N';
} else if (mode == 'C') { // Config
mode = 'S';
} else if (mode == 'D') { // Device list
mode = 'C';
} else {
mode = 'N';
}
} else {
mode = 'N';
}
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
return 0;
}
#ifdef BOARD_OBP60S3
// grab cursor key to disable page navigation
if (key == 3) {
return 0;
}
// soft reset
if (key == 4) {
ESP.restart();
}
// standby / deep sleep
if (key == 5) {
deepSleep(*commonData);
}
// Code for keylock
if (key == 11) {
commonData->keylock = !commonData->keylock;
return 0;
}
#endif
#ifdef BOARD_OBP40S3
// grab cursor keys to disable page navigation
if (key == 9 or key == 10) {
return 0;
}
// standby / deep sleep
if (key == 12) {
deepSleep(*commonData);
}
#endif
return key;
}
void displayBarcode(String serialno, uint16_t x, uint16_t y, uint16_t s) {
// Barcode with serial number
// x, y is top left corner
// s is pixel size of a single box
QRCode qrcode;
uint8_t qrcodeData[qrcode_getBufferSize(4)];
#ifdef BOARD_OBP40S3
String prefix = "OBP40:SN:";
#endif
#ifdef BOARD_OBP60S3
String prefix = "OBP60:SN:";
#endif
qrcode_initText(&qrcode, qrcodeData, 4, 0, (prefix + serialno).c_str());
int16_t x0 = x;
for (uint8_t j = 0; j < qrcode.size; j++) {
for (uint8_t i = 0; i < qrcode.size; i++) {
if (qrcode_getModule(&qrcode, i, j)) {
getdisplay().fillRect(x, y, s, s, commonData->fgcolor);
}
x += s;
}
y += s;
x = x0;
mode = 'D';
}
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayModeNormal() {
// Default system page view
// 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
LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem");
// Draw page
//***********************************************************
uint16_t x0 = 8; // left column
uint16_t y0 = 48; // data table starts here
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
if (mode == 'N') {
uint16_t y0 = 155;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System Information");
getdisplay().print("System information");
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
y0 = 155;
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
@@ -287,17 +211,67 @@ public:
getdisplay().print("Task free:");
getdisplay().setCursor(300, y0 + 32);
getdisplay().print(String(RAM_free));
}
} else if (mode == 'S') {
// Settings
void displayModeConfig() {
// Configuration interface
uint16_t x0 = 16;
uint16_t y0 = 80;
uint16_t dy = 20;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System configuration");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
/*getdisplay().setCursor(x0, y0);
getdisplay().print("CPU speed: 80 | 160 | 240");
getdisplay().setCursor(x0, y0 + 1 * dy);
getdisplay().print("Power mode: Max | 5V | Min");
getdisplay().setCursor(x0, y0 + 2 * dy);
getdisplay().print("Accesspoint: On | Off");
// TODO Change NVRAM-preferences settings here
getdisplay().setCursor(x0, y0 + 4 * dy);
getdisplay().print("Simulation: On | Off"); */
getdisplay().setFont(&Ubuntu_Bold8pt8b);
for (int i = 0 ; i < menu->getItemCount(); i++) {
ConfigMenuItem *itm = menu->getItemByIndex(i);
if (!itm) {
LOG_DEBUG(GwLog::ERROR, "Menu item not found: %d", i);
} else {
Rect r = menu->getItemRect(i);
bool inverted = (i == menu->getActiveIndex());
drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false);
if (inverted and editmode > 0) {
// triangle as edit marker
getdisplay().fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor);
}
getdisplay().setCursor(r.x + r.w + 40, r.y + r.h - 4);
if (itm->getType() == "int") {
getdisplay().print(itm->getValue());
getdisplay().print(itm->getUnit());
} else {
getdisplay().print(itm->getValue() == 0 ? "No" : "Yes");
}
}
}
}
void displayModeSettings() {
// View some of the current settings
const uint16_t x0 = 8;
const uint16_t y0 = 72;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, 48);
getdisplay().print("System settings");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
x0 = 8;
y0 = 72;
// left column
getdisplay().setCursor(x0, y0);
@@ -333,12 +307,10 @@ public:
// Home location
getdisplay().setCursor(x0, y0 + 128);
getdisplay().print("Home Lat.:");
getdisplay().setCursor(120, y0 + 128);
getdisplay().print(formatLatitude(homelat));
drawTextRalign(230, y0 + 128, formatLatitude(homelat));
getdisplay().setCursor(x0, y0 + 144);
getdisplay().print("Home Lon.:");
getdisplay().setCursor(120, y0 + 144);
getdisplay().print(formatLongitude(homelon));
drawTextRalign(230, y0 + 144, formatLongitude(homelon));
// right column
getdisplay().setCursor(202, y0);
@@ -358,23 +330,43 @@ public:
getdisplay().setCursor(320, y0 + 32);
getdisplay().print(gen_sensor);
// Gyro sensor
#ifdef BOARD_OBP60S3
// Backlight infos
getdisplay().setCursor(202, y0 + 64);
getdisplay().print("Backlight:");
getdisplay().setCursor(320, y0 + 64);
getdisplay().printf("%d%%", commonData->backlight.brightness);
// TODO test function with OBP60 device
getdisplay().setCursor(202, y0 + 80);
getdisplay().print("Bl color:");
getdisplay().setCursor(320, y0 + 80);
getdisplay().print(commonData->backlight.color.toName());
getdisplay().setCursor(202, y0 + 96);
getdisplay().print("Bl mode:");
getdisplay().setCursor(320, y0 + 96);
getdisplay().print(commonData->backlight.mode);
// TODO Buzzer mode and power
#endif
// Gyro sensor
// WIP / FEATURE
}
void displayModeSDCard() {
// SD card info
uint16_t x0 = 20;
uint16_t y0 = 72;
} else if (mode == 'C') {
// Card info
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("SD Card info");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
x0 = 20;
y0 = 72;
getdisplay().setCursor(x0, y0);
getdisplay().print("Work in progress...");
}
} else {
void displayModeDevicelist() {
// NMEA2000 device list
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
@@ -389,6 +381,176 @@ public:
getdisplay().print(String(commonData->status.n2kTx));
}
void storeConfig() {
menu->storeValues();
}
public:
PageSystem(CommonData &common){
commonData = &common;
config = commonData->config;
logger = commonData->logger;
logger->logDebug(GwLog::LOG,"Instantiate PageSystem");
if (hasFRAM) {
mode = fram.read(FRAM_SYSTEM_MODE);
}
flashLED = common.config->getString(common.config->flashLED);
chipid = ESP.getEfuseMac();
simulation = common.config->getBool(common.config->useSimuData);
#ifdef BOARD_OBP40S3
sdcard = common.config->getBool(common.config->useSDCard);
#endif
buzzer_mode = common.config->getString(common.config->buzzerMode);
buzzer_mode.toLowerCase();
buzzer_power = common.config->getInt(common.config->buzzerPower);
cpuspeed = common.config->getString(common.config->cpuSpeed);
env_module = common.config->getString(common.config->useEnvSensor);
rtc_module = common.config->getString(common.config->useRTC);
gps_module = common.config->getString(common.config->useGPS);
batt_sensor = common.config->getString(common.config->usePowSensor1);
solar_sensor = common.config->getString(common.config->usePowSensor2);
gen_sensor = common.config->getString(common.config->usePowSensor3);
rot_sensor = common.config->getString(common.config->useRotSensor);
homelat = common.config->getString(common.config->homeLAT).toDouble();
homelon = common.config->getString(common.config->homeLON).toDouble();
// CPU speed: 80 | 160 | 240
// Power mode: Max | 5V | Min
// Accesspoint: On | Off
// TODO Change NVRAM-preferences settings here
// getdisplay().setCursor(x0, y0 + 4 * dy);
// getdisplay().print("Simulation: On | Off");
// Initialize config menu
menu = new ConfigMenu("Options", 40, 80);
menu->setItemDimension(150, 20);
ConfigMenuItem *newitem;
newitem = menu->addItem("accesspoint", "Accesspoint", "bool", 0, "");
newitem = menu->addItem("simulation", "Simulation", "on/off", 0, "");
menu->setItemActive("accesspoint");
}
virtual void setupKeys(){
commonData->keydata[0].label = "EXIT";
commonData->keydata[1].label = "MODE";
commonData->keydata[2].label = "";
commonData->keydata[3].label = "RST";
commonData->keydata[4].label = "STBY";
commonData->keydata[5].label = "ILUM";
}
virtual int handleKey(int key){
// do *NOT* handle key #1 this handled by obp60task as exit
// Switch display mode
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler");
if (key == 2) {
incMode();
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
return 0;
}
#ifdef BOARD_OBP60S3
// grab cursor key to disable page navigation
if (key == 3) {
return 0;
}
// soft reset
if (key == 4) {
ESP.restart();
}
// standby / deep sleep
if (key == 5) {
deepSleep(*commonData);
}
// Code for keylock
if (key == 11) {
commonData->keylock = !commonData->keylock;
return 0;
}
#endif
#ifdef BOARD_OBP40S3
// use cursor keys for local mode navigation
if (key == 9) {
incMode();
return 0;
}
if (key == 10) {
decMode();
return 0;
}
// standby / deep sleep
if (key == 12) {
deepSleep(*commonData);
}
#endif
return key;
}
void displayBarcode(String serialno, uint16_t x, uint16_t y, uint16_t s) {
// Barcode with serial number
// x, y is top left corner
// s is pixel size of a single box
QRCode qrcode;
uint8_t qrcodeData[qrcode_getBufferSize(4)];
#ifdef BOARD_OBP40S3
String prefix = "OBP40:SN:";
#endif
#ifdef BOARD_OBP60S3
String prefix = "OBP60:SN:";
#endif
qrcode_initText(&qrcode, qrcodeData, 4, 0, (prefix + serialno).c_str());
int16_t x0 = x;
for (uint8_t j = 0; j < qrcode.size; j++) {
for (uint8_t i = 0; i < qrcode.size; i++) {
if (qrcode_getModule(&qrcode, i, j)) {
getdisplay().fillRect(x, y, s, s, commonData->fgcolor);
}
x += s;
}
y += s;
x = x0;
}
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Optical warning by limit violation (unused)
if(flashLED == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging page information
LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem, Mode=%c", mode);
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height());
// call current system page
switch (mode) {
case 'N':
displayModeNormal();
break;
case 'S':
displayModeSettings();
break;
case 'C':
displayModeConfig();
break;
case 'A':
displayModeSDCard();
break;
case 'D':
displayModeDevicelist();
break;
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
return PAGE_OK;

View File

@@ -51,7 +51,7 @@ class PageThreeValues : public Page
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue)
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
@@ -61,7 +61,7 @@ class PageThreeValues : public Page
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated

View File

@@ -49,7 +49,7 @@ class PageTwoValues : public Page
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue)
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated

View File

@@ -206,7 +206,7 @@ public:
// read boat data values; TWD only for validation test, TWS for display of current value
for (int i = 0; i < numBoatData; i++) {
bvalue = pageData.values[i];
// BDataName[i] = xdrDelete(bvalue->getName());
BDataName[i] = xdrDelete(bvalue->getName());
BDataName[i] = BDataName[i].substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue, logger); // Check if boat data value is to be calibrated
BDataValue[i] = bvalue->value; // Value as double in SI unit
@@ -326,24 +326,13 @@ public:
chrtVal = static_cast<int>(pageData.boatHstry.twdHstry->get(bufStart + (i * dataIntv))); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer
if (chrtVal == INT16_MIN) {
chrtPrevVal = INT16_MIN;
/* if (i == linesToShow - 1) {
numNoData++;
// If more than 4 invalid values in a row, reset chart
} else {
numNoData = 0; // Reset invalid value counter
}
if (numNoData > 4) {
// If more than 4 invalid values in a row, send message
getdisplay().setFont(&Ubuntu_Bold10pt8b);
getdisplay().fillRect(xCenter - 66, height / 2 - 20, 146, 24, commonData->bgcolor); // Clear area for TWS value
drawTextCenter(xCenter, height / 2 - 10, "No sensor data");
} */
} else {
chrtVal = static_cast<int>((chrtVal / 1000.0 * radToDeg) + 0.5); // Convert to degrees and round
x = ((chrtVal - wndLeft + 360) % 360) * chrtScl;
y = yOffset + cHeight - i; // Position in chart area
if (i >= (numWndVals / dataIntv) - 10)
// if (i >= (numWndVals / dataIntv) - 10)
if (i >= (numWndVals / dataIntv) - 1)
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
if ((i == 0) || (chrtPrevVal == INT16_MIN)) {
@@ -378,7 +367,8 @@ public:
int minWndDir = pageData.boatHstry.twdHstry->getMin(numWndVals) / 1000.0 * radToDeg;
int maxWndDir = pageData.boatHstry.twdHstry->getMax(numWndVals) / 1000.0 * radToDeg;
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter);
if ((minWndDir + 540 >= wndCenter + 540) || (maxWndDir + 540 <= wndCenter + 540)) {
// if ((minWndDir + 540 >= wndCenter + 540) || (maxWndDir + 540 <= wndCenter + 540)) {
if (((minWndDir - wndCenter >= 0) && (minWndDir - wndCenter < 180)) || ((maxWndDir - wndCenter <= 0) && (maxWndDir - wndCenter >=180))) {
// Check if all wind value are left or right of center value -> optimize chart range
midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals) / 1000.0 * radToDeg;
if (midWndDir != INT16_MIN) {

View File

@@ -48,7 +48,7 @@ public:
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values for AWA
// Get boat value for AWA
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name
@@ -63,8 +63,8 @@ public:
unit1old = unit1; // Save old unit
}
// Get boat values for AWS
GwApi::BoatValue *bvalue2 = pageData.values[1]; // First element in list (only one value by PageOneValue)
// Get boat value for AWS
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
@@ -77,8 +77,8 @@ public:
unit2old = unit2; // Save old unit
}
// Get boat values TWD
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
// Get boat value for TWD
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
@@ -91,8 +91,8 @@ public:
unit3old = unit3; // Save old unit
}
// Get boat values TWS
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue)
// Get boat value for TWS
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
@@ -105,8 +105,8 @@ public:
unit4old = unit4; // Save old unit
}
// Get boat values DBT
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue)
// Get boat value for DBT
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list
String name5 = xdrDelete(bvalue5->getName()); // Value name
name5 = name5.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
@@ -119,8 +119,8 @@ public:
unit5old = unit5; // Save old unit
}
// Get boat values STW
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue)
// Get boat value for STW
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list
String name6 = xdrDelete(bvalue6->getName()); // Value name
name6 = name6.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated

View File

@@ -48,7 +48,7 @@ public:
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values for AWA
// Get boat values #1
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name
@@ -63,8 +63,8 @@ public:
unit1old = unit1; // Save old unit
}
// Get boat values for AWS
GwApi::BoatValue *bvalue2 = pageData.values[1]; // First element in list (only one value by PageOneValue)
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
@@ -77,8 +77,8 @@ public:
unit2old = unit2; // Save old unit
}
// Get boat values TWD
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
// Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
@@ -91,8 +91,8 @@ public:
unit3old = unit3; // Save old unit
}
// Get boat values TWS
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue)
// Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
@@ -105,8 +105,8 @@ public:
unit4old = unit4; // Save old unit
}
// Get boat values DBT
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue)
// Get boat values #5
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list
String name5 = xdrDelete(bvalue5->getName()); // Value name
name5 = name5.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
@@ -119,8 +119,8 @@ public:
unit5old = unit5; // Save old unit
}
// Get boat values STW
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue)
// Get boat values #5
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list
String name6 = xdrDelete(bvalue6->getName()); // Value name
name6 = name6.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated
@@ -192,7 +192,7 @@ public:
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65);
if(valid3 == true){
// getdisplay().print(abs(value3 * 180 / PI), 0); // Value
// getdisplay().print(abs(value3 * 180 / M_PI), 0); // Value
getdisplay().print(svalue4); // Value
}
else{
@@ -236,7 +236,6 @@ public:
// Draw wind rose
int rInstrument = 110; // Radius of grafic instrument
float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
@@ -246,11 +245,10 @@ public:
for(int i=0; i<360; i=i+10)
{
// Scaling values
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate dots
float x = 200 + (rInstrument-30)*sin(i/180.0*M_PI); // x-coordinate dots
float y = 150 - (rInstrument-30)*cos(i/180.0*M_PI); // y-coordinate dots
const char *ii = "";
switch (i)
{
switch (i) {
case 0: ii="0"; break;
case 30 : ii="30"; break;
case 60 : ii="60"; break;
@@ -277,11 +275,11 @@ public:
}
// Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi);
float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi);
float cosx=cos(i/180.0*pi);
float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*M_PI);
// Draw sub scale with lines (two triangles)
if(i % 30 == 0){
@@ -332,26 +330,18 @@ public:
//*******************************************************************************************
// Show value6, so that it does not collide with the wind pointer
if ( cos(value1) > 0){
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
if (cos(value1) > 0){
getdisplay().setCursor(160, 200);
getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 215);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit6); // Unit
}
else{
getdisplay().print(unit6old); // Unit
}
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
} else{
getdisplay().setCursor(160, 130);
getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 90);
}
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit6); // Unit
@@ -359,8 +349,6 @@ if ( cos(value1) > 0){
else{
getdisplay().print(unit6old); // Unit
}
}
return PAGE_UPDATE;
};
@@ -380,7 +368,6 @@ PageDescription registerPageWindRoseFlex(
"WindRoseFlex", // Page name
createPage, // Action
6, // Number of bus values depends on selection in Web configuration; was zero
//{"AWA", "AWS", "COG", "SOG", "TWD", "TWS"}, // Bus values we need in the page, modified for WindRose2
true // Show display header on/off
);

View File

@@ -197,7 +197,7 @@ typedef struct{
double value;
String svalue;
String unit;
} FormatedData;
} FormattedData;
// Formatter for boat values
FormatedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);

View File

@@ -75,20 +75,6 @@
"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]",
@@ -1317,7 +1303,6 @@
"default": "Voltage",
"description": "Type of page for page 1",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -1599,7 +1584,6 @@
"default": "WindRose",
"description": "Type of page for page 2",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -1878,7 +1862,6 @@
"default": "OneValue",
"description": "Type of page for page 3",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -2154,7 +2137,6 @@
"default": "TwoValues",
"description": "Type of page for page 4",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -2427,7 +2409,6 @@
"default": "ThreeValues",
"description": "Type of page for page 5",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -2697,7 +2678,6 @@
"default": "FourValues",
"description": "Type of page for page 6",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -2964,7 +2944,6 @@
"default": "FourValues2",
"description": "Type of page for page 7",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -3228,7 +3207,6 @@
"default": "Clock",
"description": "Type of page for page 8",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -3489,7 +3467,6 @@
"default": "RollPitch",
"description": "Type of page for page 9",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -3747,7 +3724,6 @@
"default": "Battery2",
"description": "Type of page for page 10",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",

View File

@@ -75,20 +75,6 @@
"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]",
@@ -1340,7 +1326,6 @@
"default": "Clock",
"description": "Type of page for page 1",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -1622,7 +1607,6 @@
"default": "Wind",
"description": "Type of page for page 2",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -1901,7 +1885,6 @@
"default": "OneValue",
"description": "Type of page for page 3",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -2177,7 +2160,6 @@
"default": "TwoValues",
"description": "Type of page for page 4",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -2450,7 +2432,6 @@
"default": "ThreeValues",
"description": "Type of page for page 5",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -2720,7 +2701,6 @@
"default": "FourValues",
"description": "Type of page for page 6",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -2987,7 +2967,6 @@
"default": "FourValues2",
"description": "Type of page for page 7",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -3251,7 +3230,6 @@
"default": "Fluid",
"description": "Type of page for page 8",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -3512,7 +3490,6 @@
"default": "RollPitch",
"description": "Type of page for page 9",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",
@@ -3770,7 +3747,6 @@
"default": "Battery2",
"description": "Type of page for page 10",
"list": [
"Anchor",
"BME280",
"Battery",
"Battery2",

View File

@@ -34,7 +34,6 @@ no_of_fields_per_page = {
"WindPlot": 0,
"WindRose": 0,
"WindRoseFlex": 6,
"Anchor", 0
}
# No changes needed beyond this point

View File

@@ -17,7 +17,7 @@
#include "OBPDataOperations.h" // Functions lib for data operations such as true wind calculation
#ifdef BOARD_OBP40S3
#include "driver/rtc_io.h" // Needs for wakeup from deep sleep
#include "driver/rtc_io.h" // Needs for weakup from deep sleep
#include <FS.h> // SD-Card access
#include <SD.h>
#include <SPI.h>
@@ -54,31 +54,13 @@ void OBP60Init(GwApi *api){
// Check I2C devices
// Init hardware
hardwareInit(api);
// Init power rail 5.0V
// Init power
String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str());
if(powermode == "Max Power" || powermode == "Only 5.0V"){
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, true);// Power on ePaper display
setPortPin(OBP_POWER_SD, true); // Power on SD card
#endif
}
else{
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, false);// Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
#endif
}
powerInit(powermode);
#ifdef BOARD_OBP40S3
bool sdcard = config->getBool(config->useSDCard);
@@ -313,8 +295,6 @@ void registerAllPages(PageList &list){
list.add(&registerPageXTETrack);
extern PageDescription registerPageFluid;
list.add(&registerPageFluid);
extern PageDescription registerPageAnchor;
list.add(&registerPageAnchor);
}
// Undervoltage detection for shutdown display
@@ -406,10 +386,9 @@ bool addTrueWind(GwApi* api, BoatValueList* boatValues) {
hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MIN;
hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MIN;
varVal = varBVal->valid ? varBVal->value : DBL_MIN;
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task addTrueWind: AWA: %.1f, AWS: %.1f, COG: %.1f, STW: %.1f, HDT: %.1f, HDM: %.1f, VAR: %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task addTrueWind: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.1f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
// isCalculated = WindUtils::calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twdBVal->value, &twsBVal->value, &twaBVal->value);
isCalculated = WindUtils::calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa);
if (isCalculated) { // Replace values only, if successfully calculated and not already available
@@ -426,8 +405,8 @@ bool addTrueWind(GwApi* api, BoatValueList* boatValues) {
twaBVal->valid = true;
}
}
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task calcTrueWind: TWD_Valid? %d, TWD=%.1f, TWS=%.1f, TWA=%.1f, isCalculated? %d", twdBVal->valid, twdBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852,
twaBVal->value * RAD_TO_DEG, isCalculated);
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task addTrueWind: TWD_Valid %d, isCalculated %d, TWD %.1f, TWA %.1f, TWS %.1f", twdBVal->valid, isCalculated, twdBVal->value * RAD_TO_DEG,
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
return isCalculated;
}
@@ -466,8 +445,8 @@ void handleHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryB
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task handleHstryBuf: twdBVal: %f, twsBVal: %f, twaBVal: %f, TWD_isValid? %d", twdBVal->value * RAD_TO_DEG,
twsBVal->value * 3.6 / 1.852, twaBVal->value * RAD_TO_DEG, twdBVal->valid);
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task handleHstryBuf: twdBVal: %.1f, twaBVal: %.1f, twsBVal: %.1f, TWD_isValid? %d", twdBVal->value * RAD_TO_DEG,
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852, twdBVal->valid);
calBVal = new GwApi::BoatValue("TWD"); // temporary solution for calibration of history buffer values
calBVal->setFormat(twdBVal->getFormat());
if (twdBVal->valid) {