First steps towards history plotting
This commit is contained in:
parent
8d5b1a425b
commit
1197e083f4
|
@ -42,8 +42,8 @@
|
||||||
//#define GxEPD2_DRIVER_CLASS GxEPD2_260_M01 // GDEW026M01 152x296, UC8151 (IL0373)
|
//#define GxEPD2_DRIVER_CLASS GxEPD2_260_M01 // GDEW026M01 152x296, UC8151 (IL0373)
|
||||||
//#define GxEPD2_DRIVER_CLASS GxEPD2_270 // GDEW027W3 176x264, EK79652 (IL91874)
|
//#define GxEPD2_DRIVER_CLASS GxEPD2_270 // GDEW027W3 176x264, EK79652 (IL91874)
|
||||||
//#define GxEPD2_DRIVER_CLASS GxEPD2_371 // GDEW0371W7 240x416, UC8171 (IL0324)
|
//#define GxEPD2_DRIVER_CLASS GxEPD2_371 // GDEW0371W7 240x416, UC8171 (IL0324)
|
||||||
#define GxEPD2_DRIVER_CLASS GxEPD2_420 // GDEW042T2 400x300, UC8176 (IL0398)
|
//#define GxEPD2_DRIVER_CLASS GxEPD2_420 // GDEW042T2 400x300, UC8176 (IL0398)
|
||||||
//#define GxEPD2_DRIVER_CLASS GxEPD2_420_M01 // GDEW042M01 400x300, UC8176 (IL0398)
|
#define GxEPD2_DRIVER_CLASS GxEPD2_420_M01 // GDEW042M01 400x300, UC8176 (IL0398)
|
||||||
//#define GxEPD2_DRIVER_CLASS GxEPD2_583 // GDEW0583T7 600x448, UC8179 (IL0371)
|
//#define GxEPD2_DRIVER_CLASS GxEPD2_583 // GDEW0583T7 600x448, UC8179 (IL0371)
|
||||||
//#define GxEPD2_DRIVER_CLASS GxEPD2_583_T8 // GDEW0583T8 648x480, GD7965
|
//#define GxEPD2_DRIVER_CLASS GxEPD2_583_T8 // GDEW0583T8 648x480, GD7965
|
||||||
//#define GxEPD2_DRIVER_CLASS GxEPD2_750 // GDEW075T8 640x384, UC8179 (IL0371)
|
//#define GxEPD2_DRIVER_CLASS GxEPD2_750 // GDEW075T8 640x384, UC8179 (IL0371)
|
||||||
|
@ -114,8 +114,10 @@
|
||||||
// adapt the constructor parameters to your wiring
|
// adapt the constructor parameters to your wiring
|
||||||
#if !IS_GxEPD2_1248(GxEPD2_DRIVER_CLASS)
|
#if !IS_GxEPD2_1248(GxEPD2_DRIVER_CLASS)
|
||||||
#if defined(ARDUINO_LOLIN_D32_PRO)
|
#if defined(ARDUINO_LOLIN_D32_PRO)
|
||||||
|
#error Wrong constructur
|
||||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 0, /*RST=*/ 2, /*BUSY=*/ 15));
|
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 0, /*RST=*/ 2, /*BUSY=*/ 15));
|
||||||
#elif defined(ARDUINO_ESP32_DEV) // e.g. TTGO T8 ESP32-WROVER
|
#elif defined(ARDUINO_ESP32_DEV) // e.g. TTGO T8 ESP32-WROVER
|
||||||
|
#error Wrong constructur
|
||||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 2, /*RST=*/ 0, /*BUSY=*/ 4));
|
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 2, /*RST=*/ 0, /*BUSY=*/ 4));
|
||||||
#else
|
#else
|
||||||
// moved to epaper.c because of multiple definitions when in header
|
// moved to epaper.c because of multiple definitions when in header
|
||||||
|
@ -124,6 +126,7 @@ GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> displ
|
||||||
#else // GxEPD2_1248
|
#else // GxEPD2_1248
|
||||||
// Waveshare 12.48 b/w SPI display board and frame or Good Display 12.48 b/w panel GDEW1248T3
|
// Waveshare 12.48 b/w SPI display board and frame or Good Display 12.48 b/w panel GDEW1248T3
|
||||||
// general constructor for use with all parameters, e.g. for Waveshare ESP32 driver board mounted on connection board
|
// general constructor for use with all parameters, e.g. for Waveshare ESP32 driver board mounted on connection board
|
||||||
|
#error Wrong constructur
|
||||||
GxEPD2_BW < GxEPD2_1248, GxEPD2_1248::HEIGHT / 4 > display(GxEPD2_1248(/*sck=*/ 13, /*miso=*/ 12, /*mosi=*/ 14,
|
GxEPD2_BW < GxEPD2_1248, GxEPD2_1248::HEIGHT / 4 > display(GxEPD2_1248(/*sck=*/ 13, /*miso=*/ 12, /*mosi=*/ 14,
|
||||||
/*cs_m1=*/ 23, /*cs_s1=*/ 22, /*cs_m2=*/ 16, /*cs_s2=*/ 19,
|
/*cs_m1=*/ 23, /*cs_s1=*/ 22, /*cs_m2=*/ 16, /*cs_s2=*/ 19,
|
||||||
/*dc1=*/ 25, /*dc2=*/ 17, /*rst1=*/ 33, /*rst2=*/ 5,
|
/*dc1=*/ 25, /*dc2=*/ 17, /*rst1=*/ 33, /*rst2=*/ 5,
|
||||||
|
@ -142,6 +145,7 @@ GxEPD2_BW < GxEPD2_1248, GxEPD2_1248::HEIGHT / 4 > display(GxEPD2_1248(/*sck=*/
|
||||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||||
#endif
|
#endif
|
||||||
// adapt the constructor parameters to your wiring
|
// adapt the constructor parameters to your wiring
|
||||||
|
#error Wrong constructur
|
||||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
|
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -159,6 +163,7 @@ GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> displ
|
||||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||||
#endif
|
#endif
|
||||||
// adapt the constructor parameters to your wiring
|
// adapt the constructor parameters to your wiring
|
||||||
|
#error Wrong constructur
|
||||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
|
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -172,6 +177,7 @@ GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> displ
|
||||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||||
#endif
|
#endif
|
||||||
// adapt the constructor parameters to your wiring
|
// adapt the constructor parameters to your wiring
|
||||||
|
#error Wrong constructur
|
||||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=77*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
|
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=77*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -185,6 +191,7 @@ GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> displ
|
||||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||||
#endif
|
#endif
|
||||||
// adapt the constructor parameters to your wiring
|
// adapt the constructor parameters to your wiring
|
||||||
|
#error Wrong constructur
|
||||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 4, /*DC=*/ 7, /*RST=*/ 6, /*BUSY=*/ 5));
|
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 4, /*DC=*/ 7, /*RST=*/ 6, /*BUSY=*/ 5));
|
||||||
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 4, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1)); // my Seed XIOA0
|
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 4, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1)); // my Seed XIOA0
|
||||||
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 3, /*DC=*/ 2, /*RST=*/ 1, /*BUSY=*/ 0)); // my other Seed XIOA0
|
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ 3, /*DC=*/ 2, /*RST=*/ 1, /*BUSY=*/ 0)); // my other Seed XIOA0
|
||||||
|
@ -200,6 +207,7 @@ GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> displ
|
||||||
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE) / (EPD::WIDTH / 2))
|
||||||
#endif
|
#endif
|
||||||
// adapt the constructor parameters to your wiring
|
// adapt the constructor parameters to your wiring
|
||||||
|
#error Wrong constructur
|
||||||
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
|
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=4*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef EPAPER_H
|
#ifndef EPAPER_H
|
||||||
#define EPAPER_H
|
#define EPAPER_H
|
||||||
|
|
||||||
|
#include "timeseries.h"
|
||||||
|
|
||||||
#include <GxEPD2_BW.h>
|
#include <GxEPD2_BW.h>
|
||||||
#include "GxEPD2_display_selection_new_style.h"
|
#include "GxEPD2_display_selection_new_style.h"
|
||||||
|
|
||||||
|
@ -9,8 +11,8 @@ typedef void (*epaper_callback)(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HE
|
||||||
void epaper_init(void);
|
void epaper_init(void);
|
||||||
void epaper_test(void);
|
void epaper_test(void);
|
||||||
|
|
||||||
//void epaper_plot(uint16_t x, uint16_t y, uint16_t w, uint16_t h, timeseries_t *timeseries, uint32_t time_tick_interval);
|
void epaper_plot(uint16_t x, uint16_t y, uint16_t w, uint16_t h, timeseries_t *timeseries, uint32_t time_tick_interval);
|
||||||
|
|
||||||
void epaper_draw_and_hibernate(epaper_callback cb);
|
void epaper_draw_and_hibernate(epaper_callback cb, bool full_refresh);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
16
include/timeseries.h
Normal file
16
include/timeseries.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t tstart, tend, interval;
|
||||||
|
std::vector<float> data;
|
||||||
|
const char *unit;
|
||||||
|
const char *format;
|
||||||
|
} timeseries_t;
|
||||||
|
|
||||||
|
void timeseries_init(timeseries_t *ts, uint64_t tstart, uint64_t interval, const char *unit, const char *format);
|
||||||
|
|
||||||
|
void timeseries_append(timeseries_t *ts, float value);
|
|
@ -8,11 +8,11 @@
|
||||||
#include "epaper.h"
|
#include "epaper.h"
|
||||||
|
|
||||||
static GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)>
|
static GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)>
|
||||||
display(GxEPD2_DRIVER_CLASS(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
|
display(GxEPD2_DRIVER_CLASS(/*CS=*/ 17, /*DC=*/ 18, /*RST=*/ 19, /*BUSY=*/ 22));
|
||||||
|
|
||||||
void epaper_init(void)
|
void epaper_init(void)
|
||||||
{
|
{
|
||||||
display.init();
|
display.init(0, true, 2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void epaper_test(void)
|
void epaper_test(void)
|
||||||
|
@ -33,17 +33,24 @@ void epaper_test(void)
|
||||||
display.setCursor(x, y);
|
display.setCursor(x, y);
|
||||||
display.print("Hello World!");
|
display.print("Hello World!");
|
||||||
for(int i = -1; i <= 1; i++) {
|
for(int i = -1; i <= 1; i++) {
|
||||||
display.drawCircle(display.width()/2, display.height()/2, tbw*15/10/2+i, GxEPD_RED);
|
display.drawCircle(display.width()/2, display.height()/2, tbw*15/10/2+i, GxEPD_BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
display.setCursor(10, FreeMonoBold9pt7b.yAdvance + 10);
|
||||||
|
for(uint16_t c = 0; c < 256; c++) {
|
||||||
|
display.print((char)c);
|
||||||
|
|
||||||
|
if((c % 32) == 31) {
|
||||||
|
display.println();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (display.nextPage());
|
while (display.nextPage());
|
||||||
|
|
||||||
Serial.println(F("Init done. Display going to powersave…"));
|
Serial.println(F("Init done. Display going to powersave…"));
|
||||||
|
|
||||||
display.hibernate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void epaper_draw_and_hibernate(epaper_callback cb)
|
void epaper_draw_and_hibernate(epaper_callback cb, bool full_refresh)
|
||||||
{
|
{
|
||||||
display.setRotation(0);
|
display.setRotation(0);
|
||||||
display.setFullWindow();
|
display.setFullWindow();
|
||||||
|
@ -56,9 +63,8 @@ void epaper_draw_and_hibernate(epaper_callback cb)
|
||||||
display.hibernate();
|
display.hibernate();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
// find minimum and maximum values in a vector. NaN is ignored.
|
// find minimum and maximum values in a vector. NaN is ignored.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void minmax(const std::vector<T> &vec, T *min, T *max)
|
void minmax(const std::vector<T> &vec, T *min, T *max)
|
||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
@ -133,9 +139,9 @@ void epaper_plot(uint16_t x, uint16_t y, uint16_t w, uint16_t h, timeseries_t *t
|
||||||
uint16_t label_height = tbh;
|
uint16_t label_height = tbh;
|
||||||
|
|
||||||
// actual graph area
|
// actual graph area
|
||||||
uint16_t plot_x = x + 150;
|
uint16_t plot_x = x + tbw;
|
||||||
uint16_t plot_y = y;
|
uint16_t plot_y = y;
|
||||||
uint16_t plot_w = w - 150;
|
uint16_t plot_w = w - tbw;
|
||||||
uint16_t plot_h = h - label_height - 5;
|
uint16_t plot_h = h - label_height - 5;
|
||||||
|
|
||||||
// determine vertical tick interval
|
// determine vertical tick interval
|
||||||
|
@ -242,10 +248,10 @@ void epaper_plot(uint16_t x, uint16_t y, uint16_t w, uint16_t h, timeseries_t *t
|
||||||
Serial.print(py);
|
Serial.print(py);
|
||||||
Serial.println(F(")."));*/
|
Serial.println(F(")."));*/
|
||||||
|
|
||||||
display.drawLine(last_x, last_y, px, py, GxEPD_RED);
|
display.drawLine(last_x, last_y, px, py, GxEPD_BLACK);
|
||||||
|
|
||||||
// draw it again, 1px below, to give it more weight
|
// draw it again, 1px below, to give it more weight
|
||||||
display.drawLine(last_x, last_y+1, px, py+1, GxEPD_RED);
|
display.drawLine(last_x, last_y+1, px, py+1, GxEPD_BLACK);
|
||||||
} else {
|
} else {
|
||||||
//Serial.println(F("First point, not drawn."));
|
//Serial.println(F("First point, not drawn."));
|
||||||
firstpoint = false;
|
firstpoint = false;
|
||||||
|
@ -254,40 +260,4 @@ void epaper_plot(uint16_t x, uint16_t y, uint16_t w, uint16_t h, timeseries_t *t
|
||||||
last_x = px;
|
last_x = px;
|
||||||
last_y = py;
|
last_y = py;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draw large current value + unit */
|
|
||||||
|
|
||||||
size_t cur_val_idx = timeseries->data.size() - 1;
|
|
||||||
while((cur_val_idx > 0) && isnan(timeseries->data[cur_val_idx])) {
|
|
||||||
cur_val_idx--;
|
|
||||||
}
|
|
||||||
|
|
||||||
float current_value = timeseries->data[cur_val_idx];
|
|
||||||
|
|
||||||
display.setFont(&din1451e24pt7b);
|
|
||||||
display.setTextColor(GxEPD_RED);
|
|
||||||
|
|
||||||
char fmt[16];
|
|
||||||
|
|
||||||
snprintf(fmt, sizeof(fmt), "%s %%s", timeseries->format);
|
|
||||||
snprintf(s, sizeof(s), fmt, current_value, timeseries->unit);
|
|
||||||
|
|
||||||
display.getTextBounds(s, 0, 0, &tbx, &tby, &tbw, &tbh);
|
|
||||||
|
|
||||||
display.setCursor(plot_x - tbw - 35, plot_y + plot_h/2 - tby - tbh/2 - 5);
|
|
||||||
display.print(s);
|
|
||||||
|
|
||||||
uint16_t large_text_height = tbh;
|
|
||||||
|
|
||||||
time_t t = timeseries->tstart + trange * cur_val_idx / timeseries->data.size();
|
|
||||||
tm *gmt = gmtime(&t);
|
|
||||||
strftime(s, sizeof(s), "%Y-%m-%d %H:%M", gmt);
|
|
||||||
|
|
||||||
display.setFont(&Org_01);
|
|
||||||
|
|
||||||
display.getTextBounds(s, 0, 0, &tbx, &tby, &tbw, &tbh);
|
|
||||||
|
|
||||||
display.setCursor(plot_x - tbw - 35, plot_y + plot_h/2 - tby + large_text_height/2 + 5);
|
|
||||||
display.print(s);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
154
src/main.cpp
154
src/main.cpp
|
@ -1,12 +1,26 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Adafruit_SCD30.h>
|
#include <Adafruit_SCD30.h>
|
||||||
|
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Fonts/FreeSans9pt7b.h>
|
||||||
|
#include <Fonts/FreeSans12pt7b.h>
|
||||||
|
#include <Fonts/FreeSansBold12pt7b.h>
|
||||||
|
|
||||||
#include <epaper.h>
|
#include <epaper.h>
|
||||||
|
|
||||||
Adafruit_SCD30 scd30;
|
Adafruit_SCD30 scd30;
|
||||||
|
|
||||||
|
static timeseries_t ts_scd30_co2, ts_scd30_temperature, ts_scd30_humidity;
|
||||||
|
|
||||||
|
#define REF_ALTITUDE 320 // meters
|
||||||
|
#define REF_CO2_PPM 425 // clean air according to DWD at 2024-06-17
|
||||||
|
|
||||||
void initSCD30(void)
|
void initSCD30(void)
|
||||||
{
|
{
|
||||||
|
timeseries_init(&ts_scd30_co2, 0, 15, "ppm", "%.1f");
|
||||||
|
timeseries_init(&ts_scd30_temperature, 0, 15, "°C", "%.1f");
|
||||||
|
timeseries_init(&ts_scd30_humidity, 0, 15, "%", "%.1f");
|
||||||
|
|
||||||
// Try to initialize!
|
// Try to initialize!
|
||||||
if (!scd30.begin()) {
|
if (!scd30.begin()) {
|
||||||
Serial.println("scd30: Failed to find SCD30 chip");
|
Serial.println("scd30: Failed to find SCD30 chip");
|
||||||
|
@ -14,6 +28,7 @@ void initSCD30(void)
|
||||||
}
|
}
|
||||||
Serial.println("scd30: SCD30 Found!");
|
Serial.println("scd30: SCD30 Found!");
|
||||||
|
|
||||||
|
// TODO: check what this actually resets. Maybe also the calibration?
|
||||||
scd30.reset();
|
scd30.reset();
|
||||||
|
|
||||||
if (!scd30.setMeasurementInterval(15)){ // seconds
|
if (!scd30.setMeasurementInterval(15)){ // seconds
|
||||||
|
@ -24,6 +39,32 @@ void initSCD30(void)
|
||||||
Serial.print(scd30.getMeasurementInterval());
|
Serial.print(scd30.getMeasurementInterval());
|
||||||
Serial.println(" seconds");
|
Serial.println(" seconds");
|
||||||
|
|
||||||
|
uint16_t pressureOffset = scd30.getAmbientPressureOffset();
|
||||||
|
uint16_t altitudeOffset = scd30.getAltitudeOffset();
|
||||||
|
bool selfCalEnabled = scd30.selfCalibrationEnabled();
|
||||||
|
|
||||||
|
if(altitudeOffset != REF_ALTITUDE) {
|
||||||
|
Serial.println("scd30: updating altitude offset.");
|
||||||
|
if(!scd30.setAltitudeOffset(REF_ALTITUDE)) {
|
||||||
|
Serial.println("scd30: failed to set altitude offset!");
|
||||||
|
}
|
||||||
|
altitudeOffset = REF_ALTITUDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selfCalEnabled) {
|
||||||
|
Serial.println("scd30: self calibration was enabled -> disabling.");
|
||||||
|
scd30.selfCalibrationEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("scd30: offset readout:");
|
||||||
|
Serial.print(" Pressure: ");
|
||||||
|
Serial.print(pressureOffset);
|
||||||
|
Serial.println(" mbar");
|
||||||
|
Serial.print(" Altitude: ");
|
||||||
|
Serial.print(altitudeOffset);
|
||||||
|
Serial.println(" m");
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
if(!scd30.startContinuousMeasurement()) {
|
if(!scd30.startContinuousMeasurement()) {
|
||||||
Serial.println("scd30: Could not start continuous measurement");
|
Serial.println("scd30: Could not start continuous measurement");
|
||||||
}
|
}
|
||||||
|
@ -32,20 +73,113 @@ void initSCD30(void)
|
||||||
|
|
||||||
void setup(void)
|
void setup(void)
|
||||||
{
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("Hello World!");
|
||||||
|
|
||||||
|
delay(3000);
|
||||||
|
|
||||||
initSCD30();
|
initSCD30();
|
||||||
epaper_init();
|
epaper_init();
|
||||||
epaper_test();
|
epaper_test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> *display)
|
||||||
|
{
|
||||||
|
display->setRotation(1);
|
||||||
|
display->setTextColor(GxEPD_BLACK);
|
||||||
|
|
||||||
|
int16_t y = FreeSans12pt7b.yAdvance;
|
||||||
|
|
||||||
|
// first line: CO₂
|
||||||
|
display->setCursor(0, y);
|
||||||
|
|
||||||
|
display->setFont(&FreeSans12pt7b);
|
||||||
|
display->print("CO");
|
||||||
|
|
||||||
|
display->setFont(&FreeSans9pt7b);
|
||||||
|
display->print("2");
|
||||||
|
|
||||||
|
display->setFont(&FreeSans12pt7b);
|
||||||
|
display->print(": ");
|
||||||
|
|
||||||
|
y += FreeSans12pt7b.yAdvance;
|
||||||
|
display->setCursor(0, y);
|
||||||
|
|
||||||
|
display->setFont(&FreeSansBold12pt7b);
|
||||||
|
display->print(scd30.CO2, 2);
|
||||||
|
|
||||||
|
display->setFont(&FreeSans12pt7b);
|
||||||
|
display->print(" ppm");
|
||||||
|
|
||||||
|
epaper_plot(150, 5, 150, 65, &ts_scd30_co2, 900);
|
||||||
|
|
||||||
|
// second line: temperature
|
||||||
|
y = 75 + FreeSans12pt7b.yAdvance;
|
||||||
|
display->setCursor(0, y);
|
||||||
|
|
||||||
|
display->setFont(&FreeSans12pt7b);
|
||||||
|
display->print("Temp.: ");
|
||||||
|
|
||||||
|
y += FreeSans12pt7b.yAdvance;
|
||||||
|
display->setCursor(0, y);
|
||||||
|
|
||||||
|
display->setFont(&FreeSansBold12pt7b);
|
||||||
|
display->print(scd30.temperature, 2);
|
||||||
|
|
||||||
|
display->setFont(&FreeSans12pt7b);
|
||||||
|
display->print(" ");
|
||||||
|
|
||||||
|
// cheat a bit for the ° character
|
||||||
|
uint16_t stored_baseline_y = display->getCursorY();
|
||||||
|
uint16_t cursor_x = display->getCursorX();
|
||||||
|
|
||||||
|
display->setCursor(cursor_x, stored_baseline_y - FreeSans12pt7b.yAdvance/4);
|
||||||
|
display->print("o");
|
||||||
|
|
||||||
|
cursor_x = display->getCursorX();
|
||||||
|
display->setCursor(cursor_x, stored_baseline_y);
|
||||||
|
display->print("C");
|
||||||
|
|
||||||
|
epaper_plot(150, 80, 150, 65, &ts_scd30_temperature, 900);
|
||||||
|
|
||||||
|
// second line: Humidity
|
||||||
|
y = 150 + FreeSans12pt7b.yAdvance;
|
||||||
|
display->setCursor(0, y);
|
||||||
|
|
||||||
|
display->setCursor(0, y);
|
||||||
|
|
||||||
|
display->setFont(&FreeSans12pt7b);
|
||||||
|
display->print("Feuchte: ");
|
||||||
|
|
||||||
|
y += FreeSans12pt7b.yAdvance;
|
||||||
|
display->setCursor(0, y);
|
||||||
|
|
||||||
|
display->setFont(&FreeSansBold12pt7b);
|
||||||
|
display->print(scd30.relative_humidity, 1);
|
||||||
|
|
||||||
|
display->setFont(&FreeSans12pt7b);
|
||||||
|
display->print(" %rH");
|
||||||
|
|
||||||
|
epaper_plot(150, 155, 150, 65, &ts_scd30_humidity, 900);
|
||||||
|
}
|
||||||
|
|
||||||
void loop(void)
|
void loop(void)
|
||||||
{
|
{
|
||||||
|
static bool calibrationDone = true; // change to false to recalibrate at REF_CO2_PPM
|
||||||
|
static uint32_t lastEPaperRefresh = 0;
|
||||||
|
uint32_t now = millis();
|
||||||
|
|
||||||
if(scd30.dataReady()) {
|
if(scd30.dataReady()) {
|
||||||
if (!scd30.read()) {
|
if (!scd30.read()) {
|
||||||
Serial.println("Error reading sensor data");
|
Serial.println("scd30: Error reading sensor data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timeseries_append(&ts_scd30_co2, scd30.CO2);
|
||||||
|
timeseries_append(&ts_scd30_temperature, scd30.temperature);
|
||||||
|
timeseries_append(&ts_scd30_humidity, scd30.relative_humidity);
|
||||||
|
|
||||||
Serial.print("CO2: ");
|
Serial.print("CO2: ");
|
||||||
Serial.print(scd30.CO2, 3);
|
Serial.print(scd30.CO2, 3);
|
||||||
Serial.println(" ppm");
|
Serial.println(" ppm");
|
||||||
|
@ -56,6 +190,24 @@ void loop(void)
|
||||||
Serial.print(scd30.relative_humidity, 2);
|
Serial.print(scd30.relative_humidity, 2);
|
||||||
Serial.println(" %");
|
Serial.println(" %");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
|
|
||||||
|
if((now - lastEPaperRefresh) >= 60000) {
|
||||||
|
lastEPaperRefresh = now;
|
||||||
|
epaper_draw_and_hibernate(draw_epaper_callback, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!calibrationDone && now >= 300000) {
|
||||||
|
uint16_t calValue = scd30.getForcedCalibrationReference();
|
||||||
|
|
||||||
|
if(calValue != REF_CO2_PPM) {
|
||||||
|
Serial.println("scd30: Setting calibration reference value.");
|
||||||
|
if(!scd30.forceRecalibrationWithReference(REF_CO2_PPM)) {
|
||||||
|
Serial.println("scd30: Recalibration failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calibrationDone = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(1000);
|
delay(1000);
|
||||||
|
|
18
src/timeseries.cpp
Normal file
18
src/timeseries.cpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include "timeseries.h"
|
||||||
|
|
||||||
|
|
||||||
|
void timeseries_init(timeseries_t *ts, uint64_t tstart, uint64_t interval, const char *unit, const char *format)
|
||||||
|
{
|
||||||
|
ts->tstart = tstart;
|
||||||
|
ts->tend = tstart;
|
||||||
|
ts->interval = interval;
|
||||||
|
ts->unit = unit;
|
||||||
|
ts->format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void timeseries_append(timeseries_t *ts, float value)
|
||||||
|
{
|
||||||
|
ts->data.push_back(value);
|
||||||
|
ts->tend += ts->interval;
|
||||||
|
}
|
Loading…
Reference in a new issue