Compare commits

..

61 Commits

Author SHA1 Message Date
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
Thomas Hooge f4d88f1b8b Code rework at page system. Preparation for config menu. 2025-07-31 11:39:23 +02:00
Thomas Hooge 588008e370 Fix typo: Formated -> Formatted 2025-07-29 19:33:31 +02:00
Thomas Hooge 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
Norbert Walter e93193c3e0
Merge pull request #187 from thooge/master
Change xbm file header to fix strange accesspoint behaviour
2025-07-26 21:21:46 +02:00
Thomas Hooge e367d15568 Change xbm file header to fix strange accesspoint behaviour 2025-07-26 20:22:07 +02:00
Norbert Walter 2773685db3
Merge pull request #186 from Scorgan01/PageWindPlot
Page WindPlot v1, history buffer and true wind calculation
2025-07-26 18:36:58 +02:00
Norbert Walter 9953165dfe
Merge pull request #185 from thooge/master
Fix OBP40-Logo for WhitePage, use xbm image format
2025-07-26 18:35:25 +02:00
Ulrich Meine da451bee70 removed test comment from wind function call 2025-07-26 09:27:35 +02:00
Ulrich Meine f79124eed3 Revert speed change in platformio.ini 2025-07-25 19:54:01 +02:00
Ulrich Meine 33b5776421 Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-07-25 19:50:47 +02:00
Ulrich Meine 2954a9a58b adjust page call to new standard; clean debug code; fix TWS print alignment 2025-07-25 19:50:42 +02:00
Scorgan01 e6add8e6fc
Merge branch 'norbert-walter:master' into PageWindPlot 2025-07-25 19:44:02 +02:00
norbert-walter 15bcd53350 Add display infos in platformio.ini 2025-07-25 14:29:54 +02:00
Ulrich Meine 938b566bfc Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-07-25 08:43:01 +02:00
Ulrich Meine fe2223839f added calibration to buffer; separated buffer and wind code in opb60task; prepared simulation; getMin/Max fix for ringbuffer for invalid data; fix for chart center; cleanup code 2025-07-25 08:42:43 +02:00
Scorgan01 c888804aef
Merge branch 'norbert-walter:master' into PageWindPlot 2025-07-25 08:19:24 +02:00
Thomas Hooge d963153b35 Fix OBP40-Logo for WhitePage, use xbm image format 2025-07-24 19:51:38 +02:00
Norbert Walter 4934fb1346
Merge pull request #184 from thooge/master
Fix missing config data for OBP40
2025-07-24 16:22:34 +02:00
norbert-walter 8150947cd6 Fix contrast lost for GDEY042T81 display 2025-07-24 14:28:56 +02:00
Thomas Hooge 1e7368db4d Additional bug fix in JSON-configuration 2025-07-24 10:35:01 +02:00
Thomas Hooge 238e0fc79d Fix missing config data for OBP40 2025-07-24 09:50:16 +02:00
Norbert Walter 34801d1e0e
Merge pull request #183 from thooge/alarm
Preparation for upcoming alarm functionality
2025-07-23 18:50:30 +02:00
Thomas Hooge 0afe629b38 Preparation for upcoming alarm functionality 2025-07-23 14:00:06 +02:00
norbert-walter ccc0d2b6c1 Use fix lib for FRAM 2025-07-22 22:50:53 +02:00
norbert-walter 791fa10b01 Use fix lib for FRAM 2025-07-21 10:25:50 +02:00
Ulrich Meine c48c6a2e48 Move buffer handling to obp60task; reset OBPSensorTask; add true wind calculation 2025-07-19 00:26:37 +02:00
Norbert Walter 4110e3f812
Merge pull request #182 from thooge/system
Add display library version info to page system
2025-07-15 13:08:21 +02:00
Ulrich Meine bb99978177 no buffer writes for invalid data; fix ringbuffer index 2025-07-14 21:17:17 +02:00
norbert-walter 85dee2a622 Fix page refresh if using keypad 2025-07-14 14:27:33 +02:00
norbert-walter f2e069b768 Merge remote-tracking branch 'origin/master' 2025-07-14 14:14:56 +02:00
norbert-walter 71946248e2 Fix voltage sensor for OBP40 2025-07-14 14:14:50 +02:00
Ulrich Meine 91a3ac081f Merge branch 'PageWindPlot' of https://github.com/Scorgan01/esp32-nmea2000-obp60 into PageWindPlot 2025-07-13 00:26:24 +02:00
Ulrich Meine 59cf52b5d2 Semaphore + chart fixes; added simulation data 2025-07-13 00:26:16 +02:00
Scorgan01 60193fa3be
Merge branch 'norbert-walter:master' into PageWindPlot 2025-07-12 01:49:20 +02:00
norbert-walter 7f954e702d Add debugging info 2025-07-11 18:37:12 +02:00
Ulrich Meine 72ddeb3cfb Pointer correction -> no data copy; conc. access issues 2025-07-01 01:27:41 +02:00
Ulrich Meine 2729ef9cb6 Implement v1 history data storage at OBPSensorTask 2025-06-25 23:14:09 +02:00
Ulrich Meine 1f90cefbd6 Implement OBPRingBuffer class and adjust PageWindPlot accordingly 2025-06-24 00:05:15 +02:00
Ulrich Meine 9ada5be7cb Handling of missing data 2025-06-22 14:29:03 +02:00
Ulrich Meine 03d8339170 fix interval, border cross, TWS flip, range; add config_obp40; adjust axis legend 2025-06-22 00:11:54 +02:00
Ulrich Meine 73656e7d14 Buffer and interval stuff 2025-06-18 23:40:57 +02:00
Ulrich Meine bd9741d851 buffer extension; still some errors 2025-06-14 02:19:52 +02:00
Ulrich Meine 13c85adad2 completed config.json; modified TWS flipping; almost fully fixed chart rng overflow 2025-06-13 17:43:23 +02:00
Ulrich Meine 9b504469bc Fixes for TWS flip, scale calculation, chart range overflow; add axis lines 2025-06-12 23:41:15 +02:00
Ulrich Meine f0aba89301 Simulation data; ext. chart area; flexible TWS position 2025-06-09 22:32:34 +02:00
Ulrich Meine fe095a9716 Y Axis label; some interval bug fixing 2025-06-09 17:58:57 +02:00
Ulrich Meine 235188dfb2 Added update interval + no sens data msg; corrected rounding 2025-06-09 13:35:54 +02:00
Ulrich Meine aa70c34a96 Fix getMin/Max + wndCenter rounding; 2025-06-08 13:45:20 +02:00
Ulrich Meine 62aef176d3 Chart + plotshift working 2025-06-06 23:06:04 +02:00
Scorgan01 9f79a7d4bc
Switch to TWD 2025-06-06 10:17:11 +02:00
Ulrich Meine bf4dff45b4 Compact config reading code 2025-06-05 23:42:36 +02:00
Ulrich Meine f153d82825 PlotShift 2025-06-05 22:51:25 +02:00
Ulrich Meine da06f3e791 Automatic scale adjustment + plot shift 2025-06-05 01:04:07 +02:00
Ulrich Meine 7d66ec91da Principle working; several bugs incl. 2025-06-03 22:52:06 +02:00
45 changed files with 6040 additions and 610 deletions

View File

@ -526,3 +526,17 @@ env.Append(
) )
#script does not run on clean yet - maybe in the future #script does not run on clean yet - maybe in the future
env.AddPostAction("clean",cleangenerated) env.AddPostAction("clean",cleangenerated)
#look for extra task scripts and include them here
for taskdir in userTaskDirs:
script = os.path.join(taskdir, "extra_task.py")
if os.path.isfile(script):
taskname = os.path.basename(os.path.normpath(taskdir))
print("#extra task script for '{}'".format(taskname))
with open(script) as fh:
try:
code = compile(fh.read(), taskname, 'exec')
except SyntaxError:
print("#ERROR: script does not compile")
continue
exec(code)

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){ void setPortPin(uint pin, bool value){
pinMode(pin, OUTPUT); pinMode(pin, OUTPUT);
digitalWrite(pin, value); digitalWrite(pin, value);
@ -300,6 +321,49 @@ void fillPoly4(const std::vector<Point>& p4, uint16_t color) {
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color); getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color);
} }
// Split string into words, whitespace separated
std::vector<String> split(const String &s) {
std::vector<String> words;
String word = "";
for (size_t i = 0; i < s.length(); i++) {
if (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n') {
if (word.length() > 0) {
words.push_back(word);
word = "";
}
} else {
word += s[i];
}
}
if (word.length() > 0) {
words.push_back(word);
}
return words;
}
// Wordwrap single line, monospaced font
std::vector<String> wordwrap(String &line, uint16_t maxwidth) {
std::vector<String> lines;
std::vector<String> words = split(line);
String currentLine = "";
for (const auto& word : words) {
if (currentLine.length() + word.length() + 1 > maxwidth) {
if (currentLine.length() > 0) {
lines.push_back(currentLine);
currentLine = "";
}
}
if (currentLine.length() > 0) {
currentLine += " ";
}
currentLine += word;
}
if (currentLine.length() > 0) {
lines.push_back(currentLine);
}
return lines;
}
// Draw centered text // Draw centered text
void drawTextCenter(int16_t cx, int16_t cy, String text) { void drawTextCenter(int16_t cx, int16_t cy, String text) {
int16_t x1, y1; int16_t x1, y1;
@ -545,6 +609,47 @@ void displayFooter(CommonData &commonData) {
#endif #endif
} }
// Alarm overlay, to be drawn as very last draw operation
void displayAlarm(CommonData &commonData) {
const uint16_t x = 50; // overlay area
const uint16_t y = 100;
const uint16_t w = 300;
const uint16_t h = 150;
getdisplay().setFont(&Atari16px);
getdisplay().setTextColor(commonData.fgcolor);
// overlay
getdisplay().drawRect(x, y, w, h, commonData.fgcolor);
getdisplay().fillRect(x + 1, y + 1, w - 1, h - 1, commonData.bgcolor);
getdisplay().drawRect(x + 3, y + 3, w - 5, h - 5, commonData.fgcolor);
// exclamation icon in left top corner
getdisplay().drawXBitmap(x + 16, y + 16, exclamation_bits, exclamation_width, exclamation_height, commonData.fgcolor);
// title
getdisplay().setCursor(x + 64, y + 30);
getdisplay().print("A L A R M");
getdisplay().setCursor(x + 64, y + 48);
getdisplay().print("#" + commonData.alarm.id);
getdisplay().print(" from ");
getdisplay().print(commonData.alarm.source);
// message, but maximum 4 lines
std::vector<String> lines = wordwrap (commonData.alarm.message, w - 16 - 8 / 8);
int n = 0;
for (const auto& l : lines) {
getdisplay().setCursor(x + 16, y + 80 + n);
getdisplay().print(l);
n += 16;
if (n > 64) {
break;
}
}
drawTextCenter(x + w / 2, y + h - 16, "Press button 1 to dismiss alarm");
}
// Sunset und sunrise calculation // Sunset und sunrise calculation
SunData calcSunsetSunrise(double time, double date, double latitude, double longitude, float timezone){ SunData calcSunsetSunrise(double time, double date, double latitude, double longitude, float timezone){
SunData returnset; SunData returnset;

View File

@ -57,6 +57,11 @@ GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay();
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(); GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
#endif #endif
// Page display return values
#define PAGE_OK 0 // all ok, do nothing
#define PAGE_UPDATE 1 // page wants display to update
#define PAGE_HIBERNATE 2 // page wants displey to hibernate
struct Point { struct Point {
double x; double x;
double y; double y;
@ -70,6 +75,7 @@ void deepSleep(CommonData &common);
uint8_t getLastPage(); uint8_t getLastPage();
void hardwareInit(GwApi *api); void hardwareInit(GwApi *api);
void powerInit(String powermode);
void setPortPin(uint pin, bool value); // Set port pin for extension port void setPortPin(uint pin, bool value); // Set port pin for extension port
@ -97,6 +103,7 @@ void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color);
void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop); // Draw display header void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop); // Draw display header
void displayFooter(CommonData &commonData); void displayFooter(CommonData &commonData);
void displayAlarm(CommonData &commonData);
SunData calcSunsetSunrise(double time, double date, double latitude, double longitude, float timezone); // Calulate sunset and sunrise SunData calcSunsetSunrise(double time, double date, double latitude, double longitude, float timezone); // Calulate sunset and sunrise
SunData calcSunsetSunriseRTC(struct tm *rtctime, double latitude, double longitude, float timezone); SunData calcSunsetSunriseRTC(struct tm *rtctime, double latitude, double longitude, float timezone);
@ -142,12 +149,12 @@ static unsigned char fram_bits[] PROGMEM = {
0xff, 0xff, 0xf8, 0x1f, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f,
0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f }; 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f };
static unsigned char ap_bits[] = { static unsigned char ap_bits[] PROGMEM = {
0xe0, 0x03, 0x18, 0x0c, 0x04, 0x10, 0xc2, 0x21, 0x30, 0x06, 0x08, 0x08, 0xe0, 0x03, 0x18, 0x0c, 0x04, 0x10, 0xc2, 0x21, 0x30, 0x06, 0x08, 0x08,
0xc0, 0x01, 0x20, 0x02, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x20, 0x02, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01,
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
static unsigned char dish_bits[] PROGMEM= { static unsigned char dish_bits[] PROGMEM = {
0x3c, 0x00, 0x42, 0x18, 0xfa, 0x1b, 0x02, 0x04, 0x02, 0x0a, 0x02, 0x09, 0x3c, 0x00, 0x42, 0x18, 0xfa, 0x1b, 0x02, 0x04, 0x02, 0x0a, 0x02, 0x09,
0x82, 0x08, 0x06, 0x0a, 0x0e, 0x1b, 0x9c, 0x2b, 0x38, 0x2b, 0x74, 0x20, 0x82, 0x08, 0x06, 0x0a, 0x0e, 0x1b, 0x9c, 0x2b, 0x38, 0x2b, 0x74, 0x20,
0xec, 0x1f, 0x1c, 0x00, 0xf4, 0x00, 0xfe, 0x03 }; 0xec, 0x1f, 0x1c, 0x00, 0xf4, 0x00, 0xfe, 0x03 };

View File

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

View File

@ -0,0 +1,157 @@
#include "OBPDataOperations.h"
double WindUtils::to2PI(double a)
{
a = fmod(a, 2 * M_PI);
if (a < 0.0) {
a += 2 * M_PI;
}
return a;
}
double WindUtils::toPI(double a)
{
a += M_PI;
a = to2PI(a);
a -= M_PI;
return a;
}
double WindUtils::to360(double a)
{
a = fmod(a, 360);
if (a < 0.0) {
a += 360;
}
return a;
}
double WindUtils::to180(double a)
{
a += 180;
a = to360(a);
a -= 180;
return a;
}
void WindUtils::toCart(const double* phi, const double* r, double* x, double* y)
{
*x = *r * sin(*phi);
*y = *r * cos(*phi);
}
void WindUtils::toPol(const double* x, const double* y, double* phi, double* r)
{
*phi = (M_PI / 2) - atan2(*y, *x);
*phi = to2PI(*phi);
*r = sqrt(*x * *x + *y * *y);
}
void WindUtils::addPolar(const double* phi1, const double* r1,
const double* phi2, const double* r2,
double* phi, double* r)
{
double x1, y1, x2, y2;
toCart(phi1, r1, &x1, &y1);
toCart(phi2, r2, &x2, &y2);
x1 += x2;
y1 += y2;
toPol(&x1, &y1, phi, r);
}
void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
const double* CTW, const double* STW, const double* HDT,
double* TWD, double* TWS, double* TWA)
{
double awd = *AWA + *HDT;
awd = to2PI(awd);
double stw = -*STW;
// Serial.println("\ncalcTwdSA: AWA: " + String(*AWA) + ", AWS: " + String(*AWS) + ", CTW: " + String(*CTW) + ", STW: " + String(*STW) + ", HDT: " + String(*HDT));
addPolar(&awd, AWS, CTW, &stw, TWD, TWS);
// Normalize TWD and TWA to 0-360°
*TWD = to2PI(*TWD);
*TWA = toPI(*TWD - *HDT);
// Serial.println("calcTwdSA: TWD: " + String(*TWD) + ", TWS: " + String(*TWS));
}
bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal)
{
double stw, hdt, ctw;
double twd, tws, twa;
static const double DBL_MIN = std::numeric_limits<double>::lowest();
if (*hdtVal != DBL_MIN) {
hdt = *hdtVal; // Use HDT if available
} else {
if (*hdmVal != DBL_MIN && *varVal != DBL_MIN) {
hdt = *hdmVal + *varVal; // Use corrected HDM if HDT is not available
hdt = to2PI(hdt);
} 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+VAR or COG
}
}
if (*cogVal != DBL_MIN) {
ctw = *cogVal; // Use COG as CTW if available
// ctw = *cogVal + ((*cogVal - hdt) / 2); // Estimate CTW from COG
} else {
ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code
}
if (*stwVal != DBL_MIN) {
stw = *stwVal; // Use STW if available
} else if (*sogVal != DBL_MIN) {
stw = *sogVal;
} else {
// If STW and SOG are not available, we cannot calculate true wind
return false;
}
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, &stw, &hdt, &twd, &tws, &twa);
*twdVal = twd;
*twsVal = tws;
*twaVal = twa;
return true;
}
}
void HstryBuf::fillWndBufSimData(tBoatHstryData& hstryBufs)
// Fill most part of TWD and TWS history buffer with simulated data
{
double value = 20.0;
int16_t value2 = 0;
for (int i = 0; i < 900; i++) {
value += random(-20, 20);
value = WindUtils::to360(value);
value2 = static_cast<int16_t>(value * DEG_TO_RAD * 1000);
hstryBufs.twdHstry->add(value2);
}
}
/* double genTwdSimDat()
{
simTwd += random(-20, 20);
if (simTwd < 0.0)
simTwd += 360.0;
if (simTwd >= 360.0)
simTwd -= 360.0;
int16_t z = static_cast<int16_t>(DegToRad(simTwd) * 1000.0);
pageData.boatHstry.twdHstry->add(z); // Fill the buffer with some test data
simTws += random(-200, 150) / 10.0; // TWS value in knots
simTws = constrain(simTws, 0.0f, 50.0f); // Ensure TWS is between 0 and 50 knots
twsValue = simTws;
}*/

View File

@ -0,0 +1,36 @@
#pragma once
#include "GwApi.h"
#include "OBPRingBuffer.h"
#include <Arduino.h>
#include <math.h>
typedef struct {
RingBuffer<int16_t>* twdHstry;
RingBuffer<int16_t>* twsHstry;
} tBoatHstryData; // Holds pointers to all history buffers for boat data
class HstryBuf {
public:
void fillWndBufSimData(tBoatHstryData& hstryBufs); // Fill most part of the TWD and TWS history buffer with simulated data
};
class WindUtils {
public:
static double to2PI(double a);
static double toPI(double a);
static double to360(double a);
static double to180(double a);
static void toCart(const double* phi, const double* r, double* x, double* y);
static void toPol(const double* x, const double* y, double* phi, double* r);
static void addPolar(const double* phi1, const double* r1,
const double* phi2, const double* r2,
double* phi, double* r);
static void calcTwdSA(const double* AWA, const double* AWS,
const double* CTW, const double* STW, const double* HDT,
double* TWD, double* TWS, double* TWA);
static bool calcTrueWind(const double* awaVal, const double* awsVal,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal);
};

View File

@ -0,0 +1,60 @@
#pragma once
#include "GwSynchronized.h"
#include <algorithm>
#include <limits>
#include <stdexcept>
#include <vector>
#include "WString.h"
template <typename T>
class RingBuffer {
private:
mutable SemaphoreHandle_t bufLocker;
std::vector<T> buffer;
size_t capacity;
size_t head; // Points to the next insertion position
size_t first; // Points to the first (oldest) valid element
size_t last; // Points to the last (newest) valid element
size_t count; // Number of valid elements currently in buffer
bool is_Full; // Indicates that all buffer elements are used and ringing is in use
T MIN_VAL; // lowest possible value of buffer
T MAX_VAL; // highest possible value of buffer of type <T>
// metadata for buffer
String dataName; // Name of boat data in buffer
String dataFmt; // Format of boat data in buffer
int updFreq; // Update frequency in milliseconds
T smallest; // Value range of buffer: smallest value
T largest; // Value range of buffer: biggest value
public:
RingBuffer(size_t size);
void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer
bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer
String getName() const; // Get buffer name
void add(const T& value); // Add a new value to buffer
T get(size_t index) const; // Get value at specific position (0-based index from oldest to newest)
T getFirst() const; // Get the first (oldest) value in buffer
T getLast() const; // Get the last (newest) value in buffer
T getMin() const; // Get the lowest value in buffer
T getMin(size_t amount) const; // Get minimum value of the last <amount> values of buffer
T getMax() const; // Get the highest value in buffer
T getMax(size_t amount) const; // Get maximum value of the last <amount> values of buffer
T getMid() const; // Get mid value between <min> and <max> value in buffer
T getMid(size_t amount) const; // Get mid value between <min> and <max> value of the last <amount> values of buffer
T getMedian() const; // Get the median value in buffer
T getMedian(size_t amount) const; // Get the median value of the last <amount> values of buffer
size_t getCapacity() const; // Get the buffer capacity (maximum size)
size_t getCurrentSize() const; // Get the current number of elements in buffer
size_t getFirstIdx() const; // Get the index of oldest value in buffer
size_t getLastIdx() const; // Get the index of newest value in buffer
bool isEmpty() const; // Check if buffer is empty
bool isFull() const; // Check if buffer is full
T getMinVal() const; // Get lowest possible value for buffer; used for initialized buffer data
T getMaxVal() const; // Get highest possible value for buffer
void clear(); // Clear buffer
T operator[](size_t index) const; // Operator[] for convenient access (same as get())
std::vector<T> getAllValues() const; // Get all current values as a vector
};
#include "OBPRingBuffer.tpp"

View File

@ -0,0 +1,376 @@
#include "OBPRingBuffer.h"
template <typename T>
RingBuffer<T>::RingBuffer(size_t size)
: capacity(size)
, head(0)
, first(0)
, last(0)
, count(0)
, is_Full(false)
{
bufLocker = xSemaphoreCreateMutex();
if (size == 0) {
// return false;
}
MIN_VAL = std::numeric_limits<T>::lowest();
MAX_VAL = std::numeric_limits<T>::max();
dataName = "";
dataFmt = "";
updFreq = -1;
smallest = MIN_VAL;
largest = MAX_VAL;
buffer.resize(size, MIN_VAL);
// return true;
}
// Specify meta data of buffer content
template <typename T>
void RingBuffer<T>::setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue)
{
GWSYNCHRONIZED(&bufLocker);
dataName = name;
dataFmt = format;
updFreq = updateFrequency;
smallest = std::max(MIN_VAL, minValue);
largest = std::min(MAX_VAL, maxValue);
}
// Get meta data of buffer content
template <typename T>
bool RingBuffer<T>::getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue)
{
if (dataName == "" || dataFmt == "" || updFreq == -1) {
return false; // Meta data not set
}
GWSYNCHRONIZED(&bufLocker);
name = dataName;
format = dataFmt;
updateFrequency = updFreq;
minValue = smallest;
maxValue = largest;
return true;
}
// Get buffer name
template <typename T>
String RingBuffer<T>::getName() const
{
return dataName;
}
// Add a new value to buffer
template <typename T>
void RingBuffer<T>::add(const T& value)
{
GWSYNCHRONIZED(&bufLocker);
if (value < smallest || value > largest) {
buffer[head] = MIN_VAL; // Store MIN_VAL if value is out of range
} else {
buffer[head] = value;
}
last = head;
if (is_Full) {
first = (first + 1) % capacity; // Move pointer to oldest element when overwriting
} else {
count++;
if (count == capacity) {
is_Full = true;
}
}
head = (head + 1) % capacity;
}
// Get value at specific position (0-based index from oldest to newest)
template <typename T>
T RingBuffer<T>::get(size_t index) const
{
GWSYNCHRONIZED(&bufLocker);
if (isEmpty() || index < 0 || index >= count) {
return MIN_VAL;
}
size_t realIndex = (first + index) % capacity;
return buffer[realIndex];
}
// Operator[] for convenient access (same as get())
template <typename T>
T RingBuffer<T>::operator[](size_t index) const
{
return get(index);
}
// Get the first (oldest) value in the buffer
template <typename T>
T RingBuffer<T>::getFirst() const
{
if (isEmpty()) {
return MIN_VAL;
}
return get(0);
}
// Get the last (newest) value in the buffer
template <typename T>
T RingBuffer<T>::getLast() const
{
if (isEmpty()) {
return MIN_VAL;
}
return get(count - 1);
}
// Get the lowest value in the buffer
template <typename T>
T RingBuffer<T>::getMin() const
{
if (isEmpty()) {
return MIN_VAL;
}
T minVal = MAX_VAL;
T value;
for (size_t i = 0; i < count; i++) {
value = get(i);
if (value < minVal && value != MIN_VAL) {
minVal = value;
}
}
return minVal;
}
// Get minimum value of the last <amount> values of buffer
template <typename T>
T RingBuffer<T>::getMin(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return MIN_VAL;
}
if (amount > count)
amount = count;
T minVal = MAX_VAL;
T value;
for (size_t i = 0; i < amount; i++) {
value = get(count - 1 - i);
if (value < minVal && value != MIN_VAL) {
minVal = value;
}
}
return minVal;
}
// Get the highest value in the buffer
template <typename T>
T RingBuffer<T>::getMax() const
{
if (isEmpty()) {
return MIN_VAL;
}
T maxVal = MIN_VAL;
T value;
for (size_t i = 0; i < count; i++) {
value = get(i);
if (value > maxVal && value != MIN_VAL) {
maxVal = value;
}
}
return maxVal;
}
// Get maximum value of the last <amount> values of buffer
template <typename T>
T RingBuffer<T>::getMax(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return MIN_VAL;
}
if (amount > count)
amount = count;
T maxVal = MIN_VAL;
T value;
for (size_t i = 0; i < amount; i++) {
value = get(count - 1 - i);
if (value > maxVal && value != MIN_VAL) {
maxVal = value;
}
}
return maxVal;
}
// Get mid value between <min> and <max> value in the buffer
template <typename T>
T RingBuffer<T>::getMid() const
{
if (isEmpty()) {
return MIN_VAL;
}
return (getMin() + getMax()) / static_cast<T>(2);
}
// Get mid value between <min> and <max> value of the last <amount> values of buffer
template <typename T>
T RingBuffer<T>::getMid(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return MIN_VAL;
}
if (amount > count)
amount = count;
return (getMin(amount) + getMax(amount)) / static_cast<T>(2);
}
// Get the median value in the buffer
template <typename T>
T RingBuffer<T>::getMedian() const
{
if (isEmpty()) {
return MIN_VAL;
}
// Create a temporary vector with current valid elements
std::vector<T> temp;
temp.reserve(count);
for (size_t i = 0; i < count; i++) {
temp.push_back(get(i));
}
// Sort to find median
std::sort(temp.begin(), temp.end());
if (count % 2 == 1) {
// Odd number of elements
return temp[count / 2];
} else {
// Even number of elements - return average of middle two
// Note: For integer types, this truncates. For floating point, it's exact.
return (temp[count / 2 - 1] + temp[count / 2]) / 2;
}
}
// Get the median value of the last <amount> values of buffer
template <typename T>
T RingBuffer<T>::getMedian(size_t amount) const
{
if (isEmpty() || amount <= 0) {
return MIN_VAL;
}
if (amount > count)
amount = count;
// Create a temporary vector with current valid elements
std::vector<T> temp;
temp.reserve(amount);
for (size_t i = 0; i < amount; i++) {
temp.push_back(get(i));
}
// Sort to find median
std::sort(temp.begin(), temp.end());
if (amount % 2 == 1) {
// Odd number of elements
return temp[amount / 2];
} else {
// Even number of elements - return average of middle two
// Note: For integer types, this truncates. For floating point, it's exact.
return (temp[amount / 2 - 1] + temp[amount / 2]) / 2;
}
}
// Get the buffer capacity (maximum size)
template <typename T>
size_t RingBuffer<T>::getCapacity() const
{
return capacity;
}
// Get the current number of elements in the buffer
template <typename T>
size_t RingBuffer<T>::getCurrentSize() const
{
return count;
}
// Get the first index of buffer
template <typename T>
size_t RingBuffer<T>::getFirstIdx() const
{
return first;
}
// Get the last index of buffer
template <typename T>
size_t RingBuffer<T>::getLastIdx() const
{
return last;
}
// Check if buffer is empty
template <typename T>
bool RingBuffer<T>::isEmpty() const
{
return count == 0;
}
// Check if buffer is full
template <typename T>
bool RingBuffer<T>::isFull() const
{
return is_Full;
}
// Get lowest possible value for buffer; used for non-set buffer data
template <typename T>
T RingBuffer<T>::getMinVal() const
{
return MIN_VAL;
}
// Get highest possible value for buffer
template <typename T>
T RingBuffer<T>::getMaxVal() const
{
return MAX_VAL;
}
// Clear buffer
template <typename T>
void RingBuffer<T>::clear()
{
GWSYNCHRONIZED(&bufLocker);
head = 0;
first = 0;
last = 0;
count = 0;
is_Full = false;
}
// Get all current values as a vector
template <typename T>
std::vector<T> RingBuffer<T>::getAllValues() const
{
std::vector<T> result;
result.reserve(count);
for (size_t i = 0; i < count; i++) {
result.push_back(get(i));
}
return result;
}

View File

@ -498,14 +498,18 @@ void sensorTask(void *param){
// Send supply voltage value all 1s // Send supply voltage value all 1s
if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){ if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
starttime5 = millis(); starttime5 = millis();
float rawVoltage = 0; float rawVoltage = 0; // Default value
#if defined(BOARD_OBP40S3) && defined(VOLTAGE_SENSOR) #ifdef BOARD_OBP40S3
sensors.batteryVoltage = 0; // If no sensor then zero voltage
#endif
#if defined(BOARD_OBP40S3) && defined(VOLTAGE_SENSOR)
rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40 rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#endif #endif
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60 rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#endif #endif
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
// Save new data in average array // Save new data in average array
batV.reading(int(sensors.batteryVoltage * 100)); batV.reading(int(sensors.batteryVoltage * 100));
// Calculate the average values for different time lines from integer values // Calculate the average values for different time lines from integer values

View File

@ -20,7 +20,7 @@ class PageBME280 : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -176,9 +176,7 @@ class PageBME280 : public Page
// Show bus data // Show bus data
getdisplay().print(svalue3); // Real value as formated string getdisplay().print(svalue3); // Real value as formated string
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -5,7 +5,7 @@
class PageBattery : public Page class PageBattery : public Page
{ {
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
public: public:
PageBattery(CommonData &common){ PageBattery(CommonData &common){
@ -34,7 +34,7 @@ class PageBattery : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -288,9 +288,7 @@ class PageBattery : public Page
getdisplay().print("---"); // No sensor data (sensor is off) getdisplay().print("---"); // No sensor data (sensor is off)
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -44,7 +44,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -334,8 +334,7 @@ public:
getdisplay().setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("W"); getdisplay().print("W");
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -85,7 +85,7 @@ bool homevalid = false; // homelat and homelon are valid
return key; return key;
} }
virtual void displayPage(PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -120,7 +120,7 @@ bool homevalid = false; // homelat and homelon are valid
} }
else{ else{
value1 = simtime++; // Simulation data for time value 11:36 in seconds 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 bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
@ -162,7 +162,7 @@ bool homevalid = false; // homelat and homelon are valid
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageClock, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); LOG_DEBUG(GwLog::LOG,"Drawing at PageClock, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
@ -438,9 +438,7 @@ bool homevalid = false; // homelat and homelon are valid
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -61,7 +61,7 @@ class PageCompass : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -83,7 +83,7 @@ class PageCompass : public Page
String DataText[HowManyValues]; String DataText[HowManyValues];
String DataUnits[HowManyValues]; String DataUnits[HowManyValues];
String DataFormat[HowManyValues]; String DataFormat[HowManyValues];
FormatedData TheFormattedData; FormattedData TheFormattedData;
for (int i = 0; i < HowManyValues; i++){ for (int i = 0; i < HowManyValues; i++){
bvalue = pageData.values[i]; bvalue = pageData.values[i];
@ -104,7 +104,7 @@ class PageCompass : public Page
setFlashLED(false); setFlashLED(false);
} }
if (bvalue == NULL) return; if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
//*********************************************************** //***********************************************************
@ -235,12 +235,11 @@ class PageCompass : public Page
// if ( x_test > 390) // if ( x_test > 390)
// x_test = 320; // x_test = 320;
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
};
}; };
};
static Page *createPage(CommonData &common){ static Page *createPage(CommonData &common){
return new PageCompass(common); return new PageCompass(common);
}/** }/**

View File

@ -20,7 +20,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -84,7 +84,7 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageDST810, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); LOG_DEBUG(GwLog::LOG,"Drawing at PageDST810, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
@ -242,9 +242,7 @@ public:
unit4old = unit4; // Save the old unit unit4old = unit4; // Save the old unit
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -98,7 +98,7 @@ class PageFluid : public Page
commonData->logger->logDebug(GwLog::LOG,"New PageFluid: fluidtype=%d", fluidtype); commonData->logger->logDebug(GwLog::LOG,"New PageFluid: fluidtype=%d", fluidtype);
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -252,9 +252,7 @@ class PageFluid : public Page
getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor); getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor);
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -21,7 +21,7 @@ class PageFourValues : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -53,7 +53,7 @@ class PageFourValues : public Page
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // 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 String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for 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 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 String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // 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 String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for 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 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 String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4 // 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 String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
@ -89,7 +89,7 @@ class PageFourValues : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
@ -287,9 +287,7 @@ class PageFourValues : public Page
unit4old = unit4; // Save the old unit unit4old = unit4; // Save the old unit
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@ -303,7 +301,7 @@ static Page *createPage(CommonData &common){
* this will be number of BoatValue pointers in pageData.values * this will be number of BoatValue pointers in pageData.values
*/ */
PageDescription registerPageFourValues( PageDescription registerPageFourValues(
"FourValues", // Page name "FourValues", // Page name
createPage, // Action createPage, // Action
4, // Number of bus values depends on selection in Web configuration 4, // Number of bus values depends on selection in Web configuration
true // Show display header on/off true // Show display header on/off

View File

@ -21,7 +21,7 @@ class PageFourValues2 : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -89,7 +89,7 @@ class PageFourValues2 : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues2, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues2, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
@ -287,9 +287,7 @@ class PageFourValues2 : public Page
unit4old = unit4; // Save the old unit unit4old = unit4; // Save the old unit
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -20,7 +20,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -203,8 +203,7 @@ public:
getdisplay().setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("W"); getdisplay().print("W");
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -21,7 +21,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -86,21 +86,20 @@ public:
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots 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 float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots
const char *ii = " "; const char *ii = " ";
switch (i) switch (i) {
{ case 0: ii=" "; break; // Use a blank for a empty scale value
case 0: ii=" "; break; // Use a blank for a empty scale value case 30 : ii=" "; break;
case 30 : ii=" "; break; case 60 : ii=" "; break;
case 60 : ii=" "; break; case 90 : ii="45"; break;
case 90 : ii="45"; break; case 120 : ii="30"; break;
case 120 : ii="30"; break; case 150 : ii="15"; break;
case 150 : ii="15"; break; case 180 : ii="0"; break;
case 180 : ii="0"; break; case 210 : ii="15"; break;
case 210 : ii="15"; break; case 240 : ii="30"; break;
case 240 : ii="30"; break; case 270 : ii="45"; break;
case 270 : ii="45"; break; case 300 : ii=" "; break;
case 300 : ii=" "; break; case 330 : ii=" "; break;
case 330 : ii=" "; break; default: break;
default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
@ -206,9 +205,7 @@ public:
getdisplay().print("No sensor data"); // Info missing sensor getdisplay().print("No sensor data"); // Info missing sensor
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -21,7 +21,7 @@ class PageOneValue : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -53,7 +53,7 @@ class PageOneValue : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), value1); LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), value1);
// Draw page // Draw page
@ -104,9 +104,7 @@ class PageOneValue : public Page
unit1old = unit1; // Save the old unit unit1old = unit1; // Save the old unit
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -21,7 +21,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -41,9 +41,9 @@ public:
String backlightMode = config->getString(config->backlight); String backlightMode = config->getString(config->backlight);
int rolllimit = config->getInt(config->rollLimit); int rolllimit = config->getInt(config->rollLimit);
String roffset = config->getString(config->rollOffset); 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); 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 // Get boat values for roll
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll)
@ -55,17 +55,17 @@ public:
} }
else{ else{
if(simulation == true){ 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{ else{
value1 = 0; value1 = 0;
} }
} }
if(value1/(2*PI)*360 > -10 && value1/(2*PI)*360 < 10){ if(value1/(2*M_PI)*360 > -10 && value1/(2*M_PI)*360 < 10){
svalue1 = String(value1/(2*PI)*360,1); // Convert raw value to string svalue1 = String(value1/(2*M_PI)*360,1); // Convert raw value to string
} }
else{ else{
svalue1 = String(value1/(2*PI)*360,0); svalue1 = String(value1/(2*M_PI)*360,0);
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@ -80,17 +80,17 @@ public:
} }
else{ else{
if(simulation == true){ if(simulation == true){
value2 = (float(random(-5, 5)))/360*2*PI; value2 = (float(random(-5, 5)))/360*2*M_PI;
} }
else{ else{
value2 = 0; value2 = 0;
} }
} }
if(value2/(2*PI)*360 > -10 && value2/(2*PI)*360 < 10){ if(value2/(2*PI)*360 > -10 && value2/(2*M_PI)*360 < 10){
svalue2 = String(value2/(2*PI)*360,1); // Convert raw value to string svalue2 = String(value2/(2*M_PI)*360,1); // Convert raw value to string
} }
else{ else{
svalue2 = String(value2/(2*PI)*360,0); svalue2 = String(value2/(2*M_PI)*360,0);
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@ -99,7 +99,7 @@ public:
// Optical warning by limit violation // Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
// Limits for roll // 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); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
@ -109,7 +109,7 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageRollPitch, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); LOG_DEBUG(GwLog::LOG,"Drawing at PageRollPitch, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
@ -132,7 +132,7 @@ public:
getdisplay().setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115); getdisplay().setCursor(10, 115);
getdisplay().print("DEG"); getdisplay().print("DEG");
// Horizintal separator left // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor); getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
@ -164,7 +164,7 @@ public:
getdisplay().print("Deg"); getdisplay().print("Deg");
//******************************************************************************************* //*******************************************************************************************
// Draw instrument // Draw instrument
int rInstrument = 100; // Radius of instrument int rInstrument = 100; // Radius of instrument
float pi = 3.141592; float pi = 3.141592;
@ -177,19 +177,18 @@ public:
// Only scaling +/- 60 degrees // Only scaling +/- 60 degrees
if((i >= 0 && i <= 60) || (i >= 300 && i <= 360)){ if((i >= 0 && i <= 60) || (i >= 300 && i <= 360)){
// Scaling values // Scaling values
float x = 200 + (rInstrument+25)*sin(i/180.0*pi); // x-coordinate dots float x = 200 + (rInstrument+25)*sin(i/180.0*M_PI); // x-coordinate dots
float y = 150 - (rInstrument+25)*cos(i/180.0*pi); // y-coordinate cots float y = 150 - (rInstrument+25)*cos(i/180.0*M_PI); // y-coordinate cots
const char *ii = ""; const char *ii = "";
switch (i) switch (i) {
{ case 0: ii="0"; break;
case 0: ii="0"; break; case 20 : ii="20"; break;
case 20 : ii="20"; break; case 40 : ii="40"; break;
case 40 : ii="40"; break; case 60 : ii="60"; break;
case 60 : ii="60"; break; case 300 : ii="60"; break;
case 300 : ii="60"; break; case 320 : ii="40"; break;
case 320 : ii="40"; break; case 340 : ii="20"; break;
case 340 : ii="20"; break; default: break;
default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
@ -203,11 +202,11 @@ public:
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*M_PI);
// Draw sub scale with lines (two triangles) // Draw sub scale with lines (two triangles)
if(i % 20 == 0){ if(i % 20 == 0){
@ -229,17 +228,17 @@ public:
// Draw mast position pointer // Draw mast position pointer
float startwidth = 8; // Start width of 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){ if(valid1 == true || holdvalues == true || simulation == true){
float sinx=sin(value1 + pi); float sinx=sin(value1 + M_PI);
float cosx=cos(value1 + pi); float cosx=cos(value1 + M_PI);
// Normal pointer // Normal pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.7); float yy2 = -(rInstrument * 0.7);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
@ -283,7 +282,7 @@ public:
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument - 15); float yy2 = -(rInstrument - 15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
@ -305,9 +304,7 @@ public:
getdisplay().print("No sensor data"); // Info missing sensor getdisplay().print("No sensor data"); // Info missing sensor
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -22,7 +22,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -67,7 +67,7 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageRudderPosition, %s:%f", name1.c_str(), value1); LOG_DEBUG(GwLog::LOG,"Drawing at PageRudderPosition, %s:%f", name1.c_str(), value1);
// Draw page // Draw page
@ -207,8 +207,7 @@ public:
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor); getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor); getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -29,7 +29,7 @@ class PageSixValues : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -71,7 +71,7 @@ class PageSixValues : public Page
setFlashLED(false); setFlashLED(false);
} }
if (bvalue == NULL) return; if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
// Draw page // Draw page
//*********************************************************** //***********************************************************
@ -149,9 +149,7 @@ class PageSixValues : public Page
getdisplay().fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor); getdisplay().fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -20,7 +20,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -199,8 +199,7 @@ public:
getdisplay().setFont(&Ubuntu_Bold16pt8b); getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("W"); getdisplay().print("W");
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -25,29 +25,331 @@
* Consists of some sub-pages with following content: * Consists of some sub-pages with following content:
* 1. Hard and software information * 1. Hard and software information
* 2. System settings * 2. System settings
* 3. NMEA2000 device list * 3. 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 class PageSystem : public Page
{ {
uint64_t chipid; private:
bool simulation; // NVRAM config options
bool sdcard; String flashLED;
String buzzer_mode;
uint8_t buzzer_power;
String cpuspeed;
String rtc_module;
String gps_module;
String env_module;
String batt_sensor; // Generic data access
String solar_sensor;
String gen_sensor;
String rot_sensor;
double homelat;
double homelon;
char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard uint64_t chipid;
bool simulation;
bool sdcard;
String buzzer_mode;
uint8_t buzzer_power;
String cpuspeed;
String rtc_module;
String gps_module;
String env_module;
String batt_sensor;
String solar_sensor;
String gen_sensor;
String rot_sensor;
double homelat;
double homelon;
char mode = 'N'; // (N)ormal, (S)ettings, (C)onfiguration, (D)evice list, c(A)rd
void incMode() {
if (mode == 'N') { // Normal
mode = 'S';
} else if (mode == 'S') { // Settings
mode = 'C';
} else if (mode == 'C') { // Config
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 = 'D';
}
}
void displayModeNormal() {
// Default system page view
uint16_t y0 = 155;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System information");
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
displayBarcode(String(ssid), 320, 200, 2);
getdisplay().setCursor(8, 70);
getdisplay().print(String("MCUDEVICE-") + String(ssid));
getdisplay().setCursor(8, 95);
getdisplay().print("Firmware version: ");
getdisplay().setCursor(150, 95);
getdisplay().print(VERSINFO);
getdisplay().setCursor(8, 113);
getdisplay().print("Board version: ");
getdisplay().setCursor(150, 113);
getdisplay().print(BOARDINFO);
getdisplay().print(String(" HW ") + String(PCBINFO));
getdisplay().setCursor(8, 131);
getdisplay().print("Display version: ");
getdisplay().setCursor(150, 131);
getdisplay().print(DISPLAYINFO);
getdisplay().print("; GxEPD2 v");
getdisplay().print(GXEPD2INFO);
getdisplay().setCursor(8, 265);
#ifdef BOARD_OBP60S3
getdisplay().print("Press STBY to enter deep sleep mode");
#endif
#ifdef BOARD_OBP40S3
getdisplay().print("Press wheel to enter deep sleep mode");
#endif
// Flash memory size
uint32_t flash_size = ESP.getFlashChipSize();
getdisplay().setCursor(8, y0);
getdisplay().print("FLASH:");
getdisplay().setCursor(90, y0);
getdisplay().print(String(flash_size / 1024) + String(" kB"));
// PSRAM memory size
uint32_t psram_size = ESP.getPsramSize();
getdisplay().setCursor(8, y0 + 16);
getdisplay().print("PSRAM:");
getdisplay().setCursor(90, y0 + 16);
getdisplay().print(String(psram_size / 1024) + String(" kB"));
// FRAM available / status
getdisplay().setCursor(8, y0 + 32);
getdisplay().print("FRAM:");
getdisplay().setCursor(90, y0 + 32);
getdisplay().print(hasFRAM ? "available" : "not found");
#ifdef BOARD_OBP40S3
// SD-Card
getdisplay().setCursor(8, y0 + 48);
getdisplay().print("SD-Card:");
getdisplay().setCursor(90, y0 + 48);
if (sdcard) {
uint64_t cardsize = SD.cardSize() / (1024 * 1024);
getdisplay().print(String(cardsize) + String(" MB"));
} else {
getdisplay().print("off");
}
#endif
// CPU speed config / active
getdisplay().setCursor(202, y0);
getdisplay().print("CPU speed:");
getdisplay().setCursor(300, y0);
getdisplay().print(cpuspeed);
getdisplay().print(" / ");
int cpu_freq = esp_clk_cpu_freq() / 1000000;
getdisplay().print(String(cpu_freq));
// total RAM free
int Heap_free = esp_get_free_heap_size();
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Total free:");
getdisplay().setCursor(300, y0 + 16);
getdisplay().print(String(Heap_free));
// RAM free for task
int RAM_free = uxTaskGetStackHighWaterMark(NULL);
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Task free:");
getdisplay().setCursor(300, y0 + 32);
getdisplay().print(String(RAM_free));
}
void displayModeConfig() {
// Configuration interface
uint16_t x0 = 16;
uint16_t y0 = 80;
uint16_t dy = 20;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System configuration");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(x0, y0);
getdisplay().print("CPU speed: 80 | 160 | 240");
getdisplay().setCursor(x0, y0 + 1 * dy);
getdisplay().print("Power mode: Max | 5V | Min");
getdisplay().setCursor(x0, y0 + 2 * dy);
getdisplay().print("Accesspoint: On | Off");
// TODO Change NVRAM-preferences settings here
getdisplay().setCursor(x0, y0 + 4 * dy);
getdisplay().print("Simulation: On | Off");
}
void displayModeSettings() {
// View some of the current settings
const uint16_t x0 = 8;
const uint16_t y0 = 72;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, 48);
getdisplay().print("System settings");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
// left column
getdisplay().setCursor(x0, y0);
getdisplay().print("Simulation:");
getdisplay().setCursor(120, y0);
getdisplay().print(simulation ? "on" : "off");
getdisplay().setCursor(x0, y0 + 16);
getdisplay().print("Environment:");
getdisplay().setCursor(120, y0 + 16);
getdisplay().print(env_module);
getdisplay().setCursor(x0, y0 + 32);
getdisplay().print("Buzzer:");
getdisplay().setCursor(120, y0 + 32);
getdisplay().print(buzzer_mode);
getdisplay().setCursor(x0, y0 + 64);
getdisplay().print("GPS:");
getdisplay().setCursor(120, y0 + 64);
getdisplay().print(gps_module);
getdisplay().setCursor(x0, y0 + 80);
getdisplay().print("RTC:");
getdisplay().setCursor(120, y0 + 80);
getdisplay().print(rtc_module);
getdisplay().setCursor(x0, y0 + 96);
getdisplay().print("Wifi:");
getdisplay().setCursor(120, y0 + 96);
getdisplay().print(commonData->status.wifiApOn ? "on" : "off");
// Home location
getdisplay().setCursor(x0, y0 + 128);
getdisplay().print("Home Lat.:");
drawTextRalign(230, y0 + 128, formatLatitude(homelat));
getdisplay().setCursor(x0, y0 + 144);
getdisplay().print("Home Lon.:");
drawTextRalign(230, y0 + 144, formatLongitude(homelon));
// right column
getdisplay().setCursor(202, y0);
getdisplay().print("Batt. sensor:");
getdisplay().setCursor(320, y0);
getdisplay().print(batt_sensor);
// Solar sensor
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Solar sensor:");
getdisplay().setCursor(320, y0 + 16);
getdisplay().print(solar_sensor);
// Generator sensor
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Gen. sensor:");
getdisplay().setCursor(320, y0 + 32);
getdisplay().print(gen_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);
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;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("SD Card info");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(x0, y0);
getdisplay().print("Work in progress...");
}
void displayModeDevicelist() {
// NMEA2000 device list
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("NMEA2000 device list");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 80);
getdisplay().print("RxD: ");
getdisplay().print(String(commonData->status.n2kRx));
getdisplay().setCursor(20, 100);
getdisplay().print("TxD: ");
getdisplay().print(String(commonData->status.n2kTx));
}
public: public:
PageSystem(CommonData &common){ PageSystem(CommonData &common){
@ -56,6 +358,8 @@ public:
if (hasFRAM) { if (hasFRAM) {
mode = fram.read(FRAM_SYSTEM_MODE); mode = fram.read(FRAM_SYSTEM_MODE);
} }
flashLED = common.config->getString(common.config->flashLED);
chipid = ESP.getEfuseMac(); chipid = ESP.getEfuseMac();
simulation = common.config->getBool(common.config->useSimuData); simulation = common.config->getBool(common.config->useSimuData);
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
@ -90,19 +394,7 @@ public:
// Switch display mode // Switch display mode
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler"); commonData->logger->logDebug(GwLog::LOG, "System keyboard handler");
if (key == 2) { if (key == 2) {
if (mode == 'N') { incMode();
mode = 'S';
} else if (mode == 'S') {
mode = 'D';
} else if (mode == 'D') {
if (sdcard) {
mode = 'C';
} else {
mode = 'N';
}
} else {
mode = 'N';
}
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode); if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
return 0; return 0;
} }
@ -126,8 +418,13 @@ public:
} }
#endif #endif
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
// grab cursor keys to disable page navigation // use cursor keys for local mode navigation
if (key == 9 or key == 10) { if (key == 9) {
incMode();
return 0;
}
if (key == 10) {
decMode();
return 0; return 0;
} }
// standby / deep sleep // standby / deep sleep
@ -164,234 +461,44 @@ public:
} }
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
// Get config data
String flashLED = config->getString(config->flashLED);
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(flashLED == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
// Logging boat values // Logging page information
LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem"); LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem, Mode=%c", mode);
// Draw page
//***********************************************************
uint16_t x0 = 8; // left column
uint16_t y0 = 48; // data table starts here
// Set display in partial refresh mode // 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());
if (mode == 'N') { // call current system page
switch (mode) {
getdisplay().setFont(&Ubuntu_Bold12pt8b); case 'N':
getdisplay().setCursor(8, 48); displayModeNormal();
getdisplay().print("System Information"); break;
case 'S':
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor); displayModeSettings();
break;
getdisplay().setFont(&Ubuntu_Bold8pt8b); case 'C':
y0 = 155; displayModeConfig();
break;
char ssid[13]; case 'A':
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid); displayModeSDCard();
displayBarcode(String(ssid), 320, 200, 2); break;
getdisplay().setCursor(8, 70); case 'D':
getdisplay().print(String("MCUDEVICE-") + String(ssid)); displayModeDevicelist();
break;
getdisplay().setCursor(8, 95);
getdisplay().print("Firmware version: ");
getdisplay().setCursor(150, 95);
getdisplay().print(VERSINFO);
getdisplay().setCursor(8, 113);
getdisplay().print("Board version: ");
getdisplay().setCursor(150, 113);
getdisplay().print(BOARDINFO);
getdisplay().print(String(" HW ") + String(PCBINFO));
getdisplay().setCursor(8, 131);
getdisplay().print("Display version: ");
getdisplay().setCursor(150, 131);
getdisplay().print(DISPLAYINFO);
getdisplay().print("; GxEPD2 v");
getdisplay().print(GXEPD2INFO);
getdisplay().setCursor(8, 265);
#ifdef BOARD_OBP60S3
getdisplay().print("Press STBY to enter deep sleep mode");
#endif
#ifdef BOARD_OBP40S3
getdisplay().print("Press wheel to enter deep sleep mode");
#endif
// Flash memory size
uint32_t flash_size = ESP.getFlashChipSize();
getdisplay().setCursor(8, y0);
getdisplay().print("FLASH:");
getdisplay().setCursor(90, y0);
getdisplay().print(String(flash_size / 1024) + String(" kB"));
// PSRAM memory size
uint32_t psram_size = ESP.getPsramSize();
getdisplay().setCursor(8, y0 + 16);
getdisplay().print("PSRAM:");
getdisplay().setCursor(90, y0 + 16);
getdisplay().print(String(psram_size / 1024) + String(" kB"));
// FRAM available / status
getdisplay().setCursor(8, y0 + 32);
getdisplay().print("FRAM:");
getdisplay().setCursor(90, y0 + 32);
getdisplay().print(hasFRAM ? "available" : "not found");
#ifdef BOARD_OBP40S3
// SD-Card
getdisplay().setCursor(8, y0 + 48);
getdisplay().print("SD-Card:");
getdisplay().setCursor(90, y0 + 48);
if (sdcard) {
uint64_t cardsize = SD.cardSize() / (1024 * 1024);
getdisplay().print(String(cardsize) + String(" MB"));
} else {
getdisplay().print("off");
}
#endif
// CPU speed config / active
getdisplay().setCursor(202, y0);
getdisplay().print("CPU speed:");
getdisplay().setCursor(300, y0);
getdisplay().print(cpuspeed);
getdisplay().print(" / ");
int cpu_freq = esp_clk_cpu_freq() / 1000000;
getdisplay().print(String(cpu_freq));
// total RAM free
int Heap_free = esp_get_free_heap_size();
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Total free:");
getdisplay().setCursor(300, y0 + 16);
getdisplay().print(String(Heap_free));
// RAM free for task
int RAM_free = uxTaskGetStackHighWaterMark(NULL);
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Task free:");
getdisplay().setCursor(300, y0 + 32);
getdisplay().print(String(RAM_free));
} else if (mode == 'S') {
// Settings
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);
getdisplay().print("Simulation:");
getdisplay().setCursor(120, y0);
getdisplay().print(simulation ? "on" : "off");
getdisplay().setCursor(x0, y0 + 16);
getdisplay().print("Environment:");
getdisplay().setCursor(120, y0 + 16);
getdisplay().print(env_module);
getdisplay().setCursor(x0, y0 + 32);
getdisplay().print("Buzzer:");
getdisplay().setCursor(120, y0 + 32);
getdisplay().print(buzzer_mode);
getdisplay().setCursor(x0, y0 + 64);
getdisplay().print("GPS:");
getdisplay().setCursor(120, y0 + 64);
getdisplay().print(gps_module);
getdisplay().setCursor(x0, y0 + 80);
getdisplay().print("RTC:");
getdisplay().setCursor(120, y0 + 80);
getdisplay().print(rtc_module);
getdisplay().setCursor(x0, y0 + 96);
getdisplay().print("Wifi:");
getdisplay().setCursor(120, y0 + 96);
getdisplay().print(commonData->status.wifiApOn ? "on" : "off");
// Home location
getdisplay().setCursor(x0, y0 + 128);
getdisplay().print("Home Lat.:");
getdisplay().setCursor(120, y0 + 128);
getdisplay().print(formatLatitude(homelat));
getdisplay().setCursor(x0, y0 + 144);
getdisplay().print("Home Lon.:");
getdisplay().setCursor(120, y0 + 144);
getdisplay().print(formatLongitude(homelon));
// right column
getdisplay().setCursor(202, y0);
getdisplay().print("Batt. sensor:");
getdisplay().setCursor(320, y0);
getdisplay().print(batt_sensor);
// Solar sensor
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Solar sensor:");
getdisplay().setCursor(320, y0 + 16);
getdisplay().print(solar_sensor);
// Generator sensor
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Gen. sensor:");
getdisplay().setCursor(320, y0 + 32);
getdisplay().print(gen_sensor);
// Gyro sensor
} 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 {
// NMEA2000 device list
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("NMEA2000 device list");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 80);
getdisplay().print("RxD: ");
getdisplay().print(String(commonData->status.n2kRx));
getdisplay().setCursor(20, 100);
getdisplay().print("TxD: ");
getdisplay().print(String(commonData->status.n2kTx));
} }
// Update display // Update display
getdisplay().nextPage(); // Partial update (fast) getdisplay().nextPage(); // Partial update (fast)
return PAGE_OK;
}; };
}; };

View File

@ -21,7 +21,7 @@ class PageThreeValues : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -51,7 +51,7 @@ class PageThreeValues : public Page
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // 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 String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for 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 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 String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // 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 String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
@ -77,7 +77,7 @@ class PageThreeValues : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageThreeValues, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3); LOG_DEBUG(GwLog::LOG,"Drawing at PageThreeValues, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3);
// Draw page // Draw page
@ -226,8 +226,7 @@ class PageThreeValues : public Page
unit3old = unit3; // Save the old unit unit3old = unit3; // Save the old unit
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -21,7 +21,7 @@ class PageTwoValues : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -49,7 +49,7 @@ class PageTwoValues : public Page
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // 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 String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
@ -65,7 +65,7 @@ class PageTwoValues : public Page
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageTwoValues, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2); LOG_DEBUG(GwLog::LOG,"Drawing at PageTwoValues, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
@ -166,8 +166,7 @@ class PageTwoValues : public Page
unit2old = unit2; // Save the old unit unit2old = unit2; // Save the old unit
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };

View File

@ -100,7 +100,7 @@ public:
getdisplay().fillRect(x + 16, y + 11, 6, 3, color); getdisplay().fillRect(x + 16, y + 11, 6, 3, color);
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -187,7 +187,6 @@ public:
} }
// Logging voltage value // Logging voltage value
if (raw == 0) return;
LOG_DEBUG(GwLog::LOG,"Drawing at PageVoltage, Type:%s %s:=%f", batType, name1.c_str(), raw); LOG_DEBUG(GwLog::LOG,"Drawing at PageVoltage, Type:%s %s:=%f", batType, name1.c_str(), raw);
// Draw page // Draw page
@ -384,8 +383,7 @@ public:
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@ -400,11 +398,11 @@ static Page *createPage(CommonData &common){
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need
*/ */
PageDescription registerPageVoltage( PageDescription registerPageVoltage(
"Voltage", // Name of page "Voltage", // Name of page
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
{}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h) {}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@ -2,8 +2,14 @@
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "MFD_OBP60_400x300_sw.h" // MFD with logo
#include "Logo_OBP_400x300_sw.h" // OBP Logo #include "images/OBP_400x300.xbm" // OBP Logo
#ifdef BOARD_OBP60S3
#include "images/OBP60_400x300.xbm" // MFD with logo
#endif
#ifdef BOARD_OBP40S3
#include "images/OBP40_400x300.xbm" // MFD with logo
#endif
class PageWhite : public Page class PageWhite : public Page
{ {
@ -31,7 +37,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -61,17 +67,21 @@ public:
} }
if (mode == 'L') { if (mode == 'L') {
getdisplay().drawBitmap(0, 0, gImage_Logo_OBP_400x300_sw, getdisplay().width(), getdisplay().height(), commonData->fgcolor); getdisplay().drawXBitmap(0, 0, OBP_400x300_bits, OBP_400x300_width, OBP_400x300_height, commonData->fgcolor);
} else if (mode == 'M') { } else if (mode == 'M') {
getdisplay().drawBitmap(0, 0, gImage_MFD_OBP60_400x300_sw, getdisplay().width(), getdisplay().height(), commonData->fgcolor); #ifdef BOARD_OBP60S3
getdisplay().drawXBitmap(0, 0, OBP60_400x300_bits, OBP60_400x300_width, OBP60_400x300_height, commonData->fgcolor);
#endif
#ifdef BOARD_OBP40S3
getdisplay().drawXBitmap(0, 0, OBP40_400x300_bits, OBP40_400x300_width, OBP40_400x300_height, commonData->fgcolor);
#endif
} }
// Update display int ret = PAGE_UPDATE;
getdisplay().nextPage();
if (mode == 'W') { if (mode == 'W') {
getdisplay().hibernate(); ret |= PAGE_HIBERNATE;
} }
return ret;
}; };
}; };

View File

@ -296,7 +296,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData) int displayPage(PageData &pageData)
{ {
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -354,7 +354,7 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageWind, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); LOG_DEBUG(GwLog::LOG,"Drawing at PageWind, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
@ -618,9 +618,7 @@ public:
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@ -635,11 +633,11 @@ static Page *createPage(CommonData &common){
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need
*/ */
PageDescription registerPageWind( PageDescription registerPageWind(
"Wind", // Page name "Wind", // 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
{"AWS","AWA", "TWS", "TWA"}, // Bus values we need in the page {"AWS","AWA", "TWS", "TWA"}, // Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@ -0,0 +1,479 @@
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "BoatDataCalibration.h"
#include "OBP60Extensions.h"
#include "OBPRingBuffer.h"
#include "Pagedata.h"
#include <vector>
static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart
int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
{
int minVal = windDirHstry.getMinVal();
size_t count = windDirHstry.getCurrentSize();
// size_t capacity = windDirHstry.getCapacity();
// size_t last = windDirHstry.getLastIdx();
if (windDirHstry.isEmpty() || amount <= 0) {
return minVal;
}
if (amount > count)
amount = count;
int value = 0;
int rng = 0;
int maxRng = minVal;
// Start from the newest value (last) and go backwards x times
for (size_t i = 0; i < amount; i++) {
// value = windDirHstry.get(((last - i) % capacity + capacity) % capacity);
value = windDirHstry.get(count - 1 - i);
if (value == minVal) {
continue;
}
value = value / 1000.0 * radToDeg;
rng = abs(((value - center + 540) % 360) - 180);
if (rng > maxRng)
maxRng = rng;
}
if (maxRng > 180) {
maxRng = 180;
}
return maxRng;
}
// ****************************************************************
class PageWindPlot : public Page {
bool keylock = false; // Keylock
char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both
int dataIntv = 1; // Update interval for wind history chart:
// (1)|(2)|(3)|(4) seconds for approx. 4, 8, 12, 16 min. history chart
bool showTWS = true; // Show TWS value in chart area
public:
PageWindPlot(CommonData& common)
{
commonData = &common;
common.logger->logDebug(GwLog::LOG, "Instantiate PageWindPlot");
}
virtual void setupKeys()
{
Page::setupKeys();
// commonData->keydata[0].label = "MODE";
commonData->keydata[1].label = "INTV";
commonData->keydata[4].label = "TWS";
}
// Key functions
virtual int handleKey(int key)
{
// Set chart mode TWD | TWS -> to be implemented
if (key == 1) {
if (chrtMode == 'D') {
chrtMode = 'S';
} else if (chrtMode == 'S') {
chrtMode = 'B';
} else {
chrtMode = 'D';
}
return 0; // Commit the key
}
// Set interval for wind history chart update time
if (key == 2) {
if (dataIntv == 1) {
dataIntv = 2;
} else if (dataIntv == 2) {
dataIntv = 3;
} else if (dataIntv == 3) {
dataIntv = 4;
} else {
dataIntv = 1;
}
return 0; // Commit the key
}
// Switch TWS on/off
if (key == 5) {
showTWS = !showTWS;
return 0; // Commit the key
}
// Keylock function
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0; // Commit the key
}
return key;
}
int displayPage(PageData& pageData)
{
GwConfigHandler* config = commonData->config;
GwLog* logger = commonData->logger;
float twsValue; // TWS value in chart area
static String twdName, twdUnit; // TWD name and unit
static int updFreq; // Update frequency for TWD
static int16_t twdLowest, twdHighest; // TWD range
// static int16_t twdBufMinVal; // lowest possible twd buffer value; used for non-set data
// current boat data values; TWD only for validation test, TWS for display of current value
const int numBoatData = 2;
GwApi::BoatValue* bvalue;
String BDataName[numBoatData];
double BDataValue[numBoatData];
bool BDataValid[numBoatData];
String BDataText[numBoatData];
String BDataUnit[numBoatData];
String BDataFormat[numBoatData];
static bool isInitialized = false; // Flag to indicate that page is initialized
static bool wndDataValid = false; // Flag to indicate if wind data is valid
static int numNoData; // Counter for multiple invalid data values in a row
static bool simulation = false;
static bool holdValues = false;
static int width; // Screen width
static int height; // Screen height
static int xCenter; // Center of screen in x direction
static const int yOffset = 48; // Offset for y coordinates of chart area
static int cHeight; // height of chart area
static int bufSize; // History buffer size: 960 values for appox. 16 min. history chart
static int intvBufSize; // Buffer size used for currently selected time interval
int count; // current size of buffer
static int numWndVals; // number of wind values available for current interval selection
static int bufStart; // 1st data value in buffer to show
int numAddedBufVals; // Number of values added to buffer since last display
size_t currIdx; // Current index in TWD history buffer
static size_t lastIdx; // Last index of TWD history buffer
static size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added
static int oldDataIntv; // remember recent user selection of data interval
static int wndCenter; // chart wind center value position
static int wndLeft; // chart wind left value position
static int wndRight; // chart wind right value position
static int chrtRng; // Range of wind values from mid wind value to min/max wind value in degrees
int diffRng; // Difference between mid and current wind value
static const int dfltRng = 40; // Default range for chart
int midWndDir; // New value for wndCenter after chart start / shift
static int simTwd; // Simulation value for TWD
static float simTws; // Simulation value for TWS
int x, y; // x and y coordinates for drawing
static int prevX, prevY; // Last x and y coordinates for drawing
static float chrtScl; // Scale for wind values in pixels per degree
int chrtVal; // Current wind value
static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line
LOG_DEBUG(GwLog::LOG, "Display page WindPlot");
// Get config data
simulation = config->getBool(config->useSimuData);
holdValues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
if (!isInitialized) {
width = getdisplay().width();
height = getdisplay().height();
xCenter = width / 2;
cHeight = height - yOffset - 22;
bufSize = pageData.boatHstry.twdHstry->getCapacity();
numNoData = 0;
simTwd = pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg;
simTws = 0;
twsValue = 0;
bufStart = 0;
oldDataIntv = 0;
numAddedBufVals, currIdx, lastIdx = 0;
lastAddedIdx = pageData.boatHstry.twdHstry->getLastIdx();
pageData.boatHstry.twdHstry->getMetaData(twdName, twdUnit, updFreq, twdLowest, twdHighest);
wndCenter = INT_MIN;
midWndDir = 0;
diffRng = dfltRng;
chrtRng = dfltRng;
isInitialized = true; // Set flag to indicate that page is now initialized
}
// 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] = 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
BDataValid[i] = bvalue->valid;
BDataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
BDataUnit[i] = formatValue(bvalue, *commonData).unit;
BDataFormat[i] = bvalue->getFormat(); // Unit of value
}
// Optical warning by limit violation (unused)
if (String(flashLED) == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
// Identify buffer size and buffer start position for chart
count = pageData.boatHstry.twdHstry->getCurrentSize();
currIdx = pageData.boatHstry.twdHstry->getLastIdx();
numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display
if (dataIntv != oldDataIntv || count == 1) {
// new data interval selected by user
intvBufSize = cHeight * dataIntv;
numWndVals = min(count, (cHeight - 60) * dataIntv);
bufStart = max(0, count - numWndVals);
lastAddedIdx = currIdx;
oldDataIntv = dataIntv;
} else {
numWndVals = numWndVals + numAddedBufVals;
lastAddedIdx = currIdx;
if (count == bufSize) {
bufStart = max(0, bufStart - numAddedBufVals);
}
}
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Dataset: count: %d, TWD: %.0f, TWS: %.1f, TWD_valid? %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d, old: %d, act: %d",
count, pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384, BDataValid[0],
intvBufSize, numWndVals, bufStart, numAddedBufVals, pageData.boatHstry.twdHstry->getLastIdx(), oldDataIntv, dataIntv);
// Set wndCenter from 1st real buffer value
if (wndCenter == INT_MIN || (wndCenter == 0 && count == 1)) {
midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals);
if (midWndDir != INT16_MIN) {
midWndDir = midWndDir / 1000.0 * radToDeg;
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
} else {
wndCenter = 0;
}
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, TWD: %.0f, wndCenter: %d, diffRng: %d, chrtRng: %d", count, pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg,
wndCenter, diffRng, chrtRng);
} else {
// check and adjust range between left, center, and right chart limit
diffRng = getRng(*pageData.boatHstry.twdHstry, wndCenter, numWndVals);
diffRng = (diffRng == INT16_MIN ? 0 : diffRng);
if (diffRng > chrtRng) {
chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value
} else if (diffRng + 10 < chrtRng) { // Reduce chart range for higher resolution if possible
chrtRng = max(dfltRng, int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10);
}
}
chrtScl = float(width) / float(chrtRng) / 2.0; // Chart scale: pixels per degree
wndLeft = wndCenter - chrtRng;
if (wndLeft < 0)
wndLeft += 360;
wndRight = (chrtRng < 180 ? wndCenter + chrtRng : wndCenter + chrtRng - 1);
if (wndRight >= 360)
wndRight -= 360;
// Draw page
//***********************************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
// chart lines
getdisplay().fillRect(0, yOffset, width, 2, commonData->fgcolor);
getdisplay().fillRect(xCenter, yOffset, 1, cHeight, commonData->fgcolor);
// chart labels
char sWndLbl[4]; // char buffer for Wind angle label
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(xCenter - 88, yOffset - 3);
getdisplay().print("TWD"); // Wind data name
snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
drawTextCenter(xCenter, yOffset - 11, sWndLbl);
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
getdisplay().setCursor(1, yOffset - 3);
snprintf(sWndLbl, 4, "%03d", (wndLeft < 0) ? (wndLeft + 360) : wndLeft);
getdisplay().print(sWndLbl); // Wind left value
getdisplay().drawCircle(46, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(46, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
getdisplay().setCursor(width - 50, yOffset - 3);
snprintf(sWndLbl, 4, "%03d", (wndRight < 0) ? (wndRight + 360) : wndRight);
getdisplay().print(sWndLbl); // Wind right value
getdisplay().drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
if (pageData.boatHstry.twdHstry->getMax() == pageData.boatHstry.twdHstry->getMinVal()) {
// only <INT16_MIN> values in buffer -> no valid wind data available
wndDataValid = false;
} else if (!BDataValid[0]) {
// currently no valid TWD data available
numNoData++;
wndDataValid = true;
if (numNoData > 3) {
// If more than 4 invalid values in a row, send message
wndDataValid = false;
}
} else {
numNoData = 0; // reset data error counter
wndDataValid = true; // At least some wind data available
}
// Draw wind values in chart
//***********************************************************************
if (wndDataValid) {
for (int i = 0; i < (numWndVals / dataIntv); i++) {
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;
} 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) - 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)) {
// just a dot for 1st chart point or after some invalid values
prevX = x;
prevY = y;
} else {
// cross borders check; shift values to [-180..0..180]; when crossing borders, range is 2x 180 degrees
int wndLeftDlt = -180 - ((wndLeft >= 180) ? (wndLeft - 360) : wndLeft);
int chrtVal180 = ((chrtVal + wndLeftDlt + 180) % 360 + 360) % 360 - 180;
int chrtPrevVal180 = ((chrtPrevVal + wndLeftDlt + 180) % 360 + 360) % 360 - 180;
if (((chrtPrevVal180 >= -180) && (chrtPrevVal180 < -90) && (chrtVal180 > 90)) || ((chrtPrevVal180 <= 179) && (chrtPrevVal180 > 90) && chrtVal180 <= -90)) {
// If current value crosses chart borders compared to previous value, split line
int xSplit = (((chrtPrevVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl;
getdisplay().drawLine(prevX, prevY, xSplit, y, commonData->fgcolor);
getdisplay().drawLine(prevX, prevY - 1, ((xSplit != prevX) ? xSplit : xSplit - 1), ((xSplit != prevX) ? y - 1 : y), commonData->fgcolor);
prevX = (((chrtVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl;
}
}
// Draw line with 2 pixels width + make sure vertical line are drawn correctly
getdisplay().drawLine(prevX, prevY, x, y, commonData->fgcolor);
getdisplay().drawLine(prevX, prevY - 1, ((x != prevX) ? x : x - 1), ((x != prevX) ? y - 1 : y), commonData->fgcolor);
chrtPrevVal = chrtVal;
prevX = x;
prevY = y;
}
// Reaching chart area top end
if (i >= (cHeight - 1)) {
oldDataIntv = 0; // force reset of buffer start and number of values to show in next display loop
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 - 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) {
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
}
}
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: cHeight: %d, bufStart: %d, numWndVals: %d, wndCenter: %d", cHeight, bufStart, numWndVals, wndCenter);
break;
}
}
} else {
// No valid data available
LOG_DEBUG(GwLog::LOG, "PageWindPlot: No valid data available");
getdisplay().setFont(&Ubuntu_Bold10pt8b);
getdisplay().fillRect(xCenter - 33, height / 2 - 20, 66, 24, commonData->bgcolor); // Clear area for message
drawTextCenter(xCenter, height / 2 - 10, "No data");
}
// Print TWS value
if (showTWS) {
int currentZone;
static int lastZone = 0;
static bool flipTws = false;
int xPosTws;
static const int yPosTws = yOffset + 40;
twsValue = pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384; // TWS value in knots
xPosTws = flipTws ? 20 : width - 138;
currentZone = (y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146) ? 1 : 0; // Define current zone for TWS value
if (currentZone != lastZone) {
// Only flip when x moves to a different zone
if ((y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146)) {
flipTws = !flipTws;
xPosTws = flipTws ? 20 : width - 145;
}
}
lastZone = currentZone;
getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(xPosTws, yPosTws);
if (!BDataValid[1]) {
getdisplay().print("--.-");
} else {
double dbl = BDataValue[1] * 3.6 / 1.852;
if (dbl < 10.0) {
getdisplay().printf("!%3.1f", dbl); // Value, round to 1 decimal
} else {
getdisplay().printf("%4.1f", dbl); // Value, round to 1 decimal
}
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(xPosTws + 82, yPosTws - 14);
// getdisplay().print("TWS"); // Name
getdisplay().print(BDataName[1]); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
// getdisplay().setCursor(xPosTws + 78, yPosTws + 1);
getdisplay().setCursor(xPosTws + 82, yPosTws + 1);
// getdisplay().printf(" kn"); // Unit
getdisplay().print(BDataUnit[1]); // Unit
}
// chart Y axis labels; print at last to overwrite potential chart lines in label area
int yPos;
int chrtLbl;
getdisplay().setFont(&Ubuntu_Bold8pt8b);
for (int i = 1; i <= 3; i++) {
yPos = yOffset + (i * 60);
getdisplay().fillRect(0, yPos, width, 1, commonData->fgcolor);
getdisplay().fillRect(0, yPos - 8, 24, 16, commonData->bgcolor); // Clear small area to remove potential chart lines
getdisplay().setCursor(1, yPos + 4);
if (count >= intvBufSize) {
// Calculate minute value for label
chrtLbl = ((i - 1 + (prevY < yOffset + 30)) * dataIntv) * -1; // change label if last data point is more than 30 lines (= seconds) from chart line
} else {
int j = 3 - i;
chrtLbl = (int((((numWndVals / dataIntv) - 50) * dataIntv / 60) + 1) - (j * dataIntv)) * -1; // 50 lines left below last chart line
}
getdisplay().printf("%3d", chrtLbl); // Wind value label
}
return PAGE_UPDATE;
};
};
static Page* createPage(CommonData& common)
{
return new PageWindPlot(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 (0 here)
* and will will provide the names of the fixed values we need
*/
PageDescription registerPageWindPlot(
"WindPlot", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{ "TWD", "TWS" }, // Bus values we need in the page
// {}, // Bus values we need in the page
true // Show display header on/off
);
#endif

View File

@ -24,7 +24,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -48,7 +48,7 @@ public:
String flashLED = config->getString(config->flashLED); String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight); 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) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
@ -63,8 +63,8 @@ public:
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
} }
// Get boat values for AWS // Get boat value for AWS
GwApi::BoatValue *bvalue2 = pageData.values[1]; // First 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 String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
@ -77,8 +77,8 @@ public:
unit2old = unit2; // Save old unit unit2old = unit2; // Save old unit
} }
// Get boat values TWD // Get boat value for TWD
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 String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
@ -91,9 +91,9 @@ public:
unit3old = unit3; // Save old unit unit3old = unit3; // Save old unit
} }
// Get boat values TWS // Get boat value for TWS
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 String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
@ -105,9 +105,9 @@ public:
unit4old = unit4; // Save old unit unit4old = unit4; // Save old unit
} }
// Get boat values DBT // Get boat value for DBT
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list
String name5 = xdrDelete(bvalue5->getName()); // Value name String name5 = xdrDelete(bvalue5->getName()); // Value name
name5 = name5.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
double value5 = bvalue5->value; // Value as double in SI unit double value5 = bvalue5->value; // Value as double in SI unit
@ -119,9 +119,9 @@ public:
unit5old = unit5; // Save old unit unit5old = unit5; // Save old unit
} }
// Get boat values STW // Get boat value for STW
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list
String name6 = xdrDelete(bvalue6->getName()); // Value name String name6 = xdrDelete(bvalue6->getName()); // Value name
name6 = name6.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated
double value6 = bvalue6->value; // Value as double in SI unit double value6 = bvalue6->value; // Value as double in SI unit
@ -140,7 +140,7 @@ public:
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRose, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6); LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRose, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6);
// Draw page // Draw page
@ -248,7 +248,7 @@ public:
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots
const char *ii = ""; const char *ii = "";
switch (i) switch (i)
{ {
case 0: ii="0"; break; case 0: ii="0"; break;
case 30 : ii="30"; break; case 30 : ii="30"; break;
case 60 : ii="60"; break; case 60 : ii="60"; break;
@ -357,8 +357,7 @@ public:
getdisplay().print(unit6old); // Unit getdisplay().print(unit6old); // Unit
} }
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@ -373,11 +372,11 @@ static Page *createPage(CommonData &common){
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need
*/ */
PageDescription registerPageWindRose( PageDescription registerPageWindRose(
"WindRose", // Page name "WindRose", // 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
{"AWA", "AWS", "TWD", "TWS", "DBT", "STW"}, // Bus values we need in the page {"AWA", "AWS", "TWD", "TWS", "DBT", "STW"}, // Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@ -24,7 +24,7 @@ public:
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -48,7 +48,7 @@ public:
String flashLED = config->getString(config->flashLED); String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight); 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) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
@ -63,13 +63,13 @@ public:
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
} }
// Get boat values for AWS // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // First 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 String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
if(valid2 == true){ if(valid2 == true){
@ -77,13 +77,13 @@ public:
unit2old = unit2; // Save old unit unit2old = unit2; // Save old unit
} }
// Get boat values TWD // 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 String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){ if(valid3 == true){
@ -91,13 +91,13 @@ public:
unit3old = unit3; // Save old unit unit3old = unit3; // Save old unit
} }
// Get boat values TWS // 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 String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
if(valid4 == true){ if(valid4 == true){
@ -105,13 +105,13 @@ public:
unit4old = unit4; // Save old unit unit4old = unit4; // Save old unit
} }
// Get boat values DBT // Get boat values #5
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list
String name5 = xdrDelete(bvalue5->getName()); // Value name String name5 = xdrDelete(bvalue5->getName()); // Value name
name5 = name5.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
double value5 = bvalue5->value; // Value as double in SI unit double value5 = bvalue5->value; // Value as double in SI unit
bool valid5 = bvalue5->valid; // Valid information bool valid5 = bvalue5->valid; // Valid information
String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit5 = formatValue(bvalue5, *commonData).unit; // Unit of value String unit5 = formatValue(bvalue5, *commonData).unit; // Unit of value
if(valid5 == true){ if(valid5 == true){
@ -119,13 +119,13 @@ public:
unit5old = unit5; // Save old unit unit5old = unit5; // Save old unit
} }
// Get boat values STW // Get boat values #5
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list
String name6 = xdrDelete(bvalue6->getName()); // Value name String name6 = xdrDelete(bvalue6->getName()); // Value name
name6 = name6.substring(0, 6); // String length limit for 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 calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated
double value6 = bvalue6->value; // Value as double in SI unit double value6 = bvalue6->value; // Value as double in SI unit
bool valid6 = bvalue6->valid; // Valid information bool valid6 = bvalue6->valid; // Valid information
String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit6 = formatValue(bvalue6, *commonData).unit; // Unit of value String unit6 = formatValue(bvalue6, *commonData).unit; // Unit of value
if(valid6 == true){ if(valid6 == true){
@ -136,11 +136,11 @@ public:
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return; if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRoseFlex, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6); LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRoseFlex, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6);
// Draw page // Draw page
@ -192,7 +192,7 @@ public:
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65); getdisplay().setCursor(295, 65);
if(valid3 == true){ if(valid3 == true){
// getdisplay().print(abs(value3 * 180 / PI), 0); // Value // getdisplay().print(abs(value3 * 180 / M_PI), 0); // Value
getdisplay().print(svalue4); // Value getdisplay().print(svalue4); // Value
} }
else{ else{
@ -227,16 +227,15 @@ public:
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit5); // Unit getdisplay().print(unit5); // Unit
} }
else{ else{
getdisplay().print(unit5old); // Unit getdisplay().print(unit5old); // Unit
} }
//******************************************************************************************* //*******************************************************************************************
// Draw wind rose // Draw wind rose
int rInstrument = 110; // Radius of grafic instrument 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 + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
@ -246,24 +245,23 @@ public:
for(int i=0; i<360; i=i+10) for(int i=0; i<360; i=i+10)
{ {
// Scaling values // Scaling values
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots float x = 200 + (rInstrument-30)*sin(i/180.0*M_PI); // x-coordinate dots
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate dots float y = 150 - (rInstrument-30)*cos(i/180.0*M_PI); // y-coordinate dots
const char *ii = ""; const char *ii = "";
switch (i) switch (i) {
{ case 0: ii="0"; break;
case 0: ii="0"; break; case 30 : ii="30"; break;
case 30 : ii="30"; break; case 60 : ii="60"; break;
case 60 : ii="60"; break; case 90 : ii="90"; break;
case 90 : ii="90"; break; case 120 : ii="120"; break;
case 120 : ii="120"; break; case 150 : ii="150"; break;
case 150 : ii="150"; break; case 180 : ii="180"; break;
case 180 : ii="180"; break; case 210 : ii="210"; break;
case 210 : ii="210"; break; case 240 : ii="240"; break;
case 240 : ii="240"; break; case 270 : ii="270"; break;
case 270 : ii="270"; break; case 300 : ii="300"; break;
case 300 : ii="300"; break; case 330 : ii="330"; break;
case 330 : ii="330"; break; default: break;
default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
@ -277,11 +275,11 @@ public:
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*M_PI);
// Draw sub scale with lines (two triangles) // Draw sub scale with lines (two triangles)
if(i % 30 == 0){ if(i % 30 == 0){
@ -309,7 +307,7 @@ public:
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument-15); float yy2 = -(rInstrument-15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
@ -331,39 +329,28 @@ public:
//******************************************************************************************* //*******************************************************************************************
// Show value6, so that it does not collide with the wind pointer // Show value6, so that it does not collide with the wind pointer
if ( cos(value1) > 0){ getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); if (cos(value1) > 0){
getdisplay().setCursor(160, 200); getdisplay().setCursor(160, 200);
getdisplay().print(svalue6); // Value getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt8b); getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 215); getdisplay().setCursor(190, 215);
getdisplay().print(" "); } else{
if(holdvalues == false){ getdisplay().setCursor(160, 130);
getdisplay().print(unit6); // Unit getdisplay().print(svalue6); // Value
} getdisplay().setFont(&Ubuntu_Bold8pt8b);
else{ getdisplay().setCursor(190, 90);
getdisplay().print(unit6old); // Unit }
} getdisplay().print(" ");
} if(holdvalues == false){
else{ getdisplay().print(unit6); // Unit
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); }
getdisplay().setCursor(160, 130); else{
getdisplay().print(svalue6); // Value getdisplay().print(unit6old); // Unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); }
getdisplay().setCursor(190, 90);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit6); // Unit
}
else{
getdisplay().print(unit6old); // Unit
}
}
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@ -378,11 +365,10 @@ static Page *createPage(CommonData &common){
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need
*/ */
PageDescription registerPageWindRoseFlex( PageDescription registerPageWindRoseFlex(
"WindRoseFlex", // Page name "WindRoseFlex", // Page name
createPage, // Action createPage, // Action
6, // Number of bus values depends on selection in Web configuration; was zero 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
true // Show display header on/off
); );
#endif #endif

View File

@ -65,7 +65,7 @@ class PageXTETrack : public Page
return key; return key;
} }
virtual void displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config; GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger; GwLog *logger = commonData->logger;
@ -207,9 +207,7 @@ class PageXTETrack : public Page
drawSegment(399, 100, 318, 24, 289, 24, 350, 100, commonData->fgcolor, seg[4]); drawSegment(399, 100, 318, 24, 289, 24, 350, 100, commonData->fgcolor, seg[4]);
drawSegment(399, 54, 354, 24, 325, 24, 399, 90, commonData->fgcolor, seg[5]); drawSegment(399, 54, 354, 24, 325, 24, 399, 90, commonData->fgcolor, seg[5]);
// Update display return PAGE_UPDATE;
getdisplay().nextPage(); // Partial update (fast)
}; };
}; };
@ -225,11 +223,11 @@ static Page* createPage(CommonData &common){
* this will be number of BoatValue pointers in pageData.values * this will be number of BoatValue pointers in pageData.values
*/ */
PageDescription registerPageXTETrack( PageDescription registerPageXTETrack(
"XTETrack", // Page name "XTETrack", // 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
{"XTE", "COG", "DTW", "BTW"}, // Bus values we need in the page {"XTE", "COG", "DTW", "BTW"}, // Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@ -4,15 +4,19 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
#include "LedSpiTask.h" #include "LedSpiTask.h"
#include "OBPRingBuffer.h"
#include "OBPDataOperations.h"
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data #define MAX_PAGE_NUMBER 10 // Max number of pages for show data
typedef std::vector<GwApi::BoatValue *> ValueList; typedef std::vector<GwApi::BoatValue *> ValueList;
typedef struct{ typedef struct{
String pageName; String pageName;
uint8_t pageNumber; // page number in sequence of visible pages uint8_t pageNumber; // page number in sequence of visible pages
//the values will always contain the user defined values first //the values will always contain the user defined values first
ValueList values; ValueList values;
tBoatHstryData boatHstry;
} PageData; } PageData;
// Sensor data structure (only for extended sensors, not for NMEA bus sensors) // Sensor data structure (only for extended sensors, not for NMEA bus sensors)
@ -78,6 +82,22 @@ typedef struct{
bool on; // fast on/off detector bool on; // fast on/off detector
} BacklightData; } BacklightData;
enum AlarmSource {
Alarm_Generic,
Alarm_Local,
Alarm_NMEA0183,
Alarm_NMEA2000
};
typedef struct{
uint8_t id; // alarm-id e.g. 01..99 from NMEA0183
AlarmSource source;
String message; // single line of plain text
bool active = false;
uint8_t signal; // how to signal MESSAGE | LED | BUZZER
uint8_t length_sec; // seconds until alarm disappeares without user interaction
} AlarmData;
typedef struct{ typedef struct{
GwApi::Status status; GwApi::Status status;
GwLog *logger=NULL; GwLog *logger=NULL;
@ -86,6 +106,7 @@ typedef struct{
SunData sundata; SunData sundata;
TouchKeyData keydata[6]; TouchKeyData keydata[6];
BacklightData backlight; BacklightData backlight;
AlarmData alarm;
GwApi::BoatValue *time=NULL; GwApi::BoatValue *time=NULL;
GwApi::BoatValue *date=NULL; GwApi::BoatValue *date=NULL;
uint16_t fgcolor; uint16_t fgcolor;
@ -100,7 +121,7 @@ class Page{
CommonData *commonData; CommonData *commonData;
public: public:
int refreshtime = 1000; int refreshtime = 1000;
virtual void displayPage(PageData &pageData)=0; virtual int displayPage(PageData &pageData)=0;
virtual void displayNew(PageData &pageData){} virtual void displayNew(PageData &pageData){}
virtual void setupKeys() { virtual void setupKeys() {
#ifdef HARDWARE_V21 #ifdef HARDWARE_V21
@ -176,7 +197,7 @@ typedef struct{
double value; double value;
String svalue; String svalue;
String unit; String unit;
} FormatedData; } FormattedData;
// Formatter for boat values // Formatter for boat values
FormatedData formatValue(GwApi::BoatValue *value, CommonData &commondata); FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);

View File

@ -219,6 +219,17 @@
"obp60":"true" "obp60":"true"
} }
}, },
{
"name": "calcTrueWnds",
"label": "Calculate True Wind",
"type": "boolean",
"default": "false",
"description": "If not available, calculate true wind data from appearant wind and other boat data",
"category": "OBP60 Settings",
"capabilities": {
"obp60": "true"
}
},
{ {
"name": "lengthFormat", "name": "lengthFormat",
"label": "Length Format", "label": "Length Format",
@ -726,7 +737,7 @@
"condition": [ "condition": [
{ "calInstance1": "AWA" }, { "calInstance1": "AWA" },
{ "calInstance1": "AWS" }, { "calInstance1": "AWS" },
{ "calInstance2": "COG" }, { "calInstance1": "COG" },
{ "calInstance1": "DBT" }, { "calInstance1": "DBT" },
{ "calInstance1": "HDM" }, { "calInstance1": "HDM" },
{ "calInstance1": "PRPOS" }, { "calInstance1": "PRPOS" },
@ -751,7 +762,7 @@
"condition": [ "condition": [
{ "calInstance1": "AWA" }, { "calInstance1": "AWA" },
{ "calInstance1": "AWS" }, { "calInstance1": "AWS" },
{ "calInstance2": "COG" }, { "calInstance1": "COG" },
{ "calInstance1": "DBT" }, { "calInstance1": "DBT" },
{ "calInstance1": "HDM" }, { "calInstance1": "HDM" },
{ "calInstance1": "PRPOS" }, { "calInstance1": "PRPOS" },
@ -779,7 +790,7 @@
"condition": [ "condition": [
{ "calInstance1": "AWA" }, { "calInstance1": "AWA" },
{ "calInstance1": "AWS" }, { "calInstance1": "AWS" },
{ "calInstance2": "COG" }, { "calInstance1": "COG" },
{ "calInstance1": "DBT" }, { "calInstance1": "DBT" },
{ "calInstance1": "HDM" }, { "calInstance1": "HDM" },
{ "calInstance1": "PRPOS" }, { "calInstance1": "PRPOS" },
@ -1285,8 +1296,6 @@
"obp60":"true" "obp60":"true"
} }
}, },
{ {
"name": "page1type", "name": "page1type",
"label": "Type", "label": "Type",
@ -1315,6 +1324,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -1595,6 +1605,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -1872,6 +1883,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -2146,6 +2158,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -2417,6 +2430,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -2685,6 +2699,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -2950,6 +2965,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -3212,6 +3228,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -3471,6 +3488,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -3727,6 +3745,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"

View File

@ -219,6 +219,17 @@
"obp40": "true" "obp40": "true"
} }
}, },
{
"name": "calcTrueWnds",
"label": "Calculate True Wind",
"type": "boolean",
"default": "false",
"description": "If not available, calculate true wind data from appearant wind and other boat data",
"category": "OBP40 Settings",
"capabilities": {
"obp40": "true"
}
},
{ {
"name": "lengthFormat", "name": "lengthFormat",
"label": "Length Format", "label": "Length Format",
@ -707,10 +718,12 @@
"---", "---",
"AWA", "AWA",
"AWS", "AWS",
"COG",
"DBT", "DBT",
"HDM", "HDM",
"PRPOS", "PRPOS",
"RPOS", "RPOS",
"SOG",
"STW", "STW",
"TWA", "TWA",
"TWS", "TWS",
@ -730,8 +743,22 @@
"description": "Offset for data instance 1", "description": "Offset for data instance 1",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance1": "AWA" },
{ "calInstance1": "AWS" },
{ "calInstance1": "COG" },
{ "calInstance1": "DBT" },
{ "calInstance1": "HDM" },
{ "calInstance1": "PRPOS" },
{ "calInstance1": "RPOS" },
{ "calInstance1": "SOG" },
{ "calInstance1": "STW" },
{ "calInstance1": "TWA" },
{ "calInstance1": "TWS" },
{ "calInstance1": "TWD" },
{ "calInstance1": "WTemp" } ]
}, },
{ {
"name": "calSlope1", "name": "calSlope1",
@ -741,8 +768,22 @@
"description": "Slope for data instance 1", "description": "Slope for data instance 1",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance1": "AWA" },
{ "calInstance1": "AWS" },
{ "calInstance1": "COG" },
{ "calInstance1": "DBT" },
{ "calInstance1": "HDM" },
{ "calInstance1": "PRPOS" },
{ "calInstance1": "RPOS" },
{ "calInstance1": "SOG" },
{ "calInstance1": "STW" },
{ "calInstance1": "TWA" },
{ "calInstance1": "TWS" },
{ "calInstance1": "TWD" },
{ "calInstance1": "WTemp" } ]
}, },
{ {
"name": "calSmooth1", "name": "calSmooth1",
@ -752,11 +793,25 @@
"check": "checkMinMax", "check": "checkMinMax",
"min": 0, "min": 0,
"max": 10, "max": 10,
"description": "Smoothing factor for data instance 1", "description": "Smoothing factor [0..10]; 0 = no smoothing",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance1": "AWA" },
{ "calInstance1": "AWS" },
{ "calInstance1": "COG" },
{ "calInstance1": "DBT" },
{ "calInstance1": "HDM" },
{ "calInstance1": "PRPOS" },
{ "calInstance1": "RPOS" },
{ "calInstance1": "SOG" },
{ "calInstance1": "STW" },
{ "calInstance1": "TWA" },
{ "calInstance1": "TWS" },
{ "calInstance1": "TWD" },
{ "calInstance1": "WTemp" } ]
}, },
{ {
"name": "calInstance2", "name": "calInstance2",
@ -768,10 +823,12 @@
"---", "---",
"AWA", "AWA",
"AWS", "AWS",
"COG",
"DBT", "DBT",
"HDM", "HDM",
"PRPOS", "PRPOS",
"RPOS", "RPOS",
"SOG",
"STW", "STW",
"TWA", "TWA",
"TWS", "TWS",
@ -791,8 +848,22 @@
"description": "Offset for data instance 2", "description": "Offset for data instance 2",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance2": "AWA" },
{ "calInstance2": "AWS" },
{ "calInstance2": "COG" },
{ "calInstance2": "DBT" },
{ "calInstance2": "HDM" },
{ "calInstance2": "PRPOS" },
{ "calInstance2": "RPOS" },
{ "calInstance2": "SOG" },
{ "calInstance2": "STW" },
{ "calInstance2": "TWA" },
{ "calInstance2": "TWS" },
{ "calInstance2": "TWD" },
{ "calInstance2": "WTemp" } ]
}, },
{ {
"name": "calSlope2", "name": "calSlope2",
@ -802,8 +873,22 @@
"description": "Slope for data instance 2", "description": "Slope for data instance 2",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance2": "AWA" },
{ "calInstance2": "AWS" },
{ "calInstance2": "COG" },
{ "calInstance2": "DBT" },
{ "calInstance2": "HDM" },
{ "calInstance2": "PRPOS" },
{ "calInstance2": "RPOS" },
{ "calInstance2": "SOG" },
{ "calInstance2": "STW" },
{ "calInstance2": "TWA" },
{ "calInstance2": "TWS" },
{ "calInstance2": "TWD" },
{ "calInstance2": "WTemp" } ]
}, },
{ {
"name": "calSmooth2", "name": "calSmooth2",
@ -813,11 +898,25 @@
"check": "checkMinMax", "check": "checkMinMax",
"min": 0, "min": 0,
"max": 10, "max": 10,
"description": "Smoothing factor for data instance 2", "description": "Smoothing factor [0..10]; 0 = no smoothing",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance2": "AWA" },
{ "calInstance2": "AWS" },
{ "calInstance2": "COG" },
{ "calInstance2": "DBT" },
{ "calInstance2": "HDM" },
{ "calInstance2": "PRPOS" },
{ "calInstance2": "RPOS" },
{ "calInstance2": "SOG" },
{ "calInstance2": "STW" },
{ "calInstance2": "TWA" },
{ "calInstance2": "TWS" },
{ "calInstance2": "TWD" },
{ "calInstance2": "WTemp" } ]
}, },
{ {
"name": "calInstance3", "name": "calInstance3",
@ -829,10 +928,12 @@
"---", "---",
"AWA", "AWA",
"AWS", "AWS",
"COG",
"DBT", "DBT",
"HDM", "HDM",
"PRPOS", "PRPOS",
"RPOS", "RPOS",
"SOG",
"STW", "STW",
"TWA", "TWA",
"TWS", "TWS",
@ -852,8 +953,22 @@
"description": "Offset for data instance 3", "description": "Offset for data instance 3",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance3": "AWA" },
{ "calInstance3": "AWS" },
{ "calInstance3": "COG" },
{ "calInstance3": "DBT" },
{ "calInstance3": "HDM" },
{ "calInstance3": "PRPOS" },
{ "calInstance3": "RPOS" },
{ "calInstance3": "SOG" },
{ "calInstance3": "STW" },
{ "calInstance3": "TWA" },
{ "calInstance3": "TWS" },
{ "calInstance3": "TWD" },
{ "calInstance3": "WTemp" } ]
}, },
{ {
"name": "calSlope3", "name": "calSlope3",
@ -863,8 +978,22 @@
"description": "Slope for data instance 3", "description": "Slope for data instance 3",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance3": "AWA" },
{ "calInstance3": "AWS" },
{ "calInstance3": "COG" },
{ "calInstance3": "DBT" },
{ "calInstance3": "HDM" },
{ "calInstance3": "PRPOS" },
{ "calInstance3": "RPOS" },
{ "calInstance3": "SOG" },
{ "calInstance3": "STW" },
{ "calInstance3": "TWA" },
{ "calInstance3": "TWS" },
{ "calInstance3": "TWD" },
{ "calInstance3": "WTemp" } ]
}, },
{ {
"name": "calSmooth3", "name": "calSmooth3",
@ -874,11 +1003,25 @@
"check": "checkMinMax", "check": "checkMinMax",
"min": 0, "min": 0,
"max": 10, "max": 10,
"description": "Smoothing factor for data instance 3", "description": "Smoothing factor [0..10]; 0 = no smoothing",
"category": "OBP40 Calibrations", "category": "OBP40 Calibrations",
"capabilities": { "capabilities": {
"obp40": "true" "obp40":"true"
} },
"condition": [
{ "calInstance3": "AWA" },
{ "calInstance3": "AWS" },
{ "calInstance3": "COG" },
{ "calInstance3": "DBT" },
{ "calInstance3": "HDM" },
{ "calInstance3": "PRPOS" },
{ "calInstance3": "RPOS" },
{ "calInstance3": "SOG" },
{ "calInstance3": "STW" },
{ "calInstance3": "TWA" },
{ "calInstance3": "TWS" },
{ "calInstance3": "TWD" },
{ "calInstance3": "WTemp" } ]
}, },
{ {
"name": "display", "name": "display",
@ -1204,6 +1347,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -1484,6 +1628,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -1761,6 +1906,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -2035,6 +2181,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -2306,6 +2453,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -2574,6 +2722,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -2839,6 +2988,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -3101,6 +3251,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -3360,6 +3511,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -3616,6 +3768,7 @@
"Voltage", "Voltage",
"WhitePage", "WhitePage",
"Wind", "Wind",
"WindPlot",
"WindRose", "WindRose",
"WindRoseFlex", "WindRoseFlex",
"XTETrack" "XTETrack"
@ -3842,3 +3995,4 @@
] ]
} }
] ]

View File

@ -0,0 +1,6 @@
Debugging tool
##############
log.txt = text file with error messages from terminal console
tools/decoder.py -p ESP32S3 -t ~/.platformio/packages/toolchain-xtensa-esp32s3/ -e .pio/build/obp60_s3/firmware.elf log.txt

View File

@ -31,6 +31,7 @@ no_of_fields_per_page = {
"TwoValues": 2, "TwoValues": 2,
"Voltage": 0, "Voltage": 0,
"WhitePage": 0, "WhitePage": 0,
"WindPlot": 0,
"WindRose": 0, "WindRose": 0,
"WindRoseFlex": 6, "WindRoseFlex": 6,
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,8 @@
#include "OBP60Extensions.h" // Functions lib for extension board #include "OBP60Extensions.h" // Functions lib for extension board
#include "OBP60Keypad.h" // Functions for keypad #include "OBP60Keypad.h" // Functions for keypad
#include "BoatDataCalibration.h" // Functions lib for data instance calibration #include "BoatDataCalibration.h" // Functions lib for data instance calibration
#include "OBPRingBuffer.h" // Functions lib with ring buffer for history storage of some boat data
#include "OBPDataOperations.h" // Functions lib for data operations such as true wind calculation
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
#include "driver/rtc_io.h" // Needs for weakup from deep sleep #include "driver/rtc_io.h" // Needs for weakup from deep sleep
@ -52,31 +54,13 @@ void OBP60Init(GwApi *api){
// Check I2C devices // Check I2C devices
// Init hardware // Init hardware
hardwareInit(api); hardwareInit(api);
// Init power rail 5.0V // Init power
String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString(); String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str()); api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str());
if(powermode == "Max Power" || powermode == "Only 5.0V"){ powerInit(powermode);
#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
}
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
bool sdcard = config->getBool(config->useSDCard); bool sdcard = config->getBool(config->useSDCard);
@ -275,6 +259,8 @@ void registerAllPages(PageList &list){
list.add(&registerPageFourValues2); list.add(&registerPageFourValues2);
extern PageDescription registerPageWind; extern PageDescription registerPageWind;
list.add(&registerPageWind); list.add(&registerPageWind);
extern PageDescription registerPageWindPlot;
list.add(&registerPageWindPlot);
extern PageDescription registerPageWindRose; extern PageDescription registerPageWindRose;
list.add(&registerPageWindRose); list.add(&registerPageWindRose);
extern PageDescription registerPageWindRoseFlex; extern PageDescription registerPageWindRoseFlex;
@ -372,6 +358,124 @@ void underVoltageDetection(GwApi *api, CommonData &common){
} }
} }
//bool addTrueWind(GwApi* api, BoatValueList* boatValues, double *twd, double *tws, double *twa) {
bool addTrueWind(GwApi* api, BoatValueList* boatValues) {
// Calculate true wind data and add to obp60task boat data list
double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal;
double twd, tws, twa;
bool isCalculated = false;
const double DBL_MIN = std::numeric_limits<double>::lowest();
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate("TWD");
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate("TWS");
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
GwApi::BoatValue *awaBVal = boatValues->findValueOrCreate("AWA");
GwApi::BoatValue *awsBVal = boatValues->findValueOrCreate("AWS");
GwApi::BoatValue *cogBVal = boatValues->findValueOrCreate("COG");
GwApi::BoatValue *stwBVal = boatValues->findValueOrCreate("STW");
GwApi::BoatValue *sogBVal = boatValues->findValueOrCreate("SOG");
GwApi::BoatValue *hdtBVal = boatValues->findValueOrCreate("HDT");
GwApi::BoatValue *hdmBVal = boatValues->findValueOrCreate("HDM");
GwApi::BoatValue *varBVal = boatValues->findValueOrCreate("VAR");
awaVal = awaBVal->valid ? awaBVal->value : DBL_MIN;
awsVal = awsBVal->valid ? awsBVal->value : DBL_MIN;
cogVal = cogBVal->valid ? cogBVal->value : DBL_MIN;
stwVal = stwBVal->valid ? stwBVal->value : DBL_MIN;
sogVal = sogBVal->valid ? sogBVal->value : DBL_MIN;
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, 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, &twd, &tws, &twa);
if (isCalculated) { // Replace values only, if successfully calculated and not already available
if (!twdBVal->valid) {
twdBVal->value = twd;
twdBVal->valid = true;
}
if (!twsBVal->valid) {
twsBVal->value = tws;
twsBVal->valid = true;
}
if (!twaBVal->valid) {
twaBVal->value = twa;
twaBVal->valid = true;
}
}
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;
}
void initHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList) {
// Init history buffers for TWD, TWS
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
int hstryMinVal = 0; // Minimum value for these history buffers
int twdHstryMax = 6283; // Max value for wind direction (TWD) in rad (0...2*PI), shifted by 1000 for 3 decimals
int twsHstryMax = 1000; // Max value for wind speed (TWS) in m/s, shifted by 10 for 1 decimal
// Initialize history buffers with meta data
hstryBufList.twdHstry->setMetaData("TWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
hstryBufList.twsHstry->setMetaData("TWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
}
void handleHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList) {
// Handle history buffers for TWD, TWS
GwLog *logger = api->getLogger();
int16_t twdHstryMin = hstryBufList.twdHstry->getMinVal();
int16_t twdHstryMax = hstryBufList.twdHstry->getMaxVal();
int16_t twsHstryMin = hstryBufList.twsHstry->getMinVal();
int16_t twsHstryMax = hstryBufList.twsHstry->getMaxVal();
int16_t twdBuf, twsBuf;
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
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) {
calBVal->value = twdBVal->value;
calBVal->valid = twdBVal->valid;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
twdBuf = static_cast<int16_t>(std::round(calBVal->value * 1000));
if (twdBuf >= twdHstryMin && twdBuf <= twdHstryMax) {
hstryBufList.twdHstry->add(twdBuf);
}
}
delete calBVal;
calBVal = nullptr;
calBVal = new GwApi::BoatValue("TWS"); // temporary solution for calibration of history buffer values
calBVal->setFormat(twsBVal->getFormat());
if (twsBVal->valid) {
calBVal->value = twsBVal->value;
calBVal->valid = twsBVal->valid;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
twsBuf = static_cast<int16_t>(std::round(calBVal->value * 10));
if (twsBuf >= twsHstryMin && twsBuf <= twsHstryMax) {
hstryBufList.twsHstry->add(twsBuf);
}
}
delete calBVal;
calBVal = nullptr;
}
// OBP60 Task // OBP60 Task
//#################################################################################### //####################################################################################
void OBP60Task(GwApi *api){ void OBP60Task(GwApi *api){
@ -421,7 +525,7 @@ void OBP60Task(GwApi *api){
#endif #endif
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Use this for Waveshare boards with "clever" reset circuit, 2ms reset pulse getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else #else
getdisplay().init(115200); // Init for normal displays getdisplay().init(115200); // Init for normal displays
#endif #endif
@ -485,6 +589,11 @@ void OBP60Task(GwApi *api){
//commonData.distanceformat=config->getString(xxx); //commonData.distanceformat=config->getString(xxx);
//add all necessary data to common data //add all necessary data to common data
// Create ring buffers for history storage of some boat data
RingBuffer<int16_t> twdHstry(960); // Circular buffer to store wind direction values; store 960 TWD values for 16 minutes history
RingBuffer<int16_t> twsHstry(960); // Circular buffer to store wind speed values (TWS)
tBoatHstryData hstryBufList = {&twdHstry, &twsHstry};
//fill the page data from config //fill the page data from config
numPages=config->getInt(config->visiblePages,1); numPages=config->getInt(config->visiblePages,1);
if (numPages < 1) numPages=1; if (numPages < 1) numPages=1;
@ -523,6 +632,10 @@ void OBP60Task(GwApi *api){
LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i); LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i);
pages[i].parameters.values.push_back(value); pages[i].parameters.values.push_back(value);
} }
if (pages[i].description->pageName == "WindPlot") {
// Add boat history data to page parameters
pages[i].parameters.boatHstry = hstryBufList;
}
} }
// add out of band system page (always available) // add out of band system page (always available)
Page *syspage = allPages.pages[0]->creator(commonData); Page *syspage = allPages.pages[0]->creator(commonData);
@ -530,6 +643,13 @@ void OBP60Task(GwApi *api){
// Read all calibration data settings from config // Read all calibration data settings from config
calibrationData.readConfig(config, logger); calibrationData.readConfig(config, logger);
// Check user setting for true wind calculation
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
// bool simulation = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
// Initialize history buffer for certain boat data
initHstryBuf(api, &boatValues, hstryBufList);
// Display screenshot handler for HTTP request // Display screenshot handler for HTTP request
// http://192.168.15.1/api/user/OBP60Task/screenshot // http://192.168.15.1/api/user/OBP60Task/screenshot
api->registerRequestHandler("screenshot", [api, &pageNumber, pages](AsyncWebServerRequest *request) { api->registerRequestHandler("screenshot", [api, &pageNumber, pages](AsyncWebServerRequest *request) {
@ -711,7 +831,7 @@ void OBP60Task(GwApi *api){
} }
// #9 or #10 Refresh display after a new page after 4s waiting time and if refresh is disabled // #9 or #10 Refresh display after a new page after 4s waiting time and if refresh is disabled
if(refreshmode == true && (keyboardMessage == 9 || keyboardMessage == 10)){ if(refreshmode == true && (keyboardMessage == 9 || keyboardMessage == 10 || keyboardMessage == 4 || keyboardMessage == 3)){
starttime4 = millis(); starttime4 = millis();
starttime2 = millis(); // Reset the timer for full display update starttime2 = millis(); // Reset the timer for full display update
delayedDisplayUpdate = true; delayedDisplayUpdate = true;
@ -747,12 +867,22 @@ void OBP60Task(GwApi *api){
starttime1 = millis(); starttime1 = millis();
starttime2 = millis(); starttime2 = millis();
getdisplay().setFullWindow(); // Set full update getdisplay().setFullWindow(); // Set full update
getdisplay().nextPage(); if(fastrefresh == "true"){
if(fastrefresh == "false"){ getdisplay().nextPage(); // Full update
}
else{
getdisplay().fillScreen(commonData.fgcolor); // Clear display getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
getdisplay().init(115200); // Init for normal displays
#endif
getdisplay().firstPage(); // Full update
getdisplay().nextPage(); // Full update getdisplay().nextPage(); // Full update
getdisplay().fillScreen(commonData.bgcolor); // Clear display // getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().nextPage(); // Full update // getdisplay().fillScreen(commonData.bgcolor); // Clear display
// getdisplay().nextPage(); // Partial update
// getdisplay().nextPage(); // Partial update
} }
delayedDisplayUpdate = false; delayedDisplayUpdate = false;
} }
@ -764,12 +894,22 @@ void OBP60Task(GwApi *api){
starttime2 = millis(); starttime2 = millis();
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh first 5 min"); LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh first 5 min");
getdisplay().setFullWindow(); // Set full update getdisplay().setFullWindow(); // Set full update
getdisplay().nextPage(); if(fastrefresh == "true"){
if(fastrefresh == "false"){ getdisplay().nextPage(); // Full update
}
else{
getdisplay().fillScreen(commonData.fgcolor); // Clear display getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
getdisplay().init(115200); // Init for normal displays
#endif
getdisplay().firstPage(); // Full update
getdisplay().nextPage(); // Full update getdisplay().nextPage(); // Full update
getdisplay().fillScreen(commonData.bgcolor); // Clear display // getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().nextPage(); // Full update // getdisplay().fillScreen(commonData.bgcolor); // Clear display
// getdisplay().nextPage(); // Partial update
// getdisplay().nextPage(); // Partial update
} }
} }
@ -778,15 +918,25 @@ void OBP60Task(GwApi *api){
starttime2 = millis(); starttime2 = millis();
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh"); LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh");
getdisplay().setFullWindow(); // Set full update getdisplay().setFullWindow(); // Set full update
getdisplay().nextPage(); if(fastrefresh == "true"){
if(fastrefresh == "false"){
getdisplay().fillScreen(commonData.fgcolor); // Clear display
getdisplay().nextPage(); // Full update
getdisplay().fillScreen(commonData.bgcolor); // Clear display
getdisplay().nextPage(); // Full update getdisplay().nextPage(); // Full update
} }
} else{
getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
getdisplay().init(115200); // Init for normal displays
#endif
getdisplay().firstPage(); // Full update
getdisplay().nextPage(); // Full update
// getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// getdisplay().fillScreen(commonData.bgcolor); // Clear display
// getdisplay().nextPage(); // Partial update
// getdisplay().nextPage(); // Partial update
}
}
// Refresh display data, default all 1s // Refresh display data, default all 1s
currentPage = pages[pageNumber].page; currentPage = pages[pageNumber].page;
int pagetime = 1000; int pagetime = 1000;
@ -802,6 +952,12 @@ void OBP60Task(GwApi *api){
api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues); api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues);
api->getStatus(commonData.status); api->getStatus(commonData.status);
if (calcTrueWnds) {
addTrueWind(api, &boatValues);
}
// Handle history buffers for TWD, TWS for wind plot page and other usage
handleHstryBuf(api, &boatValues, hstryBufList);
// Clear display // Clear display
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor); // getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
getdisplay().fillScreen(commonData.bgcolor); // Clear display getdisplay().fillScreen(commonData.bgcolor); // Clear display
@ -843,7 +999,16 @@ void OBP60Task(GwApi *api){
if (pages[pageNumber].description && pages[pageNumber].description->header){ if (pages[pageNumber].description && pages[pageNumber].description->header){
displayFooter(commonData); displayFooter(commonData);
} }
currentPage->displayPage(pages[pageNumber].parameters); int ret = currentPage->displayPage(pages[pageNumber].parameters);
if (commonData.alarm.active) {
displayAlarm(commonData);
}
if (ret & PAGE_UPDATE) {
getdisplay().nextPage(); // Partial update (fast)
}
if (ret & PAGE_HIBERNATE) {
getdisplay().hibernate();
}
} }
} }

View File

@ -40,19 +40,20 @@ lib_deps =
paulstoffregen/OneWire@2.3.8 paulstoffregen/OneWire@2.3.8
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
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 (seral device) # -D CORE_DEBUG_LEVEL=1 #Debug level for CPU core via CDC (serial device)
# -D TIME=$UNIX_TIME #Set PC time for RTC (only settable via VSC) # -D TIME=$UNIX_TIME #Set PC time for RTC (only settable via VSC)
-D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib -D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib
-D BOARD_OBP60S3 #Board OBP60 V2.1 with ESP32S3 -D BOARD_OBP60S3 #Board OBP60 V2.1 with ESP32S3
# -D HARDWARE_V20 #OBP60 hardware revision V2.0 # -D HARDWARE_V20 #OBP60 hardware revision V2.0
-D HARDWARE_V21 #OBP60 hardware revision V2.1 -D HARDWARE_V21 #OBP60 hardware revision V2.1
# -D DISPLAY_GDEW042T2 #old E-Ink display from Waveshare, R10 0.47 ohm # -D DISPLAY_GDEW042T2 #old E-Ink display from GoodDisplay (Waveshare), R10 0.47 ohm - very good
-D DISPLAY_GDEY042T81 #new E-Ink display from Waveshare, R10 2.2 ohm -D DISPLAY_GDEY042T81 #new E-Ink display from GoodDisplay (Waveshare), R10 2.2 ohm - good (contast lost by shunshine)
# -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm # -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm - medium
# -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm # -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm - bad (burn in effects)
# -D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good
${env.build_flags} ${env.build_flags}
#CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default #CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default
upload_port = /dev/ttyACM0 #OBP60 download via USB-C direct upload_port = /dev/ttyACM0 #OBP60 download via USB-C direct
@ -91,12 +92,13 @@ lib_deps =
paulstoffregen/OneWire@2.3.8 paulstoffregen/OneWire@2.3.8
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
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
-D HARDWARE_V10 #OBP40 hardware revision V1.0 SKU:DIE07300S V1.1 (CrowPanel 4.2) -D HARDWARE_V10 #OBP40 hardware revision V1.0 SKU:DIE07300S V1.1 (CrowPanel 4.2)
-D DISPLAY_GDEY042T81 #new E-Ink display from Waveshare, R10 2.2 ohm -D DISPLAY_GDEY042T81 #new E-Ink display from Good Display (Waveshare), R10 2.2 ohm - good (contast lost by shunshine)
#-D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good
#-D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh #-D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh
#-D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors #-D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors
${env.build_flags} ${env.build_flags}