Compare commits

..

3 Commits

7 changed files with 370 additions and 1 deletions

View File

@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
/*
* Barograph WIP
* - Zoom feature
* - Saves data in FRAM if available
*/
#include "Pagedata.h"
#include "OBP60Extensions.h"
class PageBarograph : public Page
{
private:
bool keylock = false;
bool has_fram = false;
String flashLED;
String useenvsensor;
char source = 'I'; // (I)nternal, e(X)ternal
const int series[5] = {75, 150, 300, 600, 900};
const int zoom[5] = {1, 2, 3, 6, 12};
int zoomindex = 4;
uint16_t data[336] = {0}; // current data to display
// y-axis
uint16_t vmin;
uint16_t vmax;
uint16_t scalemin = 1000;
uint16_t scalemax = 1020;
uint16_t scalestep = 5;
int hist1 = 0; // one hour trend
int hist3 = 0; // three hours trend
long refresh = 0; // millis
void loadData() {
// Transfer data from history to page buffer,
// set y-axis according to data
int i = zoom[zoomindex];
// get min and max values of measured data
vmin = data[0];
vmax = data[0];
for (int x = 0; x < 336; x++) {
if (data[x] != 0) {
if (data[x] < vmin) {
vmin = data[x];
} else if (data[x] > vmax) {
vmax = data[x];
}
}
}
// calculate y-axis scale
uint16_t diff = vmax - vmin;
if (diff < 20) {
scalestep = 5;
} else if (diff < 40) {
scalestep = 10;
} else {
scalestep = 15;
}
scalemin = vmin - (vmin % scalestep);
scalemax = vmax + scalestep - (vmax % scalestep);
// TODO implement history buffer
};
public:
PageBarograph(CommonData &common): Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageBarograph");
// Get config data
useenvsensor = config->getString(common.config->useEnvSensor);
// possible values for internal sensor
static std::vector<String> sensorList = {
"BME280", "BMP280", "BMP180", "BMP085", "HTU21", "SHT21"
};
if (std::find(sensorList.begin(), sensorList.end(), useenvsensor) != sensorList.end()) {
source = 'I';
} else {
// "off" means user external data if available
source = 'X';
}
//common.logger->logDebug(GwLog::LOG,"Source=%s (%s)", source, useenvsensor);
loadData(); // initial load
}
int handleKey(int key) {
if (key == 1) {
// zoom in
if (zoomindex > 0) {
zoomindex -= 1;
}
return 0;
}
if (key == 2) {
// zoom out
if (zoomindex < sizeof(zoom)) {
zoomindex += 1;
}
return 0;
}
if (key == 11) {
keylock = !keylock;
return 0;
}
return key;
}
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageBarograph");
// Draw page
//***********************************************************
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// Frames
epd->fillRect(0, 75, 400, 2, commonData->fgcolor); // fillRect: x, y, w, h
epd->fillRect(130, 20, 2, 55, commonData->fgcolor);
epd->fillRect(270, 20, 2, 55, commonData->fgcolor);
epd->fillRect(325, 20, 2, 55, commonData->fgcolor);
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
if (source == 'I') {
drawTextCenter(360, 40, useenvsensor);
} else {
drawTextCenter(360, 40, "ext.");
}
// Trend
drawTextCenter(295, 62, "0.0");
// Alarm placeholder
drawTextCenter(70, 62, "Alarm Off");
// Zoom
int datastep = series[zoomindex];
String fmt;
if (datastep > 120) {
if (datastep % 60 == 0) {
fmt = String(datastep / 60.0, 0) + " min";
} else {
fmt = String(datastep / 60.0, 1) + " min";
}
} else {
fmt = String(datastep) + " s";
}
drawTextCenter(360, 62, fmt);
// Current measurement
epd->setFont(&Ubuntu_Bold16pt8b);
drawTextCenter(200, 40, String(commonData->data.airPressure / 100, 1));
epd->setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(200, 62, "hPa"); // Unit
// Diagram
const int xstep = 48; // x-axis-grid
const int x0 = 350; // origin
const int y0 = 270;
const int w = 7 * 48;
const int h = 180;
// epd->drawRect(x0 - w, y0 - h, w, h, commonData->fgcolor);
// x-axis are hours
for (int i = 1; i <= 6; i++) {
String label = String(-1 * zoom[zoomindex] * i);
epd->drawLine(x0 - i * xstep, y0, x0 - i * xstep, y0 - h, commonData->fgcolor);
drawTextCenter(x0 - i * xstep, y0 - 10, label);
}
// y-axis
epd->drawLine(x0 + 5, y0, x0 + 5, y0 - h, commonData->fgcolor); // drawLine: x1, y1, x2, y2
epd->drawLine(x0 - w, y0, x0 - w, y0 - h, commonData->fgcolor);
epd->drawLine(x0 - w - 5, y0, x0 - w - 5, y0 - h, commonData->fgcolor);
epd->drawLine(x0, y0, x0, y0 - h, commonData->fgcolor);
int16_t dy = 9; // px for one hPa
int16_t y = y0;
int16_t ys = scalemin;
while (y >= y0 - h) {
if (y % scalestep == 0) {
// big step, show label and long line
epd->setCursor(x0 + 10, y + 5);
epd->print(String(ys));
epd->drawLine(x0 + 5, y, x0 - w - 5, y, commonData->fgcolor);
} else {
// small step, only short lines left and right
epd->drawLine(x0 + 5, y, x0, y, commonData->fgcolor);
epd->drawLine(x0 - w - 5, y, x0 - w, y, commonData->fgcolor);
}
y -= dy;
ys += 1;
}
return PAGE_UPDATE;
};
};
static Page* createPage(CommonData &common){
return new PageBarograph(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageBarograph(
"Barograph", // Page name
createPage, // Action
0, // No bus values needed
true // Show display header on/off
);
#endif

View File

@ -39,7 +39,7 @@ To create a new page for OBP60 the following steps are necessary:
3. Register new page in /lib/obp60task/obp60task.cpp in function 3. Register new page in /lib/obp60task/obp60task.cpp in function
'registerAllPages' 'registerAllPages'
4. Add new page in /lib/obp60task/config.json for each page type 4. Add new page in /lib/obp60task/config.json for each page type
or use gen_set.py to auto-generate the relevant section of or use gen_set.py to auto-generate the relevant section of
config.json. For further information on that read the comments config.json. For further information on that read the comments
in gen_set.py. in gen_set.py.
5. Copy the changes in config.json to config_obp40.json and rename 5. Copy the changes in config.json to config_obp40.json and rename
@ -82,6 +82,10 @@ Compile result for OBP40 (CrowPanel 4.2):
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin, ready to flash to offset 0x0000 /workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin, ready to flash to offset 0x0000
Compilation issues
------------------
? Error while linking: "undefined reference to `registerPageXXX'"
: Check if the required page is enabled for current board/environment: #if defined ...
Debugging tool Debugging tool
-------------- --------------

View File

@ -1349,6 +1349,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -1659,6 +1660,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -1960,6 +1962,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -2252,6 +2255,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -2535,6 +2539,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -2809,6 +2814,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -3074,6 +3080,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -3330,6 +3337,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -3577,6 +3585,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -3815,6 +3824,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",

View File

@ -1372,6 +1372,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -1682,6 +1683,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -1983,6 +1985,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -2275,6 +2278,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -2558,6 +2562,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -2832,6 +2837,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -3097,6 +3103,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -3353,6 +3360,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -3600,6 +3608,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",
@ -3838,6 +3847,7 @@
"list": [ "list": [
"AIS", "AIS",
"Anchor", "Anchor",
"Barograph",
"BME280", "BME280",
"Battery", "Battery",
"Battery2", "Battery2",

84
lib/obp60task/hbuffer.cpp Normal file
View File

@ -0,0 +1,84 @@
/* History Buffer
*
* Storage backed buffer for sensordata
* Permanent storage only supported type: FRAM on I2C-Bus
*
* Values can be 1 to 4 bytes in length
*
* Header: 32 bytes of size
* 0 0x00 HB00 4 magic number
* 4 0x04 xxxxxxxxxxxxxxxx 16 name, space padded
* 20 0x14 n 1 byte size of values in buffer
* 21 0x15 mm 2 buffer size in count of values
* 23 0x17 dd 2 time step in seconds between values
* 25 0x19 tttt 4 unix timestamp of head
* 29 0x1d hh 2 head pointer
* 31 0x1f 0xff 1 header end sign
*
* 32 0x20 ... start of buffer data
*
* Usage example: 7 hours of data collected every 75 seconds
* TODO
*
*/
#include <stdint.h>
#include <time.h>
class HistoryBuffer {
private:
// Header prototype for permanent storage
uint8_t header[32] = {
0x41, 0x48, 0x30, 0x30, // magic: HB00
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // empty name
0x01, // byte size
0x50, 0x01, // value count
0x4b, 0x00, // time step
0x00, 0x00, 0x00, 0x00, // unix time stamp
0x00, 0x00, // head pointer
0xff // end sign
};
uint16_t head = 0; // head pointer to next new value position
time_t timestamp; // last modification time of head
uint16_t delta_t; // time step in seconds
public:
HistoryBuffer(uint16_t size) {
}
~HistoryBuffer() {
// free memory
}
void begin() {
//
}
void finish() {
}
uint16_t add() {
// returns new head value pointer
}
uint8_t* get() {
// returns complete buffer in order new to old
}
uint8_t getvalue(uint16_t dt) {
// Return a single value delta seconds ago
uint16_t index = head - abs(dt) / delta_t;
return 0;
}
uint8_t getvalue3() {
}
bool clear() {
// clears buffer and permanent storage
return true;
}
};
class History {
public:
History() {
}
~History() {
}
void *addSeries() {
}
};

21
lib/obp60task/hbuffer.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef __HBUFFER_H__
#define __HBUFFER_H__
class HistoryBuffer {
public:
HistoryBuffer(uint16_t size);
void begin();
void finish();
uint16_t add();
uint8_t* get() ;
uint8_t getvalue(uint16_t dt);
uint8_t getvalue3();
void clear();
};
class History {
public:
History();
void *addSeries();
};
#endif

View File

@ -273,6 +273,8 @@ void registerAllPages(GwLog *logger, PageList &list){
list.add(&registerPageAnchor); list.add(&registerPageAnchor);
extern PageDescription registerPageAIS; extern PageDescription registerPageAIS;
list.add(&registerPageAIS); list.add(&registerPageAIS);
extern PageDescription registerPageBarograph;
list.add(&registerPageBarograph);
logger->logDebug(GwLog::LOG,"Memory after registering pages: stack=%d, heap=%d", uxTaskGetStackHighWaterMark(NULL), ESP.getFreeHeap()); logger->logDebug(GwLog::LOG,"Memory after registering pages: stack=%d, heap=%d", uxTaskGetStackHighWaterMark(NULL), ESP.getFreeHeap());
} }