Добавляем модуль U8g2lib для работы с дисплеями

This commit is contained in:
2025-05-29 18:53:55 +03:00
parent 33d449d892
commit 4b98abfa53
4 changed files with 1267 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,631 @@
#pragma once
#include "Global.h"
#include <U8g2lib.h>
#include <Print.h>
#include <stdint.h>
// #define DEBUG_DISPLAY
#define DEFAULT_PAGE_UPDATE_ms 500
// #define DEFAULT_PAGE_TIME_ms 5000
// #define DEFAULT_ROTATION 0
// #define DEFAULT_CONTRAST 10
#define MIN_CONTRAST 10
#define MAX_CONTRAST 150
#ifndef DEBUG_DISPLAY
#define D_LOG(fmt, ...) \
do { \
(void)0; \
} while (0)
#else
#define D_LOG(fmt, ...) Serial.printf((PGM_P)PSTR(fmt), ##__VA_ARGS__)
#endif
enum rotation_t : uint8_t {
ROTATION_NONE,
ROTATION_90,
ROTATION_180,
ROTATION_270
};
uint8_t parse_contrast(int val) {
if (val < MIN_CONTRAST) val = MIN_CONTRAST;
if (val > MAX_CONTRAST) val = MAX_CONTRAST;
return val;
};
rotation_t parse_rotation(int val) {
if ((val > 0) && (val <= 90)) return ROTATION_90;
if ((val > 90) && (val <= 180)) return ROTATION_180;
if ((val > 180) && (val <= 270)) return ROTATION_270;
return ROTATION_NONE;
};
struct DisplayPage {
String key;
uint16_t time;
rotation_t rotate;
String font;
String format;
String valign;
DisplayPage(
const String& key,
uint16_t time,
rotation_t rotate,
const String& font,
const String& format,
const String& valign) : key{key}, time{time}, rotate{rotate}, font{font}, format{format}, valign{valign} {}
// void load(const JsonObject& obj) {
// // time = obj["time"].as<uint16_t>();
// // rotate = parse_rotation(obj["rotate"].as<int>());
// // font = obj["font"].as<char*>();
// // valign = obj["valign"].as<char*>();
// // format = obj["format"].as<char*>();
// }
// auto item = DisplayPage( pageObj["key"].as<char*>(), _update, _rotate, _font);
// // Загрузка настроек страницы
// item.load(pageObj);
// page.push_back(item);
};
enum position_t {
POS_AUTO,
POS_ABSOLUTE,
POS_RELATIVE,
POS_TEXT
};
struct RelativePosition {
float x;
float y;
};
struct TextPosition {
uint8_t row;
uint8_t col;
};
struct Point {
uint16_t x;
uint16_t y;
Point() : Point(0, 0) {}
Point(uint16_t x, uint16_t y) : x{x}, y{y} {}
Point(const Point& rhv) : Point(rhv.x, rhv.y) {}
};
struct Position {
position_t type;
union {
Point abs;
RelativePosition rel;
TextPosition text;
};
Position() : type{POS_AUTO} {}
Position(const Point& pos) : type{POS_ABSOLUTE} {
abs.x = pos.x;
abs.y = pos.y;
}
Position(const RelativePosition& pos) : type{POS_RELATIVE} {
rel.x = pos.x;
rel.y = pos.y;
}
Position(const TextPosition& pos) : type{POS_TEXT} {
text.col = pos.col;
text.row = pos.row;
}
Position(const Position& rhv) : type{rhv.type} {
switch (type) {
case POS_ABSOLUTE:
abs = rhv.abs;
case POS_RELATIVE:
rel = rhv.rel;
case POS_TEXT:
text = rhv.text;
default:
break;
}
}
};
class Cursor : public Printable {
private:
Point _size;
public:
TextPosition pos{0, 0};
Point abs{0, 0};
Point chr;
Cursor(){};
Cursor(const Point& size, const Point& chr) : _size{size}, chr{chr} {
D_LOG("w: %d, h: %d, ch: %d(%d)\r\n", _size.x, _size.y, chr.x, chr.y);
}
void reset() {
pos.col = 0;
pos.row = 0;
abs.x = 0;
abs.y = 0;
}
void lineFeed() {
pos.col = 0;
pos.row++;
abs.x = 0;
abs.y += chr.y;
}
void moveX(uint8_t x) {
abs.x += x;
pos.col = abs.x / chr.x;
}
void moveY(uint8_t y) {
abs.y += y;
}
void moveXY(uint8_t x, uint8_t y) {
moveX(x);
moveY(y);
}
void moveCarret(uint8_t col) {
pos.col += col;
moveX(col * chr.x);
}
bool isEndOfPage(uint8_t rows = 1) {
return (abs.y + (rows * chr.y)) > _size.y;
}
bool isEndOfLine(uint8_t cols = 1) {
return (abs.x + (cols * chr.x)) > _size.x;
}
size_t printTo(Print& p) const {
return p.printf("(c:%d, r:%d x:%d, y:%d)", pos.col, pos.row, abs.x, abs.y);
}
};
struct DisplayHardwareSettings {
int update = DEFAULT_PAGE_UPDATE_ms;
rotation_t rotate;
String font;
int pageTime;
String pageFormat;
int contrast;
bool autoPage;
String valign;
};
class Display {
private:
unsigned long _lastResfresh{0};
Cursor _cursor;
U8G2 *_obj{nullptr};
DisplayHardwareSettings *_settings;
public:
Display(U8G2 *obj, DisplayHardwareSettings *settings) : _obj{obj}, _settings(settings) {
_obj->begin();
_obj->enableUTF8Print();
_obj->setContrast(_settings->contrast);
setFont(settings->font);
setRotation(settings->rotate);
clear();
}
~Display () {
if (_obj) {
delete _obj;
_obj = nullptr;
}
}
void setRotation(rotation_t rotate) {
switch (rotate) {
case ROTATION_NONE:
_obj->setDisplayRotation(U8G2_R0);
break;
case ROTATION_90:
_obj->setDisplayRotation(U8G2_R1);
break;
case ROTATION_180:
_obj->setDisplayRotation(U8G2_R2);
break;
case ROTATION_270:
_obj->setDisplayRotation(U8G2_R3);
break;
}
}
void setFont(const String &fontName = "") {
if (fontName.isEmpty()) {
Display::setFont(_settings->font);
return;
}
if (fontName.startsWith("c6x12"))
_obj->setFont(u8g2_font_6x12_t_cyrillic);
else if (fontName.startsWith("s6x12"))
_obj->setFont(u8g2_font_6x12_t_symbols);
else if (fontName.startsWith("c6x13"))
_obj->setFont(u8g2_font_6x13_t_cyrillic);
else if (fontName.startsWith("c7x13"))
_obj->setFont(u8g2_font_7x13_t_cyrillic);
else if (fontName.startsWith("s7x13"))
_obj->setFont(u8g2_font_7x13_t_symbols);
else if (fontName.startsWith("c8x13"))
_obj->setFont(u8g2_font_8x13_t_cyrillic);
else if (fontName.startsWith("s8x13"))
_obj->setFont(u8g2_font_8x13_t_symbols);
else if (fontName.startsWith("c9x15"))
_obj->setFont(u8g2_font_9x15_t_cyrillic);
else if (fontName.startsWith("s9x15"))
_obj->setFont(u8g2_font_9x15_t_symbols);
else if (fontName.startsWith("c10x20"))
_obj->setFont(u8g2_font_10x20_t_cyrillic);
else if (fontName.startsWith("unifont"))
_obj->setFont(u8g2_font_unifont_t_symbols);
else if (fontName.startsWith("siji"))
_obj->setFont(u8g2_font_siji_t_6x10);
else
_obj->setFont(u8g2_font_6x12_t_cyrillic);
_cursor.chr.x = getMaxCharHeight();
// _cursor.chr.y = getLineHeight();
}
void initCursor() {
_cursor = Cursor(
{getWidth(), getHeight()},
{getMaxCharHeight(), getLineHeight()});
}
void getPosition(const TextPosition &a, Point &b) {
b.x = a.col * _cursor.chr.x;
b.y = (a.row + 1) * _cursor.chr.y;
}
void getPosition(const RelativePosition &a, Point &b) {
b.x = getHeight() * a.x;
b.y = getWidth() * a.y;
}
void getPosition(const Point &a, TextPosition &b) {
b.row = a.y / getLineHeight();
b.col = a.x / getMaxCharWidth();
}
void getPosition(const RelativePosition &a, TextPosition &b) {
Point tmp;
getPosition(a, tmp);
getPosition(tmp, b);
}
void draw(const RelativePosition &pos, const String &str) {
Point tmp;
getPosition(pos, tmp);
draw(tmp, str);
}
void draw(TextPosition &pos, const String &str) {
Point tmp;
getPosition(pos, tmp);
draw(tmp, str);
}
Cursor *getCursor() {
return &_cursor;
}
// print меняю cursor
void println(const String &str, bool frame = false) {
print(str, frame);
_cursor.lineFeed();
}
void print(const String &str, bool frame = false) {
//Serial.print(_cursor);
// x, y нижний левой
int width = _obj->drawUTF8(_cursor.abs.x, _cursor.abs.y + _cursor.chr.y, str.c_str());
if (frame) {
int x = _cursor.abs.x - getXSpacer();
int y = _cursor.abs.y - _cursor.chr.y;
width += (getXSpacer() * 2);
int height = _cursor.chr.y + getYSpacer() * 2;
// x, y верхней левой. длина, высота
_obj->drawFrame(x, y, width, height);
D_LOG("[x:%d y:%d w:%d h:%d]", x, y, width, height);
}
_cursor.moveX(width);
}
// draw не меняет cursor
void draw(const Point &pos, const String &str) {
Serial.printf("(x:%d,y:%d) %s", pos.x, pos.y, str.c_str());
_obj->drawStr(pos.x, pos.y, str.c_str());
}
uint8_t getLineHeight() {
return getMaxCharHeight() + getYSpacer();
}
int getXSpacer() {
int res = getWidth() / 100;
if (!res) res = 1;
return res;
}
int getYSpacer() {
int res = (getHeight() - (getLines() * getMaxCharHeight())) / getLines();
if (!res) res = 1;
return res;
}
uint8_t getWidth() {
return _obj->getDisplayWidth();
}
uint8_t getHeight() {
return _obj->getDisplayHeight();
}
uint8_t getLines() {
uint8_t res = getHeight() / _obj->getMaxCharHeight();
if (!res) res = 1;
return res;
}
uint8_t getMaxCharHeight() {
return _obj->getMaxCharHeight();
}
uint8_t getMaxCharWidth() {
return _obj->getMaxCharWidth();
}
void clear() {
_obj->clearDisplay();
_cursor.reset();
}
void startRefresh() {
_obj->clearBuffer();
_cursor.reset();
}
void endRefresh() {
_obj->sendBuffer();
_lastResfresh = millis();
}
bool isNeedsRefresh() {
// SerialPrint("[Display]", "_settings->update: " + String(_settings->update) + "ms", "");
return !_lastResfresh || (millis() > (_lastResfresh + _settings->update));
}
};
struct ParamPropeties {
// рамка
bool frame[false];
};
struct Param {
// Ключ
const String key;
// Префикс к значению
String pref;
// Суффикс к значению
String suff;
// Значение
String value;
String pref_fnt;
String suff_fnt;
String value_fnt;
String gliphs;
// значение изменилось
bool updated;
// группа
uint8_t group;
ParamPropeties props;
Position position;
Param(const String &key,
const String &pref = emptyString, const String &value = emptyString, const String &suff = emptyString,
const String &pref_fnt = emptyString, const String &value_fnt = emptyString, const String &suff_fnt = emptyString,
const String &gliphs = emptyString
) : key{key}, group{0} {
setValue(value.c_str());
setPref(pref);
setSuff(suff);
this->pref_fnt = pref_fnt;
this->value_fnt = value_fnt;
this->suff_fnt = suff_fnt;
this->gliphs = gliphs;
updated = false;
}
bool isValid() {
return !pref.isEmpty();
}
bool setPref(const String &str) {
if (!pref.equals(str)) {
pref = str;
updated = true;
return true;
}
return false;
}
bool setSuff(const String &str) {
if (!suff.equals(str)) {
suff = str;
updated = true;
return true;
}
return false;
}
bool setValue(const String &str) {
if (!value.equals(str)) {
value = str;
updated = true;
return true;
}
return false;
}
void draw(Display *obj, uint8_t line) {
}
void draw(Display *obj) {
auto type = position.type;
switch (type) {
case POS_AUTO: {
D_LOG("AUTO %s '%s%s'\r\n", key.c_str(), descr.c_str(), value.c_str());
obj->setFont(pref_fnt);
obj->print(pref.c_str());
obj->setFont(value_fnt);
obj->println(value.c_str(), false);
obj->setFont(suff_fnt);
obj->print(suff.c_str());
}
case POS_ABSOLUTE: {
auto pos = position.abs;
D_LOG("ABS(%d, %d) %s %s'\r\n", pos.x, pos.y, key.c_str(), value.c_str());
obj->draw(pos, value);
}
case POS_RELATIVE: {
auto pos = position.rel;
D_LOG("REL(%2.2f, %2.2f) %s %s'\r\n", pos.x, pos.y, key.c_str(), value.c_str());
obj->draw(pos, value);
}
case POS_TEXT: {
auto pos = position.text;
D_LOG("TXT(%d, %d) %s %s'\r\n", pos.col, pos.row, key.c_str(), value.c_str());
obj->draw(pos, value);
}
default:
D_LOG("unhadled: %d", type);
}
}
};
class ParamCollection {
std::vector<Param> _item;
public:
void load() {
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
if ((*it)->getSubtype() == "" || (*it)->getSubtype() == "U8g2lib") continue;
auto entry = find((*it)->getID());
if (!entry) {
_item.push_back({(*it)->getID(), (*it)->getID() + ": ", (*it)->getValue(), "", "", "", ""});
} else {
entry->setValue((*it)->getValue());
if (entry->pref == "")
entry->setPref((*it)->getID() + ": ");
}
}
}
void loadExtParamData(String parameters) {
String id = "";
jsonRead(parameters, "id", id, false);
if (id != "") {
String pref = "";
String suff = "";
String pref_fnt = "";
String suff_fnt = "";
String value_fnt = "";
String gliphs = "";
bool hasExtParam = false;
hasExtParam = hasExtParam + jsonRead(parameters, "pref", pref, false);
hasExtParam = hasExtParam + jsonRead(parameters, "suff", suff, false);
hasExtParam = hasExtParam + jsonRead(parameters, "pref_fnt", pref_fnt, false);
hasExtParam = hasExtParam + jsonRead(parameters, "suff_fnt", suff_fnt, false);
hasExtParam = hasExtParam + jsonRead(parameters, "value_fnt", value_fnt, false);
hasExtParam = hasExtParam + jsonRead(parameters, "gliphs", gliphs, false);
if (hasExtParam) {
_item.push_back({id, pref, "", suff, pref_fnt, value_fnt, suff_fnt, gliphs});
}
}
}
Param *find(const String &key) {
return find(key.c_str());
}
Param *find(const char *key) {
Param *res = nullptr;
for (size_t i = 0; i < _item.size(); i++) {
if (_item.at(i).key.equalsIgnoreCase(key)) {
res = &_item.at(i);
break;
}
}
return res;
}
Param *get(int n) {
return &_item.at(n);
}
size_t count() {
return _item.size();
}
// n - номер по порядку параметра
Param *getValid(int n) {
for (size_t i = 0; i < _item.size(); i++)
if (_item.at(i).isValid())
if (!(n--)) return &_item.at(i);
return nullptr;
}
size_t getVaildCount() {
size_t res = 0;
for (auto entry : _item) res += entry.isValid();
return res;
}
size_t max_group() {
size_t res = 0;
for (auto entry : _item)
if (res < entry.group) res = entry.group;
return res;
}
};

View File

@@ -0,0 +1,419 @@
#include "Global.h"
#include "classes/IoTItem.h"
#include <U8g2lib.h>
#include "DisplayTypes.h"
#define STRHELPER(x) #x
#define TO_STRING_AUX(...) "" #__VA_ARGS__
#define TO_STRING(x) TO_STRING_AUX(x)
// дополненный список параметров для вывода, который синхронизирован со списком значений IoTM
ParamCollection *extParams{nullptr};
// класс одного главного экземпляра экрана для выделения памяти только когда потребуется экран
class DisplayImplementation {
private:
unsigned long _lastPageChange{0};
bool _pageChanged{false};
// uint8_t _max_descr_width{0};
// typedef std::vector<Param *> Line;
// текущая
size_t _page_n{0};
// struct Page {
// std::vector<Line *> line;
// };
uint8_t _n{0}; // последний отображенный
DisplayHardwareSettings *_context{nullptr};
Display *_display{nullptr};
public:
DisplayImplementation(DisplayHardwareSettings *context = nullptr,
Display *display = nullptr)
: _context(context), _display(display) {
}
~DisplayImplementation() {
if (_display) {
delete _display;
_display = nullptr;
}
if (_context) {
delete _context;
_context = nullptr;
}
if (extParams) {
delete extParams;
extParams = nullptr;
}
}
std::vector<DisplayPage> page;
void nextPage() {
_n = _n + 1;
if (_n == page.size()) _n = _n - 1;
_pageChanged = true;
}
void prevPage() {
if (_n > 0) _n = _n - 1;
_pageChanged = true;
}
void rotPage() {
_n = _n + 1;
if (_n == page.size()) _n = 0;
_pageChanged = true;
}
void gotoPage(uint8_t num) {
_n = num;
if (num < 0) _n = 0;
if (num >= page.size()) _n = page.size() - 1;
_pageChanged = true;
}
void setAutoPage(bool isAuto) {
if (_context) _context->autoPage = isAuto;
_pageChanged = true;
}
uint8_t calcPageCount(ParamCollection *param, uint8_t linesPerPage) {
size_t res = 0;
size_t totalLines = param->count();
if (totalLines && linesPerPage) {
res = totalLines / linesPerPage;
if (totalLines % linesPerPage) res++;
}
return res;
}
// uint8_t getPageCount() {
// return isAutoPage() ? calcPageCount(_param, _display->getLines()) : getPageCount();
// }
// выводит на страницу параметры начиная c [n]
// возвращает [n] последнего уместившегося
uint8_t draw(Display *display, ParamCollection *param, uint8_t n) {
// Очищает буфер (не экран, а внутреннее представление) для последущего заполнения
display->startRefresh();
size_t i = 0;
// вот тут лог ошибка
for (i = n; i < param->count(); i++) {
auto cursor = display->getCursor();
auto entry = param->get(i);
auto len = entry->value.length() + entry->pref.length() + entry->suff.length() ;
if (cursor->isEndOfLine(len)) cursor->lineFeed();
printParam(display, entry, _context->font);
if (cursor->isEndOfPage(0)) break;
}
// Отправит готовый буфер страницы на дисплей
display->endRefresh();
return i;
}
String slice(const String &str, size_t index, char delim) {
size_t cnt = 0;
int subIndex[] = {0, -1};
size_t maxIndex = str.length() - 1;
for (size_t i = 0; (i <= maxIndex) && (cnt <= index); i++) {
if ((str.charAt(i) == delim) || (i == maxIndex)) {
cnt++;
subIndex[0] = subIndex[1] + 1;
subIndex[1] = (i == maxIndex) ? i + 1 : i;
}
}
return cnt > index ? str.substring(subIndex[0], subIndex[1]) : emptyString;
}
void printParam(Display *display, Param *param, const String &parentFont) {
if (!param->pref.isEmpty()) {
display->setFont(param->pref_fnt.isEmpty() ? parentFont : param->pref_fnt);
display->print(param->pref);
}
if (!param->value.isEmpty()) {
display->setFont(param->value_fnt.isEmpty() ? parentFont : param->value_fnt);
if (!param->gliphs.isEmpty() && isDigitStr(param->value)) {
int glyphIndex = param->value.toInt();
display->print(getUtf8CharByIndex(param->gliphs, glyphIndex));
} else display->print(param->value);
}
if (!param->suff.isEmpty()) {
display->setFont(param->suff_fnt.isEmpty() ? parentFont : param->suff_fnt);
display->print(param->suff);
}
}
void showXXX(Display *display, ParamCollection *param, uint8_t page) {
size_t linesPerPage = display->getLines();
size_t line_first = _page_n * linesPerPage;
size_t line_last = line_first + linesPerPage - 1;
display->startRefresh();
size_t lineOfPage = 0;
for (size_t n = line_first; n <= line_last; n++) {
auto entry = param->get(n);
if (entry) {
entry->draw(_display, lineOfPage);
lineOfPage++;
} else {
break;
}
}
display->endRefresh();
}
void drawPage(Display *display, ParamCollection *params, DisplayPage *page) {
display->setFont(page->font);
display->initCursor();
auto keys = page->key;
D_LOG("page keys: %s\r\n", keys.c_str());
size_t l = 0;
auto line_keys = slice(keys, l, '#');
while (!line_keys.isEmpty()) {
if (page->valign.equalsIgnoreCase("center")) {
display->getCursor()->moveY((display->getHeight() / 2) - display->getMaxCharHeight() / 2);
}
D_LOG("line keys: %s\r\n", keys.c_str());
size_t n = 0;
auto key = slice(line_keys, n, ',');
while (!key.isEmpty()) {
D_LOG("key: %s\r\n", key.c_str());
auto entry = params->find(key.c_str());
if (entry && entry->updated) {
if (n) display->print(" ");
printParam(display, entry, page->font);
}
key = slice(line_keys, ++n, ',');
}
display->getCursor()->lineFeed();
line_keys = slice(keys, ++l, '#');
}
}
// Режим пользовательской разбивки параметров по страницам
void showManual(Display *display, ParamCollection *param) {
auto page = getPage(_n);
if (display->isNeedsRefresh() || _pageChanged) {
D_LOG("[Display] page: %d\r\n", _n);
display->setRotation(page->rotate);
display->startRefresh();
drawPage(display, param, page);
display->endRefresh();
_pageChanged = false;
}
if (_context->autoPage && millis() >= (_lastPageChange + page->time)) {
// Если это была последняя начинаем с начала
if (++_n > (getPageCount() - 1)) _n = 0;
_pageChanged = true;
_lastPageChange = millis();
}
}
// Режим авто разбивки параметров по страницам
void showAuto(Display *display, ParamCollection *param) {
size_t param_count = param->count();
if (!param_count) return;
display->setFont(_context->font);
display->initCursor();
size_t last_n = _n;
if (display->isNeedsRefresh() || _pageChanged) {
//D_LOG("n: %d/%d\r\n", _n, param_count);
last_n = draw(display, param, _n);
}
if (_context->autoPage && millis() >= (_lastPageChange + _context->pageTime)) {
_n = last_n;
if (_n >= param_count) _n = 0;
_pageChanged = true;
_lastPageChange = millis();
}
}
void show() {
if (extParams && _display) {
extParams->load();
if (isAutoPage()) {
showAuto(_display, extParams);
} else {
showManual(_display, extParams);
}
}
}
bool isAutoPage() {
return !getPageCount();
}
uint8_t getPageCount() {
return page.size();
}
DisplayPage* getPage(uint8_t index) {
return &page.at(index);
}
};
DisplayImplementation* displayImpl = nullptr;
class U8g2lib : public IoTItem {
private:
uint8_t _pageNum = 0;
public:
U8g2lib(String parameters) : IoTItem(parameters) {
DisplayHardwareSettings *context = new DisplayHardwareSettings();
if (!context) {
D_LOG("[Display] disabled");
return;
}
jsonRead(parameters, "update", context->update);
jsonRead(parameters, "font", context->font);
int rotate;
jsonRead(parameters, "rotation", rotate);
context->rotate = parse_rotation(rotate);
jsonRead(parameters, "contrast", context->contrast);
jsonRead(parameters, "autoPage", context->autoPage);
jsonRead(parameters, "pageTime", context->pageTime);
bool itsFirstDisplayInit = false;
if (!displayImpl) {
// Значит это первый элемент U8g2lib в конфигурации - Инициализируем дисплей
itsFirstDisplayInit = true;
int dc = U8X8_PIN_NONE, cs = U8X8_PIN_NONE, data = U8X8_PIN_NONE, clock = U8X8_PIN_NONE, rst = U8X8_PIN_NONE;
jsonRead(parameters, "dc", dc);
jsonRead(parameters, "cs", cs);
jsonRead(parameters, "data", data);
jsonRead(parameters, "clock", clock);
jsonRead(parameters, "rst", rst);
if (dc == -1) dc = U8X8_PIN_NONE;
if (cs == -1) cs = U8X8_PIN_NONE;
if (data == -1) data = U8X8_PIN_NONE;
if (clock == -1) clock = U8X8_PIN_NONE;
if (rst == -1) rst = U8X8_PIN_NONE;
String type;
jsonRead(parameters, "oledType", type);
U8G2* libObj = nullptr;
if (type.startsWith("ST")) {
libObj = new U8G2_ST7565_ERC12864_F_4W_SW_SPI(U8G2_R0, clock, data, cs, dc, rst);
}
else if (type.startsWith("SS_I2C")) {
// libObj = new U8G2_SSD1306_128X64_VCOMH0_F_SW_I2C(U8G2_R0, clock, data, rst);
libObj = new U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C(U8G2_R0, clock, data, rst);
}
else if (type.startsWith("SS_SPI")) {
libObj = new U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI(U8G2_R0, clock, data, cs, dc, rst);
}
else if (type.startsWith("SH")) {
libObj = new U8G2_SH1106_128X64_NONAME_F_HW_I2C(U8G2_R0, rst, clock, data);
}
if (!libObj) {
D_LOG("[Display] disabled");
return;
}
Display *_display = new Display(libObj, context);
if (!_display) {
D_LOG("[Display] disabled");
return;
}
if (!extParams) extParams = new ParamCollection();
displayImpl = new DisplayImplementation(context, _display);
if (!displayImpl) {
D_LOG("[Display] disabled");
return;
}
}
// добавляем страницу, если указан ID для отображения
String id2show;
jsonRead(parameters, "id2show", id2show);
if (!id2show.isEmpty()) {
auto item = DisplayPage(
id2show,
context->pageTime,
context->rotate,
context->font,
context->pageFormat,
context->valign
);
_pageNum = displayImpl->page.size();
displayImpl->page.push_back(item);
if (!itsFirstDisplayInit) delete context; // если это не первый вызов, то контекст имеет временный характер только для создания страницы
}
}
void doByInterval() {
if (displayImpl) displayImpl->show();
}
IoTValue execute(String command, std::vector<IoTValue>& param) {
if (displayImpl)
if (command == "nextPage") {
displayImpl->nextPage();
} else if (command == "prevPage") {
displayImpl->prevPage();
} else if (command == "rotPage") {
displayImpl->rotPage();
} else if (command == "gotoPage") {
if (param.size() == 1) {
displayImpl->gotoPage(param[0].valD);
} else {
displayImpl->gotoPage(_pageNum);
}
} else if (command == "setAutoPage") {
if (param.size() == 1) {
displayImpl->setAutoPage(param[0].valD);
}
}
return {};
}
~U8g2lib() {
if (displayImpl) {
delete displayImpl;
displayImpl = nullptr;
}
};
};
void* getAPI_U8g2lib(String subtype, String param) {
if (subtype == F("U8g2lib")) {
// SerialPrint("[Display]", "param1: ", param);
return new U8g2lib(param);
} else {
// элемент не наш, но проверяем на налличие модификаторов, которые нужны для модуля
// вынимаем ID элемента и значения pref и suff связанные с ним
if (!extParams) extParams = new ParamCollection();
extParams->loadExtParamData(param);
return nullptr;
}
}

View File

@@ -0,0 +1,217 @@
{
"mark": "iotm",
"config": [
{
"global": 0,
"type": "Reading",
"subtype": "VButton",
"id": "btn",
"needSave": 0,
"widget": "toggle",
"page": "Ввод",
"descr": "ТестКнопка",
"int": "0",
"val": "0",
"value_fnt": "siji",
"gliphs": ""
},
{
"global": 0,
"type": "Writing",
"subtype": "Timer",
"id": "timer",
"widget": "anydataDef",
"page": "Ввод",
"descr": "Таймер",
"int": 1,
"countDown": "99",
"ticker": 1,
"repeat": 1,
"needSave": 0,
"pref": "ТАЙМЕР: ",
"suff": " сек",
"round": "0"
},
{
"global": 0,
"type": "Reading",
"subtype": "Variable",
"id": "time",
"needSave": 0,
"widget": "anydataRed",
"page": "Ввод",
"descr": "Время",
"int": "0",
"val": "",
"pref": " ⏰️",
"pref_fnt": "unifont"
},
{
"global": 0,
"type": "Reading",
"subtype": "Variable",
"id": "var",
"needSave": 0,
"widget": "inputTxt",
"page": "Ввод",
"descr": "Текст",
"int": "0",
"val": "☀️-☁️-☂️-☃️-☄️",
"map": "1024,1024,1,100",
"plus": 0,
"multiply": 1,
"round": 0,
"pref": "текст: ",
"value_fnt": "unifont"
},
{
"global": 0,
"type": "Reading",
"subtype": "Variable",
"id": "ip",
"needSave": 0,
"widget": "anydataDef",
"page": "Ввод",
"descr": "IP",
"int": "0",
"val": "",
"pref": "IP: "
},
{
"type": "Reading",
"subtype": "U8g2lib",
"id": "page1",
"widget": "nil",
"page": "",
"descr": "",
"oledType": "SS_I2C",
"int": "1",
"font": "c6x13",
"contrast": "200",
"rotation": "0",
"autoPage": "0",
"pageTime": "10000",
"dc": 19,
"cs": "-1",
"data": "21",
"clock": "22",
"rst": -1,
"id2show": "timer,lvl#ip"
},
{
"type": "Reading",
"subtype": "U8g2lib",
"id": "page2",
"widget": "nil",
"page": "",
"descr": "",
"oledType": "SS_I2C",
"int": 1,
"update": 500,
"font": "c6x13",
"contrast": "150",
"rotation": "0",
"autoPage": "0",
"pageTime": 3000,
"id2show": "var#btn,time",
"dc": "-1",
"cs": "-1",
"data": "-1",
"clock": "-1",
"rst": -1
},
{
"global": 0,
"type": "Reading",
"subtype": "VButton",
"id": "autoPage",
"needSave": 0,
"widget": "toggle",
"page": "Ввод",
"descr": "autoPage",
"int": "0",
"val": "0"
},
{
"global": 0,
"type": "Reading",
"subtype": "VButton",
"id": "nextPage",
"needSave": 0,
"widget": "toggle",
"page": "Ввод",
"descr": "nextPage",
"int": "0",
"val": "0"
},
{
"global": 0,
"type": "Reading",
"subtype": "VButton",
"id": "prevPage",
"needSave": 0,
"widget": "toggle",
"page": "Ввод",
"descr": "prevPage",
"int": "0",
"val": "0"
},
{
"global": 0,
"type": "Reading",
"subtype": "Variable",
"id": "pageN",
"needSave": 0,
"widget": "inputDgt",
"page": "Ввод",
"descr": "pageN",
"int": "0",
"val": "0.0",
"map": "1024,1024,1,100",
"plus": 0,
"multiply": 1,
"round": 0
},
{
"global": 0,
"type": "Reading",
"subtype": "VButton",
"id": "rotPage",
"needSave": 0,
"widget": "toggle",
"page": "Ввод",
"descr": "rotPage",
"int": "0",
"val": "0"
},
{
"global": 0,
"type": "Reading",
"subtype": "AnalogAdc",
"id": "lvl",
"widget": "anydataRed",
"page": "Ввод",
"descr": "Уровень",
"map": "1,1024,1,5",
"plus": 0,
"multiply": 1,
"round": "0",
"pin": "34",
"int": "1",
"avgSteps": 1,
"pref": " ",
"value_fnt": "siji",
"gliphs": ""
}
]
}
scenario=>if timer then {
ip = getIP()
time = gethhmmss()
}
if autoPage then page1.setAutoPage(1) else page1.setAutoPage(0)
if nextPage < 2 then page1.nextPage()
if prevPage < 2 then page1.prevPage()
if rotPage < 2 then page1.rotPage()
if pageN != "" then page1.gotoPage(pageN)