mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-05-25 20:29:19 +03:00
@@ -223,12 +223,12 @@ allAPI_head = ""
|
||||
allAPI_exec = ""
|
||||
for activModuleName in activeModulesName:
|
||||
allAPI_head = allAPI_head + "\nvoid* getAPI_" + activModuleName + "(String subtype, String params);"
|
||||
allAPI_exec = allAPI_exec + "\nif ((tmpAPI = getAPI_" + activModuleName + "(subtype, params)) != nullptr) return tmpAPI;"
|
||||
allAPI_exec = allAPI_exec + "\nif ((tmpAPI = getAPI_" + activModuleName + "(subtype, params)) != nullptr) foundAPI = tmpAPI;"
|
||||
apicpp = '#include "ESPConfiguration.h"\n'
|
||||
apicpp = apicpp + allAPI_head
|
||||
apicpp = apicpp + '\n\nvoid* getAPI(String subtype, String params) {\nvoid* tmpAPI;'
|
||||
apicpp = apicpp + '\n\nvoid* getAPI(String subtype, String params) {\nvoid* tmpAPI; void* foundAPI = nullptr;'
|
||||
apicpp = apicpp + allAPI_exec
|
||||
apicpp = apicpp + '\nreturn nullptr;\n}'
|
||||
apicpp = apicpp + '\nreturn foundAPI;\n}'
|
||||
with open('src/modules/API.cpp', 'w') as f:
|
||||
f.write(apicpp)
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ config.read("platformio.ini")
|
||||
deviceName = config["platformio"]["default_envs"]
|
||||
|
||||
homeDir = os.path.expanduser('~')
|
||||
os.system(homeDir + "\.platformio\penv\Scripts\pio run")
|
||||
os.system(homeDir + "\.platformio\penv\Scripts\pio run -t buildfs --disable-auto-clean")
|
||||
os.system(homeDir + "/.platformio/penv/Scripts/pio run")
|
||||
os.system(homeDir + "/.platformio/penv/Scripts/pio run -t buildfs --disable-auto-clean")
|
||||
|
||||
if copyFileIfExist("firmware.bin", deviceName) and copyFileIfExist("littlefs.bin", deviceName):
|
||||
copyFileIfExist("partitions.bin", deviceName)
|
||||
|
||||
@@ -51,3 +51,5 @@ unsigned char ChartoHex(char ch);
|
||||
std::vector<String> splitStr(const String& str, const String& delimiter);
|
||||
|
||||
bool strInVector(const String& str, const std::vector<String>& vec);
|
||||
|
||||
String getUtf8CharByIndex(const String& utf8str, int index);
|
||||
|
||||
@@ -291,6 +291,11 @@ class CallExprAST : public ExprAST {
|
||||
ret.valD = Item->getIntFromNet();
|
||||
ret.isDecimal = true;
|
||||
return &ret;
|
||||
|
||||
} else if (Cmd == F("doByInterval")) { // вызываем системную функцию периодического выполнения вне таймера
|
||||
Item->doByInterval();
|
||||
ret = Item->value;
|
||||
return &ret;
|
||||
}
|
||||
|
||||
// если все же все ок, то готовим параметры для передачи в модуль
|
||||
@@ -304,6 +309,15 @@ class CallExprAST : public ExprAST {
|
||||
return nullptr; // ArgsAsIoTValue.push_back(zeroIotVal);
|
||||
}
|
||||
|
||||
if (Cmd == F("setInterval")) { // меняем интервал выполнения задач модуля налету
|
||||
if (ArgsAsIoTValue.size() == 1) {
|
||||
Item->setInterval(ArgsAsIoTValue[0].valD);
|
||||
ret.valD = Item->getInterval();
|
||||
ret.isDecimal = true;
|
||||
return &ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = Item->execute(Cmd, ArgsAsIoTValue); // вызываем команду из модуля напрямую с передачей всех аргументов
|
||||
|
||||
// if (ret.isDecimal) Serial.printf("Call from CallExprAST ID = %s, Command = %s, exec result = %f\n", Callee.c_str(), Cmd.c_str(), ret.valD);
|
||||
@@ -343,7 +357,8 @@ enum SysOp {
|
||||
sysop_mqttPub,
|
||||
sysop_getUptime,
|
||||
sysop_mqttIsConnect,
|
||||
sysop_wifiIsConnect
|
||||
sysop_wifiIsConnect,
|
||||
sysop_setInterval
|
||||
};
|
||||
|
||||
IoTValue sysExecute(SysOp command, std::vector<IoTValue> ¶m) {
|
||||
@@ -449,7 +464,12 @@ IoTValue sysExecute(SysOp command, std::vector<IoTValue> ¶m) {
|
||||
break;
|
||||
case sysop_wifiIsConnect:
|
||||
value.valD = isNetworkActive();
|
||||
break;
|
||||
break;
|
||||
case sysop_setInterval:
|
||||
if (param.size() == 1) {
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -508,6 +528,8 @@ class SysCallExprAST : public ExprAST {
|
||||
operation = sysop_mqttIsConnect;
|
||||
else if (Callee == F("wifiIsConnect"))
|
||||
operation = sysop_wifiIsConnect;
|
||||
else if (Callee == F("setInterval"))
|
||||
operation = sysop_setInterval;
|
||||
else
|
||||
operation = sysop_notfound;
|
||||
}
|
||||
|
||||
@@ -140,6 +140,8 @@ class DwinI : public IoTUart {
|
||||
void onModuleOrder(String &key, String &value) {
|
||||
if (key == "uploadUI") {
|
||||
//SerialPrint("i", F("DwinI"), "Устанавливаем UI: " + value);
|
||||
if (value != "") uartPrintHex(value.c_str());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ public:
|
||||
|
||||
} else if (command == "setEffect") {
|
||||
if (param.size() == 1) {
|
||||
if (param[0].valD < 0 | param[0].valD > 79)
|
||||
if (param[0].valD < 0 || param[0].valD > 79)
|
||||
_effectsMode = random(0, 79);
|
||||
else
|
||||
_effectsMode = param[0].valD;
|
||||
|
||||
BIN
src/modules/display/U8g2lib.zip
Normal file
BIN
src/modules/display/U8g2lib.zip
Normal file
Binary file not shown.
631
src/modules/display/U8g2lib/DisplayTypes.h
Normal file
631
src/modules/display/U8g2lib/DisplayTypes.h
Normal 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;
|
||||
}
|
||||
};
|
||||
419
src/modules/display/U8g2lib/U8g2lib.cpp
Normal file
419
src/modules/display/U8g2lib/U8g2lib.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
217
src/modules/display/U8g2lib/example_config.json
Normal file
217
src/modules/display/U8g2lib/example_config.json
Normal 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)
|
||||
96
src/modules/display/U8g2lib/modinfo.json
Normal file
96
src/modules/display/U8g2lib/modinfo.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "Экраны U8g2",
|
||||
"type": "Reading",
|
||||
"subtype": "U8g2lib",
|
||||
"id": "u8page",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
|
||||
"oledType": "SS_I2C",
|
||||
"int": 1,
|
||||
"update": 500,
|
||||
"font": "c6x13",
|
||||
"contrast": 90,
|
||||
"rotation": 90,
|
||||
"autoPage": 1,
|
||||
"pageTime": 3000,
|
||||
"id2show": "",
|
||||
|
||||
"dc": 19,
|
||||
"cs": 5,
|
||||
"data": 23,
|
||||
"clock": 18,
|
||||
"rst": -1
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
"authorGit": "https://github.com/biveraxe",
|
||||
"specialThanks": "Yuriy Trikoz @ytrikoz",
|
||||
"moduleName": "U8g2lib",
|
||||
"moduleVersion": "1.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"moduleDesc": "Позволяет выводить на графические экраны типа SSD, ST, SH указанные параметры из конфигурации IoTM.",
|
||||
"propInfo": {
|
||||
"oledType": "Строковый код типа дисплея. В текущей верссии поддерживаются ST7565 (ST), SSD1306 (SS_I2C), SSD1306 (SS_SPI) и SH1106 (SH). Для получения списка доступных типов дисплеев, обратитесь к документации библиотеки U8g2. Добавить возможность выбора типов дисплеев можно, добавив соответствующие условия в файл модуля в конструктор класса U8g2lib.",
|
||||
"int": "Интервал обновления экрана в секундах. Если указано 0, то обновление экрана не производится.",
|
||||
"update": "Интервал обновления экрана в миллисекундах. Если указано 0, то обновление экрана не производится. (парамтер на развитие)",
|
||||
"font": "Шрифт, используемый для отображения текста на экране. Доступные шрифты можно найти в документации библиотеки U8g2 и добавить в проект в функцию setFont().",
|
||||
"contrast": "Контрастность экрана. Значение от 10 до 150, где 0 - минимальная контрастность, а 255 - максимальная.",
|
||||
"rotation": "Поворот экрана в градусах. Доступные значения: 0, 90, 180, 270.",
|
||||
"autoPage": "Автоматическая смена страниц экрана. Если установлено в 1, то экран будет автоматически переключаться на следующую страницу после указанного времени.",
|
||||
"pageTime": "Время в миллисекундах, через которое будет происходить автоматическая смена страниц экрана. Используется только если autoPage установлено в 1.",
|
||||
"id2show": "Идентификатор элемента конфигурации, значение которого будет отображаться на экране. Если указано, то на экране будет отображаться только это значение. Возможно указать несколько идентификаторов, разделенных запятыми для перечисления горизонтально и # для перевода строки.",
|
||||
"dc": "Пин, используемый для управления дисплеем по протоколу I2C. Если не используется, укажите -1.",
|
||||
"cs": "Пин, используемый для управления дисплеем по протоколу SPI. Если не используется, укажите -1.",
|
||||
"data": "Пин, используемый для передачи данных на дисплей по протоколу SPI. Если не используется, укажите -1.",
|
||||
"clock": "Пин, используемый для синхронизации данных на дисплее по протоколу SPI. Если не используется, укажите -1.",
|
||||
"rst": "Пин, используемый для сброса дисплея. Если не используется, укажите -1."
|
||||
},
|
||||
"title": "Дисплей U8g2lib",
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "nextPage",
|
||||
"descr": "Переключиться на следующую страницу",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "prevPage",
|
||||
"descr": "Переключиться на предыдущую страницу",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "rotPage",
|
||||
"descr": "Переключиться на следующую страницу с ротацией",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "gotoPage",
|
||||
"descr": "Переключиться на указанную страницу. Если номер не указать, то переключится на страницу закрепленную за элементом конфигурации.",
|
||||
"params": ["Номер страницы"]
|
||||
},
|
||||
{
|
||||
"name": "setAutoPage",
|
||||
"descr": "Установить автоматическую смену страниц.",
|
||||
"params": ["1 - включить, 0 - выключить"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32*": [
|
||||
"olikraus/U8g2 @ ^2.36.5"
|
||||
],
|
||||
"esp82*": [
|
||||
"olikraus/U8g2 @ ^2.36.5"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,18 @@
|
||||
"descr": "Получаем количество секунд доверия к значениям элемента. При -2 доверие полное, при -1 время доверия истекло. При >0 время обратного отсчета. Используется только совместно с ИД элемента: ID.getIntFromNet()",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "setInterval",
|
||||
"descr": "Меняем интервал выполнения периодиеских операций элемента в секундах. Используется только совместно с ИД элемента: ID.setInterval(5)",
|
||||
"params": ["Секунды"],
|
||||
"return": "установленный интервал"
|
||||
},
|
||||
{
|
||||
"name": "doByInterval",
|
||||
"descr": "Выполняем интервальное действие модуля вне плана. Используется только совместно с ИД элемента: ID.doByInterval()",
|
||||
"params": [],
|
||||
"return": "значение элемента после выполнения doByInterval"
|
||||
},
|
||||
{
|
||||
"name": "exit",
|
||||
"descr": "Прерываем работу сценария и выводим в консоль причину. Причина не обязательна.",
|
||||
|
||||
@@ -233,4 +233,29 @@ bool strInVector(const String& str, const std::vector<String>& vec) {
|
||||
if (vec[i] == str) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String getUtf8CharByIndex(const String& utf8str, int index) {
|
||||
if (index < 0) index = 0;
|
||||
|
||||
int len = utf8str.length();
|
||||
int charCount = 0;
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
int charLen = 1;
|
||||
unsigned char c = utf8str[i];
|
||||
if ((c & 0x80) == 0x00) charLen = 1; // 0xxxxxxx
|
||||
else if ((c & 0xE0) == 0xC0) charLen = 2; // 110xxxxx
|
||||
else if ((c & 0xF0) == 0xE0) charLen = 3; // 1110xxxx
|
||||
else if ((c & 0xF8) == 0xF0) charLen = 4; // 11110xxx
|
||||
|
||||
if (charCount == index) {
|
||||
return utf8str.substring(i, i + charLen);
|
||||
}
|
||||
|
||||
if (i + charLen >= len) return utf8str.substring(i, i + charLen);
|
||||
i += charLen;
|
||||
charCount++;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -11,7 +11,7 @@ from sys import platform
|
||||
pio_home = env.subst("$PROJECT_CORE_DIR")
|
||||
print("PLATFORMIO_DIR" + pio_home)
|
||||
|
||||
if platform == "linux" or platform == "linux2":
|
||||
if platform == "linux" or platform == "linux2" or platform == "darwin":
|
||||
# linux
|
||||
#mainPyPath = '/home/rise/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFiClient.cpp'
|
||||
mainPyPath = pio_home + '/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFiClient.cpp'
|
||||
|
||||
@@ -7,7 +7,7 @@ from sys import platform
|
||||
pio_home = env.subst("$PROJECT_CORE_DIR")
|
||||
print("PLATFORMIO_DIR" + pio_home)
|
||||
|
||||
if platform == "linux" or platform == "linux2":
|
||||
if platform == "linux" or platform == "linux2" or platform == "darwin":
|
||||
# linux
|
||||
#devkitm = '/home/rise/.platformio/platforms/espressif32/boards/esp32-c6-devkitm-1.json'
|
||||
#devkitc = '/home/rise/.platformio/platforms/espressif32/boards/esp32-c6-devkitc-1.json'
|
||||
|
||||
@@ -9,7 +9,7 @@ from sys import platform
|
||||
pio_home = env.subst("$PROJECT_CORE_DIR")
|
||||
print("PLATFORMIO_DIR" + pio_home)
|
||||
|
||||
if platform == "linux" or platform == "linux2":
|
||||
if platform == "linux" or platform == "linux2" or platform == "darwin":
|
||||
#mainPyPath = '/home/rise/.platformio/platforms/espressif8266@4.0.1/builder/main.py'
|
||||
mainPyPath = pio_home + '/platforms/espressif8266@4.0.1/builder/main.py'
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user