From 4b98abfa53cdac02fec593177b21db7a61b4e705 Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 29 May 2025 18:53:55 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F?= =?UTF-8?q?=D0=B5=D0=BC=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20U8g2lib?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=20=D0=B4=D0=B8=D1=81=D0=BF=D0=BB=D0=B5=D1=8F=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/display/U8g2lib.zip | Bin 0 -> 10908 bytes src/modules/display/U8g2lib/DisplayTypes.h | 631 ++++++++++++++++++ src/modules/display/U8g2lib/U8g2lib.cpp | 419 ++++++++++++ .../display/U8g2lib/example_config.json | 217 ++++++ 4 files changed, 1267 insertions(+) create mode 100644 src/modules/display/U8g2lib.zip create mode 100644 src/modules/display/U8g2lib/DisplayTypes.h create mode 100644 src/modules/display/U8g2lib/U8g2lib.cpp create mode 100644 src/modules/display/U8g2lib/example_config.json diff --git a/src/modules/display/U8g2lib.zip b/src/modules/display/U8g2lib.zip new file mode 100644 index 0000000000000000000000000000000000000000..aacb2535ce60605b654728ff9b23668052740378 GIT binary patch literal 10908 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 literal 0 HcmV?d00001 diff --git a/src/modules/display/U8g2lib/DisplayTypes.h b/src/modules/display/U8g2lib/DisplayTypes.h new file mode 100644 index 00000000..4288dc75 --- /dev/null +++ b/src/modules/display/U8g2lib/DisplayTypes.h @@ -0,0 +1,631 @@ +#pragma once +#include "Global.h" +#include +#include +#include + +// #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(); + // // rotate = parse_rotation(obj["rotate"].as()); + // // font = obj["font"].as(); + // // valign = obj["valign"].as(); + // // format = obj["format"].as(); + // } + + // auto item = DisplayPage( pageObj["key"].as(), _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 _item; + + public: + void load() { + for (std::list::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; + } +}; \ No newline at end of file diff --git a/src/modules/display/U8g2lib/U8g2lib.cpp b/src/modules/display/U8g2lib/U8g2lib.cpp new file mode 100644 index 00000000..97dc291f --- /dev/null +++ b/src/modules/display/U8g2lib/U8g2lib.cpp @@ -0,0 +1,419 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include +#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 Line; + // текущая + size_t _page_n{0}; + // struct Page { + // std::vector 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 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& 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; + } +} diff --git a/src/modules/display/U8g2lib/example_config.json b/src/modules/display/U8g2lib/example_config.json new file mode 100644 index 00000000..48809c20 --- /dev/null +++ b/src/modules/display/U8g2lib/example_config.json @@ -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) \ No newline at end of file