diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp index 282a1e2..b78cfa3 100644 --- a/lib/obp60task/OBP60Extensions.cpp +++ b/lib/obp60task/OBP60Extensions.cpp @@ -179,6 +179,24 @@ String xdrDelete(String input){ return input; } +// Draw centered text +void drawTextCenter(int16_t cx, int16_t cy, String text) { + int16_t x1, y1; + uint16_t w, h; + getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h); + getdisplay().setCursor(cx - w / 2, cy + h / 2); + getdisplay().print(text); +} + +// Draw right aligned text +void drawTextRalign(int16_t x, int16_t y, String text) { + int16_t x1, y1; + uint16_t w, h; + getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h); + getdisplay().setCursor(x - w, y); + getdisplay().print(text); +} + // Show a triangle for trend direction high (x, y is the left edge) void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){ getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color); @@ -404,4 +422,4 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){ getdisplay().print("G"); } -#endif \ No newline at end of file +#endif diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h index e02cd8b..c8cef01 100644 --- a/lib/obp60task/OBP60Extensions.h +++ b/lib/obp60task/OBP60Extensions.h @@ -58,6 +58,9 @@ void setBuzzerPower(uint power); // Set buzzer power String xdrDelete(String input); // Delete xdr prefix from string +void drawTextCenter(int16_t cx, int16_t cy, String text); +void drawTextRalign(int16_t x, int16_t y, String text); + void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color); @@ -70,4 +73,4 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor); // S void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic with fill level void startLedTask(GwApi *api); -#endif \ No newline at end of file +#endif diff --git a/lib/obp60task/PageBarograph.cpp b/lib/obp60task/PageBarograph.cpp new file mode 100644 index 0000000..bd63566 --- /dev/null +++ b/lib/obp60task/PageBarograph.cpp @@ -0,0 +1,228 @@ +#ifdef BOARD_OBP60S3 + +#include "Pagedata.h" +#include "OBP60Extensions.h" + +class PageBarograph : public Page{ + + 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){ + common.logger->logDebug(GwLog::LOG,"Instantiate PageBarograph"); + // Get config data + flashLED = common.config->getString(common.config->flashLED); + useenvsensor = common.config->getString(common.config->useEnvSensor); + // possible values for internal sensor + static std::vector 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 + } + + virtual 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; + } + + virtual void displayPage(CommonData &commonData, PageData &pageData){ + GwLog *logger=commonData.logger; + + // Optical warning by limit violation (unused) + if (String(flashLED) == "Limit Violation") { + setBlinkingLED(false); + setFlashLED(false); + } + + // Logging boat values + LOG_DEBUG(GwLog::LOG,"Drawing at PageBarograph"); + + // Draw page + //*********************************************************** + + // Set display in partial refresh mode + getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update + + // Frames + getdisplay().fillRect(0, 75, 400, 2, commonData.fgcolor); // fillRect: x, y, w, h + getdisplay().fillRect(130, 20, 2, 55, commonData.fgcolor); + getdisplay().fillRect(270, 20, 2, 55, commonData.fgcolor); + getdisplay().fillRect(325, 20, 2, 55, commonData.fgcolor); + + getdisplay().setTextColor(commonData.fgcolor); + + getdisplay().setFont(&Ubuntu_Bold8pt7b); + 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 + getdisplay().setFont(&Ubuntu_Bold16pt7b); + drawTextCenter(200, 40, String(commonData.data.airPressure / 100, 1)); + getdisplay().setFont(&Ubuntu_Bold8pt7b); + 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; + + // getdisplay().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); + getdisplay().drawLine(x0 - i * xstep, y0, x0 - i * xstep, y0 - h, commonData.fgcolor); + drawTextCenter(x0 - i * xstep, y0 - 10, label); + } + + // y-axis + getdisplay().drawLine(x0 + 5, y0, x0 + 5, y0 - h, commonData.fgcolor); // drawLine: x1, y1, x2, y2 + getdisplay().drawLine(x0 - w, y0, x0 - w, y0 - h, commonData.fgcolor); + getdisplay().drawLine(x0 - w - 5, y0, x0 - w - 5, y0 - h, commonData.fgcolor); + getdisplay().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 + getdisplay().setCursor(x0 + 10, y + 5); + getdisplay().print(String(ys)); + getdisplay().drawLine(x0 + 5, y, x0 - w - 5, y, commonData.fgcolor); + } else { + // small step, only short lines left and right + getdisplay().drawLine(x0 + 5, y, x0, y, commonData.fgcolor); + getdisplay().drawLine(x0 - w - 5, y, x0 - w, y, commonData.fgcolor); + } + y -= dy; + ys += 1; + } + + // Update display + getdisplay().nextPage(); // Partial update (fast) + + }; +}; + +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 diff --git a/lib/obp60task/config.json b/lib/obp60task/config.json index ca57f33..73cc899 100644 --- a/lib/obp60task/config.json +++ b/lib/obp60task/config.json @@ -904,7 +904,7 @@ "type": "list", "default": "Voltage", "description": "Type of page for page 1", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 1", "capabilities": { "obp60":"true" @@ -1009,7 +1009,7 @@ "type": "list", "default": "WindRose", "description": "Type of page for page 2", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 2", "capabilities": { "obp60":"true" @@ -1115,7 +1115,7 @@ "type": "list", "default": "OneValue", "description": "Type of page for page 3", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 3", "capabilities": { "obp60":"true" @@ -1221,7 +1221,7 @@ "type": "list", "default": "TwoValues", "description": "Type of page for page 4", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 4", "capabilities": { "obp60":"true" @@ -1327,7 +1327,7 @@ "type": "list", "default": "ThreeValues", "description": "Type of page for page 5", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 5", "capabilities": { "obp60":"true" @@ -1433,7 +1433,7 @@ "type": "list", "default": "FourValues", "description": "Type of page for page 6", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 6", "capabilities": { "obp60":"true" @@ -1539,7 +1539,7 @@ "type": "list", "default": "FourValues2", "description": "Type of page for page 7", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 7", "capabilities": { "obp60":"true" @@ -1645,7 +1645,7 @@ "type": "list", "default": "Clock", "description": "Type of page for page 8", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 8", "capabilities": { "obp60":"true" @@ -1751,7 +1751,7 @@ "type": "list", "default": "RollPitch", "description": "Type of page for page 9", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 9", "capabilities": { "obp60":"true" @@ -1857,7 +1857,7 @@ "type": "list", "default": "Battery2", "description": "Type of page for page 10", - "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid"], + "list":["OneValue","TwoValues","ThreeValues","FourValues","FourValues2","ApparentWind","WindRose","WindRoseFlex","Voltage","DST810","Clock","WhitePage","BME280","RudderPosition","KeelPosition","Battery","Battery2","RollPitch","Solar","Generator","XTETrack","Fluid","Barograph"], "category": "OBP60 Page 10", "capabilities": { "obp60":"true" diff --git a/lib/obp60task/hbuffer.cpp b/lib/obp60task/hbuffer.cpp new file mode 100644 index 0000000..659380b --- /dev/null +++ b/lib/obp60task/hbuffer.cpp @@ -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 + #include + + 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() { + } +}; diff --git a/lib/obp60task/hbuffer.h b/lib/obp60task/hbuffer.h new file mode 100644 index 0000000..1ff8b96 --- /dev/null +++ b/lib/obp60task/hbuffer.h @@ -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 diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index 3b4c52d..f4eb481 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -246,6 +246,8 @@ void registerAllPages(PageList &list){ list.add(®isterPageXTETrack); extern PageDescription registerPageFluid; list.add(®isterPageFluid); + extern PageDescription registerPageBarograph; + list.add(®isterPageBarograph); } // Undervoltage detection for shutdown display