From 6a288bb002c0c28f96168885445806eb1a7bbffa Mon Sep 17 00:00:00 2001 From: Ilya Belyakov Date: Wed, 14 Aug 2024 00:00:52 +0500 Subject: [PATCH 01/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=B0=D0=BB=D1=8C=D1=82=D0=B5=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D0=B2=D0=BD=D1=8B=D0=B9=20=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D1=87=D0=BD=D0=B8=D0=BA=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../virtual/UpdateServer/UpdateServer.cpp | 23 +++++++++++ src/modules/virtual/UpdateServer/modinfo.json | 39 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/modules/virtual/UpdateServer/UpdateServer.cpp create mode 100644 src/modules/virtual/UpdateServer/modinfo.json diff --git a/src/modules/virtual/UpdateServer/UpdateServer.cpp b/src/modules/virtual/UpdateServer/UpdateServer.cpp new file mode 100644 index 00000000..b622e1b0 --- /dev/null +++ b/src/modules/virtual/UpdateServer/UpdateServer.cpp @@ -0,0 +1,23 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +class UpdateServer : public IoTItem { + public: + UpdateServer(String parameters) : IoTItem(parameters) {} + + void onModuleOrder(String &key, String &value) { + if (key == "startUpdate") { + upgrade_firmware(3, value); + } + } + + ~UpdateServer() {}; +}; + +void* getAPI_UpdateServer(String subtype, String param) { + if (subtype == F("UpdateServer")) { + return new UpdateServer(param); + } else { + return nullptr; + } +} diff --git a/src/modules/virtual/UpdateServer/modinfo.json b/src/modules/virtual/UpdateServer/modinfo.json new file mode 100644 index 00000000..7249644c --- /dev/null +++ b/src/modules/virtual/UpdateServer/modinfo.json @@ -0,0 +1,39 @@ +{ + "menuSection": "virtual_elments", + "configItem": [ + { + "global": 0, + "name": "Свой сервер обновлений", + "type": "Reading", + "subtype": "UpdateServer", + "id": "UpdateServer", + "widget": "", + "page": "", + "descr": "", + "btn-startUpdate": "http://192.168.11.112/iotm" + } + ], + "about": { + "authorName": "Ilya Belyakov", + "authorContact": "https://t.me/Biveraxe", + "authorGit": "https://github.com/biveraxe", + "exampleURL": "https://iotmanager.org/", + "specialThanks": "", + "moduleName": "UpdateServer", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "Свой сервер обновлений", + "moduleDesc": "Модуль для получения прошивки из своего сервера обновлений.", + "propInfo": { + "btn-startUpdate": "Кнопка запуска процесса обновления из указанного URL" + } + }, + "defActive": true, + "usedLibs": { + "esp32*": [], + "esp82*": [] + } +} \ No newline at end of file From d3d01f1ae6ef0817fc59aa9c2100f2c6782144c8 Mon Sep 17 00:00:00 2001 From: Ilya Belyakov Date: Wed, 14 Aug 2024 00:33:21 +0500 Subject: [PATCH 02/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=D1=8B=20=D1=87=D0=B0=D1=81=D1=82=D0=B8=D1=87=D0=BD=D0=BE=D0=B9?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D1=88=D0=B8=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/virtual/UpdateServer/UpdateServer.cpp | 6 +++++- src/modules/virtual/UpdateServer/modinfo.json | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/virtual/UpdateServer/UpdateServer.cpp b/src/modules/virtual/UpdateServer/UpdateServer.cpp index b622e1b0..b9f6b75a 100644 --- a/src/modules/virtual/UpdateServer/UpdateServer.cpp +++ b/src/modules/virtual/UpdateServer/UpdateServer.cpp @@ -6,8 +6,12 @@ class UpdateServer : public IoTItem { UpdateServer(String parameters) : IoTItem(parameters) {} void onModuleOrder(String &key, String &value) { - if (key == "startUpdate") { + if (key == "startUpdateAll") { upgrade_firmware(3, value); + } else if (key == "startUpdateFS") { + upgrade_firmware(2, value); + } else if (key == "startUpdateFW") { + upgrade_firmware(1, value); } } diff --git a/src/modules/virtual/UpdateServer/modinfo.json b/src/modules/virtual/UpdateServer/modinfo.json index 7249644c..c4b8fc70 100644 --- a/src/modules/virtual/UpdateServer/modinfo.json +++ b/src/modules/virtual/UpdateServer/modinfo.json @@ -10,7 +10,9 @@ "widget": "", "page": "", "descr": "", - "btn-startUpdate": "http://192.168.11.112/iotm" + "btn-startUpdateAll": "http://192.168.11.112/iotm/esp8266_4mb/400", + "btn-startUpdateFS": "http://192.168.11.112/iotm/esp8266_4mb/400", + "btn-startUpdateFW": "http://192.168.11.112/iotm/esp8266_4mb/400" } ], "about": { From 1ead6a7e025bff52013d3c337edf632b921e5e68 Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 13 May 2025 22:10:24 +0300 Subject: [PATCH 03/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8E?= =?UTF-8?q?=20hexstr=20=D0=B2=20uint32=5Ft?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/StringUtils.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/utils/StringUtils.cpp b/src/utils/StringUtils.cpp index bfb0ec1d..d6c8c3f7 100644 --- a/src/utils/StringUtils.cpp +++ b/src/utils/StringUtils.cpp @@ -103,6 +103,7 @@ uint8_t hexStringToUint8(const String& hex) { if (tmp >= 0x00 && tmp <= 0xFF) { return tmp; } + return 0; } uint16_t hexStringToUint16(const String& hex) { @@ -110,6 +111,15 @@ uint16_t hexStringToUint16(const String& hex) { if (tmp >= 0x0000 && tmp <= 0xFFFF) { return tmp; } + return 0; +} + +uint32_t hexStringToUint32(const String& hex) { + uint32_t tmp = strtol(hex.c_str(), NULL, 0); + if (tmp >= 0x0000 && tmp <= 0xFFFFFF) { + return tmp; + } + return 0; } size_t itemsCount2(String str, const String& separator) { From 9b54a179e5d8afa90443548173c07c846ca4e722 Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 13 May 2025 22:11:54 +0300 Subject: [PATCH 04/18] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D0=BC=20=D0=BE=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=BA=20=D0=B2=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=20AhtXX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/sensors/AhtXX/AhtXX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sensors/AhtXX/AhtXX.cpp b/src/modules/sensors/AhtXX/AhtXX.cpp index a49e8fe1..7bac81c1 100644 --- a/src/modules/sensors/AhtXX/AhtXX.cpp +++ b/src/modules/sensors/AhtXX/AhtXX.cpp @@ -88,7 +88,7 @@ void* getAPI_AhtXX(String subtype, String param) { if (ahts.find(addr) == ahts.end()) { int shtType; - jsonRead(param, "type", shtType); + jsonRead(param, "shtType", shtType); ahts[addr] = new AHTxx(hexStringToUint8(addr), (AHTXX_I2C_SENSOR)shtType); From e9cc823ebfe4f97692791d0d8bf6a4c898ea29fd Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 13 May 2025 22:13:18 +0300 Subject: [PATCH 05/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20?= =?UTF-8?q?=D1=83=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?led=20=D0=BB=D0=B5=D0=BD=D1=82=D0=BE=D0=B9=20LedFX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/display/LedFX/LedFX.cpp | 332 ++++++++++++++++++ src/modules/display/LedFX/modinfo.json | 163 +++++++++ .../display/LedFX/Пример на 7 диодов.json | 125 +++++++ 3 files changed, 620 insertions(+) create mode 100644 src/modules/display/LedFX/LedFX.cpp create mode 100644 src/modules/display/LedFX/modinfo.json create mode 100644 src/modules/display/LedFX/Пример на 7 диодов.json diff --git a/src/modules/display/LedFX/LedFX.cpp b/src/modules/display/LedFX/LedFX.cpp new file mode 100644 index 00000000..8515d389 --- /dev/null +++ b/src/modules/display/LedFX/LedFX.cpp @@ -0,0 +1,332 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include "ESPConfiguration.h" +#include + + + +WS2812FX *_glob_strip = nullptr; // глобальный указатель на WS2812FX для использования в функциях для кастомных эффектов +std::vector vuMeterBands; // массив указателей на элементы IoTValue, которые будут использоваться для передачи данных в эффект VU Meter + +uint16_t vuMeter2(void) { +// функция взята из WS2812FX.cpp для демонстрации возможности создания своего алгоритма эффекта +// в данном случае - VU Meter имеет тот же смысл отображения уровня сигнала, что и в WS2812FX с сохранением алгоритма вывода данных из нескольких источников +// но вместо использования внешнего источника данных для встроенного эффекта, мы используем данные из других элементов конфигурации IoTM +// Если данные не поступают (IoTValue.valS == "1"), то используется генерация случайных чисел для демонстрации работы эффекта + + if (_glob_strip == nullptr) return 0; // Проверяем, инициализирована ли библиотека WS2812FX + + uint16_t numBands = vuMeterBands.size(); // Получаем количество полос VU Meter из массива указателей + if (numBands == 0) return 0; // Если нет полос, выходим из функции + + WS2812FX::Segment* seg = _glob_strip->getSegment(); + uint16_t seglen = seg->stop - seg->start + 1; + uint16_t bandSize = seglen / numBands; + + if (vuMeterBands[0]->valS == "R") + for (uint8_t i=0; i < numBands; i++) { + int randomData = vuMeterBands[i]->valD + _glob_strip->random8(32) - _glob_strip->random8(32); + vuMeterBands[i]->valD = (randomData < 0 || randomData > 255) ? 128 : randomData; + } + + for(uint8_t i=0; ivalD)); + uint8_t scaledBand = (vuMeterBands[i]->valD * bandSize) / 256; + for(uint16_t j=0; jstart + (i * bandSize) + j; + if(j <= scaledBand) { + if(j < bandSize - 4) _glob_strip->setPixelColor(index, GREEN); + else if(j < bandSize - 2) _glob_strip->setPixelColor(index, YELLOW); + else _glob_strip->setPixelColor(index, RED); + } else { + _glob_strip->setPixelColor(index, BLACK); + } + } + } + _glob_strip->setCycle(); + + return seg->speed; +} + + +class LedFX : public IoTItem +{ +private: + WS2812FX *_strip; + + int _data_pin = 2; + int _numLeds = 1; + int _brightness = 100; + int _speed = 15000; + int _effectsMode = 0; + int _valueMode = 0; + int _color = 0xFF0000; // default color red + + uint8_t _BrightnessFadeOutStep = 0; + uint8_t _BrightnessFadeOutMin = 1; + uint8_t _BrightnessFadeInStep = 0; + uint8_t _BrightnessFadeInMax = 50; + +public: + LedFX(String parameters) : IoTItem(parameters) { + jsonRead(parameters, F("data_pin"), _data_pin); + jsonRead(parameters, F("speed"), _speed); + jsonRead(parameters, F("numLeds"), _numLeds); + jsonRead(parameters, F("brightness"), _brightness); + + String tmpStr; + jsonRead(parameters, F("color"), tmpStr); + _color = hexStringToUint32(tmpStr); + + jsonRead(parameters, F("effectsMode"), _effectsMode); + jsonRead(parameters, F("valueMode"), _valueMode); + + + //_strip = new WS2812FX(_numLeds, _data_pin, NEO_BRG + NEO_KHZ400); // SM16703 + _strip = new WS2812FX(_numLeds, _data_pin, NEO_GRB + NEO_KHZ800); // WS2812B + + if (_strip != nullptr) { + _glob_strip = _strip; // Сохраняем указатель в глобальной переменной + + _strip->init(); + _strip->setBrightness(_brightness); + _strip->setSpeed(_speed); + _strip->setMode(_effectsMode); + _strip->setColor(_color); + if (_effectsMode >= 0) _strip->start(); + } + } + + void fadeOutNonBlocking() { + + } + + void loop() { + if (!_strip) return; + + static unsigned long lastUpdate = 0; // Время последнего обновления + unsigned long now = millis(); + if (now - lastUpdate >= 70) { // Проверяем, прошло ли достаточно времени + lastUpdate = now; + + if (_BrightnessFadeOutStep > 0) { + int currentBrightness = _strip->getBrightness(); // Получаем текущую яркость + currentBrightness -= _BrightnessFadeOutStep; + if (currentBrightness < _BrightnessFadeOutMin) { + currentBrightness = _BrightnessFadeOutMin; // Убедимся, что яркость не уйдет в отрицательные значения + _BrightnessFadeOutStep = 0; // Останавливаем затухание + } + _strip->setBrightness(currentBrightness); + _strip->show(); + } + + if (_BrightnessFadeInStep > 0) { + int currentBrightness = _strip->getBrightness(); // Получаем текущую яркость + currentBrightness += _BrightnessFadeInStep; + if (currentBrightness > _BrightnessFadeInMax) { + currentBrightness = _BrightnessFadeInMax; // Убедимся, что яркость не уйдет за пределы + _BrightnessFadeInStep = 0; // Останавливаем затухание + } + _strip->setBrightness(currentBrightness); + _strip->show(); + } + } + + _strip->service(); + IoTItem::loop(); + } + + void doByInterval() { + + } + + IoTValue execute(String command, std::vector ¶m) { + if (!_strip) return {}; + if (command == "fadeOut") { + if (param.size() == 2) { + _BrightnessFadeOutMin = param[0].valD; + _BrightnessFadeOutStep = param[1].valD; + SerialPrint("E", "Strip LedFX", "BrightnessFadeOut"); + } + + } else if (command == "fadeIn") { + if (param.size() == 2) { + _BrightnessFadeInMax = param[0].valD; + _BrightnessFadeInStep = param[1].valD; + SerialPrint("E", "Strip LedFX", "BrightnessFadeIn"); + } + + } else if (command == "setColor") { + if (param.size() == 1) { + _color = hexStringToUint32(param[0].valS); + _strip->setColor(_color); + _strip->show(); + SerialPrint("E", "Strip LedFX", "setColor:" + param[0].valS); + } + + } else if (command == "setEffect") { + if (param.size() == 1) { + if (param[0].valD < 0 | param[0].valD > 79) + _effectsMode = random(0, 79); + else + _effectsMode = param[0].valD; + _strip->setMode(_effectsMode); + _strip->show(); + _strip->start(); + SerialPrint("E", "Strip LedFX", "setEffect:" + param[0].valS); + } + + } else if (command == "setSpeed") { + if (param.size() == 1) { + _speed = param[0].valD; + _strip->setSpeed(_speed); + _strip->show(); + SerialPrint("E", "Strip LedFX", "setSpeed:" + param[0].valS); + } + + } else if (command == "setBrightness") { + if (param.size() == 1) { + _brightness = param[0].valD; + _strip->setBrightness(_brightness); + _strip->show(); + SerialPrint("E", "Strip LedFX", "setBrightness:" + param[0].valS); + } + + } else if (command == "stop") { + _strip->stop(); + SerialPrint("E", "Strip LedFX", "stop"); + + } else if (command == "start") { + _strip->start(); + SerialPrint("E", "Strip LedFX", "start"); + + } else if (command == "pause") { + _strip->pause(); + SerialPrint("E", "Strip LedFX", "pause"); + + } else if (command == "resume") { + _strip->resume(); + SerialPrint("E", "Strip LedFX", "resume"); + + } else if (command == "setSegment") { + if (param.size() == 6) { + _strip->setSegment(param[0].valD, param[1].valD, param[2].valD, param[3].valD, hexStringToUint32(param[4].valS), param[5].valD); + _strip->show(); + _strip->start(); + SerialPrint("E", "Strip LedFX", "setSegment:" + param[0].valS + " start:" + param[1].valS + " stop:" + param[2].valS + " mode:" + param[3].valS, " color:" + param[4].valS + " speed:" + param[5].valS); + } + + } else if(command == "noShowOne"){ + if (param.size() == 1) { + _strip->setPixelColor(param[0].valD, _strip->Color(0, 0, 0)); + _strip->show(); + SerialPrint("E", "Strip LedFX", "noShowOne"); + } + + } else if (command == "showLed"){ + if (param.size() == 2) { + uint32_t color = hexStringToUint32(param[1].valS); + _strip->setPixelColor(param[0].valD, color); + _strip->show(); + _strip->start(); + SerialPrint("E", "Strip LedFX", "showLed:" + param[0].valS + " color:" + param[1].valS); + } + + } else if (command == "vuMeter") { + if (param.size() == 2) { + int bandCnt = param[0].valD; + if (param[1].valS == "") { + for (int i=0; i < vuMeterBands.size(); i++) { + delete vuMeterBands[i]; + } + vuMeterBands.clear(); + + for (uint8_t i=0; i < bandCnt; i++) { + IoTValue *band = new IoTValue(); // создаем новый элемент IoTValue для полос VU Meter + band->valD = 0; + band->valS = "R"; + vuMeterBands.push_back(band); // добавляем указатель в массив + } + } else { + // Очищаем массив vuMeterBands перед заполнением + vuMeterBands.clear(); + + String id; + String idsStr = param[1].valS; + // Разделяем строку idsStr на идентификаторы, используя запятую как разделитель + while (idsStr.length() > 0) { + // Извлекаем идентификатор до первой запятой + id = selectToMarker(idsStr, ","); + + // Ищем элемент IoTItem по идентификатору + IoTItem* item = findIoTItem(id); + if (item != nullptr) { + // Добавляем указатель на поле value найденного элемента в vuMeterBands + vuMeterBands.push_back(&(item->value)); + SerialPrint("E", "LedFX", "Добавлен элемент в vuMeterBands: " + id); + } else { + SerialPrint("E", "LedFX", "Элемент не найден: " + id); + } + + int8_t oldSize = idsStr.length(); + // Удаляем обработанный идентификатор из строки + idsStr = deleteBeforeDelimiter(idsStr, ","); + if (idsStr.length() == oldSize) { + // Если длина строки не изменилась, значит, больше нет запятых и это был последний идентификатор + break; + } + } + + } + _strip->setCustomMode(vuMeter2); + _strip->setMode(FX_MODE_CUSTOM); + _strip->start(); + SerialPrint("E", "Strip LedFX", "vuMeter bands:" + param[0].valS + " IDs to show:" + param[1].valS); + } + } + + return {}; + } + + void setValue(const IoTValue& Value, bool genEvent = true) { + if (!_strip) return; + + if (_valueMode == 0) { + _strip->setMode(Value.valD); + _effectsMode = Value.valD; + } else if (_valueMode == 1) { + _strip->setBrightness(Value.valD); + _brightness = Value.valD; + } else if (_valueMode == 2) { + _color = hexStringToUint32(Value.valS); + _strip->setColor(_color); + } else if (_valueMode == 3) { + _strip->setSpeed(Value.valD); + _speed = Value.valD; + } + + value = Value; + regEvent(value.valD, "LedFX", false, genEvent); + } + + ~LedFX() { + if (_strip != nullptr) { + delete _strip; + _strip = nullptr; + _glob_strip = nullptr; // Обнуляем глобальный указатель + } + }; +}; + +void *getAPI_LedFX(String subtype, String param) +{ + if (subtype == F("LedFX")) { + return new LedFX(param); + } else { + return nullptr; + } +} + + + + diff --git a/src/modules/display/LedFX/modinfo.json b/src/modules/display/LedFX/modinfo.json new file mode 100644 index 00000000..7b09f028 --- /dev/null +++ b/src/modules/display/LedFX/modinfo.json @@ -0,0 +1,163 @@ +{ + "menuSection": "screens", + "configItem": [ + { + "global": 0, + "name": "LedFX", + "type": "Reading", + "subtype": "LedFX", + "id": "fl", + "widget": "inputTxt", + "page": "Кнопки", + "descr": "Лента", + "int": 15, + "needSave": 0, + + "data_pin": "2", + "numLeds": "3", + "brightness": "50", + "speed": "3000", + "color": "0xFF0000", + "effectsMode": 0, + "valueMode": 0 + } + ], + "about": { + "authorName": "Ilya Belyakov", + "authorContact": "https://t.me/Biveraxe", + "authorGit": "https://github.com/biveraxe", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "Yuriy Kuneev (https://t.me/Kuneev07)", + "moduleName": "LedFX", + "moduleVersion": "1.0.1", + "moduleDesc": "Позволяет управлять адресными светодиодными лентами WS2812B и аналогичными.", + "propInfo": { + "int": "Период времени в секундах обновления.", + "data_pin": "Пин к которому подключена лента.", + "speed": "Скорость обновления ленты.", + "numLeds": "Количество пикселей в ленте.", + "needSave": "Запись значения элемента в энергонезависимую память", + "brightness": "Яркость ленты можно менять из сценария.", + "color": "Цвет ленты в формате 0xRRGGBB, например, 0xFF0000 - красный, 0x00FF00 - зеленый, 0x0000FF - синий.", + "effectsMode": "Режим эффектов ленты. 0-79. 0 - Статичный цвет.", + "valueMode": "Режим применения значения элемета. 0 - установка режима эффектов, 1 - регулирование яркости, 2 - изменение цвета, 3 - изменение скорости." + }, + "title": "Адресная светодиодная лента", + "funcInfo": [ + { + "name": "noShowOne", + "descr": "Выключить один светодиод на ленте", + "params": [ + "номер пикселя" + ] + }, + { + "name": "showLed", + "descr": "Зажечь один диод", + "params": [ + "номер пикселя", + "цвет в формате 0xRRGGBB" + ] + }, + { + "name": "setBrightness", + "descr": "Устанавливает общую яркость ленты от 0 до 255", + "params": [ + "яркость от 0 до 255" + ] + }, + { + "name": "vuMeter", + "descr": "Включает режим VU Meter. Важно что бы элемент ленты был ниже в списке чем элемент с датчиком, ИД которого нужно будет мониторить.", + "params": [ + "Количество каналов для отображения на ленте", + "Список ID датчиков через запятую (если указать пустую строку, то каналы будут заполняться случайными числами от 0 до 255)" + ] + }, + { + "name": "setColor", + "descr": "Устанавливает цвет ленты в формате 0xRRGGBB, например, 0xFF0000 - красный, 0x00FF00 - зеленый, 0x0000FF - синий.", + "params": [ + "цвет в формате 0xRRGGBB" + ] + }, + { + "name": "setEffect", + "descr": "Устанавливает эффект ленты. 0-79. 0 - Статичный цвет.", + "params": [ + "номер эффекта от 0 до 79" + ] + }, + { + "name": "setSpeed", + "descr": "Устанавливает скорость эффекта от 0 до 255", + "params": [ + "скорость от 0 до 255" + ] + }, + { + "name": "fadeOut", + "descr": "Плавное затухание яркости", + "params": [ + "Целевое значение яркости, до которого будет затухать", + "Шаг затухания" + ] + }, + { + "name": "fadeIn", + "descr": "Плавное нарастание яркости", + "params": [ + "Целевое значение яркости, до которого будет нарастать", + "Шаг нарастания" + ] + }, + { + "name": "stop", + "descr": "Останавливает эффект", + "params": [] + + }, + { + "name": "start", + "descr": "Запускает эффект", + "params": [] + + }, + { + "name": "pause", + "descr": "Пауза эффекта", + "params": [] + + }, + { + "name": "resume", + "descr": "Возобновляет эффект", + "params": [] + + }, + { + "name": "setSegment", + "descr": "Устанавливает сегмент ленты", + "params": [ + "номер сегмента от 0 до 7", + "глобальный номер первого пикселя в сегменте", + "глобальный номер последнего пикселя в сегменте", + "номер эффекта от 0 до 79", + "цвет в формате 0xRRGGBB, например, 0xFF0000 - красный, 0x00FF00 - зеленый, 0x0000FF - синий.", + "скорость" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32*": [ + "aadafruit/Adafruit NeoPixel @ ^1.12.5", + "kitesurfer1404/WS2812FX @ ^1.4.5" + ], + "esp82*": [ + "adafruit/Adafruit NeoPixel @ ^1.12.5", + "kitesurfer1404/WS2812FX @ ^1.4.5" + ] + } +} \ No newline at end of file diff --git a/src/modules/display/LedFX/Пример на 7 диодов.json b/src/modules/display/LedFX/Пример на 7 диодов.json new file mode 100644 index 00000000..c034bb0c --- /dev/null +++ b/src/modules/display/LedFX/Пример на 7 диодов.json @@ -0,0 +1,125 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Reading", + "subtype": "AnalogAdc", + "id": "t1", + "widget": "anydataRed", + "page": "Сенсоры", + "descr": "Аналог", + "map": "1,1024,1,255", + "plus": 0, + "multiply": 1, + "round": 1, + "pin": 0, + "int": "1", + "avgSteps": 1 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "b1", + "needSave": 0, + "widget": "rangeServo", + "page": "Сенсоры", + "descr": "Бар 1", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,255", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "b2", + "needSave": 0, + "widget": "rangeServo", + "page": "Сенсоры", + "descr": "Бар 2", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,255", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "LedFX", + "id": "fl20", + "widget": "inputTxt", + "page": "Кнопки", + "descr": "Лента", + "int": 15, + "needSave": 0, + "data_pin": "2", + "numLeds": "7", + "brightness": "50", + "speed": "100", + "color": "0xFF0000", + "effectsMode": "11", + "valueMode": 0, + "show": false + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "pause", + "needSave": 0, + "widget": "toggle", + "page": "Кнопки", + "descr": "Пауза", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "bars", + "needSave": 0, + "widget": "toggle", + "page": "Кнопки", + "descr": "Бары", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "any", + "needSave": 0, + "widget": "toggle", + "page": "Кнопки", + "descr": "Анимация", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "fade", + "needSave": 0, + "widget": "toggle", + "page": "Кнопки", + "descr": "Скрыть", + "int": "0", + "val": "0" + } + ] +} + +scenario=>if bars then fl20.vuMeter(7, "t1") else fl20.stop() +if any then fl20.setEffect(100) else fl20.stop() +if pause then fl20.pause() else fl20.resume() +if fade then fl20.fadeOut(1, 3) else fl20.fadeIn(60, 3) \ No newline at end of file From d30408c3953ae190acca4cf68582c38e2e47e8b5 Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 13 May 2025 22:14:41 +0300 Subject: [PATCH 06/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=20=D0=B4=D0=BB=D1=8F=20=D1=84=D1=83=D0=BD=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20hexStringToUint32?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/utils/StringUtils.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/utils/StringUtils.h b/include/utils/StringUtils.h index 9fac0aa5..6d3afa15 100644 --- a/include/utils/StringUtils.h +++ b/include/utils/StringUtils.h @@ -12,6 +12,8 @@ uint8_t hexStringToUint8(const String& hex); uint16_t hexStringToUint16(const String& hex); +uint32_t hexStringToUint32(const String& hex); + String selectToMarkerLast(String str, const String& found); String selectToMarker(String str, const String& found); From d417f96248f442dddb752e743aa10da6cd8d9d58 Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 13 May 2025 22:16:21 +0300 Subject: [PATCH 07/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BF=D0=B8=D0=BB=D1=8F=D1=86=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B8=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=88=D0=B8=D0=B2=D0=BA=D0=B8=20=D0=94=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=83=D0=BF=D1=80=D0=BE=D1=89=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B2=D1=8B=D1=85=20=D1=88=D0=B0=D0=B3?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B8=20=D0=BE=D1=82=D0=BA=D0=B0=D0=B7=D0=B0=20?= =?UTF-8?q?=D0=BE=D1=82=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9?= =?UTF-8?q?=D1=81=D0=B0=20PlatformIO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- run.py | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 run.py diff --git a/run.py b/run.py new file mode 100644 index 00000000..95808a19 --- /dev/null +++ b/run.py @@ -0,0 +1,109 @@ +# Скрипт для простой прошивки ESP с учетом профиля из myProfile.json и поиска нужной кнопочки в интерфейсе PlatformIO +# Если ничего не указано в параметрах, то выполняется последовательно набор команд: +# 1. run PrepareProject.py +# 2. platformio run -t clean +# 3. platformio run -t uploadfs -е default_envs +# 4. platformio run -t upload -е default_envs +# 5. platformio run -t monitor +# где default_envs - это параметр default_envs из myProfile.json +# +# Если указан параметр -p или --profile <ИмяФайла>, то выполняется первая команда PrepareProject.py -p <ИмяФайла> +# Если указан парамтер -l или --lite, то пропускаются команды 1, 2 и 5 с предварительной компиляцией +# Если указан параметр -d или --debug, то выполняется только команда 4 с предварительной компиляцией + +import os +import subprocess +import sys +import json + +def get_platformio_path(): + """ + Возвращает путь к PlatformIO в зависимости от операционной системы. + """ + if os.name == 'nt': # Windows + return os.path.join(os.environ['USERPROFILE'], '.platformio', 'penv', 'Scripts', 'pio.exe') + else: # Linux/MacOS + return os.path.join(os.environ['HOME'], '.platformio', 'penv', 'bin', 'pio') + +def load_default_envs(profile_path="myProfile.json"): + """ + Загружает значение default_envs из файла myProfile.json. + """ + if not os.path.isfile(profile_path): + print(f"Файл профиля {profile_path} не найден.") + sys.exit(1) + + try: + with open(profile_path, 'r', encoding='utf-8') as file: + profile_data = json.load(file) + return profile_data["projectProp"]["platformio"]["default_envs"] + except KeyError: + print("Не удалось найти ключ 'default_envs' в myProfile.json.") + sys.exit(1) + except json.JSONDecodeError: + print(f"Ошибка при чтении файла {profile_path}: некорректный JSON.") + sys.exit(1) + +def run_command(command): + """ + Выполняет указанную команду в subprocess. + """ + try: + print(f"Выполнение команды: {' '.join(command)}") + subprocess.run(command, check=True) + except subprocess.CalledProcessError as e: + print(f"Ошибка при выполнении команды: {e}") + sys.exit(e.returncode) + +def run_platformio(): + """ + Основная логика выполнения команд в зависимости от параметров. + """ + pio_path = get_platformio_path() + + # Проверяем, существует ли PlatformIO + if not os.path.isfile(pio_path): + print(f"PlatformIO не найден по пути: {pio_path}") + sys.exit(1) + # print(f"PlatformIO найден по пути: {pio_path}") + + # Читаем аргументы командной строки + args = sys.argv[1:] + lite_mode = '-l' in args or '--lite' in args + debug_mode = '-d' in args or '--debug' in args + profile_index = next((i for i, arg in enumerate(args) if arg in ('-p', '--profile')), None) + profile_file = args[profile_index + 1] if profile_index is not None and len(args) > profile_index + 1 else "myProfile.json" + + # Загружаем default_envs из myProfile.json + default_envs = load_default_envs(profile_path=profile_file) + + print(f"Используем default_envs: {default_envs}") + print(f"Режим Lite: {lite_mode}, Режим отладки: {debug_mode}") + print(f"Профиль: {profile_file}") + + # Выполнение команд в зависимости от параметров + if not lite_mode and not debug_mode: + # Полный набор команд + run_command(['python', 'PrepareProject.py', '-p', profile_file]) + + # Добавляем сообщение о необходимости дождаться завершения обновления конфигурации + input(f"\x1b[1;31;42m Подождите завершения обновления конфигурации PlatformIO, затем нажмите Ввод для продолжения...\x1b[0m") + + run_command([pio_path, 'run', '-t', 'clean']) + run_command([pio_path, 'run', '-t', 'uploadfs', '--environment', default_envs]) + run_command([pio_path, 'run', '-t', 'upload', '--environment', default_envs]) + run_command([pio_path, 'run', '-t', 'monitor']) + elif lite_mode: + # Упрощенный режим (пропускаем команды 1, 2 и 5) + run_command([pio_path, 'run', '-t', 'buildfs', '--environment', default_envs]) + run_command([pio_path, 'run', '-t', 'uploadfs', '--environment', default_envs]) + run_command([pio_path, 'run', '--environment', default_envs]) + run_command([pio_path, 'run', '-t', 'upload', '--environment', default_envs]) + elif debug_mode: + # Режим отладки (только команда 4) + run_command([pio_path, 'run', '--environment', default_envs]) + run_command([pio_path, 'run', '-t', 'upload', '--environment', default_envs]) + +if __name__ == "__main__": + run_platformio() + From 060373ec31b07bc507cef2a6672354c527f78fb2 Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 15 May 2025 07:59:54 +0300 Subject: [PATCH 08/18] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D0=BC=20=D0=BE=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=BA=D1=83=20=D0=B2=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B8=D0=B8=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/display/LedFX/modinfo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/display/LedFX/modinfo.json b/src/modules/display/LedFX/modinfo.json index 7b09f028..5c0ad13b 100644 --- a/src/modules/display/LedFX/modinfo.json +++ b/src/modules/display/LedFX/modinfo.json @@ -152,7 +152,7 @@ "defActive": false, "usedLibs": { "esp32*": [ - "aadafruit/Adafruit NeoPixel @ ^1.12.5", + "adafruit/Adafruit NeoPixel @ ^1.12.5", "kitesurfer1404/WS2812FX @ ^1.4.5" ], "esp82*": [ From fbdcb6ee42646716f997cafcda56f79491cfd714 Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 15 May 2025 08:00:44 +0300 Subject: [PATCH 09/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D1=80=20board=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PrepareProject.py | 2 ++ run.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/PrepareProject.py b/PrepareProject.py index 7e8a495d..24bcdc5a 100644 --- a/PrepareProject.py +++ b/PrepareProject.py @@ -13,6 +13,8 @@ # python PrepareProject.py --profile <ИмяФайла> # python PrepareProject.py -p <ИмяФайла> # +# Используя параметры -b или --board можно уточнить для какой платы нужно подготовить проект +# # поддерживаемые контроллеры (профили): # esp8266_4mb # esp8266_16mb diff --git a/run.py b/run.py index 95808a19..44edf8f5 100644 --- a/run.py +++ b/run.py @@ -8,6 +8,7 @@ # где default_envs - это параметр default_envs из myProfile.json # # Если указан параметр -p или --profile <ИмяФайла>, то выполняется первая команда PrepareProject.py -p <ИмяФайла> +# Если указан параметр -b или --board , то выполняется первая команда PrepareProject.py -b # Если указан парамтер -l или --lite, то пропускаются команды 1, 2 и 5 с предварительной компиляцией # Если указан параметр -d или --debug, то выполняется только команда 4 с предварительной компиляцией @@ -74,8 +75,9 @@ def run_platformio(): profile_index = next((i for i, arg in enumerate(args) if arg in ('-p', '--profile')), None) profile_file = args[profile_index + 1] if profile_index is not None and len(args) > profile_index + 1 else "myProfile.json" - # Загружаем default_envs из myProfile.json - default_envs = load_default_envs(profile_path=profile_file) + # Загружаем default_envs из myProfile.json, если не указан параметр -b, который имеет больший приоритет + board_index = next((i for i, arg in enumerate(args) if arg in ('-b', '--board')), None) + default_envs = args[board_index + 1] if board_index is not None and len(args) > board_index + 1 else load_default_envs(profile_path=profile_file) print(f"Используем default_envs: {default_envs}") print(f"Режим Lite: {lite_mode}, Режим отладки: {debug_mode}") From b0d1ec946555cc00e03a99d77a477288cb35edcd Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 15 May 2025 12:31:22 +0300 Subject: [PATCH 10/18] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D0=BC=20=D0=BE=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=BA=D1=83=20=D0=B2=20=D1=83=D1=81=D0=BB=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D0=B8=20LrdFX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/display/LedFX/LedFX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/display/LedFX/LedFX.cpp b/src/modules/display/LedFX/LedFX.cpp index 8515d389..b692c811 100644 --- a/src/modules/display/LedFX/LedFX.cpp +++ b/src/modules/display/LedFX/LedFX.cpp @@ -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; From bdb8c29d8f2b1da0529869ec77ae99e4738b665e Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 15 May 2025 12:35:32 +0300 Subject: [PATCH 11/18] =?UTF-8?q?=D0=94=D0=BE=D0=BF=D0=B8=D1=81=D1=8B?= =?UTF-8?q?=D0=B2=D0=B0=D0=B5=D0=BC=20onModuleOrder=20=D0=B2=20DwinI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/display/DwinI/DwinI.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/display/DwinI/DwinI.cpp b/src/modules/display/DwinI/DwinI.cpp index 3df8fe8e..ccaa7c9f 100644 --- a/src/modules/display/DwinI/DwinI.cpp +++ b/src/modules/display/DwinI/DwinI.cpp @@ -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()); + } } From 892e6d32abaecbeeb5c6d80c8698e5ce2544b3b5 Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 21 May 2025 09:35:51 +0300 Subject: [PATCH 12/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=BD=D0=BE=D0=B2=D1=83=D1=8E=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8E=20=D0=B2=20=D1=81=D1=86?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=D1=80=D0=B8=D0=B8=20doByInterval=20=D0=92?= =?UTF-8?q?=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D1=8F=D0=B5=D0=BC=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D0=B2=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=B5=20?= =?UTF-8?q?=D0=B4=D0=B5=D0=B9=D1=81=D1=82=D0=B2=D0=B8=D0=B5=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D1=83=D0=BB=D1=8F=20=D0=B2=D0=BD=D0=B5=20=D0=BF=D0=BB?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0.=20=D0=98=D1=81=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D1=83=D0=B5=D1=82=D1=81=D1=8F=20=D1=82=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=BA=D0=BE=20=D1=81=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BD?= =?UTF-8?q?=D0=BE=20=D1=81=20=D0=98=D0=94=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0:=20ID.doByInterval()=20=D0=92=D0=BE=D0=B7?= =?UTF-8?q?=D0=B2=D1=80=D0=B0=D1=82=20-=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=B2=D1=8B=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20doByInterval?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/classes/IoTScenario.cpp | 7 +++++++ src/modules/sceninfo.json | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/src/classes/IoTScenario.cpp b/src/classes/IoTScenario.cpp index 754d12c5..7245b2f8 100644 --- a/src/classes/IoTScenario.cpp +++ b/src/classes/IoTScenario.cpp @@ -293,6 +293,13 @@ class CallExprAST : public ExprAST { return &ret; } + // вызываем системную функцию периодического выполнения вне таймера + if (Cmd == F("doByInterval")) { + Item->doByInterval(); + ret = Item->value; + return &ret; + } + // если все же все ок, то готовим параметры для передачи в модуль std::vector ArgsAsIoTValue; for (unsigned int i = 0; i < Args.size(); i++) { diff --git a/src/modules/sceninfo.json b/src/modules/sceninfo.json index 53265548..b7d0be54 100644 --- a/src/modules/sceninfo.json +++ b/src/modules/sceninfo.json @@ -14,6 +14,12 @@ "descr": "Получаем количество секунд доверия к значениям элемента. При -2 доверие полное, при -1 время доверия истекло. При >0 время обратного отсчета. Используется только совместно с ИД элемента: ID.getIntFromNet()", "params": [] }, + { + "name": "doByInterval", + "descr": "Выполняем интервальное действие модуля вне плана. Используется только совместно с ИД элемента: ID.doByInterval()", + "params": [], + "return": "значение элемента после выполнения doByInterval" + }, { "name": "exit", "descr": "Прерываем работу сценария и выводим в консоль причину. Причина не обязательна.", From d11f69b08d996a9dec849e39d2f1b38ee6ed54c6 Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 22 May 2025 14:08:28 +0300 Subject: [PATCH 13/18] =?UTF-8?q?=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D1=83=D0=B5=D0=BC=20=D1=81=D0=BA=D1=80=D0=B8?= =?UTF-8?q?=D0=BF=D1=82=D1=8B=20=D1=81=20=D1=83=D1=87=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=BC=20MACOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PrepareServer.py | 4 ++-- tools/patch32_ws.py | 2 +- tools/patch32c6.py | 2 +- tools/patch8266_16m.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/PrepareServer.py b/PrepareServer.py index 7289a2f5..d0750773 100644 --- a/PrepareServer.py +++ b/PrepareServer.py @@ -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) diff --git a/tools/patch32_ws.py b/tools/patch32_ws.py index 33214618..c0914f99 100644 --- a/tools/patch32_ws.py +++ b/tools/patch32_ws.py @@ -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' diff --git a/tools/patch32c6.py b/tools/patch32c6.py index 5933dc68..e1d569e7 100644 --- a/tools/patch32c6.py +++ b/tools/patch32c6.py @@ -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' diff --git a/tools/patch8266_16m.py b/tools/patch8266_16m.py index b65c22de..c6426761 100644 --- a/tools/patch8266_16m.py +++ b/tools/patch8266_16m.py @@ -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: From 33d449d892912db72535634da49666460ab15320 Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 29 May 2025 18:52:23 +0300 Subject: [PATCH 14/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D0=B8=D0=BC=D0=B2=D0=BE=D0=BB=D0=B0=20get?= =?UTF-8?q?Utf8CharByIndex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/utils/StringUtils.h | 2 ++ src/utils/StringUtils.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/utils/StringUtils.h b/include/utils/StringUtils.h index 6d3afa15..62dca319 100644 --- a/include/utils/StringUtils.h +++ b/include/utils/StringUtils.h @@ -51,3 +51,5 @@ unsigned char ChartoHex(char ch); std::vector splitStr(const String& str, const String& delimiter); bool strInVector(const String& str, const std::vector& vec); + +String getUtf8CharByIndex(const String& utf8str, int index); diff --git a/src/utils/StringUtils.cpp b/src/utils/StringUtils.cpp index d6c8c3f7..80cfa61a 100644 --- a/src/utils/StringUtils.cpp +++ b/src/utils/StringUtils.cpp @@ -233,4 +233,29 @@ bool strInVector(const String& str, const std::vector& 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 ""; } \ No newline at end of file From 4b98abfa53cdac02fec593177b21db7a61b4e705 Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 29 May 2025 18:53:55 +0300 Subject: [PATCH 15/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20U8g?= =?UTF-8?q?2lib=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B=20=D1=81=20=D0=B4=D0=B8=D1=81=D0=BF=D0=BB=D0=B5=D1=8F?= =?UTF-8?q?=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/display/U8g2lib.zip | Bin 0 -> 10908 bytes src/modules/display/U8g2lib/DisplayTypes.h | 631 ++++++++++++++++++ src/modules/display/U8g2lib/U8g2lib.cpp | 419 ++++++++++++ .../display/U8g2lib/example_config.json | 217 ++++++ 4 files changed, 1267 insertions(+) create mode 100644 src/modules/display/U8g2lib.zip create mode 100644 src/modules/display/U8g2lib/DisplayTypes.h create mode 100644 src/modules/display/U8g2lib/U8g2lib.cpp create mode 100644 src/modules/display/U8g2lib/example_config.json diff --git a/src/modules/display/U8g2lib.zip b/src/modules/display/U8g2lib.zip new file mode 100644 index 0000000000000000000000000000000000000000..aacb2535ce60605b654728ff9b23668052740378 GIT binary patch literal 10908 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 literal 0 HcmV?d00001 diff --git a/src/modules/display/U8g2lib/DisplayTypes.h b/src/modules/display/U8g2lib/DisplayTypes.h new file mode 100644 index 00000000..4288dc75 --- /dev/null +++ b/src/modules/display/U8g2lib/DisplayTypes.h @@ -0,0 +1,631 @@ +#pragma once +#include "Global.h" +#include +#include +#include + +// #define DEBUG_DISPLAY + +#define DEFAULT_PAGE_UPDATE_ms 500 +// #define DEFAULT_PAGE_TIME_ms 5000 +// #define DEFAULT_ROTATION 0 +// #define DEFAULT_CONTRAST 10 +#define MIN_CONTRAST 10 +#define MAX_CONTRAST 150 + +#ifndef DEBUG_DISPLAY +#define D_LOG(fmt, ...) \ + do { \ + (void)0; \ + } while (0) +#else +#define D_LOG(fmt, ...) Serial.printf((PGM_P)PSTR(fmt), ##__VA_ARGS__) +#endif + +enum rotation_t : uint8_t { + ROTATION_NONE, + ROTATION_90, + ROTATION_180, + ROTATION_270 +}; + +uint8_t parse_contrast(int val) { + if (val < MIN_CONTRAST) val = MIN_CONTRAST; + if (val > MAX_CONTRAST) val = MAX_CONTRAST; + return val; +}; + +rotation_t parse_rotation(int val) { + if ((val > 0) && (val <= 90)) return ROTATION_90; + if ((val > 90) && (val <= 180)) return ROTATION_180; + if ((val > 180) && (val <= 270)) return ROTATION_270; + return ROTATION_NONE; +}; + +struct DisplayPage { + String key; + uint16_t time; + rotation_t rotate; + String font; + String format; + String valign; + + DisplayPage( + const String& key, + uint16_t time, + rotation_t rotate, + const String& font, + const String& format, + const String& valign) : key{key}, time{time}, rotate{rotate}, font{font}, format{format}, valign{valign} {} + + // void load(const JsonObject& obj) { + // // time = obj["time"].as(); + // // rotate = parse_rotation(obj["rotate"].as()); + // // font = obj["font"].as(); + // // valign = obj["valign"].as(); + // // format = obj["format"].as(); + // } + + // auto item = DisplayPage( pageObj["key"].as(), _update, _rotate, _font); + // // Загрузка настроек страницы + // item.load(pageObj); + // page.push_back(item); + + +}; + +enum position_t { + POS_AUTO, + POS_ABSOLUTE, + POS_RELATIVE, + POS_TEXT +}; + +struct RelativePosition { + float x; + float y; +}; + +struct TextPosition { + uint8_t row; + uint8_t col; +}; + +struct Point { + uint16_t x; + uint16_t y; + + Point() : Point(0, 0) {} + + Point(uint16_t x, uint16_t y) : x{x}, y{y} {} + + Point(const Point& rhv) : Point(rhv.x, rhv.y) {} +}; + +struct Position { + position_t type; + union { + Point abs; + RelativePosition rel; + TextPosition text; + }; + + Position() : type{POS_AUTO} {} + + Position(const Point& pos) : type{POS_ABSOLUTE} { + abs.x = pos.x; + abs.y = pos.y; + } + + Position(const RelativePosition& pos) : type{POS_RELATIVE} { + rel.x = pos.x; + rel.y = pos.y; + } + + Position(const TextPosition& pos) : type{POS_TEXT} { + text.col = pos.col; + text.row = pos.row; + } + + Position(const Position& rhv) : type{rhv.type} { + switch (type) { + case POS_ABSOLUTE: + abs = rhv.abs; + case POS_RELATIVE: + rel = rhv.rel; + case POS_TEXT: + text = rhv.text; + default: + break; + } + } +}; + +class Cursor : public Printable { + private: + Point _size; + + public: + TextPosition pos{0, 0}; + Point abs{0, 0}; + Point chr; + Cursor(){}; + + Cursor(const Point& size, const Point& chr) : _size{size}, chr{chr} { + D_LOG("w: %d, h: %d, ch: %d(%d)\r\n", _size.x, _size.y, chr.x, chr.y); + } + + void reset() { + pos.col = 0; + pos.row = 0; + abs.x = 0; + abs.y = 0; + } + + void lineFeed() { + pos.col = 0; + pos.row++; + abs.x = 0; + abs.y += chr.y; + } + + void moveX(uint8_t x) { + abs.x += x; + pos.col = abs.x / chr.x; + } + + void moveY(uint8_t y) { + abs.y += y; + } + + void moveXY(uint8_t x, uint8_t y) { + moveX(x); + moveY(y); + } + + void moveCarret(uint8_t col) { + pos.col += col; + moveX(col * chr.x); + } + + bool isEndOfPage(uint8_t rows = 1) { + return (abs.y + (rows * chr.y)) > _size.y; + } + + bool isEndOfLine(uint8_t cols = 1) { + return (abs.x + (cols * chr.x)) > _size.x; + } + + size_t printTo(Print& p) const { + return p.printf("(c:%d, r:%d x:%d, y:%d)", pos.col, pos.row, abs.x, abs.y); + } +}; + + +struct DisplayHardwareSettings { + int update = DEFAULT_PAGE_UPDATE_ms; + rotation_t rotate; + String font; + int pageTime; + String pageFormat; + int contrast; + bool autoPage; + String valign; +}; + +class Display { + private: + unsigned long _lastResfresh{0}; + Cursor _cursor; + U8G2 *_obj{nullptr}; + DisplayHardwareSettings *_settings; + + public: + Display(U8G2 *obj, DisplayHardwareSettings *settings) : _obj{obj}, _settings(settings) { + _obj->begin(); + _obj->enableUTF8Print(); + _obj->setContrast(_settings->contrast); + setFont(settings->font); + setRotation(settings->rotate); + clear(); + } + + ~Display () { + if (_obj) { + delete _obj; + _obj = nullptr; + } + } + + void setRotation(rotation_t rotate) { + switch (rotate) { + case ROTATION_NONE: + _obj->setDisplayRotation(U8G2_R0); + break; + case ROTATION_90: + _obj->setDisplayRotation(U8G2_R1); + break; + case ROTATION_180: + _obj->setDisplayRotation(U8G2_R2); + break; + case ROTATION_270: + _obj->setDisplayRotation(U8G2_R3); + break; + } + } + + void setFont(const String &fontName = "") { + if (fontName.isEmpty()) { + Display::setFont(_settings->font); + return; + } + + if (fontName.startsWith("c6x12")) + _obj->setFont(u8g2_font_6x12_t_cyrillic); + else if (fontName.startsWith("s6x12")) + _obj->setFont(u8g2_font_6x12_t_symbols); + + else if (fontName.startsWith("c6x13")) + _obj->setFont(u8g2_font_6x13_t_cyrillic); + + else if (fontName.startsWith("c7x13")) + _obj->setFont(u8g2_font_7x13_t_cyrillic); + else if (fontName.startsWith("s7x13")) + _obj->setFont(u8g2_font_7x13_t_symbols); + + else if (fontName.startsWith("c8x13")) + _obj->setFont(u8g2_font_8x13_t_cyrillic); + else if (fontName.startsWith("s8x13")) + _obj->setFont(u8g2_font_8x13_t_symbols); + + else if (fontName.startsWith("c9x15")) + _obj->setFont(u8g2_font_9x15_t_cyrillic); + else if (fontName.startsWith("s9x15")) + _obj->setFont(u8g2_font_9x15_t_symbols); + + else if (fontName.startsWith("c10x20")) + _obj->setFont(u8g2_font_10x20_t_cyrillic); + else if (fontName.startsWith("unifont")) + _obj->setFont(u8g2_font_unifont_t_symbols); + else if (fontName.startsWith("siji")) + _obj->setFont(u8g2_font_siji_t_6x10); + else + _obj->setFont(u8g2_font_6x12_t_cyrillic); + + _cursor.chr.x = getMaxCharHeight(); + // _cursor.chr.y = getLineHeight(); + } + + void initCursor() { + _cursor = Cursor( + {getWidth(), getHeight()}, + {getMaxCharHeight(), getLineHeight()}); + } + + void getPosition(const TextPosition &a, Point &b) { + b.x = a.col * _cursor.chr.x; + b.y = (a.row + 1) * _cursor.chr.y; + } + + void getPosition(const RelativePosition &a, Point &b) { + b.x = getHeight() * a.x; + b.y = getWidth() * a.y; + } + + void getPosition(const Point &a, TextPosition &b) { + b.row = a.y / getLineHeight(); + b.col = a.x / getMaxCharWidth(); + } + + void getPosition(const RelativePosition &a, TextPosition &b) { + Point tmp; + getPosition(a, tmp); + getPosition(tmp, b); + } + + void draw(const RelativePosition &pos, const String &str) { + Point tmp; + getPosition(pos, tmp); + draw(tmp, str); + } + + void draw(TextPosition &pos, const String &str) { + Point tmp; + getPosition(pos, tmp); + draw(tmp, str); + } + + Cursor *getCursor() { + return &_cursor; + } + + // print меняю cursor + void println(const String &str, bool frame = false) { + print(str, frame); + _cursor.lineFeed(); + } + + void print(const String &str, bool frame = false) { + //Serial.print(_cursor); + // x, y нижний левой + int width = _obj->drawUTF8(_cursor.abs.x, _cursor.abs.y + _cursor.chr.y, str.c_str()); + if (frame) { + int x = _cursor.abs.x - getXSpacer(); + int y = _cursor.abs.y - _cursor.chr.y; + width += (getXSpacer() * 2); + int height = _cursor.chr.y + getYSpacer() * 2; + // x, y верхней левой. длина, высота + _obj->drawFrame(x, y, width, height); + D_LOG("[x:%d y:%d w:%d h:%d]", x, y, width, height); + } + _cursor.moveX(width); + } + + // draw не меняет cursor + void draw(const Point &pos, const String &str) { + Serial.printf("(x:%d,y:%d) %s", pos.x, pos.y, str.c_str()); + _obj->drawStr(pos.x, pos.y, str.c_str()); + } + + uint8_t getLineHeight() { + return getMaxCharHeight() + getYSpacer(); + } + + int getXSpacer() { + int res = getWidth() / 100; + if (!res) res = 1; + return res; + } + + int getYSpacer() { + int res = (getHeight() - (getLines() * getMaxCharHeight())) / getLines(); + if (!res) res = 1; + return res; + } + + uint8_t getWidth() { + return _obj->getDisplayWidth(); + } + + uint8_t getHeight() { + return _obj->getDisplayHeight(); + } + + uint8_t getLines() { + uint8_t res = getHeight() / _obj->getMaxCharHeight(); + if (!res) res = 1; + return res; + } + + uint8_t getMaxCharHeight() { + return _obj->getMaxCharHeight(); + } + + uint8_t getMaxCharWidth() { + return _obj->getMaxCharWidth(); + } + + void clear() { + _obj->clearDisplay(); + _cursor.reset(); + } + + void startRefresh() { + _obj->clearBuffer(); + _cursor.reset(); + } + + void endRefresh() { + _obj->sendBuffer(); + _lastResfresh = millis(); + } + + bool isNeedsRefresh() { + // SerialPrint("[Display]", "_settings->update: " + String(_settings->update) + "ms", ""); + return !_lastResfresh || (millis() > (_lastResfresh + _settings->update)); + } +}; + +struct ParamPropeties { + // рамка + bool frame[false]; +}; + +struct Param { + // Ключ + const String key; + // Префикс к значению + String pref; + // Суффикс к значению + String suff; + // Значение + String value; + + String pref_fnt; + String suff_fnt; + String value_fnt; + + String gliphs; + + // значение изменилось + bool updated; + // группа + uint8_t group; + ParamPropeties props; + Position position; + + Param(const String &key, + const String &pref = emptyString, const String &value = emptyString, const String &suff = emptyString, + const String &pref_fnt = emptyString, const String &value_fnt = emptyString, const String &suff_fnt = emptyString, + const String &gliphs = emptyString + ) : key{key}, group{0} { + setValue(value.c_str()); + setPref(pref); + setSuff(suff); + this->pref_fnt = pref_fnt; + this->value_fnt = value_fnt; + this->suff_fnt = suff_fnt; + this->gliphs = gliphs; + updated = false; + } + + bool isValid() { + return !pref.isEmpty(); + } + + bool setPref(const String &str) { + if (!pref.equals(str)) { + pref = str; + updated = true; + return true; + } + return false; + } + + bool setSuff(const String &str) { + if (!suff.equals(str)) { + suff = str; + updated = true; + return true; + } + return false; + } + + bool setValue(const String &str) { + if (!value.equals(str)) { + value = str; + updated = true; + return true; + } + return false; + } + + + + void draw(Display *obj, uint8_t line) { + } + + void draw(Display *obj) { + auto type = position.type; + switch (type) { + case POS_AUTO: { + D_LOG("AUTO %s '%s%s'\r\n", key.c_str(), descr.c_str(), value.c_str()); + obj->setFont(pref_fnt); + obj->print(pref.c_str()); + + obj->setFont(value_fnt); + obj->println(value.c_str(), false); + + obj->setFont(suff_fnt); + obj->print(suff.c_str()); + } + case POS_ABSOLUTE: { + auto pos = position.abs; + D_LOG("ABS(%d, %d) %s %s'\r\n", pos.x, pos.y, key.c_str(), value.c_str()); + obj->draw(pos, value); + } + case POS_RELATIVE: { + auto pos = position.rel; + D_LOG("REL(%2.2f, %2.2f) %s %s'\r\n", pos.x, pos.y, key.c_str(), value.c_str()); + obj->draw(pos, value); + } + case POS_TEXT: { + auto pos = position.text; + D_LOG("TXT(%d, %d) %s %s'\r\n", pos.col, pos.row, key.c_str(), value.c_str()); + obj->draw(pos, value); + } + default: + D_LOG("unhadled: %d", type); + } + } +}; + +class ParamCollection { + std::vector _item; + + public: + void load() { + for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { + if ((*it)->getSubtype() == "" || (*it)->getSubtype() == "U8g2lib") continue; + + auto entry = find((*it)->getID()); + if (!entry) { + _item.push_back({(*it)->getID(), (*it)->getID() + ": ", (*it)->getValue(), "", "", "", ""}); + } else { + entry->setValue((*it)->getValue()); + if (entry->pref == "") + entry->setPref((*it)->getID() + ": "); + } + } + } + + void loadExtParamData(String parameters) { + String id = ""; + jsonRead(parameters, "id", id, false); + if (id != "") { + String pref = ""; + String suff = ""; + String pref_fnt = ""; + String suff_fnt = ""; + String value_fnt = ""; + String gliphs = ""; + + bool hasExtParam = false; + + hasExtParam = hasExtParam + jsonRead(parameters, "pref", pref, false); + hasExtParam = hasExtParam + jsonRead(parameters, "suff", suff, false); + hasExtParam = hasExtParam + jsonRead(parameters, "pref_fnt", pref_fnt, false); + hasExtParam = hasExtParam + jsonRead(parameters, "suff_fnt", suff_fnt, false); + hasExtParam = hasExtParam + jsonRead(parameters, "value_fnt", value_fnt, false); + hasExtParam = hasExtParam + jsonRead(parameters, "gliphs", gliphs, false); + + if (hasExtParam) { + _item.push_back({id, pref, "", suff, pref_fnt, value_fnt, suff_fnt, gliphs}); + } + } + } + + Param *find(const String &key) { + return find(key.c_str()); + } + + Param *find(const char *key) { + Param *res = nullptr; + for (size_t i = 0; i < _item.size(); i++) { + if (_item.at(i).key.equalsIgnoreCase(key)) { + res = &_item.at(i); + break; + } + } + return res; + } + + Param *get(int n) { + return &_item.at(n); + } + + size_t count() { + return _item.size(); + } + + // n - номер по порядку параметра + Param *getValid(int n) { + for (size_t i = 0; i < _item.size(); i++) + if (_item.at(i).isValid()) + if (!(n--)) return &_item.at(i); + return nullptr; + } + + size_t getVaildCount() { + size_t res = 0; + for (auto entry : _item) res += entry.isValid(); + return res; + } + + size_t max_group() { + size_t res = 0; + for (auto entry : _item) + if (res < entry.group) res = entry.group; + return res; + } +}; \ No newline at end of file diff --git a/src/modules/display/U8g2lib/U8g2lib.cpp b/src/modules/display/U8g2lib/U8g2lib.cpp new file mode 100644 index 00000000..97dc291f --- /dev/null +++ b/src/modules/display/U8g2lib/U8g2lib.cpp @@ -0,0 +1,419 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include +#include "DisplayTypes.h" + +#define STRHELPER(x) #x +#define TO_STRING_AUX(...) "" #__VA_ARGS__ +#define TO_STRING(x) TO_STRING_AUX(x) + + +// дополненный список параметров для вывода, который синхронизирован со списком значений IoTM +ParamCollection *extParams{nullptr}; + +// класс одного главного экземпляра экрана для выделения памяти только когда потребуется экран +class DisplayImplementation { + private: + unsigned long _lastPageChange{0}; + bool _pageChanged{false}; + // uint8_t _max_descr_width{0}; + // typedef std::vector Line; + // текущая + size_t _page_n{0}; + // struct Page { + // std::vector line; + // }; + + uint8_t _n{0}; // последний отображенный + + DisplayHardwareSettings *_context{nullptr}; + Display *_display{nullptr}; + + public: + DisplayImplementation(DisplayHardwareSettings *context = nullptr, + Display *display = nullptr) + : _context(context), _display(display) { + + } + + ~DisplayImplementation() { + if (_display) { + delete _display; + _display = nullptr; + } + if (_context) { + delete _context; + _context = nullptr; + } + if (extParams) { + delete extParams; + extParams = nullptr; + } + } + + std::vector page; + + void nextPage() { + _n = _n + 1; + if (_n == page.size()) _n = _n - 1; + _pageChanged = true; + } + + void prevPage() { + if (_n > 0) _n = _n - 1; + _pageChanged = true; + } + + void rotPage() { + _n = _n + 1; + if (_n == page.size()) _n = 0; + _pageChanged = true; + } + + void gotoPage(uint8_t num) { + _n = num; + if (num < 0) _n = 0; + if (num >= page.size()) _n = page.size() - 1; + _pageChanged = true; + } + + void setAutoPage(bool isAuto) { + if (_context) _context->autoPage = isAuto; + _pageChanged = true; + } + + uint8_t calcPageCount(ParamCollection *param, uint8_t linesPerPage) { + size_t res = 0; + size_t totalLines = param->count(); + if (totalLines && linesPerPage) { + res = totalLines / linesPerPage; + if (totalLines % linesPerPage) res++; + } + return res; + } + + // uint8_t getPageCount() { + // return isAutoPage() ? calcPageCount(_param, _display->getLines()) : getPageCount(); + // } + + // выводит на страницу параметры начиная c [n] + // возвращает [n] последнего уместившегося + uint8_t draw(Display *display, ParamCollection *param, uint8_t n) { + // Очищает буфер (не экран, а внутреннее представление) для последущего заполнения + display->startRefresh(); + size_t i = 0; + // вот тут лог ошибка + for (i = n; i < param->count(); i++) { + auto cursor = display->getCursor(); + auto entry = param->get(i); + auto len = entry->value.length() + entry->pref.length() + entry->suff.length() ; + if (cursor->isEndOfLine(len)) cursor->lineFeed(); + + printParam(display, entry, _context->font); + + if (cursor->isEndOfPage(0)) break; + } + // Отправит готовый буфер страницы на дисплей + display->endRefresh(); + return i; + } + + String slice(const String &str, size_t index, char delim) { + size_t cnt = 0; + int subIndex[] = {0, -1}; + size_t maxIndex = str.length() - 1; + + for (size_t i = 0; (i <= maxIndex) && (cnt <= index); i++) { + if ((str.charAt(i) == delim) || (i == maxIndex)) { + cnt++; + subIndex[0] = subIndex[1] + 1; + subIndex[1] = (i == maxIndex) ? i + 1 : i; + } + } + return cnt > index ? str.substring(subIndex[0], subIndex[1]) : emptyString; + } + + void printParam(Display *display, Param *param, const String &parentFont) { + if (!param->pref.isEmpty()) { + display->setFont(param->pref_fnt.isEmpty() ? parentFont : param->pref_fnt); + display->print(param->pref); + } + + if (!param->value.isEmpty()) { + display->setFont(param->value_fnt.isEmpty() ? parentFont : param->value_fnt); + if (!param->gliphs.isEmpty() && isDigitStr(param->value)) { + int glyphIndex = param->value.toInt(); + display->print(getUtf8CharByIndex(param->gliphs, glyphIndex)); + } else display->print(param->value); + } + + if (!param->suff.isEmpty()) { + display->setFont(param->suff_fnt.isEmpty() ? parentFont : param->suff_fnt); + display->print(param->suff); + } + } + + void showXXX(Display *display, ParamCollection *param, uint8_t page) { + size_t linesPerPage = display->getLines(); + size_t line_first = _page_n * linesPerPage; + size_t line_last = line_first + linesPerPage - 1; + + display->startRefresh(); + + size_t lineOfPage = 0; + for (size_t n = line_first; n <= line_last; n++) { + auto entry = param->get(n); + if (entry) { + entry->draw(_display, lineOfPage); + lineOfPage++; + } else { + break; + } + } + display->endRefresh(); + } + + void drawPage(Display *display, ParamCollection *params, DisplayPage *page) { + display->setFont(page->font); + display->initCursor(); + + auto keys = page->key; + D_LOG("page keys: %s\r\n", keys.c_str()); + size_t l = 0; + auto line_keys = slice(keys, l, '#'); + while (!line_keys.isEmpty()) { + if (page->valign.equalsIgnoreCase("center")) { + display->getCursor()->moveY((display->getHeight() / 2) - display->getMaxCharHeight() / 2); + } + D_LOG("line keys: %s\r\n", keys.c_str()); + size_t n = 0; + auto key = slice(line_keys, n, ','); + while (!key.isEmpty()) { + D_LOG("key: %s\r\n", key.c_str()); + auto entry = params->find(key.c_str()); + if (entry && entry->updated) { + if (n) display->print(" "); + printParam(display, entry, page->font); + } + key = slice(line_keys, ++n, ','); + } + display->getCursor()->lineFeed(); + line_keys = slice(keys, ++l, '#'); + } + } + + // Режим пользовательской разбивки параметров по страницам + void showManual(Display *display, ParamCollection *param) { + auto page = getPage(_n); + + if (display->isNeedsRefresh() || _pageChanged) { + D_LOG("[Display] page: %d\r\n", _n); + display->setRotation(page->rotate); + display->startRefresh(); + drawPage(display, param, page); + display->endRefresh(); + _pageChanged = false; + } + + if (_context->autoPage && millis() >= (_lastPageChange + page->time)) { + // Если это была последняя начинаем с начала + if (++_n > (getPageCount() - 1)) _n = 0; + _pageChanged = true; + _lastPageChange = millis(); + } + } + + // Режим авто разбивки параметров по страницам + void showAuto(Display *display, ParamCollection *param) { + size_t param_count = param->count(); + + if (!param_count) return; + + display->setFont(_context->font); + display->initCursor(); + + size_t last_n = _n; + if (display->isNeedsRefresh() || _pageChanged) { + //D_LOG("n: %d/%d\r\n", _n, param_count); + last_n = draw(display, param, _n); + } + + if (_context->autoPage && millis() >= (_lastPageChange + _context->pageTime)) { + _n = last_n; + if (_n >= param_count) _n = 0; + _pageChanged = true; + _lastPageChange = millis(); + } + } + + void show() { + if (extParams && _display) { + extParams->load(); + + if (isAutoPage()) { + showAuto(_display, extParams); + } else { + showManual(_display, extParams); + } + } + } + + bool isAutoPage() { + return !getPageCount(); + } + + uint8_t getPageCount() { + return page.size(); + } + + DisplayPage* getPage(uint8_t index) { + return &page.at(index); + } +}; + + +DisplayImplementation* displayImpl = nullptr; + + +class U8g2lib : public IoTItem { + private: + uint8_t _pageNum = 0; + + public: + U8g2lib(String parameters) : IoTItem(parameters) { + DisplayHardwareSettings *context = new DisplayHardwareSettings(); + if (!context) { + D_LOG("[Display] disabled"); + return; + } + + jsonRead(parameters, "update", context->update); + jsonRead(parameters, "font", context->font); + + int rotate; + jsonRead(parameters, "rotation", rotate); + context->rotate = parse_rotation(rotate); + + jsonRead(parameters, "contrast", context->contrast); + jsonRead(parameters, "autoPage", context->autoPage); + jsonRead(parameters, "pageTime", context->pageTime); + + bool itsFirstDisplayInit = false; + if (!displayImpl) { + // Значит это первый элемент U8g2lib в конфигурации - Инициализируем дисплей + itsFirstDisplayInit = true; + int dc = U8X8_PIN_NONE, cs = U8X8_PIN_NONE, data = U8X8_PIN_NONE, clock = U8X8_PIN_NONE, rst = U8X8_PIN_NONE; + jsonRead(parameters, "dc", dc); + jsonRead(parameters, "cs", cs); + jsonRead(parameters, "data", data); + jsonRead(parameters, "clock", clock); + jsonRead(parameters, "rst", rst); + if (dc == -1) dc = U8X8_PIN_NONE; + if (cs == -1) cs = U8X8_PIN_NONE; + if (data == -1) data = U8X8_PIN_NONE; + if (clock == -1) clock = U8X8_PIN_NONE; + if (rst == -1) rst = U8X8_PIN_NONE; + + String type; + jsonRead(parameters, "oledType", type); + U8G2* libObj = nullptr; + if (type.startsWith("ST")) { + libObj = new U8G2_ST7565_ERC12864_F_4W_SW_SPI(U8G2_R0, clock, data, cs, dc, rst); + } + else if (type.startsWith("SS_I2C")) { + // libObj = new U8G2_SSD1306_128X64_VCOMH0_F_SW_I2C(U8G2_R0, clock, data, rst); + libObj = new U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C(U8G2_R0, clock, data, rst); + + } + else if (type.startsWith("SS_SPI")) { + libObj = new U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI(U8G2_R0, clock, data, cs, dc, rst); + } + else if (type.startsWith("SH")) { + libObj = new U8G2_SH1106_128X64_NONAME_F_HW_I2C(U8G2_R0, rst, clock, data); + } + + if (!libObj) { + D_LOG("[Display] disabled"); + return; + } + + Display *_display = new Display(libObj, context); + if (!_display) { + D_LOG("[Display] disabled"); + return; + } + + if (!extParams) extParams = new ParamCollection(); + + displayImpl = new DisplayImplementation(context, _display); + if (!displayImpl) { + D_LOG("[Display] disabled"); + return; + } + } + + // добавляем страницу, если указан ID для отображения + String id2show; + jsonRead(parameters, "id2show", id2show); + if (!id2show.isEmpty()) { + auto item = DisplayPage( + id2show, + context->pageTime, + context->rotate, + context->font, + context->pageFormat, + context->valign + ); + _pageNum = displayImpl->page.size(); + displayImpl->page.push_back(item); + if (!itsFirstDisplayInit) delete context; // если это не первый вызов, то контекст имеет временный характер только для создания страницы + } + } + + void doByInterval() { + if (displayImpl) displayImpl->show(); + } + + IoTValue execute(String command, std::vector& param) { + if (displayImpl) + if (command == "nextPage") { + displayImpl->nextPage(); + } else if (command == "prevPage") { + displayImpl->prevPage(); + } else if (command == "rotPage") { + displayImpl->rotPage(); + } else if (command == "gotoPage") { + if (param.size() == 1) { + displayImpl->gotoPage(param[0].valD); + } else { + displayImpl->gotoPage(_pageNum); + } + } else if (command == "setAutoPage") { + if (param.size() == 1) { + displayImpl->setAutoPage(param[0].valD); + } + } + + return {}; + } + + ~U8g2lib() { + if (displayImpl) { + delete displayImpl; + displayImpl = nullptr; + } + }; +}; + +void* getAPI_U8g2lib(String subtype, String param) { + if (subtype == F("U8g2lib")) { + // SerialPrint("[Display]", "param1: ", param); + return new U8g2lib(param); + } else { + // элемент не наш, но проверяем на налличие модификаторов, которые нужны для модуля + // вынимаем ID элемента и значения pref и suff связанные с ним + if (!extParams) extParams = new ParamCollection(); + extParams->loadExtParamData(param); + return nullptr; + } +} diff --git a/src/modules/display/U8g2lib/example_config.json b/src/modules/display/U8g2lib/example_config.json new file mode 100644 index 00000000..48809c20 --- /dev/null +++ b/src/modules/display/U8g2lib/example_config.json @@ -0,0 +1,217 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "btn", + "needSave": 0, + "widget": "toggle", + "page": "Ввод", + "descr": "ТестКнопка", + "int": "0", + "val": "0", + "value_fnt": "siji", + "gliphs": "" + }, + { + "global": 0, + "type": "Writing", + "subtype": "Timer", + "id": "timer", + "widget": "anydataDef", + "page": "Ввод", + "descr": "Таймер", + "int": 1, + "countDown": "99", + "ticker": 1, + "repeat": 1, + "needSave": 0, + "pref": "ТАЙМЕР: ", + "suff": " сек", + "round": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "time", + "needSave": 0, + "widget": "anydataRed", + "page": "Ввод", + "descr": "Время", + "int": "0", + "val": "", + "pref": " ⏰️", + "pref_fnt": "unifont" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "var", + "needSave": 0, + "widget": "inputTxt", + "page": "Ввод", + "descr": "Текст", + "int": "0", + "val": "☀️-☁️-☂️-☃️-☄️", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "pref": "текст: ", + "value_fnt": "unifont" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "ip", + "needSave": 0, + "widget": "anydataDef", + "page": "Ввод", + "descr": "IP", + "int": "0", + "val": "", + "pref": "IP: " + }, + { + "type": "Reading", + "subtype": "U8g2lib", + "id": "page1", + "widget": "nil", + "page": "", + "descr": "", + "oledType": "SS_I2C", + "int": "1", + "font": "c6x13", + "contrast": "200", + "rotation": "0", + "autoPage": "0", + "pageTime": "10000", + "dc": 19, + "cs": "-1", + "data": "21", + "clock": "22", + "rst": -1, + "id2show": "timer,lvl#ip" + }, + { + "type": "Reading", + "subtype": "U8g2lib", + "id": "page2", + "widget": "nil", + "page": "", + "descr": "", + "oledType": "SS_I2C", + "int": 1, + "update": 500, + "font": "c6x13", + "contrast": "150", + "rotation": "0", + "autoPage": "0", + "pageTime": 3000, + "id2show": "var#btn,time", + "dc": "-1", + "cs": "-1", + "data": "-1", + "clock": "-1", + "rst": -1 + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "autoPage", + "needSave": 0, + "widget": "toggle", + "page": "Ввод", + "descr": "autoPage", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "nextPage", + "needSave": 0, + "widget": "toggle", + "page": "Ввод", + "descr": "nextPage", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "prevPage", + "needSave": 0, + "widget": "toggle", + "page": "Ввод", + "descr": "prevPage", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "pageN", + "needSave": 0, + "widget": "inputDgt", + "page": "Ввод", + "descr": "pageN", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "rotPage", + "needSave": 0, + "widget": "toggle", + "page": "Ввод", + "descr": "rotPage", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "AnalogAdc", + "id": "lvl", + "widget": "anydataRed", + "page": "Ввод", + "descr": "Уровень", + "map": "1,1024,1,5", + "plus": 0, + "multiply": 1, + "round": "0", + "pin": "34", + "int": "1", + "avgSteps": 1, + "pref": " ", + "value_fnt": "siji", + "gliphs": "" + } + ] +} + +scenario=>if timer then { +ip = getIP() +time = gethhmmss() +} +if autoPage then page1.setAutoPage(1) else page1.setAutoPage(0) +if nextPage < 2 then page1.nextPage() +if prevPage < 2 then page1.prevPage() +if rotPage < 2 then page1.rotPage() +if pageN != "" then page1.gotoPage(pageN) \ No newline at end of file From 6194169990c14400eef9a75a04ae084185cad34c Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 29 May 2025 18:56:15 +0300 Subject: [PATCH 16/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20U8g?= =?UTF-8?q?2lib=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=8B=20=D1=81=20=D0=B4=D0=B8=D1=81=D0=BF=D0=BB=D0=B5=D1=8F?= =?UTF-8?q?=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/display/U8g2lib/modinfo.json | 96 ++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/modules/display/U8g2lib/modinfo.json diff --git a/src/modules/display/U8g2lib/modinfo.json b/src/modules/display/U8g2lib/modinfo.json new file mode 100644 index 00000000..d4e18bb3 --- /dev/null +++ b/src/modules/display/U8g2lib/modinfo.json @@ -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" + ] + } +} \ No newline at end of file From 1a79776bfbd8e7e9489b986c703745aa73023c3c Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 29 May 2025 18:57:46 +0300 Subject: [PATCH 17/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D1=8B=20setInterv?= =?UTF-8?q?al=20=D0=B8=20doByInterval=20=D0=94=D0=BB=D1=8F=20=D0=B2=D0=BE?= =?UTF-8?q?=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20=D0=B8?= =?UTF-8?q?=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D0=B2=D0=B0=D0=BB=D0=B0=20=D0=B2=D1=8B=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20doByInterval=20?= =?UTF-8?q?=D0=B8=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D0=B0=20=D0=B5=D0=B3?= =?UTF-8?q?=D0=BE=20=D0=B2=D0=BD=D0=B5=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/classes/IoTScenario.cpp | 27 +++++++++++++++++++++------ src/modules/sceninfo.json | 6 ++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/classes/IoTScenario.cpp b/src/classes/IoTScenario.cpp index 7245b2f8..7efa73ca 100644 --- a/src/classes/IoTScenario.cpp +++ b/src/classes/IoTScenario.cpp @@ -291,10 +291,8 @@ class CallExprAST : public ExprAST { ret.valD = Item->getIntFromNet(); ret.isDecimal = true; return &ret; - } - - // вызываем системную функцию периодического выполнения вне таймера - if (Cmd == F("doByInterval")) { + + } else if (Cmd == F("doByInterval")) { // вызываем системную функцию периодического выполнения вне таймера Item->doByInterval(); ret = Item->value; return &ret; @@ -311,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); @@ -350,7 +357,8 @@ enum SysOp { sysop_mqttPub, sysop_getUptime, sysop_mqttIsConnect, - sysop_wifiIsConnect + sysop_wifiIsConnect, + sysop_setInterval }; IoTValue sysExecute(SysOp command, std::vector ¶m) { @@ -456,7 +464,12 @@ IoTValue sysExecute(SysOp command, std::vector ¶m) { break; case sysop_wifiIsConnect: value.valD = isNetworkActive(); - break; + break; + case sysop_setInterval: + if (param.size() == 1) { + + } + break; } return value; @@ -515,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; } diff --git a/src/modules/sceninfo.json b/src/modules/sceninfo.json index b7d0be54..bdc834e0 100644 --- a/src/modules/sceninfo.json +++ b/src/modules/sceninfo.json @@ -14,6 +14,12 @@ "descr": "Получаем количество секунд доверия к значениям элемента. При -2 доверие полное, при -1 время доверия истекло. При >0 время обратного отсчета. Используется только совместно с ИД элемента: ID.getIntFromNet()", "params": [] }, + { + "name": "setInterval", + "descr": "Меняем интервал выполнения периодиеских операций элемента в секундах. Используется только совместно с ИД элемента: ID.setInterval(5)", + "params": ["Секунды"], + "return": "установленный интервал" + }, { "name": "doByInterval", "descr": "Выполняем интервальное действие модуля вне плана. Используется только совместно с ИД элемента: ID.doByInterval()", From fe9d9827db72699378e4925e2078a11df4fafcab Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 29 May 2025 19:00:19 +0300 Subject: [PATCH 18/18] =?UTF-8?q?=D0=9C=D0=B5=D0=BD=D1=8F=D0=B5=D0=BC=20?= =?UTF-8?q?=D0=B0=D0=BB=D0=B3=D0=BE=D1=80=D0=B8=D1=82=D0=BC=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=20getAPI=5F=20=D0=94=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D1=81=D0=BE=D1=82?= =?UTF-8?q?=D0=B8=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=BE=D0=B2=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0=D1=86=D0=B8=D0=B9=20=D0=B2?= =?UTF-8?q?=D1=81=D0=B5=D1=85=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B2=20=D0=BB=D1=8E=D0=B1=D0=BE=D0=BC=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=20=D0=B4=D0=BB=D1=8F=20=D1=84?= =?UTF-8?q?=D0=BE=D1=80=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D1=81=D1=88=D0=B8=D1=80=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D0=BC=D0=BE=D0=B4=D0=B8=D1=84=D0=B8=D0=BA=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PrepareProject.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PrepareProject.py b/PrepareProject.py index 24bcdc5a..a848dbf7 100644 --- a/PrepareProject.py +++ b/PrepareProject.py @@ -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)