Настройки собраны в config.h: пины, ориентация, пороги тревоги, шкала графика, тайминги и скорость Serial. Раньше они были раскиданы по модулям, и подстройка требовала лазить по коду. Тревога включается на ALARM_ON_C и отпускает только на ALARM_OFF_C (85 и 82 градуса). Один порог заставлял бы её дёргаться каждую секунду, пока температура топчется на границе. В тревоге экран мигает негативом (инверсия буфера, а не команда контроллера - не зависит от микросхемы дисплея), светодиод GPIO8 мигает с ним в общей фазе. Без данных светодиод горит ровно, в норме погашен. Зажигается низким уровнем: проверено опытом, заодно в простое пин стоит высоким, что безопасно для GPIO8 как пина начальной загрузки. Переменная alarm переименована в alarmActive: имя alarm занято функцией из unistd.h, которая попадает в сборку ESP32. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
88 lines
2.7 KiB
C++
88 lines
2.7 KiB
C++
#include "ui.h"
|
|
|
|
#include <U8g2lib.h>
|
|
#include <string.h>
|
|
|
|
#include "config.h"
|
|
|
|
// Порядок параметров: (rotation, reset, clock, data) — SCL идёт раньше SDA.
|
|
static U8G2_SSD1306_72X40_ER_F_HW_I2C display(DISPLAY_ROTATION, U8X8_PIN_NONE, PIN_SCL, PIN_SDA);
|
|
|
|
static const uint8_t GRAPH_W = 72; // по пикселю на отсчёт: 72 точки = 72 секунды
|
|
static const uint8_t GRAPH_TOP = 28;
|
|
static const uint8_t GRAPH_H = 12;
|
|
|
|
static uint8_t history[GRAPH_W]; // 0 = нет данных, иначе высота столбика 1..GRAPH_H
|
|
|
|
void uiBegin() {
|
|
display.begin();
|
|
display.clearBuffer();
|
|
display.setFont(u8g2_font_6x10_tf);
|
|
display.drawStr(0, 24, "WAIT");
|
|
display.sendBuffer();
|
|
}
|
|
|
|
void uiPushSample(float tempC) {
|
|
if (tempC < SCALE_MIN) tempC = SCALE_MIN;
|
|
if (tempC > SCALE_MAX) tempC = SCALE_MAX;
|
|
|
|
const float norm = (tempC - SCALE_MIN) / (SCALE_MAX - SCALE_MIN);
|
|
uint8_t h = (uint8_t)(norm * GRAPH_H + 0.5f);
|
|
if (h < 1) h = 1;
|
|
|
|
memmove(history, history + 1, GRAPH_W - 1);
|
|
history[GRAPH_W - 1] = h;
|
|
}
|
|
|
|
static void drawGraph() {
|
|
for (uint8_t x = 0; x < GRAPH_W; x++) {
|
|
const uint8_t h = history[x];
|
|
if (h == 0) continue;
|
|
display.drawVLine(x, GRAPH_TOP + GRAPH_H - h, h);
|
|
}
|
|
}
|
|
|
|
// Негатив всего кадра. Через буфер, а не командой контроллера: так поведение
|
|
// не зависит от конкретной микросхемы дисплея.
|
|
static void invertBuffer() {
|
|
uint8_t *b = display.getBufferPtr();
|
|
const size_t n = (size_t)display.getBufferTileWidth() * display.getBufferTileHeight() * 8;
|
|
for (size_t i = 0; i < n; i++) b[i] = (uint8_t)~b[i];
|
|
}
|
|
|
|
void uiRender(bool fresh, float tempC, int loadPct, bool invert) {
|
|
display.clearBuffer();
|
|
|
|
if (!fresh) {
|
|
display.setFont(u8g2_font_6x10_tf);
|
|
display.drawStr(0, 14, "NO");
|
|
display.drawStr(0, 26, "DATA");
|
|
if (invert) invertBuffer();
|
|
display.sendBuffer();
|
|
return;
|
|
}
|
|
|
|
char buf[8];
|
|
|
|
// Крупные цифры температуры.
|
|
snprintf(buf, sizeof(buf), "%d", (int)(tempC + 0.5f));
|
|
display.setFont(u8g2_font_logisoso24_tn);
|
|
display.drawStr(0, 24, buf);
|
|
const int numWidth = display.getStrWidth(buf);
|
|
|
|
// Градус — надстрочным знаком, вровень с верхом цифр.
|
|
display.setFont(u8g2_font_5x7_tf);
|
|
display.drawStr(numWidth + 2, 8, "\xb0");
|
|
|
|
// Загрузка — шрифтом вдвое выше прежнего.
|
|
display.setFont(u8g2_font_7x14_tf);
|
|
snprintf(buf, sizeof(buf), "%d%%", loadPct);
|
|
display.drawStr(numWidth + 2, 24, buf);
|
|
|
|
drawGraph();
|
|
|
|
if (invert) invertBuffer();
|
|
|
|
display.sendBuffer();
|
|
}
|