SensorCube-Firmware/src/epaper.cpp

294 lines
7.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <algorithm>
#include <Adafruit_GFX.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Font_din1451e.h>
#include <Fonts/Org_01.h>
#include "epaper.h"
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));
void epaper_init(void)
{
display.init();
}
void epaper_test(void)
{
display.setRotation(0);
display.setFont(&FreeMonoBold9pt7b);
display.setTextColor(GxEPD_BLACK);
int16_t tbx, tby; uint16_t tbw, tbh;
display.getTextBounds("Hello World!", 0, 0, &tbx, &tby, &tbw, &tbh);
// center the bounding box by transposition of the origin:
uint16_t x = ((display.width() - tbw) / 2) - tbx;
uint16_t y = ((display.height() - tbh) / 2) - tby;
display.setFullWindow();
display.firstPage();
do
{
display.fillScreen(GxEPD_WHITE);
display.setCursor(x, y);
display.print("Hello World!");
for(int i = -1; i <= 1; i++) {
display.drawCircle(display.width()/2, display.height()/2, tbw*15/10/2+i, GxEPD_RED);
}
}
while (display.nextPage());
Serial.println(F("Init done. Display going to powersave…"));
display.hibernate();
}
void epaper_draw_and_hibernate(epaper_callback cb)
{
display.setRotation(0);
display.setFullWindow();
display.firstPage();
do {
cb(&display);
} while(display.nextPage());
display.hibernate();
}
#if 0
// find minimum and maximum values in a vector. NaN is ignored.
template<typename T>
void minmax(const std::vector<T> &vec, T *min, T *max)
{
bool first = true;
// fallback values: will be used if the vector is empty or only consists of NaNs
*min = 0;
*max = 1;
for(auto &v: vec) {
if(isnan(v)) {
continue;
}
if(first) {
*min = *max = v;
first = false;
continue;
}
if(v < *min) {
*min = v;
}
if(v > *max) {
*max = v;
}
}
}
void epaper_plot(uint16_t x, uint16_t y, uint16_t w, uint16_t h, timeseries_t *timeseries, uint32_t time_tick_interval)
{
char s[32];
uint32_t trange = timeseries->tend - timeseries->tstart;
uint32_t tstep = timeseries->interval;
if(trange == 0) {
trange = 3600;
}
float vmin, vmax;
minmax(timeseries->data, &vmin, &vmax);
float vrange = vmax - vmin;
Serial.print(F("Value range: "));
Serial.print(vrange);
Serial.print(F(" Min: "));
Serial.print(vmin);
Serial.print(F(" Max: "));
Serial.println(vmax);
if(vrange == 0.0f) {
vrange = 1.0f;
}
// add some margin
vmax += vrange * 0.05f;
vmin -= vrange * 0.05f;
vrange = vmax - vmin;
bool firstpoint = true;
uint16_t last_x;
uint16_t last_y;
// set font for axis labels
display.setFont(&Org_01);
display.setTextColor(GxEPD_BLACK);
int16_t tbx, tby; uint16_t tbw, tbh;
display.getTextBounds("20:48", 0, 0, &tbx, &tby, &tbw, &tbh);
uint16_t label_height = tbh;
// actual graph area
uint16_t plot_x = x + 150;
uint16_t plot_y = y;
uint16_t plot_w = w - 150;
uint16_t plot_h = h - label_height - 5;
// determine vertical tick interval
uint16_t max_vert_tick_count = plot_h / (label_height + 5);
float min_vert_tick_step = vrange / (float)max_vert_tick_count;
static const PROGMEM float TEST_FACTORS[] = {1.0f, 0.5f, 0.25f, 0.1f};
float scale = 1e3f;
float accepted_scale = scale;
bool scale_found = false;
while((scale > 1e-3f) && !scale_found) {
float tmp_scale;
for(auto factor: TEST_FACTORS) {
tmp_scale = scale * factor;
if(tmp_scale < min_vert_tick_step) {
scale_found = true;
break;
}
accepted_scale = tmp_scale;
}
scale = tmp_scale;
}
/* Start drawing */
display.drawRect(plot_x, plot_y, plot_w, plot_h, GxEPD_BLACK);
// draw vertical grid lines
for(uint64_t reltime = ((timeseries->tstart / time_tick_interval) + 1) * time_tick_interval - timeseries->tstart;
reltime < trange;
reltime += time_tick_interval) {
uint16_t px = plot_x + plot_w * reltime / trange;
uint16_t py = plot_y;
uint16_t ph = plot_h + 2;
display.drawFastVLine(px, py, ph, GxEPD_BLACK);
time_t t = reltime + timeseries->tstart;
tm *gmt = gmtime(&t);
if(t % 86400 == 0) {
strftime(s, sizeof(s), "%d.%m.", gmt);
} else {
strftime(s, sizeof(s), "%H:%M", gmt);
}
display.getTextBounds(s, 0, 0, &tbx, &tby, &tbw, &tbh);
display.setCursor(px - tbw/2, y+h + tby);
display.print(s);
}
// draw horizontal grid lines
for(float tick_val = (float)((int)(vmin / accepted_scale) + 1) * accepted_scale;
tick_val < vmax;
tick_val += accepted_scale) {
uint16_t py = plot_y - (float)plot_h * (tick_val - vmax) / vrange;
uint16_t px = plot_x - 3;
uint16_t pw = plot_w + 3;
display.drawFastHLine(px, py, pw, GxEPD_BLACK);
snprintf(s, sizeof(s), timeseries->format, tick_val);
display.getTextBounds(s, 0, 0, &tbx, &tby, &tbw, &tbh);
display.setCursor(px - tbw - 2, py + tby + tbh);
display.print(s);
}
size_t i = 0;
for(uint32_t reltime = 0; reltime < trange; reltime += tstep) {
if(i >= timeseries->data.size()) {
Serial.println(F("Index out of range! Time range does not match data points."));
break;
}
/*Serial.print(F("Processing data point at dt="));
Serial.print(reltime);
Serial.print(F("… "));*/
float val = timeseries->data[i++];
if(isnan(val)) {
//Serial.println(F("Skipping NaN!"));
firstpoint = true;
continue;
}
uint16_t px = plot_x + plot_w * reltime / trange;
uint16_t py = plot_y - (float)plot_h * (val - vmax) / vrange;
if(!firstpoint) {
/*Serial.print(F("Line from ("));
Serial.print(last_x);
Serial.print(F(","));
Serial.print(last_y);
Serial.print(F(") to ("));
Serial.print(px);
Serial.print(F(","));
Serial.print(py);
Serial.println(F(")."));*/
display.drawLine(last_x, last_y, px, py, GxEPD_RED);
// draw it again, 1px below, to give it more weight
display.drawLine(last_x, last_y+1, px, py+1, GxEPD_RED);
} else {
//Serial.println(F("First point, not drawn."));
firstpoint = false;
}
last_x = px;
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