mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-06-10 11:59:19 +03:00
@@ -223,12 +223,12 @@ allAPI_head = ""
|
|||||||
allAPI_exec = ""
|
allAPI_exec = ""
|
||||||
for activModuleName in activeModulesName:
|
for activModuleName in activeModulesName:
|
||||||
allAPI_head = allAPI_head + "\nvoid* getAPI_" + activModuleName + "(String subtype, String params);"
|
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 = '#include "ESPConfiguration.h"\n'
|
||||||
apicpp = apicpp + allAPI_head
|
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 + allAPI_exec
|
||||||
apicpp = apicpp + '\nreturn nullptr;\n}'
|
apicpp = apicpp + '\nreturn foundAPI;\n}'
|
||||||
with open('src/modules/API.cpp', 'w') as f:
|
with open('src/modules/API.cpp', 'w') as f:
|
||||||
f.write(apicpp)
|
f.write(apicpp)
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ config.read("platformio.ini")
|
|||||||
deviceName = config["platformio"]["default_envs"]
|
deviceName = config["platformio"]["default_envs"]
|
||||||
|
|
||||||
homeDir = os.path.expanduser('~')
|
homeDir = os.path.expanduser('~')
|
||||||
os.system(homeDir + "\.platformio\penv\Scripts\pio run")
|
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 -t buildfs --disable-auto-clean")
|
||||||
|
|
||||||
if copyFileIfExist("firmware.bin", deviceName) and copyFileIfExist("littlefs.bin", deviceName):
|
if copyFileIfExist("firmware.bin", deviceName) and copyFileIfExist("littlefs.bin", deviceName):
|
||||||
copyFileIfExist("partitions.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);
|
std::vector<String> splitStr(const String& str, const String& delimiter);
|
||||||
|
|
||||||
bool strInVector(const String& str, const std::vector<String>& vec);
|
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.valD = Item->getIntFromNet();
|
||||||
ret.isDecimal = true;
|
ret.isDecimal = true;
|
||||||
return &ret;
|
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);
|
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); // вызываем команду из модуля напрямую с передачей всех аргументов
|
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);
|
// 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_mqttPub,
|
||||||
sysop_getUptime,
|
sysop_getUptime,
|
||||||
sysop_mqttIsConnect,
|
sysop_mqttIsConnect,
|
||||||
sysop_wifiIsConnect
|
sysop_wifiIsConnect,
|
||||||
|
sysop_setInterval
|
||||||
};
|
};
|
||||||
|
|
||||||
IoTValue sysExecute(SysOp command, std::vector<IoTValue> ¶m) {
|
IoTValue sysExecute(SysOp command, std::vector<IoTValue> ¶m) {
|
||||||
@@ -449,7 +464,12 @@ IoTValue sysExecute(SysOp command, std::vector<IoTValue> ¶m) {
|
|||||||
break;
|
break;
|
||||||
case sysop_wifiIsConnect:
|
case sysop_wifiIsConnect:
|
||||||
value.valD = isNetworkActive();
|
value.valD = isNetworkActive();
|
||||||
break;
|
break;
|
||||||
|
case sysop_setInterval:
|
||||||
|
if (param.size() == 1) {
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@@ -508,6 +528,8 @@ class SysCallExprAST : public ExprAST {
|
|||||||
operation = sysop_mqttIsConnect;
|
operation = sysop_mqttIsConnect;
|
||||||
else if (Callee == F("wifiIsConnect"))
|
else if (Callee == F("wifiIsConnect"))
|
||||||
operation = sysop_wifiIsConnect;
|
operation = sysop_wifiIsConnect;
|
||||||
|
else if (Callee == F("setInterval"))
|
||||||
|
operation = sysop_setInterval;
|
||||||
else
|
else
|
||||||
operation = sysop_notfound;
|
operation = sysop_notfound;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,8 @@ class DwinI : public IoTUart {
|
|||||||
void onModuleOrder(String &key, String &value) {
|
void onModuleOrder(String &key, String &value) {
|
||||||
if (key == "uploadUI") {
|
if (key == "uploadUI") {
|
||||||
//SerialPrint("i", F("DwinI"), "Устанавливаем UI: " + value);
|
//SerialPrint("i", F("DwinI"), "Устанавливаем UI: " + value);
|
||||||
|
if (value != "") uartPrintHex(value.c_str());
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ public:
|
|||||||
|
|
||||||
} else if (command == "setEffect") {
|
} else if (command == "setEffect") {
|
||||||
if (param.size() == 1) {
|
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);
|
_effectsMode = random(0, 79);
|
||||||
else
|
else
|
||||||
_effectsMode = param[0].valD;
|
_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()",
|
"descr": "Получаем количество секунд доверия к значениям элемента. При -2 доверие полное, при -1 время доверия истекло. При >0 время обратного отсчета. Используется только совместно с ИД элемента: ID.getIntFromNet()",
|
||||||
"params": []
|
"params": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setInterval",
|
||||||
|
"descr": "Меняем интервал выполнения периодиеских операций элемента в секундах. Используется только совместно с ИД элемента: ID.setInterval(5)",
|
||||||
|
"params": ["Секунды"],
|
||||||
|
"return": "установленный интервал"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "doByInterval",
|
||||||
|
"descr": "Выполняем интервальное действие модуля вне плана. Используется только совместно с ИД элемента: ID.doByInterval()",
|
||||||
|
"params": [],
|
||||||
|
"return": "значение элемента после выполнения doByInterval"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "exit",
|
"name": "exit",
|
||||||
"descr": "Прерываем работу сценария и выводим в консоль причину. Причина не обязательна.",
|
"descr": "Прерываем работу сценария и выводим в консоль причину. Причина не обязательна.",
|
||||||
|
|||||||
@@ -233,4 +233,29 @@ bool strInVector(const String& str, const std::vector<String>& vec) {
|
|||||||
if (vec[i] == str) return true;
|
if (vec[i] == str) return true;
|
||||||
}
|
}
|
||||||
return false;
|
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")
|
pio_home = env.subst("$PROJECT_CORE_DIR")
|
||||||
print("PLATFORMIO_DIR" + pio_home)
|
print("PLATFORMIO_DIR" + pio_home)
|
||||||
|
|
||||||
if platform == "linux" or platform == "linux2":
|
if platform == "linux" or platform == "linux2" or platform == "darwin":
|
||||||
# linux
|
# linux
|
||||||
#mainPyPath = '/home/rise/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFiClient.cpp'
|
#mainPyPath = '/home/rise/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFiClient.cpp'
|
||||||
mainPyPath = pio_home + '/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")
|
pio_home = env.subst("$PROJECT_CORE_DIR")
|
||||||
print("PLATFORMIO_DIR" + pio_home)
|
print("PLATFORMIO_DIR" + pio_home)
|
||||||
|
|
||||||
if platform == "linux" or platform == "linux2":
|
if platform == "linux" or platform == "linux2" or platform == "darwin":
|
||||||
# linux
|
# linux
|
||||||
#devkitm = '/home/rise/.platformio/platforms/espressif32/boards/esp32-c6-devkitm-1.json'
|
#devkitm = '/home/rise/.platformio/platforms/espressif32/boards/esp32-c6-devkitm-1.json'
|
||||||
#devkitc = '/home/rise/.platformio/platforms/espressif32/boards/esp32-c6-devkitc-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")
|
pio_home = env.subst("$PROJECT_CORE_DIR")
|
||||||
print("PLATFORMIO_DIR" + pio_home)
|
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 = '/home/rise/.platformio/platforms/espressif8266@4.0.1/builder/main.py'
|
||||||
mainPyPath = pio_home + '/platforms/espressif8266@4.0.1/builder/main.py'
|
mainPyPath = pio_home + '/platforms/espressif8266@4.0.1/builder/main.py'
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user