Merge pull request #402 from biveraxe/ver4dev

Новый скрипт прошивки, модуль управления LED лентой и др. мелкие правки
This commit is contained in:
2025-05-13 22:25:50 +03:00
committed by GitHub
7 changed files with 742 additions and 1 deletions

View File

@@ -12,6 +12,8 @@ uint8_t hexStringToUint8(const String& hex);
uint16_t hexStringToUint16(const String& hex); uint16_t hexStringToUint16(const String& hex);
uint32_t hexStringToUint32(const String& hex);
String selectToMarkerLast(String str, const String& found); String selectToMarkerLast(String str, const String& found);
String selectToMarker(String str, const String& found); String selectToMarker(String str, const String& found);

109
run.py Normal file
View File

@@ -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()

View File

@@ -0,0 +1,332 @@
#include "Global.h"
#include "classes/IoTItem.h"
#include "ESPConfiguration.h"
#include <WS2812FX.h>
WS2812FX *_glob_strip = nullptr; // глобальный указатель на WS2812FX для использования в функциях для кастомных эффектов
std::vector<IoTValue*> 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; i<numBands; i++) {
// SerialPrint("i", "LedFX", String(vuMeterBands[i]->valD));
uint8_t scaledBand = (vuMeterBands[i]->valD * bandSize) / 256;
for(uint16_t j=0; j<bandSize; j++) {
uint16_t index = seg->start + (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<IoTValue> &param) {
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;
}
}

View File

@@ -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"
]
}
}

View File

@@ -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)

View File

@@ -88,7 +88,7 @@ void* getAPI_AhtXX(String subtype, String param) {
if (ahts.find(addr) == ahts.end()) { if (ahts.find(addr) == ahts.end()) {
int shtType; int shtType;
jsonRead(param, "type", shtType); jsonRead(param, "shtType", shtType);
ahts[addr] = new AHTxx(hexStringToUint8(addr), (AHTXX_I2C_SENSOR)shtType); ahts[addr] = new AHTxx(hexStringToUint8(addr), (AHTXX_I2C_SENSOR)shtType);

View File

@@ -103,6 +103,7 @@ uint8_t hexStringToUint8(const String& hex) {
if (tmp >= 0x00 && tmp <= 0xFF) { if (tmp >= 0x00 && tmp <= 0xFF) {
return tmp; return tmp;
} }
return 0;
} }
uint16_t hexStringToUint16(const String& hex) { uint16_t hexStringToUint16(const String& hex) {
@@ -110,6 +111,15 @@ uint16_t hexStringToUint16(const String& hex) {
if (tmp >= 0x0000 && tmp <= 0xFFFF) { if (tmp >= 0x0000 && tmp <= 0xFFFF) {
return tmp; 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) { size_t itemsCount2(String str, const String& separator) {