mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-06-10 03:49:20 +03:00
Merge branch 'IoTManagerProject:ver4dev' into ver4dev
This commit is contained in:
@@ -2,64 +2,94 @@
|
||||
|
||||
const String getThisDevice() {
|
||||
String thisDevice = "{}";
|
||||
jsonWriteStr_(thisDevice, F("devicelist_"), ""); //метка для парсинга
|
||||
jsonWriteStr_(thisDevice, F("wg"), jsonReadStr(settingsFlashJson, F("wg"))); //рабочая группа
|
||||
// jsonWriteStr_(thisDevice, F("devicelist_"), ""); // метка для парсинга - она не нужна теперь
|
||||
jsonWriteStr_(thisDevice, F("wg"), jsonReadStr(settingsFlashJson, F("wg"))); // рабочая группа
|
||||
jsonWriteStr_(thisDevice, F("ip"), jsonReadStr(settingsFlashJson, F("ip")));
|
||||
jsonWriteStr_(thisDevice, F("id"), jsonReadStr(settingsFlashJson, F("id")));
|
||||
jsonWriteStr_(thisDevice, F("name"), jsonReadStr(settingsFlashJson, F("name")));
|
||||
// приложение svelte хранит свой список и в нем поддерживает корректные статусы, а при получении этого списка
|
||||
// лишь изменяет в своем те элементы массива которые добавились новые, не трогая остальные
|
||||
jsonWriteBool_(thisDevice, F("status"), false);
|
||||
jsonWriteInt_(thisDevice, F("fv"), FIRMWARE_VERSION);
|
||||
thisDevice = "[" + thisDevice + "]";
|
||||
return thisDevice;
|
||||
}
|
||||
|
||||
void addThisDeviceToList() {
|
||||
devListHeapJson = getThisDevice();
|
||||
SerialPrint("i", "List", "Add this dev to list");
|
||||
if (jsonReadInt(settingsFlashJson, F("udps")) != 0) {
|
||||
// если включен автопоиск то записываем это устройство в оперативную память
|
||||
devListHeapJson = getThisDevice();
|
||||
} else {
|
||||
// если выключен автопоиск то записываем это устройство в флешь память
|
||||
// если файла не было никогда
|
||||
String list = readFile("/devlist.json", 2048);
|
||||
if (list == "failed") {
|
||||
writeFile("/devlist.json", getThisDevice());
|
||||
} else {
|
||||
// если файл был то перепишем в нем первое устройство - to do
|
||||
}
|
||||
}
|
||||
|
||||
SerialPrint("i", F("List"), F("This dev added to list"));
|
||||
}
|
||||
|
||||
#ifdef UDP_ENABLED
|
||||
AsyncUDP asyncUdp;
|
||||
|
||||
void asyncUdpInit() {
|
||||
void udpListningInit() {
|
||||
if (asyncUdp.listenMulticast(IPAddress(239, 255, 255, 255), 4210)) {
|
||||
asyncUdp.onPacket([](AsyncUDPPacket packet) {
|
||||
// Serial.print("UDP Packet Type: ");
|
||||
// Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast");
|
||||
// Serial.print("From: ");
|
||||
// Serial.print(packet.remoteIP());
|
||||
// Serial.print(":");
|
||||
// Serial.println(packet.remotePort());
|
||||
// Serial.print("To: ");
|
||||
// Serial.print(packet.localIP());
|
||||
// Serial.print(":");
|
||||
// Serial.println(packet.localPort());
|
||||
// Serial.print(", Length: ");
|
||||
// Serial.print(packet.length());
|
||||
// Serial.print(", Data: ");
|
||||
// Serial.write(packet.data(), packet.length());
|
||||
|
||||
//String data = {packet.data(), packet.length()}; // для ESP32 подходит как замена uint8tToString, но 8266 не переваривает
|
||||
String data = uint8tToString(packet.data(), packet.length());
|
||||
String remoteWorkgroup = "";
|
||||
data.replace("[", "");
|
||||
data.replace("]", "");
|
||||
if (jsonRead(data, F("wg"), remoteWorkgroup)) { // проверяем чтоб полученный формат был Json и заодно вытягиваем имя группы
|
||||
String loacalWorkgroup = "";
|
||||
jsonRead(settingsFlashJson, F("wg"), loacalWorkgroup);
|
||||
if (remoteWorkgroup == loacalWorkgroup) {
|
||||
SerialPrint("i", F("UDP"), "IP: " + packet.remoteIP().toString() + ":" + String(packet.remotePort()));
|
||||
jsonMergeArrays(devListHeapJson, data);
|
||||
// если был включен автоматический поиск устройств то начнем запись в оперативную память
|
||||
if (jsonReadInt(settingsFlashJson, F("udps")) != 0) {
|
||||
// Serial.print("UDP Packet Type: ");
|
||||
// Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast");
|
||||
// Serial.print("From: ");
|
||||
// Serial.print(packet.remoteIP());
|
||||
// Serial.print(":");
|
||||
// Serial.println(packet.remotePort());
|
||||
// Serial.print("To: ");
|
||||
// Serial.print(packet.localIP());
|
||||
// Serial.print(":");
|
||||
// Serial.println(packet.localPort());
|
||||
// Serial.print(", Length: ");
|
||||
// Serial.print(packet.length());
|
||||
// Serial.print(", Data: ");
|
||||
// Serial.write(packet.data(), packet.length());
|
||||
// String data = {packet.data(), packet.length()}; // для ESP32 подходит как замена uint8tToString, но 8266 не переваривает
|
||||
|
||||
String data = uint8tToString(packet.data(), packet.length());
|
||||
String remoteWorkgroup = "";
|
||||
data.replace("[", "");
|
||||
data.replace("]", "");
|
||||
if (jsonRead(data, F("wg"), remoteWorkgroup)) { // проверяем чтоб полученный формат был Json и заодно вытягиваем имя группы
|
||||
String loacalWorkgroup = "";
|
||||
jsonRead(settingsFlashJson, F("wg"), loacalWorkgroup);
|
||||
if (remoteWorkgroup == loacalWorkgroup) {
|
||||
SerialPrint("i", F("UDP"), "IP: " + packet.remoteIP().toString() + ":" + String(packet.remotePort()));
|
||||
jsonMergeArrays(devListHeapJson, data);
|
||||
// эксперементальный вариант отправки нового списка сразу по приходу
|
||||
// sendStringToWs("devlis", devListHeapJson, -1);
|
||||
}
|
||||
} else {
|
||||
SerialPrint("E", F("UDP"), F("Udp packet invalid"));
|
||||
}
|
||||
|
||||
// reply to the client
|
||||
// String ip = WiFi.localIP().toString();
|
||||
// asyncUdp.broadcastTo(ip.c_str(), packet.remotePort());
|
||||
// packet.printf(ip.c_str(), packet.length());
|
||||
|
||||
} else {
|
||||
SerialPrint("E", F("UDP"), F("Udp packet invalid"));
|
||||
devListHeapJson = "";
|
||||
}
|
||||
// reply to the client
|
||||
// String ip = WiFi.localIP().toString();
|
||||
// asyncUdp.broadcastTo(ip.c_str(), packet.remotePort());
|
||||
// packet.printf(ip.c_str(), packet.length());
|
||||
});
|
||||
}
|
||||
|
||||
//будем отправлять каждые 60 секунд презентацию данного устройства
|
||||
SerialPrint("i", F("UDP"), F("Udp listning inited"));
|
||||
}
|
||||
|
||||
void udpBroadcastInit() {
|
||||
// будем отправлять каждые 60 секунд презентацию данного устройства
|
||||
ts.add(
|
||||
UDP, 60000, [&](void*) { // UDPP
|
||||
if (isNetworkActive()) {
|
||||
@@ -71,11 +101,11 @@ void asyncUdpInit() {
|
||||
},
|
||||
nullptr, true);
|
||||
|
||||
SerialPrint("i", F("UDP"), F("Udp Init"));
|
||||
SerialPrint("i", F("UDP"), F("Udp broadcast inited"));
|
||||
}
|
||||
|
||||
void jsonMergeArrays(String& existJson, String& incJson) {
|
||||
DynamicJsonDocument incJsonDoc(1024);
|
||||
DynamicJsonDocument incJsonDoc(4096);
|
||||
DeserializationError incJsonError = deserializeJson(incJsonDoc, incJson);
|
||||
// if (incJsonError) { // upd: devlist заведомо верный, зачем проверять еще раз?
|
||||
// SerialPrint("E", F("UDP"), "Invailed json in incomming udp packet " + String(incJsonError.f_str()));
|
||||
@@ -83,7 +113,7 @@ void jsonMergeArrays(String& existJson, String& incJson) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
DynamicJsonDocument existJsonDoc(1024);
|
||||
DynamicJsonDocument existJsonDoc(4096);
|
||||
DeserializationError existJsonError = deserializeJson(existJsonDoc, existJson);
|
||||
// if (existJsonError) { // upd: полученный json уже проверен на целостность
|
||||
// SerialPrint("E", F("UDP"), "Invailed json in existing udp dev list " + String(incJsonError.f_str()));
|
||||
|
||||
@@ -31,7 +31,10 @@ void configure(String path) {
|
||||
if (driver = myIoTItem->getGpioDriver()) IoTgpio.regDriver((IoTGpio*)driver);
|
||||
// пробуем спросить драйвер RTC
|
||||
if (driver = myIoTItem->getRtcDriver()) rtcItem = (IoTItem*)driver;
|
||||
|
||||
// пробуем спросить драйвер CAM
|
||||
//if (driver = myIoTItem->getCAMDriver()) camItem = (IoTItem*)driver;
|
||||
// пробуем спросить драйвер Telegram_v2
|
||||
if (driver = myIoTItem->getTlgrmDriver()) tlgrmItem = (IoTItem*)driver;
|
||||
IoTItems.push_back(myIoTItem);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +47,8 @@ void configure(String path) {
|
||||
void clearConfigure() {
|
||||
Serial.printf("Start clearing config\n");
|
||||
rtcItem = nullptr;
|
||||
//camItem = nullptr;
|
||||
tlgrmItem = nullptr;
|
||||
IoTgpio.clearDrivers();
|
||||
|
||||
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "EspFileSystem.h"
|
||||
#include "Global.h"
|
||||
|
||||
bool fileSystemInit() {
|
||||
bool fileSystemInit()
|
||||
{
|
||||
if (!FileFS.begin()) {
|
||||
SerialPrint(F("E"), F("FS"), F("Init ERROR, may be FS was not flashed"));
|
||||
return false;
|
||||
@@ -10,7 +11,8 @@ bool fileSystemInit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void globalVarsSync() {
|
||||
void globalVarsSync()
|
||||
{
|
||||
settingsFlashJson = readFile(F("settings.json"), 4096);
|
||||
settingsFlashJson.replace("\r\n", "");
|
||||
|
||||
@@ -18,44 +20,42 @@ void globalVarsSync() {
|
||||
valuesFlashJson.replace("\r\n", "");
|
||||
|
||||
mqttPrefix = jsonReadStr(settingsFlashJson, F("mqttPrefix"));
|
||||
mqttRootDevice = mqttPrefix + "/" + chipId;
|
||||
jsonWriteStr_(settingsFlashJson, "root", mqttRootDevice);
|
||||
jsonWriteStr_(settingsFlashJson, "id", chipId);
|
||||
|
||||
// jsonWriteStr_(errorsHeapJson, "errors_", ""); //метка для парсинга удалить
|
||||
// jsonWriteStr_(ssidListHeapJson, "ssids_", ""); //метка для парсинга удалить
|
||||
mqttRootDevice = mqttPrefix + "/" + chipId;
|
||||
|
||||
// это не используется - удалить в последствии
|
||||
jsonWriteStr_(settingsFlashJson, "root", mqttRootDevice);
|
||||
}
|
||||
|
||||
//к удалению. не используется
|
||||
// String getParamsJson() {
|
||||
// String json;
|
||||
// serializeJson(*getLocalItemsAsJSON(), json);
|
||||
// jsonWriteStr_(json, "params", "");
|
||||
// return json;
|
||||
// }
|
||||
|
||||
void syncSettingsFlashJson() {
|
||||
void syncSettingsFlashJson()
|
||||
{
|
||||
writeFile(F("settings.json"), settingsFlashJson);
|
||||
}
|
||||
|
||||
void syncValuesFlashJson() {
|
||||
void syncValuesFlashJson()
|
||||
{
|
||||
writeFile(F("values.json"), valuesFlashJson);
|
||||
}
|
||||
|
||||
const String getChipId() {
|
||||
return String(ESP_getChipId()) + "-" + String(ESP_getFlashChipId());
|
||||
const String getChipId()
|
||||
{
|
||||
return String(ESP_getChipId()) + "-" + String(getFlashChipIdNew()); // + "v" + String(FIRMWARE_VERSION);
|
||||
}
|
||||
|
||||
void setChipId() {
|
||||
void setChipId()
|
||||
{
|
||||
chipId = getChipId();
|
||||
SerialPrint("i", "System", "id: " + chipId);
|
||||
}
|
||||
|
||||
const String getUniqueId(const char* name) {
|
||||
const String getUniqueId(const char* name)
|
||||
{
|
||||
return String(name) + getMacAddress();
|
||||
}
|
||||
|
||||
const String getWebVersion() {
|
||||
const String getWebVersion()
|
||||
{
|
||||
String text = readFile("/index.html", 2000);
|
||||
text = selectFromMarkerToMarker(text, "title", 1);
|
||||
text = selectFromMarkerToMarker(text, " ", 2);
|
||||
@@ -64,7 +64,8 @@ const String getWebVersion() {
|
||||
return text;
|
||||
}
|
||||
|
||||
uint32_t ESP_getChipId(void) {
|
||||
uint32_t ESP_getChipId(void)
|
||||
{
|
||||
#ifdef ESP32
|
||||
uint32_t id = 0;
|
||||
for (uint32_t i = 0; i < 17; i = i + 8) {
|
||||
@@ -76,7 +77,11 @@ uint32_t ESP_getChipId(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t ESP_getFlashChipId(void) {
|
||||
// устарела используем новую функцию ниже
|
||||
#if !defined(esp32s2_4mb) && !defined(esp32c3m_4mb) && !defined(esp32s3_16mb)
|
||||
//#ifndef esp32s2_4mb
|
||||
uint32_t ESP_getFlashChipId(void)
|
||||
{
|
||||
#ifdef ESP32
|
||||
// Нет аналогичной (без доп.кода) функций в 32
|
||||
// надо использовать другой id - варианты есть
|
||||
@@ -85,10 +90,38 @@ uint32_t ESP_getFlashChipId(void) {
|
||||
return ESP.getFlashChipId();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
const String getMacAddress() {
|
||||
// https://github.com/espressif/arduino-esp32/issues/6945#issuecomment-1199900892
|
||||
// получение flash ch id из проекта esp easy
|
||||
|
||||
uint32_t getFlashChipIdNew()
|
||||
{
|
||||
// Cache since size does not change
|
||||
static uint32_t flashChipId = 0;
|
||||
|
||||
if (flashChipId == 0) {
|
||||
#ifdef ESP32
|
||||
uint32_t tmp = g_rom_flashchip.device_id;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
flashChipId = flashChipId << 8;
|
||||
flashChipId |= (tmp & 0xFF);
|
||||
tmp = tmp >> 8;
|
||||
}
|
||||
|
||||
// esp_flash_read_id(nullptr, &flashChipId);
|
||||
#elif defined(ESP8266)
|
||||
flashChipId = ESP.getFlashChipId();
|
||||
#endif // ifdef ESP32
|
||||
}
|
||||
return flashChipId;
|
||||
}
|
||||
|
||||
const String getMacAddress()
|
||||
{
|
||||
uint8_t mac[6];
|
||||
char buf[13] = {0};
|
||||
char buf[13] = { 0 };
|
||||
#if defined(ESP8266)
|
||||
WiFi.macAddress(mac);
|
||||
sprintf(buf, MACSTR, MAC2STR(mac));
|
||||
|
||||
@@ -51,16 +51,21 @@ void handleEvent() {
|
||||
if (eventBuf.length()) {
|
||||
String event = selectToMarker(eventBuf, ",");
|
||||
SerialPrint("i", F("EVENT"), event);
|
||||
String enentIdName = selectToMarker(event, " ");
|
||||
String eventIdName = selectToMarker(event, " ");
|
||||
IoTItem* eventIoTItem = findIoTItem(eventIdName);
|
||||
|
||||
if (eventIoTItem)
|
||||
// распространяем событие через хуки
|
||||
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
|
||||
(*it)->onRegEvent(eventIoTItem); // прямой хук
|
||||
|
||||
// распространяем событие через хуки
|
||||
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
|
||||
(*it)->onRegEvent(findIoTItem(enentIdName));
|
||||
}
|
||||
// вызов хука при условии отслеживания изменения
|
||||
if ((*it)->isTracking(eventIoTItem)) (*it)->onTrackingValue(eventIoTItem);
|
||||
}
|
||||
|
||||
//здесь нужно пропускать данное событие через условия сценариев
|
||||
//и если оно есть в условии сценария и совподает
|
||||
iotScen.exec(enentIdName);
|
||||
//и если оно есть в условии сценария и совпадает
|
||||
iotScen.exec(eventIdName);
|
||||
|
||||
eventBuf = deleteBeforeDelimiter(eventBuf, ",");
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ WebSocketsServer standWebSocket = WebSocketsServer(81);
|
||||
**********************************************************************************************************************/
|
||||
IoTGpio IoTgpio(0);
|
||||
IoTItem* rtcItem = nullptr;
|
||||
|
||||
//IoTItem* camItem = nullptr;
|
||||
IoTItem* tlgrmItem = nullptr;
|
||||
String settingsFlashJson = "{}"; // переменная в которой хранятся все настройки, находится в оперативной памяти и синхронизированна с flash памятью
|
||||
String valuesFlashJson = "{}"; // переменная в которой хранятся все значения элементов, которые необходимо сохранить на flash. Находится в оперативной памяти и синхронизированна с flash памятью
|
||||
String errorsHeapJson = "{}"; // переменная в которой хранятся все ошибки, находится в оперативной памяти только
|
||||
|
||||
97
src/Main.cpp
97
src/Main.cpp
@@ -3,14 +3,15 @@
|
||||
#include "classes/IoTDB.h"
|
||||
#include "utils/Statistic.h"
|
||||
#include <Wire.h>
|
||||
#if defined(esp32s2_4mb) || defined(esp32s3_16mb)
|
||||
#include <USB.h>
|
||||
#endif
|
||||
|
||||
IoTScenario iotScen; // объект управления сценарием
|
||||
|
||||
String volStrForSave = "";
|
||||
unsigned long currentMillis;
|
||||
unsigned long prevMillis;
|
||||
|
||||
|
||||
// unsigned long currentMillis; // это сдесь лишнее
|
||||
// unsigned long prevMillis;
|
||||
|
||||
void elementsLoop() {
|
||||
// передаем управление каждому элементу конфигурации для выполнения своих функций
|
||||
@@ -29,69 +30,65 @@ void elementsLoop() {
|
||||
handleEvent();
|
||||
}
|
||||
|
||||
#define SETUPBASE_ERRORMARKER 0
|
||||
#define SETUPCONF_ERRORMARKER 1
|
||||
#define SETUPSCEN_ERRORMARKER 2
|
||||
#define SETUPINET_ERRORMARKER 3
|
||||
#define SETUPLAST_ERRORMARKER 4
|
||||
#define TICKER_ERRORMARKER 5
|
||||
#define HTTP_ERRORMARKER 6
|
||||
#define SOCKETS_ERRORMARKER 7
|
||||
#define MQTT_ERRORMARKER 8
|
||||
#define MODULES_ERRORMARKER 9
|
||||
|
||||
#define COUNTER_ERRORMARKER 4 // количество шагов счетчика
|
||||
#define STEPPER_ERRORMARKER 100000 // размер шага счетчика интервала доверия выполнения блока кода мкс
|
||||
|
||||
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
|
||||
|
||||
|
||||
#define SETUPBASE_ERRORMARKER 0
|
||||
#define SETUPCONF_ERRORMARKER 1
|
||||
#define SETUPSCEN_ERRORMARKER 2
|
||||
#define SETUPINET_ERRORMARKER 3
|
||||
#define SETUPLAST_ERRORMARKER 4
|
||||
#define TICKER_ERRORMARKER 5
|
||||
#define HTTP_ERRORMARKER 6
|
||||
#define SOCKETS_ERRORMARKER 7
|
||||
#define MQTT_ERRORMARKER 8
|
||||
#define MODULES_ERRORMARKER 9
|
||||
|
||||
#define COUNTER_ERRORMARKER 4 // количество шагов счетчика
|
||||
#define STEPPER_ERRORMARKER 100000 // размер шага счетчика интервала доверия выполнения блока кода мкс
|
||||
|
||||
#ifdef esp32_4mb
|
||||
|
||||
static int IRAM_ATTR initErrorMarkerId = 0; // ИД маркера
|
||||
static int IRAM_ATTR initErrorMarkerId = 0; // ИД маркера
|
||||
static int IRAM_ATTR errorMarkerId = 0;
|
||||
static int IRAM_ATTR errorMarkerCounter = 0;
|
||||
|
||||
hw_timer_t *My_timer = NULL;
|
||||
void IRAM_ATTR onTimer(){
|
||||
void IRAM_ATTR onTimer() {
|
||||
if (errorMarkerCounter >= 0) {
|
||||
if (errorMarkerCounter >= COUNTER_ERRORMARKER) {
|
||||
errorMarkerId = initErrorMarkerId;
|
||||
errorMarkerCounter = -1;
|
||||
} else
|
||||
} else
|
||||
errorMarkerCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void initErrorMarker(int id) {
|
||||
#ifdef esp32_4mb
|
||||
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
|
||||
initErrorMarkerId = id;
|
||||
errorMarkerCounter = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void stopErrorMarker(int id) {
|
||||
#ifdef esp32_4mb
|
||||
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
|
||||
errorMarkerCounter = -1;
|
||||
if (errorMarkerId)
|
||||
SerialPrint("I", "WARNING!", "A lazy (freezing loop more than " + (String)(COUNTER_ERRORMARKER * STEPPER_ERRORMARKER / 1000) + " ms) section has been found! With ID=" + (String)errorMarkerId);
|
||||
if (errorMarkerId)
|
||||
SerialPrint("I", "WARNING!", "A lazy (freezing loop more than " + (String)(COUNTER_ERRORMARKER * STEPPER_ERRORMARKER / 1000) + " ms) section has been found! With ID=" + (String)errorMarkerId);
|
||||
errorMarkerId = 0;
|
||||
initErrorMarkerId = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
|
||||
#ifdef esp32_4mb
|
||||
#if defined(esp32s2_4mb) || defined(esp32s3_16mb)
|
||||
USB.begin();
|
||||
#endif
|
||||
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
|
||||
My_timer = timerBegin(0, 80, true);
|
||||
timerAttachInterrupt(My_timer, &onTimer, true);
|
||||
timerAlarmWrite(My_timer, STEPPER_ERRORMARKER, true);
|
||||
timerAlarmEnable(My_timer);
|
||||
//timerAlarmDisable(My_timer);
|
||||
// timerAlarmDisable(My_timer);
|
||||
|
||||
initErrorMarker(SETUPBASE_ERRORMARKER);
|
||||
#endif
|
||||
@@ -102,7 +99,7 @@ void setup() {
|
||||
Serial.println(F("--------------started----------------"));
|
||||
|
||||
// создание экземпляров классов
|
||||
// myNotAsyncActions = new NotAsync(do_LAST);
|
||||
// myNotAsyncActions = new NotAsync(do_LAST);
|
||||
|
||||
// инициализация файловой системы
|
||||
fileSystemInit();
|
||||
@@ -110,6 +107,9 @@ void setup() {
|
||||
Serial.println("FIRMWARE NAME " + String(FIRMWARE_NAME));
|
||||
Serial.println("FIRMWARE VERSION " + String(FIRMWARE_VERSION));
|
||||
Serial.println("WEB VERSION " + getWebVersion());
|
||||
const String buildTime = String(BUILD_DAY) + "." + String(BUILD_MONTH) + "." + String(BUILD_YEAR) + " " + String(BUILD_HOUR) + ":" + String(BUILD_MIN) + ":" + String(BUILD_SEC);
|
||||
Serial.println("BUILD TIME " + buildTime);
|
||||
jsonWriteStr_(errorsHeapJson, F("bt"), buildTime);
|
||||
Serial.println(F("------------------------"));
|
||||
|
||||
// получение chip id
|
||||
@@ -129,7 +129,7 @@ void setup() {
|
||||
jsonRead(settingsFlashJson, "i2cFreq", i2cFreq, false);
|
||||
jsonRead(settingsFlashJson, "i2c", i2c, false);
|
||||
if (i2c != 0) {
|
||||
#ifdef esp32_4mb
|
||||
#ifdef ESP32
|
||||
Wire.end();
|
||||
Wire.begin(pinSDA, pinSCL, (uint32_t)i2cFreq);
|
||||
#else
|
||||
@@ -141,23 +141,19 @@ void setup() {
|
||||
|
||||
// настраиваем микроконтроллер
|
||||
configure("/config.json");
|
||||
|
||||
|
||||
stopErrorMarker(SETUPCONF_ERRORMARKER);
|
||||
|
||||
|
||||
|
||||
initErrorMarker(SETUPSCEN_ERRORMARKER);
|
||||
|
||||
|
||||
// подготавливаем сценарии
|
||||
iotScen.loadScenario("/scenario.txt");
|
||||
// создаем событие завершения инициализации основных моментов для возможности выполнения блока кода при загрузке
|
||||
createItemFromNet("onInit", "1", 1);
|
||||
elementsLoop();
|
||||
|
||||
stopErrorMarker(SETUPSCEN_ERRORMARKER);
|
||||
|
||||
|
||||
|
||||
stopErrorMarker(SETUPSCEN_ERRORMARKER);
|
||||
|
||||
initErrorMarker(SETUPINET_ERRORMARKER);
|
||||
|
||||
// подключаемся к роутеру
|
||||
@@ -181,8 +177,6 @@ void setup() {
|
||||
|
||||
stopErrorMarker(SETUPINET_ERRORMARKER);
|
||||
|
||||
|
||||
|
||||
initErrorMarker(SETUPLAST_ERRORMARKER);
|
||||
|
||||
// NTP
|
||||
@@ -191,11 +185,10 @@ void setup() {
|
||||
// инициализация задач переодического выполнения
|
||||
periodicTasksInit();
|
||||
|
||||
// синхронизация списка устройств
|
||||
addThisDeviceToList();
|
||||
|
||||
// запуск работы udp
|
||||
asyncUdpInit();
|
||||
addThisDeviceToList();
|
||||
udpListningInit();
|
||||
udpBroadcastInit();
|
||||
|
||||
// создаем событие завершения конфигурирования для возможности выполнения блока кода при загрузке
|
||||
createItemFromNet("onStart", "1", 1);
|
||||
@@ -227,8 +220,6 @@ void setup() {
|
||||
stopErrorMarker(SETUPLAST_ERRORMARKER);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void loop() {
|
||||
#ifdef LOOP_DEBUG
|
||||
unsigned long st = millis();
|
||||
@@ -253,7 +244,7 @@ void loop() {
|
||||
initErrorMarker(MQTT_ERRORMARKER);
|
||||
mqttLoop();
|
||||
stopErrorMarker(MQTT_ERRORMARKER);
|
||||
|
||||
|
||||
initErrorMarker(MODULES_ERRORMARKER);
|
||||
elementsLoop();
|
||||
stopErrorMarker(MODULES_ERRORMARKER);
|
||||
|
||||
@@ -5,8 +5,8 @@ void mqttInit() {
|
||||
ts.add(
|
||||
WIFI_MQTT_CONNECTION_CHECK, MQTT_RECONNECT_INTERVAL,
|
||||
[&](void*) {
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
SerialPrint("i", F("WIFI"), "OK: " + jsonReadStr(settingsFlashJson, F("ip")));
|
||||
if (isNetworkActive()) {
|
||||
SerialPrint("i", F("WIFI"), "http://" + jsonReadStr(settingsFlashJson, F("ip")));
|
||||
wifiUptimeCalc();
|
||||
if (mqtt.connected()) {
|
||||
SerialPrint("i", F("MQTT"), "OK");
|
||||
@@ -95,6 +95,10 @@ void mqttReconnect() {
|
||||
mqttConnect();
|
||||
}
|
||||
|
||||
bool mqttIsConnect(){
|
||||
return mqtt.connected();
|
||||
}
|
||||
|
||||
void getMqttData() {
|
||||
mqttServer = jsonReadStr(settingsFlashJson, F("mqttServer"));
|
||||
mqttPort = jsonReadInt(settingsFlashJson, F("mqttPort"));
|
||||
@@ -114,6 +118,28 @@ void mqttSubscribe() {
|
||||
mqtt.subscribe((mqttPrefix + "/+/+/order/#").c_str());
|
||||
mqtt.subscribe((mqttPrefix + "/+/+/info").c_str());
|
||||
}
|
||||
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
|
||||
if ((*it)->getSubtype() == "ExternalMQTT") {
|
||||
String tmps = (*it)->getMqttExterSub();
|
||||
if (tmps != ""){
|
||||
mqtt.subscribe(tmps.c_str());
|
||||
SerialPrint("i", F("MQTT"), ("subscribed external " + tmps).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mqttSubscribeExternal(String topic, bool usePrefix) {
|
||||
|
||||
// SerialPrint("i", F("MQTT"), mqttRootDevice);
|
||||
String _sb_topic = topic;
|
||||
if (usePrefix)
|
||||
{
|
||||
_sb_topic = mqttPrefix + "/" + topic;
|
||||
}
|
||||
mqtt.subscribe(_sb_topic.c_str());
|
||||
|
||||
SerialPrint("i", F("MQTT"), ("subscribed external " + _sb_topic).c_str());
|
||||
}
|
||||
|
||||
void mqttCallback(char* topic, uint8_t* payload, size_t length) {
|
||||
@@ -133,10 +159,10 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) {
|
||||
if (payloadStr.startsWith("HELLO")) {
|
||||
SerialPrint("i", F("MQTT"), F("Full update"));
|
||||
|
||||
//публикация всех виджетов
|
||||
// публикация всех виджетов
|
||||
publishWidgets();
|
||||
|
||||
//публикация всех статус сообщений при подключении приложения и генерация события подключения приложения в модулях
|
||||
// публикация всех статус сообщений при подключении приложения и генерация события подключения приложения в модулях
|
||||
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
|
||||
if ((*it)->iAmLocal) {
|
||||
publishStatusMqtt((*it)->getID(), (*it)->getValue());
|
||||
@@ -144,7 +170,7 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
//отправка данных графиков - данный код будет оптимизирован после завершения написания приложения с новыми графиками
|
||||
// отправка данных графиков - данный код будет оптимизирован после завершения написания приложения с новыми графиками
|
||||
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
|
||||
if ((*it)->getSubtype() == "Loging" || "LogingDaily") {
|
||||
(*it)->setPublishDestination(TO_MQTT);
|
||||
@@ -162,16 +188,17 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) {
|
||||
|
||||
else if (topicStr.indexOf(F("control")) != -1) {
|
||||
String key = selectFromMarkerToMarker(topicStr, "/", 3);
|
||||
|
||||
String valueIfJson = ""; // проверяем формат, если json то берем статус, иначе - как есть
|
||||
|
||||
String valueIfJson = ""; // проверяем формат, если json то берем статус, иначе - как есть
|
||||
if (!jsonRead(payloadStr, F("status"), valueIfJson, false))
|
||||
generateOrder(key, payloadStr);
|
||||
else generateOrder(key, valueIfJson);
|
||||
else
|
||||
generateOrder(key, valueIfJson);
|
||||
|
||||
SerialPrint("i", F("=>MQTT"), "Msg from iotmanager app: " + key + " " + payloadStr);
|
||||
}
|
||||
|
||||
//здесь мы получаем события с других устройств, которые потом проверяются в сценариях этого устройства
|
||||
// здесь мы получаем события с других устройств, которые потом проверяются в сценариях этого устройства
|
||||
else if (topicStr.indexOf("event") != -1) {
|
||||
if (!jsonReadBool(settingsFlashJson, "mqttin")) {
|
||||
return;
|
||||
@@ -251,7 +278,7 @@ void publishWidgets() {
|
||||
DeserializationError error = deserializeJson(doc, file);
|
||||
if (error) {
|
||||
SerialPrint("E", F("MQTT"), error.f_str());
|
||||
jsonWriteInt(errorsHeapJson, F("jse3"), 1); //Ошибка чтения json файла с виджетами при отправки в mqtt
|
||||
jsonWriteInt(errorsHeapJson, F("jse3"), 1); // Ошибка чтения json файла с виджетами при отправки в mqtt
|
||||
}
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
for (JsonVariant value : arr) {
|
||||
@@ -292,43 +319,43 @@ void handleMqttStatus(bool send, int state) {
|
||||
|
||||
const String getStateStr(int e) {
|
||||
switch (e) {
|
||||
case -4: //Нет ответа от сервера
|
||||
case -4: // Нет ответа от сервера
|
||||
return F("e1");
|
||||
break;
|
||||
case -3: //Соединение было разорвано
|
||||
case -3: // Соединение было разорвано
|
||||
return F("e2");
|
||||
break;
|
||||
case -2: //Ошибка соединения. Обычно возникает когда неверно указано название сервера MQTT
|
||||
case -2: // Ошибка соединения. Обычно возникает когда неверно указано название сервера MQTT
|
||||
return F("e3");
|
||||
break;
|
||||
case -1: //Клиент был отключен
|
||||
case -1: // Клиент был отключен
|
||||
return F("e4");
|
||||
break;
|
||||
case 0: //подключено
|
||||
case 0: // подключено
|
||||
return F("e5");
|
||||
break;
|
||||
case 1: //Ошибка версии
|
||||
case 1: // Ошибка версии
|
||||
return F("e6");
|
||||
break;
|
||||
case 2: //Отклонен идентификатор
|
||||
case 2: // Отклонен идентификатор
|
||||
return F("e7");
|
||||
break;
|
||||
case 3: //Не могу установить соединение
|
||||
case 3: // Не могу установить соединение
|
||||
return F("e8");
|
||||
break;
|
||||
case 4: //Неправильное имя пользователя/пароль
|
||||
case 4: // Неправильное имя пользователя/пароль
|
||||
return F("e9");
|
||||
break;
|
||||
case 5: //Не авторизован для подключения
|
||||
case 5: // Не авторизован для подключения
|
||||
return F("e10");
|
||||
break;
|
||||
case 6: //Название сервера пустое
|
||||
case 6: // Название сервера пустое
|
||||
return F("e11");
|
||||
break;
|
||||
case 7: //Имя пользователя или пароль пустые
|
||||
case 7: // Имя пользователя или пароль пустые
|
||||
return F("e12");
|
||||
break;
|
||||
case 8: //Подключение в процессе
|
||||
case 8: // Подключение в процессе
|
||||
return F("e13");
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -184,6 +184,14 @@ const String getDateTimeDotFormatedFromUnix(unsigned long unixTime) {
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
const String getTimeDotFormatedFromUnix(unsigned long unixTime) {
|
||||
Time_t time;
|
||||
breakEpochToTime(unixTime, time);
|
||||
char buf[32];
|
||||
sprintf(buf, "%02d:%02d:%02d", time.hour, time.minute, time.second);
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
const String getDateDotFormatedFromUnix(unsigned long unixTime) {
|
||||
Time_t time;
|
||||
breakEpochToTime(unixTime, time);
|
||||
|
||||
@@ -58,7 +58,50 @@ void printGlobalVarSize() {
|
||||
String ESP_getResetReason(void) {
|
||||
return ESP.getResetReason();
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
#if defined(esp32s2_4mb) || defined(esp32s3_16mb) || defined(esp32c3m_4mb)
|
||||
String ESP_getResetReason(void) {
|
||||
return ESP32GetResetReason(0); // CPU 0
|
||||
}
|
||||
String ESP32GetResetReason(uint32_t cpu_no) {
|
||||
// tools\sdk\include\esp32\rom\rtc.h
|
||||
switch (rtc_get_reset_reason((RESET_REASON)cpu_no)) {
|
||||
case POWERON_RESET:
|
||||
return F("Vbat power on reset"); // 1
|
||||
case RTC_SW_SYS_RESET:
|
||||
return F("Software reset digital core"); // 3
|
||||
// case OWDT_RESET:
|
||||
// return F("Legacy Watchdog reset digital core"); // 4
|
||||
case DEEPSLEEP_RESET:
|
||||
return F("Deep Sleep reset digital core"); // 5
|
||||
// case SDIO_RESET:
|
||||
// return F("Reset by SLC module, reset digital core"); // 6
|
||||
case TG0WDT_SYS_RESET:
|
||||
return F("Timer Group0 Watchdog reset digital core"); // 7
|
||||
case TG1WDT_SYS_RESET:
|
||||
return F("Timer Group1 Watchdog reset digital core"); // 8
|
||||
case RTCWDT_SYS_RESET:
|
||||
return F("RTC Watchdog Reset digital core"); // 9
|
||||
case INTRUSION_RESET:
|
||||
return F("Instrusion tested to reset CPU"); // 10
|
||||
case TG0WDT_CPU_RESET:
|
||||
return F("Time Group reset CPU"); // 11
|
||||
case RTC_SW_CPU_RESET:
|
||||
return F("Software reset CPU"); // 12
|
||||
case RTCWDT_CPU_RESET:
|
||||
return F("RTC Watchdog Reset CPU"); // 13
|
||||
// case EXT_CPU_RESET:
|
||||
// return F("or APP CPU, reseted by PRO CPU"); // 14
|
||||
case RTCWDT_BROWN_OUT_RESET:
|
||||
return F("Reset when the vdd voltage is not stable"); // 15
|
||||
case RTCWDT_RTC_RESET:
|
||||
return F("RTC Watchdog reset digital core and rtc module"); // 16
|
||||
default:
|
||||
return F("NO_MEAN"); // 0
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
|
||||
String ESP_getResetReason(void) {
|
||||
return ESP32GetResetReason(0); // CPU 0
|
||||
}
|
||||
|
||||
@@ -7,24 +7,22 @@ String unsupportedFiles = String();
|
||||
static const char TEXT_PLAIN[] PROGMEM = "text/plain";
|
||||
static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR";
|
||||
static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound";
|
||||
//static bool fsOK;
|
||||
//const char* fsName = "LittleFS";
|
||||
// static bool fsOK;
|
||||
// const char* fsName = "LittleFS";
|
||||
|
||||
void standWebServerInit()
|
||||
{
|
||||
void standWebServerInit() {
|
||||
// Кэшировать файлы для быстрой работы
|
||||
HTTP.serveStatic("/bundle.js", FileFS, "/", "max-age=31536000"); // кеширование на 1 год
|
||||
HTTP.serveStatic("/bundle.css", FileFS, "/", "max-age=31536000"); // кеширование на 1 год
|
||||
HTTP.serveStatic("/bundle.js.gz", FileFS, "/", "max-age=31536000"); // кеширование на 1 год
|
||||
HTTP.serveStatic("/bundle.css.gz", FileFS, "/", "max-age=31536000"); // кеширование на 1 год
|
||||
HTTP.serveStatic("/favicon.png", FileFS, "/", "max-age=31536000"); // кеширование на 1 год
|
||||
// если указана директория то все файлы будут отмечены как Directory Request Handler
|
||||
// если указан файл то он будет отмечен как File Request Handler
|
||||
HTTP.serveStatic("/build", FileFS, "/build", "max-age=31536000"); // кеширование на 1 год
|
||||
HTTP.serveStatic("/favicon.ico", FileFS, "/favicon.ico", "max-age=31536000"); // кеширование на 1 год
|
||||
|
||||
// HTTP.on("/devicelist.json", HTTP_GET, []() {
|
||||
// HTTP.send(200, "application/json", devListHeapJson);
|
||||
// });
|
||||
HTTP.on("/settings.h.json", HTTP_GET, []() {
|
||||
HTTP.send(200, "application/json", settingsFlashJson);
|
||||
});
|
||||
// HTTP.on("/settings.h.json", HTTP_GET, []() {
|
||||
// HTTP.send(200, "application/json", settingsFlashJson);
|
||||
//});
|
||||
// HTTP.on("/settings.f.json", HTTP_GET, []() {
|
||||
// HTTP.send(200, "application/json", readFile(F("settings.json"), 20000));
|
||||
// });
|
||||
@@ -46,21 +44,19 @@ void standWebServerInit()
|
||||
// HTTP.send(200, "text/plain", "ok");
|
||||
// });
|
||||
|
||||
|
||||
HTTP.on("/set", HTTP_GET, []() {
|
||||
if (HTTP.hasArg(F("routerssid")) && WiFi.getMode() == WIFI_AP) {
|
||||
jsonWriteStr(settingsFlashJson, F("routerssid"), HTTP.arg(F("routerssid")));
|
||||
syncSettingsFlashJson();
|
||||
HTTP.send(200, "text/plain", "ok");
|
||||
}
|
||||
jsonWriteStr(settingsFlashJson, F("routerssid"), HTTP.arg(F("routerssid")));
|
||||
syncSettingsFlashJson();
|
||||
HTTP.send(200, "text/plain", "ok");
|
||||
}
|
||||
|
||||
if (HTTP.hasArg(F("routerpass")) && WiFi.getMode() == WIFI_AP) {
|
||||
jsonWriteStr(settingsFlashJson, F("routerpass"), HTTP.arg(F("routerpass")));
|
||||
syncSettingsFlashJson();
|
||||
HTTP.send(200, "text/plain", "ok");
|
||||
}
|
||||
|
||||
});
|
||||
jsonWriteStr(settingsFlashJson, F("routerpass"), HTTP.arg(F("routerpass")));
|
||||
syncSettingsFlashJson();
|
||||
HTTP.send(200, "text/plain", "ok");
|
||||
}
|
||||
});
|
||||
|
||||
// Добавляем функцию Update для перезаписи прошивки по WiFi при 1М(256K FileFS) и выше
|
||||
// httpUpdater.setup(&HTTP);
|
||||
@@ -101,39 +97,33 @@ void standWebServerInit()
|
||||
////////////////////////////////
|
||||
// Utils to return HTTP codes, and determine content-type
|
||||
|
||||
void replyOK()
|
||||
{
|
||||
void replyOK() {
|
||||
HTTP.send(200, FPSTR(TEXT_PLAIN), "");
|
||||
}
|
||||
|
||||
void replyOKWithMsg(String msg)
|
||||
{
|
||||
void replyOKWithMsg(String msg) {
|
||||
HTTP.send(200, FPSTR(TEXT_PLAIN), msg);
|
||||
}
|
||||
|
||||
void replyNotFound(String msg)
|
||||
{
|
||||
void replyNotFound(String msg) {
|
||||
HTTP.send(404, FPSTR(TEXT_PLAIN), msg);
|
||||
}
|
||||
|
||||
void replyBadRequest(String msg)
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println(msg);
|
||||
void replyBadRequest(String msg) {
|
||||
// DBG_OUTPUT_PORT.println(msg);
|
||||
HTTP.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n");
|
||||
}
|
||||
|
||||
void replyServerError(String msg)
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println(msg);
|
||||
void replyServerError(String msg) {
|
||||
// DBG_OUTPUT_PORT.println(msg);
|
||||
HTTP.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n");
|
||||
}
|
||||
|
||||
/*
|
||||
Return the FS type, status and size info
|
||||
*/
|
||||
void handleStatus()
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println("handleStatus");
|
||||
void handleStatus() {
|
||||
// DBG_OUTPUT_PORT.println("handleStatus");
|
||||
String json;
|
||||
json.reserve(128);
|
||||
|
||||
@@ -141,22 +131,22 @@ void handleStatus()
|
||||
json += FS_NAME;
|
||||
json += "\", \"isOk\":";
|
||||
|
||||
#ifdef ESP8266
|
||||
#ifdef ESP8266
|
||||
FSInfo fs_info;
|
||||
|
||||
FileFS.info(fs_info);
|
||||
json += F("\"true\", \"totalBytes\":\"");
|
||||
json += fs_info.totalBytes;
|
||||
json += F("\", \"usedBytes\":\"");
|
||||
json += fs_info.usedBytes;
|
||||
json += "\"";
|
||||
#endif
|
||||
FileFS.info(fs_info);
|
||||
json += F("\"true\", \"totalBytes\":\"");
|
||||
json += fs_info.totalBytes;
|
||||
json += F("\", \"usedBytes\":\"");
|
||||
json += fs_info.usedBytes;
|
||||
json += "\"";
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
json += F("\"true\", \"totalBytes\":\"");
|
||||
json += String(FileFS.totalBytes());
|
||||
json += F("\", \"usedBytes\":\"");
|
||||
json += String(FileFS.usedBytes());
|
||||
json += "\"";
|
||||
json += F("\"true\", \"totalBytes\":\"");
|
||||
json += String(FileFS.totalBytes());
|
||||
json += F("\", \"usedBytes\":\"");
|
||||
json += String(FileFS.usedBytes());
|
||||
json += "\"";
|
||||
#endif
|
||||
|
||||
json += F(",\"unsupportedFiles\":\"");
|
||||
@@ -168,74 +158,66 @@ void handleStatus()
|
||||
|
||||
#ifdef ESP32
|
||||
String getContentType(String filename) {
|
||||
if (HTTP.hasArg("download")) {
|
||||
return "application/octet-stream";
|
||||
} else if (filename.endsWith(".htm")) {
|
||||
return "text/html";
|
||||
} else if (filename.endsWith(".html")) {
|
||||
return "text/html";
|
||||
} else if (filename.endsWith(".css")) {
|
||||
return "text/css";
|
||||
} else if (filename.endsWith(".js")) {
|
||||
return "application/javascript";
|
||||
} else if (filename.endsWith(".png")) {
|
||||
return "image/png";
|
||||
} else if (filename.endsWith(".gif")) {
|
||||
return "image/gif";
|
||||
} else if (filename.endsWith(".jpg")) {
|
||||
return "image/jpeg";
|
||||
} else if (filename.endsWith(".ico")) {
|
||||
return "image/x-icon";
|
||||
} else if (filename.endsWith(".xml")) {
|
||||
return "text/xml";
|
||||
} else if (filename.endsWith(".pdf")) {
|
||||
return "application/x-pdf";
|
||||
} else if (filename.endsWith(".zip")) {
|
||||
return "application/x-zip";
|
||||
} else if (filename.endsWith(".gz")) {
|
||||
return "application/x-gzip";
|
||||
}
|
||||
return "text/plain";
|
||||
if (HTTP.hasArg("download")) {
|
||||
return "application/octet-stream";
|
||||
} else if (filename.endsWith(".htm")) {
|
||||
return "text/html";
|
||||
} else if (filename.endsWith(".html")) {
|
||||
return "text/html";
|
||||
} else if (filename.endsWith(".css")) {
|
||||
return "text/css";
|
||||
} else if (filename.endsWith(".js")) {
|
||||
return "application/javascript";
|
||||
} else if (filename.endsWith(".png")) {
|
||||
return "image/png";
|
||||
} else if (filename.endsWith(".gif")) {
|
||||
return "image/gif";
|
||||
} else if (filename.endsWith(".jpg")) {
|
||||
return "image/jpeg";
|
||||
} else if (filename.endsWith(".ico")) {
|
||||
return "image/x-icon";
|
||||
} else if (filename.endsWith(".xml")) {
|
||||
return "text/xml";
|
||||
} else if (filename.endsWith(".pdf")) {
|
||||
return "application/x-pdf";
|
||||
} else if (filename.endsWith(".zip")) {
|
||||
return "application/x-zip";
|
||||
} else if (filename.endsWith(".gz")) {
|
||||
return "application/x-gzip";
|
||||
}
|
||||
return "text/plain";
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Read the given file from the filesystem and stream it back to the client
|
||||
*/
|
||||
bool handleFileRead(String path)
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path);
|
||||
if (path.endsWith("/"))
|
||||
{
|
||||
bool handleFileRead(String path) {
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path);
|
||||
if (path.endsWith("/")) {
|
||||
path += "index.html";
|
||||
}
|
||||
|
||||
String contentType;
|
||||
if (HTTP.hasArg("download"))
|
||||
{
|
||||
if (HTTP.hasArg("download")) {
|
||||
contentType = F("application/octet-stream");
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ESP32
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
contentType = getContentType(path);
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
contentType = mime::getContentType(path);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!FileFS.exists(path))
|
||||
{
|
||||
if (!FileFS.exists(path)) {
|
||||
// File not found, try gzip version
|
||||
path = path + ".gz";
|
||||
}
|
||||
if (FileFS.exists(path))
|
||||
{
|
||||
if (FileFS.exists(path)) {
|
||||
File file = FileFS.open(path, "r");
|
||||
if (HTTP.streamFile(file, contentType) != file.size())
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println("Sent less data than expected!");
|
||||
if (HTTP.streamFile(file, contentType) != file.size()) {
|
||||
// DBG_OUTPUT_PORT.println("Sent less data than expected!");
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
@@ -248,90 +230,69 @@ bool handleFileRead(String path)
|
||||
As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed,
|
||||
return the path of the closest parent still existing
|
||||
*/
|
||||
String lastExistingParent(String path)
|
||||
{
|
||||
while (!path.isEmpty() && !FileFS.exists(path))
|
||||
{
|
||||
if (path.lastIndexOf('/') > 0)
|
||||
{
|
||||
String lastExistingParent(String path) {
|
||||
while (!path.isEmpty() && !FileFS.exists(path)) {
|
||||
if (path.lastIndexOf('/') > 0) {
|
||||
path = path.substring(0, path.lastIndexOf('/'));
|
||||
}
|
||||
else
|
||||
{
|
||||
path = String(); // No slash => the top folder does not exist
|
||||
} else {
|
||||
path = String(); // No slash => the top folder does not exist
|
||||
}
|
||||
}
|
||||
// DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path);
|
||||
// DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path);
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
Handle a file upload request
|
||||
*/
|
||||
void handleFileUpload()
|
||||
{
|
||||
if (HTTP.uri() != "/edit")
|
||||
{
|
||||
void handleFileUpload() {
|
||||
if (HTTP.uri() != "/edit") {
|
||||
return;
|
||||
}
|
||||
HTTPUpload &upload = HTTP.upload();
|
||||
if (upload.status == UPLOAD_FILE_START)
|
||||
{
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
String filename = upload.filename;
|
||||
// Make sure paths always start with "/"
|
||||
if (!filename.startsWith("/"))
|
||||
{
|
||||
if (!filename.startsWith("/")) {
|
||||
filename = "/" + filename;
|
||||
}
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename);
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename);
|
||||
uploadFile = FileFS.open(filename, "w");
|
||||
if (!uploadFile)
|
||||
{
|
||||
if (!uploadFile) {
|
||||
return replyServerError(F("CREATE FAILED"));
|
||||
}
|
||||
// DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename);
|
||||
}
|
||||
else if (upload.status == UPLOAD_FILE_WRITE)
|
||||
{
|
||||
if (uploadFile)
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename);
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (uploadFile) {
|
||||
size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize);
|
||||
if (bytesWritten != upload.currentSize)
|
||||
{
|
||||
if (bytesWritten != upload.currentSize) {
|
||||
return replyServerError(F("WRITE FAILED"));
|
||||
}
|
||||
}
|
||||
// DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize);
|
||||
}
|
||||
else if (upload.status == UPLOAD_FILE_END)
|
||||
{
|
||||
if (uploadFile)
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize);
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
if (uploadFile) {
|
||||
uploadFile.close();
|
||||
}
|
||||
// DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize);
|
||||
// DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef ESP8266
|
||||
void deleteRecursive(String path)
|
||||
{
|
||||
void deleteRecursive(String path) {
|
||||
File file = FileFS.open(path, "r");
|
||||
bool isDir = file.isDirectory();
|
||||
file.close();
|
||||
|
||||
// If it's a plain file, delete it
|
||||
if (!isDir)
|
||||
{
|
||||
if (!isDir) {
|
||||
FileFS.remove(path);
|
||||
return;
|
||||
}
|
||||
Dir dir = FileFS.openDir(path);
|
||||
while (dir.next())
|
||||
{
|
||||
while (dir.next()) {
|
||||
deleteRecursive(path + '/' + dir.fileName());
|
||||
}
|
||||
}
|
||||
|
||||
// Then delete the folder itself
|
||||
FileFS.rmdir(path);
|
||||
@@ -339,41 +300,37 @@ void deleteRecursive(String path)
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
struct treename{
|
||||
uint8_t type;
|
||||
char *name;
|
||||
struct treename {
|
||||
uint8_t type;
|
||||
char *name;
|
||||
};
|
||||
|
||||
void deleteRecursive(String path) {
|
||||
fs::File dir = FileFS.open(path);
|
||||
|
||||
void deleteRecursive( String path ){
|
||||
fs::File dir = FileFS.open( path );
|
||||
|
||||
if(!dir.isDirectory()){
|
||||
Serial.printf("%s is a file\n", path);
|
||||
dir.close();
|
||||
Serial.printf( "result of removing file %s: %d\n", path, FileFS.remove( path ) );
|
||||
return;
|
||||
if (!dir.isDirectory()) {
|
||||
Serial.printf("%s is a file\n", path);
|
||||
dir.close();
|
||||
Serial.printf("result of removing file %s: %d\n", path, FileFS.remove(path));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Serial.printf("%s is a directory\n", path);
|
||||
|
||||
|
||||
fs::File entry, nextentry;
|
||||
|
||||
while ( entry = dir.openNextFile() ){
|
||||
|
||||
if ( entry.isDirectory() ){
|
||||
deleteRecursive( entry.path() );
|
||||
} else{
|
||||
String tmpname = path+"/"+strdup( entry.name() ); // buffer file name
|
||||
entry.close();
|
||||
Serial.printf( "result of removing file %s: %d\n", tmpname, FileFS.remove( tmpname ) );
|
||||
}
|
||||
|
||||
|
||||
while (entry = dir.openNextFile()) {
|
||||
if (entry.isDirectory()) {
|
||||
deleteRecursive(entry.path());
|
||||
} else {
|
||||
String tmpname = path + "/" + strdup(entry.name()); // buffer file name
|
||||
entry.close();
|
||||
Serial.printf("result of removing file %s: %d\n", tmpname, FileFS.remove(tmpname));
|
||||
}
|
||||
}
|
||||
|
||||
dir.close();
|
||||
Serial.printf( "result of removing directory %s: %d\n", path, FileFS.rmdir( path ) );
|
||||
|
||||
dir.close();
|
||||
Serial.printf("result of removing directory %s: %d\n", path, FileFS.rmdir(path));
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
@@ -383,23 +340,18 @@ if ( entry.isDirectory() ){
|
||||
Delete file | parent of deleted file, or remaining ancestor
|
||||
Delete folder | parent of deleted folder, or remaining ancestor
|
||||
*/
|
||||
void handleFileDelete()
|
||||
{
|
||||
void handleFileDelete() {
|
||||
String path = HTTP.arg(0);
|
||||
if (path.isEmpty() || path == "/")
|
||||
{
|
||||
if (path.isEmpty() || path == "/") {
|
||||
return replyBadRequest("BAD PATH");
|
||||
}
|
||||
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path);
|
||||
if (!FileFS.exists(path))
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path);
|
||||
if (!FileFS.exists(path)) {
|
||||
return replyNotFound(FPSTR(FILE_NOT_FOUND));
|
||||
}
|
||||
deleteRecursive(path);
|
||||
|
||||
|
||||
|
||||
replyOKWithMsg(lastExistingParent(path));
|
||||
}
|
||||
|
||||
@@ -414,93 +366,72 @@ void handleFileDelete()
|
||||
Rename folder | parent of source folder
|
||||
Move folder | parent of source folder, or remaining ancestor
|
||||
*/
|
||||
void handleFileCreate()
|
||||
{
|
||||
void handleFileCreate() {
|
||||
String path = HTTP.arg("path");
|
||||
if (path.isEmpty())
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return replyBadRequest(F("PATH ARG MISSING"));
|
||||
}
|
||||
|
||||
#ifdef USE_SPIFFS
|
||||
if (checkForUnsupportedPath(path).length() > 0)
|
||||
{
|
||||
if (checkForUnsupportedPath(path).length() > 0) {
|
||||
return replyServerError(F("INVALID FILENAME"));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (path == "/")
|
||||
{
|
||||
if (path == "/") {
|
||||
return replyBadRequest("BAD PATH");
|
||||
}
|
||||
if (FileFS.exists(path))
|
||||
{
|
||||
if (FileFS.exists(path)) {
|
||||
return replyBadRequest(F("PATH FILE EXISTS"));
|
||||
}
|
||||
|
||||
String src = HTTP.arg("src");
|
||||
if (src.isEmpty())
|
||||
{
|
||||
if (src.isEmpty()) {
|
||||
// No source specified: creation
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path);
|
||||
if (path.endsWith("/"))
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path);
|
||||
if (path.endsWith("/")) {
|
||||
// Create a folder
|
||||
path.remove(path.length() - 1);
|
||||
if (!FileFS.mkdir(path))
|
||||
{
|
||||
if (!FileFS.mkdir(path)) {
|
||||
return replyServerError(F("MKDIR FAILED"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Create a file
|
||||
File file = FileFS.open(path, "w");
|
||||
if (file)
|
||||
{
|
||||
#ifdef ESP8266
|
||||
if (file) {
|
||||
#ifdef ESP8266
|
||||
file.write((const char *)0);
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
file.write(0);
|
||||
#endif
|
||||
#endif
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return replyServerError(F("CREATE FAILED"));
|
||||
}
|
||||
}
|
||||
if (path.lastIndexOf('/') > -1)
|
||||
{
|
||||
if (path.lastIndexOf('/') > -1) {
|
||||
path = path.substring(0, path.lastIndexOf('/'));
|
||||
}
|
||||
replyOKWithMsg(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Source specified: rename
|
||||
if (src == "/")
|
||||
{
|
||||
if (src == "/") {
|
||||
return replyBadRequest("BAD SRC");
|
||||
}
|
||||
if (!FileFS.exists(src))
|
||||
{
|
||||
if (!FileFS.exists(src)) {
|
||||
return replyBadRequest(F("SRC FILE NOT FOUND"));
|
||||
}
|
||||
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src);
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src);
|
||||
|
||||
if (path.endsWith("/"))
|
||||
{
|
||||
if (path.endsWith("/")) {
|
||||
path.remove(path.length() - 1);
|
||||
}
|
||||
if (src.endsWith("/"))
|
||||
{
|
||||
if (src.endsWith("/")) {
|
||||
src.remove(src.length() - 1);
|
||||
}
|
||||
if (!FileFS.rename(src, path))
|
||||
{
|
||||
if (!FileFS.rename(src, path)) {
|
||||
return replyServerError(F("RENAME FAILED"));
|
||||
}
|
||||
replyOKWithMsg(lastExistingParent(src));
|
||||
@@ -512,26 +443,22 @@ void handleFileCreate()
|
||||
Also demonstrates the use of chunked responses.
|
||||
*/
|
||||
#ifdef ESP8266
|
||||
void handleFileList()
|
||||
{
|
||||
if (!HTTP.hasArg("dir"))
|
||||
{
|
||||
void handleFileList() {
|
||||
if (!HTTP.hasArg("dir")) {
|
||||
return replyBadRequest(F("DIR ARG MISSING"));
|
||||
}
|
||||
|
||||
String path = HTTP.arg("dir");
|
||||
if (path != "/" && !FileFS.exists(path))
|
||||
{
|
||||
if (path != "/" && !FileFS.exists(path)) {
|
||||
return replyBadRequest("BAD PATH");
|
||||
}
|
||||
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileList: ") + path);
|
||||
// DBG_OUTPUT_PORT.println(String("handleFileList: ") + path);
|
||||
Dir dir = FileFS.openDir(path);
|
||||
path.clear();
|
||||
|
||||
// use HTTP/1.1 Chunked response to avoid building a huge temporary string
|
||||
if (!HTTP.chunkedResponseModeStart(200, "text/json"))
|
||||
{
|
||||
if (!HTTP.chunkedResponseModeStart(200, "text/json")) {
|
||||
HTTP.send(505, F("text/html"), F("HTTP1.1 required"));
|
||||
return;
|
||||
}
|
||||
@@ -539,47 +466,36 @@ void handleFileList()
|
||||
// use the same string for every line
|
||||
String output;
|
||||
output.reserve(64);
|
||||
while (dir.next())
|
||||
{
|
||||
while (dir.next()) {
|
||||
#ifdef USE_SPIFFS
|
||||
String error = checkForUnsupportedPath(dir.fileName());
|
||||
if (error.length() > 0)
|
||||
{
|
||||
// DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName());
|
||||
if (error.length() > 0) {
|
||||
// DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName());
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (output.length())
|
||||
{
|
||||
if (output.length()) {
|
||||
// send string from previous iteration
|
||||
// as an HTTP chunk
|
||||
HTTP.sendContent(output);
|
||||
output = ',';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
output = '[';
|
||||
}
|
||||
|
||||
output += "{\"type\":\"";
|
||||
if (dir.isDirectory())
|
||||
{
|
||||
if (dir.isDirectory()) {
|
||||
output += "dir";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
output += F("file\",\"size\":\"");
|
||||
output += dir.fileSize();
|
||||
}
|
||||
|
||||
output += F("\",\"name\":\"");
|
||||
// Always return names without leading "/"
|
||||
if (dir.fileName()[0] == '/')
|
||||
{
|
||||
if (dir.fileName()[0] == '/') {
|
||||
output += &(dir.fileName()[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
output += dir.fileName();
|
||||
}
|
||||
|
||||
@@ -595,65 +511,57 @@ void handleFileList()
|
||||
|
||||
#ifdef ESP32
|
||||
void handleFileList() {
|
||||
if (!HTTP.hasArg("dir")) {
|
||||
HTTP.send(500, "text/plain", "BAD ARGS");
|
||||
return;
|
||||
}
|
||||
if (!HTTP.hasArg("dir")) {
|
||||
HTTP.send(500, "text/plain", "BAD ARGS");
|
||||
return;
|
||||
}
|
||||
|
||||
String path = HTTP.arg("dir");
|
||||
// DBG_OUTPUT_PORT.println("handleFileList: " + path);
|
||||
String path = HTTP.arg("dir");
|
||||
// DBG_OUTPUT_PORT.println("handleFileList: " + path);
|
||||
|
||||
File root = FileFS.open(path);
|
||||
path = String();
|
||||
|
||||
File root = FileFS.open(path);
|
||||
path = String();
|
||||
String output = "[";
|
||||
if (root.isDirectory()) {
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (output != "[") {
|
||||
output += ',';
|
||||
}
|
||||
output += "{\"type\":\"";
|
||||
// output += (file.isDirectory()) ? "dir" : "file";
|
||||
if (file.isDirectory()) {
|
||||
output += "dir";
|
||||
} else {
|
||||
output += F("file\",\"size\":\"");
|
||||
output += file.size();
|
||||
}
|
||||
|
||||
String output = "[";
|
||||
if(root.isDirectory()){
|
||||
File file = root.openNextFile();
|
||||
while(file){
|
||||
if (output != "[") {
|
||||
output += ',';
|
||||
}
|
||||
output += "{\"type\":\"";
|
||||
// output += (file.isDirectory()) ? "dir" : "file";
|
||||
if (file.isDirectory())
|
||||
{
|
||||
output += "dir";
|
||||
}
|
||||
else
|
||||
{
|
||||
output += F("file\",\"size\":\"");
|
||||
output += file.size();
|
||||
}
|
||||
|
||||
output += "\",\"name\":\"";
|
||||
output += "\",\"name\":\"";
|
||||
output += String(file.name());
|
||||
output += "\"}";
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
output += "]";
|
||||
HTTP.send(200, "text/json", output);
|
||||
|
||||
output += "\"}";
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
output += "]";
|
||||
HTTP.send(200, "text/json", output);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
The "Not Found" handler catches all URI not explicitly declared in code
|
||||
First try to find and return the requested file from the filesystem,
|
||||
and if it fails, return a 404 page with debug information
|
||||
*/
|
||||
void handleNotFound()
|
||||
{
|
||||
void handleNotFound() {
|
||||
#ifdef ESP8266
|
||||
String uri = ESP8266WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks
|
||||
String uri = ESP8266WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks
|
||||
String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks
|
||||
#endif
|
||||
if (handleFileRead(uri))
|
||||
{
|
||||
if (handleFileRead(uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -667,8 +575,7 @@ void handleNotFound()
|
||||
message += F("\nArguments: ");
|
||||
message += HTTP.args();
|
||||
message += '\n';
|
||||
for (uint8_t i = 0; i < HTTP.args(); i++)
|
||||
{
|
||||
for (uint8_t i = 0; i < HTTP.args(); i++) {
|
||||
message += F(" NAME:");
|
||||
message += HTTP.argName(i);
|
||||
message += F("\n VALUE:");
|
||||
@@ -678,7 +585,7 @@ void handleNotFound()
|
||||
message += "path=";
|
||||
message += HTTP.arg("path");
|
||||
message += '\n';
|
||||
// DBG_OUTPUT_PORT.print(message);
|
||||
// DBG_OUTPUT_PORT.print(message);
|
||||
|
||||
return replyNotFound(message);
|
||||
}
|
||||
@@ -689,10 +596,8 @@ void handleNotFound()
|
||||
embedded in the program code.
|
||||
Otherwise, fails with a 404 page with debug information
|
||||
*/
|
||||
void handleGetEdit()
|
||||
{
|
||||
if (handleFileRead(F("/edit.htm")))
|
||||
{
|
||||
void handleGetEdit() {
|
||||
if (handleFileRead(F("/edit.htm"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
updateFirm update;
|
||||
|
||||
void upgrade_firmware(int type) {
|
||||
void upgrade_firmware(int type, String path) {
|
||||
putUserDataToRam();
|
||||
// сбросим файл статуса последнего обновления
|
||||
writeFile("ota.json", "{}");
|
||||
|
||||
// only build
|
||||
if (type == 1) {
|
||||
if (upgradeBuild()) {
|
||||
if (upgradeBuild(path)) {
|
||||
saveUserDataToFlash();
|
||||
restartEsp();
|
||||
}
|
||||
@@ -15,7 +17,7 @@ void upgrade_firmware(int type) {
|
||||
|
||||
// only littlefs
|
||||
else if (type == 2) {
|
||||
if (upgradeFS()) {
|
||||
if (upgradeFS(path)) {
|
||||
saveUserDataToFlash();
|
||||
restartEsp();
|
||||
}
|
||||
@@ -23,71 +25,83 @@ void upgrade_firmware(int type) {
|
||||
|
||||
// littlefs and build
|
||||
else if (type == 3) {
|
||||
if (upgradeFS()) {
|
||||
if (upgradeFS(path)) {
|
||||
saveUserDataToFlash();
|
||||
if (upgradeBuild()) {
|
||||
if (upgradeBuild(path)) {
|
||||
restartEsp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool upgradeFS() {
|
||||
bool upgradeFS(String path) {
|
||||
bool ret = false;
|
||||
WiFiClient wifiClient;
|
||||
SerialPrint("!!!", F("Update"), F("Start upgrade FS..."));
|
||||
handleUpdateStatus(true, UPDATE_FS_IN_PROGRESS);
|
||||
if (getBinPath("") == "error") {
|
||||
SerialPrint("!!!", F("Update"), "Start upgrade FS... " + path);
|
||||
|
||||
if (path == "") {
|
||||
SerialPrint("E", F("Update"), F("FS Path error"));
|
||||
handleUpdateStatus(true, PATH_ERROR);
|
||||
saveUpdeteStatus("fs", PATH_ERROR);
|
||||
return ret;
|
||||
}
|
||||
#ifdef ESP8266
|
||||
ESPhttpUpdate.rebootOnUpdate(false);
|
||||
t_httpUpdate_return retFS = ESPhttpUpdate.updateFS(wifiClient, getBinPath("littlefs.bin"));
|
||||
t_httpUpdate_return retFS = ESPhttpUpdate.updateFS(wifiClient, path + "/littlefs.bin");
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
httpUpdate.rebootOnUpdate(false);
|
||||
//обновляем little fs с помощью метода обновления spiffs
|
||||
HTTPUpdateResult retFS = httpUpdate.updateSpiffs(wifiClient, getBinPath("littlefs.bin"));
|
||||
// обновляем little fs с помощью метода обновления spiffs!!!!
|
||||
HTTPUpdateResult retFS = httpUpdate.updateSpiffs(wifiClient, path + "/littlefs.bin");
|
||||
#endif
|
||||
|
||||
//если FS обновилась успешно
|
||||
// если FS обновилась успешно
|
||||
if (retFS == HTTP_UPDATE_OK) {
|
||||
SerialPrint("!!!", F("Update"), F("FS upgrade done!"));
|
||||
handleUpdateStatus(true, UPDATE_FS_COMPLETED);
|
||||
saveUpdeteStatus("fs", UPDATE_COMPLETED);
|
||||
ret = true;
|
||||
} else {
|
||||
saveUpdeteStatus("fs", UPDATE_FAILED);
|
||||
if (retFS == HTTP_UPDATE_FAILED) {
|
||||
SerialPrint("E", F("Update"), "HTTP_UPDATE_FAILED");
|
||||
} else if (retFS == HTTP_UPDATE_NO_UPDATES) {
|
||||
SerialPrint("E", F("Update"), "HTTP_UPDATE_NO_UPDATES");
|
||||
}
|
||||
}
|
||||
handleUpdateStatus(true, UPDATE_FS_FAILED);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool upgradeBuild() {
|
||||
bool upgradeBuild(String path) {
|
||||
bool ret = false;
|
||||
WiFiClient wifiClient;
|
||||
SerialPrint("!!!", F("Update"), F("Start upgrade BUILD..."));
|
||||
handleUpdateStatus(true, UPDATE_BUILD_IN_PROGRESS);
|
||||
if (getBinPath("") == "error") {
|
||||
SerialPrint("!!!", F("Update"), "Start upgrade BUILD... " + path);
|
||||
|
||||
if (path == "") {
|
||||
SerialPrint("E", F("Update"), F("Build Path error"));
|
||||
handleUpdateStatus(true, PATH_ERROR);
|
||||
saveUpdeteStatus("build", PATH_ERROR);
|
||||
return ret;
|
||||
}
|
||||
#if defined (esp8266_4mb) || defined (esp8266_1mb) || defined (esp8266_1mb_ota) || defined (esp8266_2mb) || defined (esp8266_2mb_ota)
|
||||
#if defined(esp8266_4mb) || defined(esp8266_16mb) || defined(esp8266_1mb) || defined(esp8266_1mb_ota) || defined(esp8266_2mb) || defined(esp8266_2mb_ota)
|
||||
ESPhttpUpdate.rebootOnUpdate(false);
|
||||
t_httpUpdate_return retBuild = ESPhttpUpdate.update(wifiClient, getBinPath("firmware.bin"));
|
||||
t_httpUpdate_return retBuild = ESPhttpUpdate.update(wifiClient, path + "/firmware.bin");
|
||||
#endif
|
||||
#ifdef esp32_4mb
|
||||
#ifdef ESP32
|
||||
httpUpdate.rebootOnUpdate(false);
|
||||
HTTPUpdateResult retBuild = httpUpdate.update(wifiClient, getBinPath("firmware.bin"));
|
||||
HTTPUpdateResult retBuild = httpUpdate.update(wifiClient, path + "/firmware.bin");
|
||||
#endif
|
||||
|
||||
//если BUILD обновился успешно
|
||||
// если BUILD обновился успешно
|
||||
if (retBuild == HTTP_UPDATE_OK) {
|
||||
SerialPrint("!!!", F("Update"), F("BUILD upgrade done!"));
|
||||
handleUpdateStatus(true, UPDATE_BUILD_COMPLETED);
|
||||
saveUpdeteStatus("build", UPDATE_COMPLETED);
|
||||
ret = true;
|
||||
} else {
|
||||
saveUpdeteStatus("build", UPDATE_FAILED);
|
||||
if (retBuild == HTTP_UPDATE_FAILED) {
|
||||
SerialPrint("E", F("Update"), "HTTP_UPDATE_FAILED");
|
||||
} else if (retBuild == HTTP_UPDATE_NO_UPDATES) {
|
||||
SerialPrint("E", F("Update"), "HTTP_UPDATE_NO_UPDATES");
|
||||
}
|
||||
}
|
||||
handleUpdateStatus(true, UPDATE_BUILD_FAILED);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -97,29 +111,31 @@ void restartEsp() {
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
const String getBinPath(String file) {
|
||||
String path = "error";
|
||||
int targetVersion = 0;
|
||||
String serverip;
|
||||
if (jsonRead(errorsHeapJson, F("chver"), targetVersion)) {
|
||||
if (targetVersion >= 400) {
|
||||
if (jsonRead(settingsFlashJson, F("serverip"), serverip)) {
|
||||
if (serverip != "") {
|
||||
path = jsonReadStr(settingsFlashJson, F("serverip")) + "/iotm/" + String(FIRMWARE_NAME) + "/" + String(targetVersion) + "/" + file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SerialPrint("i", F("Update"), "path: " + path);
|
||||
return path;
|
||||
}
|
||||
// теперь путь к обнавленю прошивки мы получаем из веб интерфейса
|
||||
// const String getBinPath(String file) {
|
||||
// String path = "error";
|
||||
// int targetVersion = 0;
|
||||
// String serverip;
|
||||
// if (jsonRead(errorsHeapJson, F("chver"), targetVersion)) {
|
||||
// if (targetVersion >= 400) {
|
||||
// if (jsonRead(settingsFlashJson, F("serverip"), serverip)) {
|
||||
// if (serverip != "") {
|
||||
// path = jsonReadStr(settingsFlashJson, F("serverip")) + "/iotm/" + String(FIRMWARE_NAME) + "/" + String(targetVersion) + "/" + file;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// SerialPrint("i", F("Update"), "path: " + path);
|
||||
// return path;
|
||||
// }
|
||||
|
||||
// https://t.me/IoTmanager/128814/164752 - убрал ограничение
|
||||
void putUserDataToRam() {
|
||||
update.configJson = readFile("config.json", 4096);
|
||||
update.settingsFlashJson = readFile("settings.json", 4096);
|
||||
update.layoutJson = readFile("layout.json", 4096);
|
||||
update.scenarioTxt = readFile("scenario.txt", 4096);
|
||||
update.chartsData = createDataBaseSting();
|
||||
update.configJson = readFile("config.json", 4096 * 4);
|
||||
update.settingsFlashJson = readFile("settings.json", 4096 * 4);
|
||||
update.layoutJson = readFile("layout.json", 4096 * 4);
|
||||
update.scenarioTxt = readFile("scenario.txt", 4096 * 4);
|
||||
// update.chartsData = createDataBaseSting();
|
||||
}
|
||||
|
||||
void saveUserDataToFlash() {
|
||||
@@ -127,10 +143,13 @@ void saveUserDataToFlash() {
|
||||
writeFile("/settings.json", update.settingsFlashJson);
|
||||
writeFile("/layout.json", update.layoutJson);
|
||||
writeFile("/scenario.txt", update.scenarioTxt);
|
||||
writeDataBaseSting(update.chartsData);
|
||||
// writeDataBaseSting(update.chartsData);
|
||||
}
|
||||
|
||||
void handleUpdateStatus(bool send, int state) {
|
||||
jsonWriteInt_(errorsHeapJson, F("upd"), state);
|
||||
if (!send) sendStringToWs("errors", errorsHeapJson, -1);
|
||||
void saveUpdeteStatus(String key, int val) {
|
||||
const String path = "ota.json";
|
||||
String json = readFile(path, 1024);
|
||||
if (json == "failed") json = "{}";
|
||||
jsonWriteInt_(json, key, val);
|
||||
writeFile(path, json);
|
||||
}
|
||||
108
src/WsServer.cpp
108
src/WsServer.cpp
@@ -28,8 +28,9 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
||||
standWebSocket.close();
|
||||
standWebSocketsInit();
|
||||
}
|
||||
// Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
|
||||
// standWebSocket.sendTXT(num, "Connected");
|
||||
// Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0],
|
||||
// ip[1], ip[2], ip[3], payload); standWebSocket.sendTXT(num,
|
||||
// "Connected");
|
||||
} break;
|
||||
|
||||
case WStype_TEXT: {
|
||||
@@ -118,7 +119,8 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
||||
clearConfigure();
|
||||
configure("/config.json");
|
||||
iotScen.loadScenario("/scenario.txt");
|
||||
// создаем событие завершения конфигурирования для возможности выполнения блока кода при загрузке
|
||||
// создаем событие завершения конфигурирования для возможности
|
||||
// выполнения блока кода при загрузке
|
||||
createItemFromNet("onStart", "1", 1);
|
||||
}
|
||||
|
||||
@@ -128,33 +130,42 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
||||
|
||||
// отвечаем данными на запрос страницы
|
||||
if (headerStr == "/connection|") {
|
||||
sendFileToWsByFrames("/widgets.json", "widget", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
sendFileToWsByFrames("/config.json", "config", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
sendStringToWs("settin", settingsFlashJson, num);
|
||||
sendStringToWs("ssidli", ssidListHeapJson, num);
|
||||
sendStringToWs("errors", errorsHeapJson, num);
|
||||
// запуск асинхронного сканирования wifi сетей при переходе на страницу соединений
|
||||
// RouterFind(jsonReadStr(settingsFlashJson, F("routerssid")));
|
||||
// запуск асинхронного сканирования wifi сетей при переходе на страницу
|
||||
// соединений RouterFind(jsonReadStr(settingsFlashJson,
|
||||
// F("routerssid")));
|
||||
}
|
||||
|
||||
// обработка кнопки сохранить настройки wifi
|
||||
// обработка кнопки сохранить settings.json
|
||||
if (headerStr == "/sgnittes|") {
|
||||
writeUint8tToString(payload, length, headerLenth, settingsFlashJson);
|
||||
writeFileUint8tByFrames("settings.json", payload, length, headerLenth, 256);
|
||||
sendStringToWs("errors", errorsHeapJson, num);
|
||||
// если не было создано приема данных по udp - то создадим его
|
||||
addThisDeviceToList();
|
||||
}
|
||||
|
||||
// обработка кнопки сохранить настройки mqtt
|
||||
if (headerStr == "/mqtt|") {
|
||||
sendStringToWs("settin", settingsFlashJson, num); // отправляем в ответ новые полученные настройки
|
||||
handleMqttStatus(false, 8); // меняем статус на неопределенный
|
||||
mqttReconnect(); // начинаем переподключение
|
||||
sendStringToWs("errors", errorsHeapJson, num); // отправляем что статус неопределен
|
||||
sendStringToWs("settin", settingsFlashJson,
|
||||
num); // отправляем в ответ новые полученные настройки
|
||||
handleMqttStatus(false, 8); // меняем статус на неопределенный
|
||||
mqttReconnect(); // начинаем переподключение
|
||||
sendStringToWs("errors", errorsHeapJson,
|
||||
num); // отправляем что статус неопределен
|
||||
sendStringToWs("ssidli", ssidListHeapJson, num);
|
||||
}
|
||||
|
||||
// запуск асинхронного сканирования wifi сетей при нажатии выпадающего списка
|
||||
// запуск асинхронного сканирования wifi сетей при нажатии выпадающего
|
||||
// списка
|
||||
if (headerStr == "/scan|") {
|
||||
RouterFind(jsonReadStr(settingsFlashJson, F("routerssid")));
|
||||
std::vector<String> jArray;
|
||||
jsonReadArray(settingsFlashJson, "routerssid", jArray);
|
||||
RouterFind(jArray);
|
||||
sendStringToWs("ssidli", ssidListHeapJson, num);
|
||||
}
|
||||
|
||||
@@ -162,9 +173,23 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
||||
// Страница веб интерфейса list
|
||||
//----------------------------------------------------------------------//
|
||||
|
||||
// отвечаем данными на запрос страницы
|
||||
// отвечаем данными на запрос страницы list
|
||||
if (headerStr == "/list|") {
|
||||
sendStringToWs("devlis", devListHeapJson, num);
|
||||
sendStringToWs("settin", settingsFlashJson, num);
|
||||
// отправим список устройств в зависимости от того что выбрал user
|
||||
// sendDeviceList(num);
|
||||
}
|
||||
|
||||
// отвечаем на запрос списка устройств (это отдельный запрос который
|
||||
// делает приложение при подключении)
|
||||
if (headerStr == "/devlist|") {
|
||||
// отправим список устройств в зависимости от того что выбрал user
|
||||
sendDeviceList(num);
|
||||
}
|
||||
|
||||
// сохраняем данные листа
|
||||
if (headerStr == "/tsil|") {
|
||||
writeFileUint8tByFrames("devlist.json", payload, length, headerLenth, 256);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
@@ -185,10 +210,22 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
||||
sendStringToWs("settin", settingsFlashJson, num);
|
||||
sendFileToWsByFrames("/config.json", "config", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
sendFileToWsByFrames("/items.json", "itemsj", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
// sendFileToWsByFrames("/layout.json", "layout", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
// sendFileToWsByFrames("/layout.json", "layout", "", num,
|
||||
// WEB_SOCKETS_FRAME_SIZE);
|
||||
}
|
||||
|
||||
if (headerStr == "/test|") {
|
||||
//----------------------------------------------------------------------//
|
||||
// Страница веб интерфейса update
|
||||
//----------------------------------------------------------------------//
|
||||
if (headerStr == "/profile|") {
|
||||
// для версии 451 отдаем myProfile.json
|
||||
sendFileToWsByFrames("/ota.json", "otaupd", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
if (FileFS.exists("/myProfile.json")) {
|
||||
sendFileToWsByFrames("/myProfile.json", "prfile", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
// для версии 452 и более отдаем flashProfile.json
|
||||
} else if (FileFS.exists("/flashProfile.json")) {
|
||||
sendFileToWsByFrames("/flashProfile.json", "prfile", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
@@ -212,7 +249,9 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
||||
|
||||
// команда обновления прошивки esp
|
||||
if (headerStr == "/update|") {
|
||||
upgrade_firmware(3);
|
||||
String path;
|
||||
writeUint8tToString(payload, length, headerLenth, path);
|
||||
upgrade_firmware(3, path);
|
||||
}
|
||||
|
||||
// Прием команд control c dashboard
|
||||
@@ -286,7 +325,8 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
// публикация статус сообщений в ws (недостаток в том что делаем бродкаст всем клиентам поднятым в свелте!!!)
|
||||
// публикация статус сообщений в ws (недостаток в том что делаем бродкаст всем
|
||||
// клиентам поднятым в свелте!!!)
|
||||
void publishStatusWs(const String& topic, const String& data) {
|
||||
String path = mqttRootDevice + "/" + topic;
|
||||
String json = "{}";
|
||||
@@ -307,7 +347,10 @@ void publishJsonWs(const String& topic, String& json) {
|
||||
void periodicWsSend() {
|
||||
sendStringToWs("ssidli", ssidListHeapJson, -1);
|
||||
sendStringToWs("errors", errorsHeapJson, -1);
|
||||
sendStringToWs("devlis", devListHeapJson, -1);
|
||||
// отправляем переодичестки только в авто режиме
|
||||
if (jsonReadInt(settingsFlashJson, F("udps")) != 0) {
|
||||
sendStringToWs("devlis", devListHeapJson, -1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
@@ -340,7 +383,8 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St
|
||||
}
|
||||
|
||||
size_t totalSize = file.size();
|
||||
// Serial.println("Send file '" + String(filename) + "', file size: " + String(totalSize));
|
||||
// Serial.println("Send file '" + String(filename) + "', file size: " +
|
||||
// String(totalSize));
|
||||
|
||||
char buf[32];
|
||||
sprintf(buf, "%04d", json.length() + 12);
|
||||
@@ -380,7 +424,9 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St
|
||||
continuation = true;
|
||||
}
|
||||
|
||||
// Serial.println(String(i) + ") " + "ws: " + String(client_id) + " fr sz: " + String(size) + " fin: " + String(fin) + " cnt: " + String(continuation));
|
||||
// Serial.println(String(i) + ") " + "ws: " + String(client_id) + " fr sz:
|
||||
// " + String(size) + " fin: " + String(fin) + " cnt: " +
|
||||
// String(continuation));
|
||||
|
||||
if (client_id == -1) {
|
||||
standWebSocket.broadcastBIN(frameBuf, size, fin, continuation);
|
||||
@@ -397,6 +443,12 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St
|
||||
}
|
||||
|
||||
void sendStringToWs(const String& header, String& payload, int client_id) {
|
||||
if ((!getNumAPClients() && !isNetworkActive()) || !getNumWSClients()) {
|
||||
// standWebSocket.disconnect(); // это и ниже надо сделать при -
|
||||
// standWebSocket.close(); // - отключении AP И WiFi(STA), надо менять ядро WiFi. Сейчас не закрывается сессия клиента при пропаже AP И WiFi(STA)
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.length() != 6) {
|
||||
SerialPrint("E", "FS", F("wrong header size"));
|
||||
return;
|
||||
@@ -413,3 +465,17 @@ void sendStringToWs(const String& header, String& payload, int client_id) {
|
||||
standWebSocket.sendBIN(client_id, (uint8_t*)dataArray, totalSize);
|
||||
}
|
||||
}
|
||||
|
||||
void sendDeviceList(uint8_t num) {
|
||||
if (jsonReadInt(settingsFlashJson, F("udps")) != 0) {
|
||||
// если включен автопоиск то отдаем список из оперативной памяти
|
||||
SerialPrint("i", "FS", "heap list");
|
||||
sendStringToWs("devlis", devListHeapJson, num);
|
||||
} else {
|
||||
// если выключен автопоиск то отдаем список из флешь памяти
|
||||
sendFileToWsByFrames("/devlist.json", "devlis", "", num, WEB_SOCKETS_FRAME_SIZE);
|
||||
SerialPrint("i", "FS", "flash list");
|
||||
}
|
||||
}
|
||||
|
||||
int getNumWSClients() { return standWebSocket.connectedClients(false); }
|
||||
@@ -7,14 +7,13 @@
|
||||
|
||||
IoTItem::IoTItem(const String& parameters) {
|
||||
jsonRead(parameters, F("int"), _interval, false);
|
||||
if (_interval == 0) enableDoByInt = false; // выключаем использование периодического выполнения в модуле
|
||||
if (_interval > 0) _interval = _interval * 1000; // если int положителен, то считаем, что получены секунды
|
||||
if (_interval < 0) _interval = _interval * -1; // если int отрицательный, то миллисекунды
|
||||
setInterval(_interval);
|
||||
jsonRead(parameters, F("subtype"), _subtype, false);
|
||||
jsonRead(parameters, F("id"), _id);
|
||||
if (!jsonRead(parameters, F("multiply"), _multiply, false)) _multiply = 1;
|
||||
if (!jsonRead(parameters, F("plus"), _plus, false)) _plus = 0;
|
||||
if (!jsonRead(parameters, F("round"), _round, false)) _round = -1;
|
||||
if (!jsonRead(parameters, F("numDigits"), _numDigits, false)) _numDigits = 1;
|
||||
|
||||
if (!jsonRead(parameters, F("global"), _global, false)) _global = false;
|
||||
|
||||
@@ -35,14 +34,25 @@ IoTItem::IoTItem(const String& parameters) {
|
||||
jsonRead(parameters, F("needSave"), _needSave, false);
|
||||
if (_needSave && jsonRead(valuesFlashJson, _id, valAsStr, false)) // пробуем достать из сохранения значение элемента, если указано, что нужно сохранять
|
||||
setValue(valAsStr, false);
|
||||
|
||||
// проверяем нужно ли отслеживать значение другого элемента
|
||||
String trackingID = "";
|
||||
IoTItem* item = nullptr;
|
||||
if (jsonRead(parameters, F("trackingID"), trackingID, false) && (item = findIoTItem(trackingID)) != nullptr) {
|
||||
_trackingValue = &(item->value);
|
||||
}
|
||||
}
|
||||
|
||||
void IoTItem::suspendNextDoByInt(unsigned long _delay) { // 0 - force
|
||||
nextMillis = millis() + _delay;
|
||||
}
|
||||
|
||||
void IoTItem::loop() {
|
||||
if (enableDoByInt) {
|
||||
currentMillis = millis();
|
||||
difference = currentMillis - prevMillis;
|
||||
if (difference >= _interval) {
|
||||
prevMillis = millis();
|
||||
unsigned long currentMillis = millis(); // _interval должен быть < 2147483647 мс (24 суток)
|
||||
if (nextMillis - currentMillis > 2147483647UL /*ULONG_MAX/2*/ ) {
|
||||
nextMillis = currentMillis + _interval;
|
||||
// SerialPrint(F("i"), _id, "this->doByInterval");
|
||||
this->doByInterval();
|
||||
}
|
||||
}
|
||||
@@ -60,19 +70,19 @@ long IoTItem::getInterval() { return _interval; }
|
||||
bool IoTItem::isGlobal() { return _global; }
|
||||
|
||||
void IoTItem::setValue(const String& valStr, bool genEvent) {
|
||||
value.isDecimal = isDigitDotCommaStr(valStr);
|
||||
|
||||
value.isDecimal = isDigitDotCommaStr(valStr);
|
||||
|
||||
if (value.isDecimal) {
|
||||
value.valD = valStr.toFloat();
|
||||
getRoundValue();
|
||||
getRoundValue();
|
||||
} else {
|
||||
value.valS = valStr;
|
||||
value.valS = valStr;
|
||||
}
|
||||
setValue(value, genEvent);
|
||||
}
|
||||
|
||||
void IoTItem::setValue(const IoTValue& Value, bool genEvent) {
|
||||
value = Value;
|
||||
value = Value;
|
||||
|
||||
if (value.isDecimal) {
|
||||
regEvent(value.valD, "", false, genEvent);
|
||||
@@ -90,7 +100,7 @@ void IoTItem::sendSubWidgetsValues(String& id, String& json) {
|
||||
|
||||
// когда событие случилось
|
||||
void IoTItem::regEvent(const String& value, const String& consoleInfo, bool error, bool genEvent) {
|
||||
if (_needSave) {
|
||||
if (_needSave) {
|
||||
jsonWriteStr_(valuesFlashJson, _id, value);
|
||||
needSaveValues = true;
|
||||
}
|
||||
@@ -119,9 +129,9 @@ String IoTItem::getRoundValue() {
|
||||
if (_round >= 0 && _round <= 6) {
|
||||
int sot = _round ? pow(10, (int)_round) : 1;
|
||||
value.valD = round(value.valD * sot) / sot;
|
||||
|
||||
//todo: оптимизировать. Вынести расчет строки формата округления, чтоб использовать постоянно готовую
|
||||
char buf[15];
|
||||
sprintf(buf, ("%1." + (String)_round + "f").c_str(), value.valD);
|
||||
sprintf(buf, ("%0" + (String)(_numDigits + _round) + "." + (String)_round + "f").c_str(), value.valD);
|
||||
value.valS = (String)buf;
|
||||
return value.valS;
|
||||
} else {
|
||||
@@ -131,7 +141,7 @@ String IoTItem::getRoundValue() {
|
||||
}
|
||||
|
||||
void IoTItem::regEvent(float regvalue, const String& consoleInfo, bool error, bool genEvent) {
|
||||
value.valD = regvalue;
|
||||
value.valD = regvalue;
|
||||
|
||||
if (_multiply) value.valD = value.valD * _multiply;
|
||||
if (_plus) value.valD = value.valD + _plus;
|
||||
@@ -175,14 +185,29 @@ void IoTItem::checkIntFromNet() {
|
||||
}
|
||||
}
|
||||
|
||||
String IoTItem::getMqttExterSub() {
|
||||
return "";
|
||||
}
|
||||
|
||||
// хуки для системных событий (должны начинаться с "on")
|
||||
void IoTItem::onRegEvent(IoTItem* item) {}
|
||||
void IoTItem::onMqttRecive(String& topic, String& msg) {}
|
||||
void IoTItem::onMqttWsAppConnectEvent() {}
|
||||
void IoTItem::onModuleOrder(String& key, String& value) {}
|
||||
void IoTItem::onTrackingValue(IoTItem* item) {
|
||||
setValue(item->getValue(), false);
|
||||
}
|
||||
|
||||
bool IoTItem::isTracking(IoTItem* item) {
|
||||
return &(item->value) == _trackingValue;
|
||||
}
|
||||
|
||||
// делаем доступным модулям отправку сообщений в телеграм
|
||||
void IoTItem::sendTelegramMsg(bool often, String msg) {}
|
||||
void IoTItem::sendFoto(uint8_t *buf, uint32_t length, const String &name) {}
|
||||
void IoTItem::editFoto(uint8_t *buf, uint32_t length, const String &name) {}
|
||||
// для обновления экрана Nextion из телеграм
|
||||
void IoTItem::uploadNextionTlgrm(String &url) {}
|
||||
|
||||
// методы для графиков (будет упрощено)
|
||||
void IoTItem::publishValue() {}
|
||||
@@ -200,7 +225,13 @@ bool IoTItem::isStrInID(const String& str) {
|
||||
}
|
||||
|
||||
void IoTItem::setInterval(long interval) {
|
||||
_interval = interval;
|
||||
if (interval == 0) enableDoByInt = false; // выключаем использование периодического выполнения в модуле
|
||||
else {
|
||||
enableDoByInt = true;
|
||||
if (interval > 0) _interval = interval * 1000; // если int положителен, то считаем, что получены секунды
|
||||
else if (interval < 0) _interval = interval * -1; // если int отрицательный, то миллисекунды
|
||||
}
|
||||
// SerialPrint(F("i"), F("IoTItem"), "setInterval: " + _interval.toString);
|
||||
}
|
||||
|
||||
IoTGpio* IoTItem::getGpioDriver() {
|
||||
@@ -210,6 +241,14 @@ IoTGpio* IoTItem::getGpioDriver() {
|
||||
IoTItem* IoTItem::getRtcDriver() {
|
||||
return nullptr;
|
||||
}
|
||||
/*
|
||||
IoTItem* IoTItem::getCAMDriver() {
|
||||
return nullptr;
|
||||
}
|
||||
*/
|
||||
IoTItem* IoTItem::getTlgrmDriver() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned long IoTItem::getRtcUnixTime() {
|
||||
return 0;
|
||||
|
||||
@@ -341,7 +341,8 @@ enum SysOp {
|
||||
sysop_getRSSI,
|
||||
sysop_getIP,
|
||||
sysop_mqttPub,
|
||||
sysop_getUptime
|
||||
sysop_getUptime,
|
||||
sysop_mqttIsConnect
|
||||
};
|
||||
|
||||
IoTValue sysExecute(SysOp command, std::vector<IoTValue> ¶m) {
|
||||
@@ -442,6 +443,9 @@ IoTValue sysExecute(SysOp command, std::vector<IoTValue> ¶m) {
|
||||
value.valS = jsonReadStr(errorsHeapJson, F("upt"));
|
||||
value.isDecimal = false;
|
||||
break;
|
||||
case sysop_mqttIsConnect:
|
||||
value.valD = mqttIsConnect();
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -496,6 +500,8 @@ class SysCallExprAST : public ExprAST {
|
||||
operation = sysop_getTime;
|
||||
else if (Callee == F("getUptime"))
|
||||
operation = sysop_getUptime;
|
||||
else if (Callee == F("mqttIsConnect"))
|
||||
operation = sysop_mqttIsConnect;
|
||||
else
|
||||
operation = sysop_notfound;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
void* getAPI_Cron(String subtype, String params);
|
||||
void* getAPI_Loging(String subtype, String params);
|
||||
void* getAPI_LogingDaily(String subtype, String params);
|
||||
void* getAPI_owmWeather(String subtype, String params);
|
||||
void* getAPI_Timer(String subtype, String params);
|
||||
void* getAPI_Variable(String subtype, String params);
|
||||
void* getAPI_VariableColor(String subtype, String params);
|
||||
void* getAPI_VButton(String subtype, String params);
|
||||
void* getAPI_A02Distance(String subtype, String params);
|
||||
void* getAPI_Acs712(String subtype, String params);
|
||||
@@ -35,15 +35,17 @@ void* getAPI_Pcf8574(String subtype, String params);
|
||||
void* getAPI_Pwm8266(String subtype, String params);
|
||||
void* getAPI_TelegramLT(String subtype, String params);
|
||||
void* getAPI_Lcd2004(String subtype, String params);
|
||||
void* getAPI_Oled64(String subtype, String params);
|
||||
void* getAPI_TM16XX(String subtype, String params);
|
||||
|
||||
void* getAPI(String subtype, String params) {
|
||||
void* tmpAPI;
|
||||
if ((tmpAPI = getAPI_Cron(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_Loging(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_LogingDaily(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_owmWeather(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_Timer(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_Variable(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_VariableColor(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_VButton(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_A02Distance(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_Acs712(subtype, params)) != nullptr) return tmpAPI;
|
||||
@@ -73,5 +75,7 @@ if ((tmpAPI = getAPI_Pcf8574(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_Pwm8266(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_TelegramLT(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_Lcd2004(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_Oled64(subtype, params)) != nullptr) return tmpAPI;
|
||||
if ((tmpAPI = getAPI_TM16XX(subtype, params)) != nullptr) return tmpAPI;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
{
|
||||
"menuSection": "Экраны",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "LCD Dwin экран",
|
||||
"type": "Reading",
|
||||
"subtype": "DwinI",
|
||||
"id": "dwin",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
|
||||
"int": 15,
|
||||
"TX": 17,
|
||||
"RX": 16,
|
||||
"line": 2,
|
||||
"speed": 115200
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "LCD Dwin экран",
|
||||
"type": "Reading",
|
||||
"subtype": "DwinI",
|
||||
"id": "dwin",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 15,
|
||||
"TX": 17,
|
||||
"RX": 16,
|
||||
"line": 2,
|
||||
"speed": 115200
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -33,6 +32,7 @@
|
||||
"propInfo": {
|
||||
"int": ""
|
||||
},
|
||||
"title": "Экраны от компании Dwin",
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "rrrr",
|
||||
@@ -41,21 +41,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
],
|
||||
"esp8266_1mb": [
|
||||
],
|
||||
"esp8266_1mb_ota": [
|
||||
],
|
||||
"esp8285_1mb": [
|
||||
],
|
||||
"esp8285_1mb_ota": [
|
||||
]
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
"esp8285_1mb": [],
|
||||
"esp8285_1mb_ota": []
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,18 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
//#include "LiquidCrystal_I2C.h"
|
||||
#include <RobotClass_LiquidCrystal_I2C.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
void scanI2C();
|
||||
|
||||
//LiquidCrystal_I2C *LCDI2C;
|
||||
RobotClass_LiquidCrystal_I2C *LCDI2C;
|
||||
RobotClass_LiquidCrystal_I2C* LCDI2C;
|
||||
|
||||
class Lcd2004 : public IoTItem {
|
||||
private:
|
||||
unsigned int _x;
|
||||
unsigned int _y;
|
||||
String _id2show;
|
||||
String _descr;
|
||||
String _id2show, _prefix = "", _postfix = "";
|
||||
int _prevStrSize;
|
||||
String _addr;
|
||||
|
||||
bool _isShow = true; // экран показывает
|
||||
bool _isShow = true; // экран показывает
|
||||
|
||||
public:
|
||||
Lcd2004(String parameters) : IoTItem(parameters) {
|
||||
@@ -34,45 +26,58 @@ class Lcd2004 : public IoTItem {
|
||||
}
|
||||
|
||||
jsonRead(parameters, "size", size);
|
||||
int w = selectFromMarkerToMarker(size, ",", 0).toInt(); //количество столбцов
|
||||
int h = selectFromMarkerToMarker(size, ",", 1).toInt(); //количество строк
|
||||
if (LCDI2C == nullptr) { //инициализации экрана еще не было
|
||||
//LCDI2C = new LiquidCrystal_I2C(hexStringToUint8(_addr), w, h);
|
||||
int w = selectFromMarkerToMarker(size, ",", 0).toInt(); // количество столбцов
|
||||
int h = selectFromMarkerToMarker(size, ",", 1).toInt(); // количество строк
|
||||
if (LCDI2C == nullptr) { // инициализации экрана еще не было
|
||||
LCDI2C = new RobotClass_LiquidCrystal_I2C(hexStringToUint8(_addr), w, h, CP_UTF8);
|
||||
if (LCDI2C != nullptr) {
|
||||
LCDI2C->init();
|
||||
LCDI2C->clear();
|
||||
LCDI2C->backlight();
|
||||
}
|
||||
}
|
||||
|
||||
LCDI2C->clear();
|
||||
LCDI2C->backlight();
|
||||
|
||||
jsonRead(parameters, "coord", xy);
|
||||
_x = selectFromMarkerToMarker(xy, ",", 0).toInt();
|
||||
_y = selectFromMarkerToMarker(xy, ",", 1).toInt();
|
||||
|
||||
jsonRead(parameters, "descr", _descr);
|
||||
jsonRead(parameters, "id2show", _id2show);
|
||||
jsonRead(parameters, "prefix", _prefix);
|
||||
jsonRead(parameters, "postfix", _postfix);
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
if (LCDI2C != nullptr) {
|
||||
printBlankStr(_prevStrSize);
|
||||
|
||||
String tmpStr = getItemValue(_id2show);
|
||||
if (_descr != "none") tmpStr = _descr + " " + tmpStr;
|
||||
LCDI2C->setCursor(_x, _y);
|
||||
LCDI2C->print(tmpStr);
|
||||
|
||||
//LCDI2C->print("Helloy,Manager 404 !");
|
||||
//Serial.printf("ffff %s\n", _id2show);
|
||||
_prevStrSize = tmpStr.length();
|
||||
} else {
|
||||
void drawItem(IoTItem* item) {
|
||||
String tmpStr = _prefix;
|
||||
tmpStr += item->getValue();
|
||||
tmpStr += _postfix;
|
||||
|
||||
printBlankStr(_prevStrSize);
|
||||
LCDI2C->setCursor(_x, _y);
|
||||
LCDI2C->print(tmpStr);
|
||||
_prevStrSize = tmpStr.length();
|
||||
}
|
||||
|
||||
void setValue(const IoTValue& Value, bool genEvent = true) {
|
||||
if (LCDI2C == nullptr) return;
|
||||
|
||||
value = Value;
|
||||
drawItem(this);
|
||||
IoTItem::setValue(Value, genEvent);
|
||||
}
|
||||
|
||||
void onRegEvent(IoTItem* eventItem) {
|
||||
if (LCDI2C == nullptr) {
|
||||
scanI2C();
|
||||
return;
|
||||
}
|
||||
if (!eventItem || _id2show == "") return;
|
||||
|
||||
if (_id2show == eventItem->getID()) {
|
||||
setValue(eventItem->value, false);
|
||||
}
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m) { // будет возможным использовать, когда сценарии запустятся
|
||||
IoTValue execute(String command, std::vector<IoTValue>& param) {
|
||||
if (command == "noBacklight")
|
||||
LCDI2C->noBacklight();
|
||||
else if (command == "backlight")
|
||||
@@ -87,7 +92,7 @@ class Lcd2004 : public IoTItem {
|
||||
if (_isShow) {
|
||||
LCDI2C->noDisplay();
|
||||
_isShow = false;
|
||||
} else {
|
||||
} else {
|
||||
LCDI2C->display();
|
||||
_isShow = true;
|
||||
}
|
||||
@@ -99,9 +104,13 @@ class Lcd2004 : public IoTItem {
|
||||
if (param.size()) {
|
||||
_y = param[0].valD;
|
||||
}
|
||||
} else if (command == "descr") {
|
||||
} else if (command == "prefix") {
|
||||
if (param.size()) {
|
||||
_descr = param[0].valS;
|
||||
_prefix = param[0].valS;
|
||||
}
|
||||
} else if (command == "postfix") {
|
||||
if (param.size()) {
|
||||
_postfix = param[0].valS;
|
||||
}
|
||||
} else if (command == "id2show") {
|
||||
if (param.size()) {
|
||||
@@ -113,7 +122,7 @@ class Lcd2004 : public IoTItem {
|
||||
return {};
|
||||
}
|
||||
|
||||
//печать пустой строки нужной длинны для затирания предыдущего значения на экране
|
||||
// печать пустой строки нужной длинны для затирания предыдущего значения на экране
|
||||
void printBlankStr(int strSize) {
|
||||
String tmpStr = "";
|
||||
for (int i = 0; i < strSize; i++) tmpStr += " ";
|
||||
@@ -121,13 +130,13 @@ class Lcd2004 : public IoTItem {
|
||||
LCDI2C->print(tmpStr);
|
||||
}
|
||||
|
||||
~Lcd2004(){
|
||||
~Lcd2004() {
|
||||
if (LCDI2C) delete LCDI2C;
|
||||
LCDI2C = nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
void *getAPI_Lcd2004(String subtype, String param) {
|
||||
void* getAPI_Lcd2004(String subtype, String param) {
|
||||
if (subtype == F("Lcd2004")) {
|
||||
return new Lcd2004(param);
|
||||
} else {
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
{
|
||||
"menuSection": "Экраны",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "LCD экран 2004",
|
||||
"type": "Reading",
|
||||
"subtype": "Lcd2004",
|
||||
"id": "Lcd",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "T",
|
||||
|
||||
"int": 15,
|
||||
"addr": "0x27",
|
||||
"size": "20,4",
|
||||
"coord": "0,0",
|
||||
"id2show": "id датчика"
|
||||
},
|
||||
{
|
||||
"name": "LCD экран 1602",
|
||||
"type": "Reading",
|
||||
"subtype": "Lcd2004",
|
||||
"id": "Lcd",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "T",
|
||||
|
||||
"int": 15,
|
||||
"addr": "0x27",
|
||||
"size": "16,2",
|
||||
"coord": "0,0",
|
||||
"id2show": "id датчика"
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "LCD экран 2004",
|
||||
"type": "Reading",
|
||||
"subtype": "Lcd2004",
|
||||
"id": "Lcd",
|
||||
"widget": "inputTxt",
|
||||
"page": "screens",
|
||||
"descr": "LCD Экран",
|
||||
"addr": "0x27",
|
||||
"size": "20,4",
|
||||
"coord": "0,0",
|
||||
"id2show": "",
|
||||
"prefix": "",
|
||||
"postfix": ""
|
||||
},
|
||||
{
|
||||
"name": "LCD экран 1602",
|
||||
"type": "Reading",
|
||||
"subtype": "Lcd2004",
|
||||
"id": "Lcd",
|
||||
"widget": "inputTxt",
|
||||
"page": "screens",
|
||||
"descr": "LCD Экран",
|
||||
"addr": "0x27",
|
||||
"size": "16,2",
|
||||
"coord": "0,0",
|
||||
"id2show": "",
|
||||
"prefix": "",
|
||||
"postfix": ""
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -46,12 +46,14 @@
|
||||
},
|
||||
"moduleDesc": "Позволяет выводить на символьные экраны по указанным позициям значения других элементов конфигурации.",
|
||||
"propInfo": {
|
||||
"int": "Период времени в секундах обновления информации на экране по конкретному элементу.",
|
||||
"addr": "Адрес устройства на шине, обычно 0x27.",
|
||||
"addr": "Адрес устройства на шине, обычно 0x27. Установите пустую строку для включения режима сканирования адресов на шине (результат в консоли).",
|
||||
"size": "Размерность матрицы экрана.",
|
||||
"coord": "Координата позиции для вывода данных элемента конфигурации.",
|
||||
"id2show": "id элемента конфигурации."
|
||||
"id2show": "id элемента конфигурации для отображения на экране. Если пустое значение, то данные берутся из собственной переменной.",
|
||||
"prefix": "Символы до значения.",
|
||||
"postfix": "Символы после значения."
|
||||
},
|
||||
"title": "Символьный дисплей Lcd2004",
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "noBacklight",
|
||||
@@ -81,33 +83,54 @@
|
||||
{
|
||||
"name": "x",
|
||||
"descr": "Устанавливает первую координату",
|
||||
"params": ["Номер строки первого символа"]
|
||||
"params": [
|
||||
"Номер строки первого символа"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"descr": "Устанавливает вторую координату",
|
||||
"params": ["Номер столбца первого символа"]
|
||||
"params": [
|
||||
"Номер столбца первого символа"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "descr",
|
||||
"name": "prefix",
|
||||
"descr": "Задает приставку слева от значения",
|
||||
"params": ["Строка"]
|
||||
"params": [
|
||||
"Строка"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "postfix",
|
||||
"descr": "Задает приставку справа от значения",
|
||||
"params": [
|
||||
"Строка"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "id2show",
|
||||
"descr": "Задает ИД элемента, значение которого хотим отображать на экране",
|
||||
"params": ["Имя элемента конфигурации"]
|
||||
"params": [
|
||||
"Имя элемента конфигурации"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/robotclass/RobotClass_LiquidCrystal_I2C",
|
||||
"marcoschwartz/LiquidCrystal_I2C@^1.1.4"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/robotclass/RobotClass_LiquidCrystal_I2C",
|
||||
"marcoschwartz/LiquidCrystal_I2C@^1.1.4"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/robotclass/RobotClass_LiquidCrystal_I2C",
|
||||
"marcoschwartz/LiquidCrystal_I2C@^1.1.4"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/robotclass/RobotClass_LiquidCrystal_I2C",
|
||||
"marcoschwartz/LiquidCrystal_I2C@^1.1.4"
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
#include "classes/IoTItem.h"
|
||||
#include "ESPNexUpload.h"
|
||||
bool updated = false;
|
||||
// const char *host = "live-control.com";
|
||||
// const char *url = "/iotm/Live-Control.tft";
|
||||
extern IoTGpio IoTgpio;
|
||||
|
||||
class NextionUpload : public IoTItem
|
||||
{
|
||||
@@ -15,17 +12,21 @@ private:
|
||||
String _host;
|
||||
int _NEXT_RX;
|
||||
int _NEXT_TX;
|
||||
bool _UpTelegram;
|
||||
|
||||
public:
|
||||
NextionUpload(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_url = jsonReadStr(parameters, "url");
|
||||
_url = "/" + _url;
|
||||
_host = jsonReadStr(parameters, "host");
|
||||
|
||||
_NEXT_RX = jsonReadInt(parameters, "NEXT_RX");
|
||||
_NEXT_TX = jsonReadInt(parameters, "NEXT_TX");
|
||||
#define NEXT_RX _NEXT_RX // Nextion RX pin | Default 16
|
||||
#define NEXT_TX _NEXT_TX // Nextion TX pin | Default 17
|
||||
jsonRead(parameters, "UpTelegram", _UpTelegram);
|
||||
|
||||
//#define NEXT_RX _NEXT_RX // Nextion RX pin | Default 16
|
||||
//#define NEXT_TX _NEXT_TX // Nextion TX pin | Default 17
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m)
|
||||
@@ -43,50 +44,23 @@ public:
|
||||
#if defined ESP8266
|
||||
if (!http.begin(_host, 80, _url))
|
||||
{
|
||||
#elif defined ESP32
|
||||
if (!http.begin(String("http://") + _host + _url))
|
||||
{
|
||||
#endif
|
||||
// Serial.println("connection failed");
|
||||
SerialPrint("I", F("NextionUpdate"), "connection failed ");
|
||||
}
|
||||
#elif defined ESP32
|
||||
if (!http.begin(String("http://") + _host + _url))
|
||||
{
|
||||
// Serial.println("connection failed");
|
||||
SerialPrint("I", F("NextionUpdate"), "connection failed ");
|
||||
}
|
||||
#endif
|
||||
|
||||
SerialPrint("I", F("NextionUpdate"), "Requesting file: " + (String)_url);
|
||||
int code = http.GET();
|
||||
int contentLength = http.getSize();
|
||||
|
||||
// Update the nextion display
|
||||
if (code == 200)
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "File received. Update Nextion... ");
|
||||
bool result;
|
||||
ESPNexUpload nextion(115200);
|
||||
nextion.setUpdateProgressCallback([]()
|
||||
{ SerialPrint("I", F("NextionUpdate"), "... "); });
|
||||
|
||||
result = nextion.prepareUpload(contentLength);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "Error: " + (String)nextion.statusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "Start upload. File size is: " + (String)contentLength);
|
||||
result = nextion.upload(*http.getStreamPtr());
|
||||
|
||||
if (result)
|
||||
{
|
||||
updated = true;
|
||||
SerialPrint("I", F("NextionUpdate"), "Succesfully updated Nextion! ");
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "Error updating Nextion: " + (String)nextion.statusMessage);
|
||||
}
|
||||
|
||||
nextion.end();
|
||||
}
|
||||
flashNextion(http);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -101,6 +75,75 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
void uploadNextionTlgrm(String &url)
|
||||
{
|
||||
if (!_UpTelegram)
|
||||
return;
|
||||
if (!updated)
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "connecting to " + url);
|
||||
|
||||
HTTPClient http;
|
||||
|
||||
#ifdef ESP8266 // esp8266 требует SSl
|
||||
return;
|
||||
// BearSSL::WiFiClientSecure client;
|
||||
// client.setInsecure();
|
||||
// http.begin(client, url); // пингуем файл
|
||||
#else // esp32 сама умеет SSL
|
||||
if (!http.begin(url)) // пингуем файл
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "connection failed ");
|
||||
}
|
||||
#endif
|
||||
SerialPrint("I", F("NextionUpdate"), "Requesting file: OK" );
|
||||
int code = http.GET();
|
||||
// Update the nextion display
|
||||
if (code == 200)
|
||||
{ // файл доступен
|
||||
flashNextion(http);
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "HTTP error: " + (String)http.errorToString(code).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
SerialPrint("I", F("NextionUpdate"), "Closing connection ");
|
||||
}
|
||||
}
|
||||
|
||||
void flashNextion(HTTPClient &http)
|
||||
{
|
||||
int contentLength = http.getSize();
|
||||
SerialPrint("I", F("NextionUpdate"), "File received. Update Nextion... ");
|
||||
bool result;
|
||||
ESPNexUpload nextion(115200, _NEXT_RX, _NEXT_TX);
|
||||
nextion.setUpdateProgressCallback([]()
|
||||
{ SerialPrint("I", F("NextionUpdate"), "... "); });
|
||||
|
||||
result = nextion.prepareUpload(contentLength);
|
||||
if (!result)
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "Error: " + (String)nextion.statusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "Start upload. File size is: " + (String)contentLength);
|
||||
result = nextion.upload(*http.getStreamPtr());
|
||||
if (result)
|
||||
{
|
||||
updated = true;
|
||||
SerialPrint("I", F("NextionUpdate"), "Succesfully updated Nextion! ");
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("I", F("NextionUpdate"), "Error updating Nextion: " + (String)nextion.statusMessage);
|
||||
}
|
||||
nextion.end();
|
||||
}
|
||||
}
|
||||
|
||||
~NextionUpload(){};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"menuSection": "Экраны",
|
||||
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -12,19 +11,19 @@
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"host": "192.168.1.10",
|
||||
"url": "castom_nextion.tif",
|
||||
"url": "nextion.tft",
|
||||
"NEXT_TX": 16,
|
||||
"NEXT_RX": 17
|
||||
"NEXT_RX": 17,
|
||||
"UpTelegram": 1
|
||||
}
|
||||
],
|
||||
|
||||
"about": {
|
||||
"authorName": "AVAKS",
|
||||
"authorContact": "https://t.me/@avaks_dev",
|
||||
"authorGit": "https://github.com/avaksru",
|
||||
"specialThanks": "",
|
||||
"moduleName": "NextionUpload",
|
||||
"moduleVersion": "1.0",
|
||||
"moduleVersion": "1.1",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
@@ -32,19 +31,19 @@
|
||||
"title": "Nextion Upload",
|
||||
"moduleDesc": "загрузка прошивки в дисплей Nextion. Команда для запуска обновления дисплея: Nextion.Update(); ",
|
||||
"propInfo": {
|
||||
"host": "Сервер обновления",
|
||||
"url": "файл прошивки"
|
||||
"host": "Сервер обновления. Можно использовать LiveServer из VisualCode, указывать ip адрес",
|
||||
"url": "файл прошивки экрана, указывать с расширением, например nextion.tft или iotm/test.tft",
|
||||
"UpTelegram": "1 - разрешает прошивать экран через модуль Telegram_v2"
|
||||
}
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": ["https://github.com/avaksru/ESPNexUpload.git"],
|
||||
"esp8266_4mb": ["https://github.com/avaksru/ESPNexUpload.git"],
|
||||
"esp8266_1mb": ["https://github.com/avaksru/ESPNexUpload.git"],
|
||||
"esp8266_1mb_ota": ["https://github.com/avaksru/ESPNexUpload.git"],
|
||||
"esp8285_1mb": ["https://github.com/avaksru/ESPNexUpload.git"],
|
||||
"esp8285_1mb_ota": ["https://github.com/avaksru/ESPNexUpload.git"]
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
"esp8285_1mb": [],
|
||||
"esp8285_1mb_ota": []
|
||||
}
|
||||
}
|
||||
}
|
||||
174
src/modules/display/Oled128/Oled128.cpp
Normal file
174
src/modules/display/Oled128/Oled128.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
#include <GyverOLED.h>
|
||||
|
||||
GyverOLED<SSD1306_128x64, OLED_BUFFER> oled;
|
||||
|
||||
// GyverOLED<SSD1306_128x32, OLED_BUFFER> oled;
|
||||
// GyverOLED<SSD1306_128x32, OLED_NO_BUFFER> oled;
|
||||
// GyverOLED<SSD1306_128x64, OLED_NO_BUFFER> oled;
|
||||
// GyverOLED<SSD1306_128x64, OLED_BUFFER, OLED_SPI, 8, 7, 6> oled;
|
||||
// GyverOLED<SSH1106_128x64> oled;
|
||||
|
||||
class Oled128 : public IoTItem {
|
||||
private:
|
||||
unsigned int _x;
|
||||
unsigned int _y;
|
||||
|
||||
unsigned int _k;
|
||||
|
||||
int _shrift;
|
||||
|
||||
String _id2show;
|
||||
String _descr;
|
||||
String _descr1;
|
||||
|
||||
int _prevStrSize;
|
||||
|
||||
bool _isShow = true; // экран показывает
|
||||
|
||||
public:
|
||||
Oled128(String parameters) : IoTItem(parameters) {
|
||||
String addr, size, xy, k;
|
||||
_prevStrSize = 0;
|
||||
|
||||
jsonRead(parameters, "addr", addr);
|
||||
if (addr == "") {
|
||||
scanI2C();
|
||||
return;
|
||||
}
|
||||
|
||||
jsonRead(parameters, "coord", xy);
|
||||
_x = selectFromMarkerToMarker(xy, ",", 0).toInt();
|
||||
_y = selectFromMarkerToMarker(xy, ",", 1).toInt();
|
||||
|
||||
jsonRead(parameters, "descr", _descr);
|
||||
jsonRead(parameters, "id2show", _id2show);
|
||||
jsonRead(parameters, "descr1", _descr1);
|
||||
// jsonRead(parameters, "scale", _k);
|
||||
jsonRead(parameters, "shrift", _shrift);
|
||||
|
||||
// Wire.begin(2,0); // Инициализация шины I2C для модуля E01
|
||||
|
||||
oled.init(); // инициализация экрана
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
printBlankStr(_prevStrSize);
|
||||
|
||||
String tmpStr = "";
|
||||
|
||||
// if (_descr != "none") tmpStr = _descr + " " + getItemValue(_id2show);
|
||||
if (_descr != "none")
|
||||
tmpStr = _descr + " " + getItemValue(_id2show) + " " + _descr1;
|
||||
else
|
||||
tmpStr = getItemValue(_id2show);
|
||||
|
||||
// oled.setScale(2);
|
||||
|
||||
oled.setScale(_shrift);
|
||||
|
||||
oled.setCursorXY(_x, _y);
|
||||
|
||||
oled.print(tmpStr);
|
||||
|
||||
oled.update();
|
||||
|
||||
_prevStrSize = tmpStr.length();
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m) { // будет возможным использовать, когда сценарии запустятся
|
||||
|
||||
if (command == "scroll") {
|
||||
String tmpStr = "";
|
||||
oled.clear();
|
||||
uint32_t tmr = millis();
|
||||
oled.autoPrintln(false);
|
||||
int val = 128;
|
||||
for (;;) {
|
||||
// oled.clear(); // ЗАКОММЕНТИРУЙ, ЕСЛИ ВКЛЮЧЕН БУФЕР
|
||||
// oled.setScale(2);
|
||||
|
||||
oled.setScale(_shrift);
|
||||
|
||||
oled.setCursor(val, _y);
|
||||
|
||||
oled.print(tmpStr);
|
||||
oled.update();
|
||||
val--;
|
||||
if (millis() - tmr > 5000)
|
||||
; // return;
|
||||
|
||||
_isShow = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (command == "stopscroll") {
|
||||
_isShow = true;
|
||||
// display->backlight();
|
||||
// else if (command == "noDisplay") {
|
||||
// display->noDisplay();
|
||||
// _isShow = false;
|
||||
} else if (command == "display") {
|
||||
// display.display();
|
||||
_isShow = true;
|
||||
} else if (command == "toggle") {
|
||||
if (_isShow) {
|
||||
// display->noDisplay();
|
||||
_isShow = false;
|
||||
} else {
|
||||
// display.display();
|
||||
_isShow = true;
|
||||
}
|
||||
} else if (command == "x") {
|
||||
if (param.size()) {
|
||||
_x = param[0].valD;
|
||||
}
|
||||
} else if (command == "y") {
|
||||
if (param.size()) {
|
||||
_y = param[0].valD;
|
||||
}
|
||||
} else if (command == "descr") {
|
||||
if (param.size()) {
|
||||
_descr = param[0].valS;
|
||||
}
|
||||
} else if (command == "descr1") {
|
||||
if (param.size()) {
|
||||
_descr1 = param[0].valS;
|
||||
}
|
||||
} else if (command == "id2show") {
|
||||
if (param.size()) {
|
||||
_id2show = param[0].valS;
|
||||
}
|
||||
}
|
||||
|
||||
doByInterval();
|
||||
return {};
|
||||
}
|
||||
|
||||
// печать пустой строки нужной длинны для затирания предыдущего значения на экране
|
||||
void printBlankStr(int strSize) {
|
||||
String tmpStr = "";
|
||||
for (int i = 0; i < strSize; i++) tmpStr += " ";
|
||||
|
||||
// oled.setScale(2);
|
||||
|
||||
oled.setScale(_shrift);
|
||||
|
||||
oled.setCursorXY(_x, _y);
|
||||
|
||||
oled.print(tmpStr);
|
||||
}
|
||||
|
||||
~Oled128(){};
|
||||
};
|
||||
|
||||
void *getAPI_Oled128(String subtype, String param) {
|
||||
if (subtype == F("Oled128")) {
|
||||
return new Oled128(param);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
89
src/modules/display/Oled128/modinfo.json
Normal file
89
src/modules/display/Oled128/modinfo.json
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "OLED экран 128*64",
|
||||
"type": "Reading",
|
||||
"subtype": "Oled128",
|
||||
"id": "oled",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "T",
|
||||
"descr1": "C",
|
||||
"int": 1,
|
||||
"addr": "0x3C",
|
||||
"coord": "0,10",
|
||||
"id2show": "id датчика",
|
||||
"shrift": "2"
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Serghei Crasnicov",
|
||||
"authorContact": "https://t.me/Serghei63",
|
||||
"authorGit": "https://github.com/Serghei63",
|
||||
"specialThanks": "Ilya Belyakov @Biveraxe",
|
||||
"moduleName": "Oled128",
|
||||
"moduleVersion": "1.0",
|
||||
"moduleDesc": "Позволяет выводить на матричные Oled экраны по указанным позициям значения других элементов конфигурации.",
|
||||
"usedRam": 15,
|
||||
"propInfo": {
|
||||
"int": "Период времени в секундах обновления информации на экране по конкретному элементу.",
|
||||
"addr": "Адрес устройства на шине, обычно 0x3c.",
|
||||
"coord": "Координата позиции для вывода данных элемента конфигурации.",
|
||||
"id2show": "id элемента конфигурации.",
|
||||
"shrift": "Шрифт текста от 1 до 4 "
|
||||
},
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "x",
|
||||
"descr": "Устанавливает первую координату",
|
||||
"params": [
|
||||
"Номер строки первого символа"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"descr": "Устанавливает вторую координату",
|
||||
"params": [
|
||||
"Номер столбца первого символа"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "descr",
|
||||
"descr": "Задает приставку слева от значения, если none значит пусто",
|
||||
"params": [
|
||||
"Строка"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "descr1",
|
||||
"descr": "Задает приставку справа от значения. Если descr none , то не выводится",
|
||||
"params": [
|
||||
"Строка"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "id2show",
|
||||
"descr": "Задает ИД элемента, значение которого хотим отображать на экране",
|
||||
"params": [
|
||||
"Имя элемента конфигурации"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"gyverlibs/GyverOLED @ 1.4"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"gyverlibs/GyverOLED @ 1.4"
|
||||
],
|
||||
"esp32_16mb": [
|
||||
"gyverlibs/GyverOLED @ 1.4"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"gyverlibs/GyverOLED @ 1.4"
|
||||
]
|
||||
}
|
||||
}
|
||||
134
src/modules/display/Oled64/Oled64.cpp
Normal file
134
src/modules/display/Oled64/Oled64.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
|
||||
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
|
||||
#define OLED_RESET 0
|
||||
Adafruit_SSD1306 display(OLED_RESET);
|
||||
|
||||
class Oled64 : public IoTItem {
|
||||
private:
|
||||
unsigned int _x;
|
||||
unsigned int _y;
|
||||
String _id2show, _prefix = "", _postfix = "";
|
||||
String _size = "1";
|
||||
String _addr;
|
||||
|
||||
int _prevStrSize;
|
||||
|
||||
bool _isShow = true;
|
||||
|
||||
public:
|
||||
Oled64(String parameters) : IoTItem(parameters) {
|
||||
String size, xy;
|
||||
_prevStrSize = 0;
|
||||
|
||||
jsonRead(parameters, "addr", _addr);
|
||||
if (_addr == "") {
|
||||
scanI2C();
|
||||
return;
|
||||
}
|
||||
|
||||
display.begin(SSD1306_SWITCHCAPVCC, hexStringToUint8(_addr));
|
||||
|
||||
display.display();
|
||||
|
||||
display.clearDisplay();
|
||||
|
||||
jsonRead(parameters, "coord", xy);
|
||||
_x = selectFromMarkerToMarker(xy, ",", 0).toInt();
|
||||
_y = selectFromMarkerToMarker(xy, ",", 1).toInt();
|
||||
jsonRead(parameters, "id2show", _id2show);
|
||||
jsonRead(parameters, "prefix", _prefix);
|
||||
jsonRead(parameters, "postfix", _postfix);
|
||||
jsonRead(parameters, "size", _size);
|
||||
}
|
||||
|
||||
void drawItem(IoTItem *item) {
|
||||
String tmpStr = _prefix;
|
||||
tmpStr += item->getValue();
|
||||
tmpStr += _postfix;
|
||||
|
||||
display.setRotation(0);
|
||||
display.setCursor(_x, _y);
|
||||
display.setTextColor(WHITE, BLACK);
|
||||
display.setTextSize(_size.toInt());
|
||||
|
||||
printBlankStr(_prevStrSize);
|
||||
|
||||
display.setCursor(_x, _y);
|
||||
|
||||
display.print(tmpStr);
|
||||
_prevStrSize = tmpStr.length();
|
||||
display.display();
|
||||
_prevStrSize = tmpStr.length();
|
||||
}
|
||||
|
||||
void setValue(const IoTValue &Value, bool genEvent = true) {
|
||||
value = Value;
|
||||
drawItem(this);
|
||||
IoTItem::setValue(Value, genEvent);
|
||||
}
|
||||
|
||||
void onRegEvent(IoTItem *eventItem) {
|
||||
if (!eventItem || _id2show == "") return;
|
||||
|
||||
if (_id2show == eventItem->getID()) {
|
||||
setValue(eventItem->value, false);
|
||||
}
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m) {
|
||||
if (command == "display") {
|
||||
_isShow = true;
|
||||
} else if (command == "noDisplay") {
|
||||
_isShow = false;
|
||||
} else if (command == "x") {
|
||||
if (param.size()) {
|
||||
_x = param[0].valD;
|
||||
}
|
||||
} else if (command == "y") {
|
||||
if (param.size()) {
|
||||
_y = param[0].valD;
|
||||
}
|
||||
} else if (command == "prefix") {
|
||||
if (param.size()) {
|
||||
_prefix = param[0].valS;
|
||||
}
|
||||
} else if (command == "postfix") {
|
||||
if (param.size()) {
|
||||
_postfix = param[0].valS;
|
||||
}
|
||||
} else if (command == "id2show") {
|
||||
if (param.size()) {
|
||||
_id2show = param[0].valS;
|
||||
}
|
||||
}
|
||||
|
||||
doByInterval();
|
||||
return {};
|
||||
}
|
||||
|
||||
// печать пустой строки нужной длинны для затирания предыдущего значения на экране
|
||||
void printBlankStr(int strSize) {
|
||||
String tmpStr = "";
|
||||
for (int i = 0; i < strSize; i++) tmpStr += " ";
|
||||
display.setCursor(_x, _y);
|
||||
display.print(tmpStr);
|
||||
}
|
||||
|
||||
~Oled64(){};
|
||||
};
|
||||
|
||||
void *getAPI_Oled64(String subtype, String param) {
|
||||
if (subtype == F("Oled64")) {
|
||||
return new Oled64(param);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
97
src/modules/display/Oled64/modinfo.json
Normal file
97
src/modules/display/Oled64/modinfo.json
Normal file
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "OLED экран 64 8266",
|
||||
"type": "Reading",
|
||||
"subtype": "Oled64",
|
||||
"id": "Oled",
|
||||
"widget": "inputTxt",
|
||||
"page": "screens",
|
||||
"descr": "OLED Экран",
|
||||
"addr": "0x3C",
|
||||
"coord": "0,0",
|
||||
"size": "1",
|
||||
"id2show": "",
|
||||
"prefix": "",
|
||||
"postfix": ""
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Serghei Crasnicov",
|
||||
"authorContact": "https://t.me/Serghei63",
|
||||
"authorGit": "https://github.com/Serghei63",
|
||||
"specialThanks": "Valentin Khandriga @Valiuhaaa",
|
||||
"moduleName": "Oled64",
|
||||
"moduleVersion": "1.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"title": "Модуль отображения на экранах OLED 64*48",
|
||||
"moduleDesc": "Позволяет выводить на OLED экраны по указанным позициям значения других элементов конфигурации.",
|
||||
"propInfo": {
|
||||
"addr": "Адрес устройства на шине, обычно 0x3C.",
|
||||
"coord": "Координата позиции для вывода данных элемента конфигурации.",
|
||||
"id2show": "id элемента конфигурации.",
|
||||
"size": "Размер шрифта. Допускается 1, 2, 3, 4"
|
||||
},
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "x",
|
||||
"descr": "Устанавливает первую координату",
|
||||
"params": [
|
||||
"Номер строки первого символа"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"descr": "Устанавливает вторую координату",
|
||||
"params": [
|
||||
"Номер столбца первого символа"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "descr",
|
||||
"descr": "Задает приставку слева от значения",
|
||||
"params": [
|
||||
"Строка"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "descr1",
|
||||
"descr1": "Задает приставку справа от значения",
|
||||
"params": [
|
||||
"Строка"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "id2show",
|
||||
"descr": "Задает ИД элемента, значение которого хотим отображать на экране",
|
||||
"params": [
|
||||
"Имя элемента конфигурации"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED",
|
||||
"https://github.com/adafruit/Adafruit-GFX-Library"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED",
|
||||
"https://github.com/adafruit/Adafruit-GFX-Library"
|
||||
],
|
||||
"esp8266_1mb": [
|
||||
"https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED",
|
||||
"https://github.com/adafruit/Adafruit-GFX-Library"
|
||||
],
|
||||
"esp8266_1mb_ota": [
|
||||
"https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED",
|
||||
"https://github.com/adafruit/Adafruit-GFX-Library"
|
||||
]
|
||||
}
|
||||
}
|
||||
150
src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.cpp
Normal file
150
src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "Modbus_master_for_Smi2_m.h"
|
||||
|
||||
Smi_display::Smi_display() {
|
||||
}
|
||||
|
||||
// Modbus Master
|
||||
void Smi_display::modbus_update() {
|
||||
idle();
|
||||
}
|
||||
|
||||
void Smi_display::idle() {
|
||||
static unsigned int packet_index;
|
||||
|
||||
unsigned int failed_connections = 0;
|
||||
|
||||
unsigned char current_connection;
|
||||
|
||||
do {
|
||||
if (packet_index == total_no_of_packets) // wrap around to the beginning
|
||||
packet_index = 0;
|
||||
|
||||
// proceed to the next packet
|
||||
packet = &packetArray[packet_index];
|
||||
|
||||
// get the current connection status
|
||||
current_connection = packet->connection;
|
||||
|
||||
if (!current_connection) {
|
||||
// If all the connection attributes are false return
|
||||
// immediately to the main sketch
|
||||
if (++failed_connections == total_no_of_packets)
|
||||
return;
|
||||
}
|
||||
packet_index++;
|
||||
|
||||
// if a packet has no connection get the next one
|
||||
} while (!current_connection);
|
||||
|
||||
constructPacket();
|
||||
}
|
||||
|
||||
void Smi_display::constructPacket() {
|
||||
packet->requests++;
|
||||
frame[0] = packet->id;
|
||||
frame[1] = packet->function;
|
||||
frame[2] = packet->address >> 8; // address Hi
|
||||
frame[3] = packet->address & 0xFF; // address Lo
|
||||
frame[4] = packet->data >> 8; // MSB
|
||||
frame[5] = packet->data & 0xFF; // LSB
|
||||
|
||||
unsigned char frameSize;
|
||||
|
||||
// construct the frame according to the modbus function
|
||||
if (packet->function == PRESET_MULTIPLE_REGISTERS)
|
||||
frameSize = construct_F16();
|
||||
else // else functions 1,2,3,4,5 & 6 is assumed. They all share the exact same request format.
|
||||
frameSize = 8; // the request is always 8 bytes in size for the above mentioned functions.
|
||||
|
||||
unsigned int crc16 = calculateCRC(frameSize - 2);
|
||||
frame[frameSize - 2] = crc16 >> 8; // split crc into 2 bytes
|
||||
frame[frameSize - 1] = crc16 & 0xFF;
|
||||
sendPacket(frameSize);
|
||||
}
|
||||
|
||||
unsigned char Smi_display::construct_F16() {
|
||||
unsigned char no_of_bytes = packet->data * 2;
|
||||
|
||||
// first 6 bytes of the array + no_of_bytes + 2 bytes CRC
|
||||
frame[6] = no_of_bytes; // number of bytes
|
||||
unsigned char index = 7; // user data starts at index 7
|
||||
unsigned char no_of_registers = packet->data;
|
||||
/*unsigned*/ int temp;
|
||||
|
||||
for (unsigned char i = 0; i < no_of_registers; i++) {
|
||||
temp = register_array[packet->local_start_address + i]; // get the data
|
||||
frame[index] = temp >> 8;
|
||||
index++;
|
||||
frame[index] = temp & 0xFF;
|
||||
index++;
|
||||
}
|
||||
unsigned char frameSize = (9 + no_of_bytes); // first 7 bytes of the array + 2 bytes CRC + noOfBytes
|
||||
return frameSize;
|
||||
}
|
||||
|
||||
void Smi_display::modbus_configure(HardwareSerial* SerialPort,
|
||||
long baud,
|
||||
unsigned char byteFormat,
|
||||
int rx,
|
||||
int tx,
|
||||
unsigned int _TxEnablePin,
|
||||
Packet* _packets,
|
||||
unsigned int _total_no_of_packets,
|
||||
/*unsigned*/ int* _register_array) {
|
||||
TxEnablePin = _TxEnablePin;
|
||||
total_no_of_packets = _total_no_of_packets;
|
||||
packetArray = _packets;
|
||||
register_array = _register_array;
|
||||
|
||||
ModbusPort = SerialPort;
|
||||
(*ModbusPort).begin(baud, byteFormat, rx, tx);
|
||||
|
||||
pinMode(TxEnablePin, OUTPUT);
|
||||
digitalWrite(TxEnablePin, LOW);
|
||||
}
|
||||
|
||||
void Smi_display::modbus_construct(Packet* _packet,
|
||||
unsigned char id,
|
||||
unsigned char function,
|
||||
unsigned int address,
|
||||
unsigned int data,
|
||||
unsigned int local_start_address) {
|
||||
_packet->id = id;
|
||||
_packet->function = function;
|
||||
_packet->address = address;
|
||||
_packet->data = data;
|
||||
_packet->local_start_address = local_start_address;
|
||||
_packet->connection = 1;
|
||||
}
|
||||
|
||||
unsigned int Smi_display::calculateCRC(unsigned char bufferSize) {
|
||||
unsigned int temp, temp2, flag;
|
||||
temp = 0xFFFF;
|
||||
for (unsigned char i = 0; i < bufferSize; i++) {
|
||||
temp = temp ^ frame[i];
|
||||
for (unsigned char j = 1; j <= 8; j++) {
|
||||
flag = temp & 0x0001;
|
||||
temp >>= 1;
|
||||
if (flag)
|
||||
temp ^= 0xA001;
|
||||
}
|
||||
}
|
||||
// Reverse byte order.
|
||||
temp2 = temp >> 8;
|
||||
temp = (temp << 8) | temp2;
|
||||
temp &= 0xFFFF;
|
||||
// the returned value is already swapped
|
||||
// crcLo byte is first & crcHi byte is last
|
||||
return temp;
|
||||
}
|
||||
|
||||
void Smi_display::sendPacket(unsigned char bufferSize) {
|
||||
digitalWrite(TxEnablePin, HIGH);
|
||||
|
||||
for (unsigned char i = 0; i < bufferSize; i++)
|
||||
(*ModbusPort).write(frame[i]);
|
||||
|
||||
(*ModbusPort).flush();
|
||||
|
||||
digitalWrite(TxEnablePin, LOW);
|
||||
}
|
||||
71
src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.h
Normal file
71
src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "HardwareSerial.h"
|
||||
|
||||
#define PRESET_MULTIPLE_REGISTERS 16
|
||||
#define BUFFER_SIZE 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char id;
|
||||
unsigned char function;
|
||||
unsigned int address;
|
||||
|
||||
unsigned int data;
|
||||
unsigned int local_start_address;
|
||||
|
||||
unsigned int requests;
|
||||
unsigned int successful_requests;
|
||||
unsigned int failed_requests;
|
||||
unsigned int exception_errors;
|
||||
unsigned int retries;
|
||||
|
||||
unsigned char connection;
|
||||
|
||||
} Packet;
|
||||
|
||||
class Smi_display {
|
||||
public:
|
||||
Smi_display();
|
||||
|
||||
~Smi_display();
|
||||
|
||||
public:
|
||||
void modbus_update();
|
||||
void modbus_construct(Packet* _packet,
|
||||
unsigned char id,
|
||||
unsigned char function,
|
||||
unsigned int address,
|
||||
unsigned int data,
|
||||
unsigned _local_start_address);
|
||||
|
||||
void modbus_configure(HardwareSerial* SerialPort,
|
||||
long baud,
|
||||
unsigned char byteFormat,
|
||||
int rx,
|
||||
int tx,
|
||||
unsigned int _TxEnablePin,
|
||||
Packet* _packets,
|
||||
unsigned int _total_no_of_packets,
|
||||
/*unsigned*/ int* _register_array);
|
||||
|
||||
private:
|
||||
void idle();
|
||||
void constructPacket();
|
||||
unsigned char construct_F16();
|
||||
unsigned int calculateCRC(unsigned char bufferSize);
|
||||
void sendPacket(unsigned char bufferSize);
|
||||
|
||||
private:
|
||||
unsigned char state;
|
||||
unsigned char retry_count;
|
||||
unsigned int TxEnablePin;
|
||||
unsigned char frame[BUFFER_SIZE];
|
||||
unsigned char buffer;
|
||||
unsigned int T1_5;
|
||||
unsigned int frameDelay;
|
||||
unsigned int total_no_of_packets;
|
||||
Packet* packetArray;
|
||||
Packet* packet;
|
||||
/*unsigned*/ int* register_array;
|
||||
HardwareSerial* ModbusPort;
|
||||
};
|
||||
71
src/modules/display/Smi2_m/Smi2_m.cpp
Normal file
71
src/modules/display/Smi2_m/Smi2_m.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
#include "Modbus_master_for_Smi2_m.h"
|
||||
|
||||
// Общая сумма доступной памяти на ведущем устройстве, чтобы хранить данные
|
||||
#define TOTAL_NO_OF_REGISTERS 4
|
||||
|
||||
// This is the easiest way to create new packets
|
||||
// Add as many as you want. TOTAL_NO_OF_PACKETS
|
||||
// is automatically updated.
|
||||
enum {
|
||||
PACKET1,
|
||||
TOTAL_NO_OF_PACKETS // leave this last entry
|
||||
};
|
||||
|
||||
// Масив пакетов модбус
|
||||
Packet packets[TOTAL_NO_OF_PACKETS];
|
||||
|
||||
// Массив хранения содержимого принятых и передающихся регистров
|
||||
/*unsigned*/ int regs[TOTAL_NO_OF_REGISTERS];
|
||||
|
||||
class Smi2_m : public IoTItem {
|
||||
private:
|
||||
Smi_display* smi;
|
||||
unsigned int _pin; // номер порта, управляющий передачей по RS-485
|
||||
long int _baud; // скорость обмена, бит/с
|
||||
int _rx, _tx; // номера ножек мк, к которым подключен UART
|
||||
String _show;
|
||||
int i = 10;
|
||||
|
||||
public:
|
||||
public:
|
||||
Smi2_m(String parameters) : IoTItem(parameters) {
|
||||
smi = new Smi_display();
|
||||
|
||||
_pin = jsonReadInt(parameters, "pin");
|
||||
_baud = jsonReadLInt(parameters, "baud");
|
||||
_rx = jsonReadInt(parameters, "rx");
|
||||
_tx = jsonReadInt(parameters, "tx");
|
||||
// Настраиваем пакеты
|
||||
// Шестой параметр - это индекс ячейки в массиве, размещенном в памяти ведущего устройства, в которую будет
|
||||
// помещен результат или из которой будут браться данные для передачи в подчиненное устройство. В нашем коде - это массив reg
|
||||
// Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
|
||||
// Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
|
||||
smi->modbus_construct(&packets[PACKET1], 1, PRESET_MULTIPLE_REGISTERS, 4200, 1, 0);
|
||||
smi->modbus_configure(&Serial, _baud, SERIAL_8N1, _rx, _tx, _pin, packets, TOTAL_NO_OF_PACKETS, regs);
|
||||
|
||||
jsonRead(parameters, "id2show", _show);
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
if (smi) {
|
||||
smi->modbus_update();
|
||||
|
||||
float tmpStr = getItemValue(_show).toFloat();
|
||||
regs[0] = tmpStr * 10;
|
||||
}
|
||||
}
|
||||
|
||||
~Smi2_m(){};
|
||||
};
|
||||
|
||||
void* getAPI_Smi2_m(String subtype, String param) {
|
||||
if (subtype == F("Smi2_m")) {
|
||||
return new Smi2_m(param);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
59
src/modules/display/Smi2_m/modinfo.json
Normal file
59
src/modules/display/Smi2_m/modinfo.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Smi2_m",
|
||||
"type": "Writing",
|
||||
"subtype": "Smi2_m",
|
||||
"id": "Smi",
|
||||
"widget": "",
|
||||
"page": "Дисплеи",
|
||||
"descr": "",
|
||||
"int": 2,
|
||||
"pin": 4,
|
||||
"id2show": "id датчика",
|
||||
"baud": "9600",
|
||||
"rx": "16",
|
||||
"tx": "17"
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"moduleName": "Smi2_m",
|
||||
"moduleVersion": "1.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"moduleDesc": "Позволяет вывести индикацию на семисегментный индикатор по Modbus (Работает в Slave режиме, Read Holding Registers 0х03)",
|
||||
"propInfo": {
|
||||
"int": "Период времени в секундах обновления информации на экране по конкретному элементу.",
|
||||
"pin": "номер порта, управляющий передачей по RS-485",
|
||||
"id2show": "id элемента конфигурации.",
|
||||
"baud": "скорость обмена, бит/с"
|
||||
},
|
||||
"title": "СМИ2-М трёхцветный Modbus-индикатор",
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "descr",
|
||||
"descr": "Задает приставку слева от значения",
|
||||
"params": [
|
||||
"Строка"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "id2show",
|
||||
"descr": "Задает ИД элемента, значение которого хотим отображать на экране",
|
||||
"params": [
|
||||
"Имя элемента конфигурации"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": []
|
||||
}
|
||||
}
|
||||
86
src/modules/display/TM16XX/TM16XX.cpp
Normal file
86
src/modules/display/TM16XX/TM16XX.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
#include <TM1637.h>
|
||||
#include <TM1638.h>
|
||||
#include <TM16xxDisplay.h>
|
||||
|
||||
|
||||
|
||||
class TM16XX : public IoTItem {
|
||||
private:
|
||||
TM16xxDisplay *_display = nullptr;
|
||||
TM16xx *_module = nullptr;
|
||||
std::vector<String> _ids2show;
|
||||
|
||||
public:
|
||||
TM16XX(String parameters) : IoTItem(parameters) {
|
||||
//jsonRead(parameters, "id2show", _id2show);
|
||||
|
||||
int DIO, CLK, STB, chip, numDigits, intensity;
|
||||
bool onoff;
|
||||
String id2show;
|
||||
jsonRead(parameters, "DIO", DIO);
|
||||
jsonRead(parameters, "CLK", CLK);
|
||||
jsonRead(parameters, "STB", STB);
|
||||
jsonRead(parameters, "chip", chip);
|
||||
jsonRead(parameters, "numDigits", numDigits);
|
||||
jsonRead(parameters, "intensity", intensity);
|
||||
jsonRead(parameters, "on", onoff);
|
||||
|
||||
jsonRead(parameters, "id2show", id2show);
|
||||
if (id2show != "") _ids2show = splitStr(id2show, ",");
|
||||
|
||||
if (chip == 1637) {
|
||||
_module = new TM1637(DIO, CLK, numDigits);
|
||||
} else if (chip == 1638) {
|
||||
_module = new TM1638(DIO, CLK, STB, numDigits);
|
||||
}
|
||||
_module->setupDisplay(onoff, intensity);
|
||||
_display = new TM16xxDisplay(_module, numDigits);
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
|
||||
}
|
||||
|
||||
void setValue(const IoTValue& Value, bool genEvent = true) {
|
||||
if (_display == nullptr) return;
|
||||
|
||||
value = Value;
|
||||
_display->println(getValue());
|
||||
IoTItem::setValue(Value, genEvent);
|
||||
}
|
||||
|
||||
void onRegEvent(IoTItem* eventItem) {
|
||||
if (_display == nullptr) return;
|
||||
if (!eventItem || _ids2show.size() == 0) return;
|
||||
|
||||
if (strInVector(eventItem->getID(), _ids2show)) {
|
||||
if (_ids2show.size() == 1) {
|
||||
_display->println(eventItem->getValue());
|
||||
} else {
|
||||
_display->println();
|
||||
for (int i = 0; i < _ids2show.size(); i++) {
|
||||
IoTItem* item = findIoTItem(_ids2show[i]);
|
||||
if (item) {
|
||||
_display->print(item->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~TM16XX() {
|
||||
delete _display;
|
||||
delete _module;
|
||||
};
|
||||
};
|
||||
|
||||
void *getAPI_TM16XX(String subtype, String param) {
|
||||
if (subtype == F("TM16XX")) {
|
||||
return new TM16XX(param);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
156
src/modules/display/TM16XX/modinfo.json
Normal file
156
src/modules/display/TM16XX/modinfo.json
Normal file
@@ -0,0 +1,156 @@
|
||||
{
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "7 сегментный дисплей TM16XX",
|
||||
"type": "Writing",
|
||||
"subtype": "TM16XX",
|
||||
"id": "tm",
|
||||
"widget": "inputTxt",
|
||||
"page": "screens",
|
||||
"descr": "Экран",
|
||||
"round": 0,
|
||||
"chip": 1637,
|
||||
"numDigits": 4,
|
||||
"DIO": "13",
|
||||
"CLK": "14",
|
||||
"STB": "12",
|
||||
"intensity": "5",
|
||||
"on": "1",
|
||||
"id2show": ""
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
"authorGit": "https://github.com/biveraxe",
|
||||
"specialThanks": "",
|
||||
"moduleName": "TM16XX",
|
||||
"moduleVersion": "1.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"moduleDesc": "Позволяет выводить на 7 сегментный экран серии TM16XX (TM1637, TM1638). Может быть расширен до поддержки TM1616, TM1620, TM1628, TM1630, TM1637, TM1638, TM1640, TM1650, TM1652 и TM1668",
|
||||
"propInfo": {
|
||||
"int": "Период времени в секундах обновления информации на экране по конкретному элементу.",
|
||||
"chip": "Номер чипа TM1637 или TM1638",
|
||||
"numDigits": "Число цифр на дисплее",
|
||||
"DIO": "Номер пина данных",
|
||||
"CLK": "Номер пина часового сигнала",
|
||||
"intensity": "Яркость 0-7",
|
||||
"on": "Вкл/выкл при старте 1/0",
|
||||
"STB": "Номер пина стекового сигнала - не используется на определенных моделях",
|
||||
"id2show": "id элемента конфигурации для отображения. Если пустая строка, то дисплей использует свою переменную. Если указать несколько значений через запятую, то все данные будут последовательно выводиться в строку."
|
||||
},
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "noBacklight",
|
||||
"descr": "Выключить подсветку",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "backlight",
|
||||
"descr": "Включить подсветку",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "noDisplay",
|
||||
"descr": "Спрятать все данные",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"descr": "Показать данные на экране",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "toggle",
|
||||
"descr": "Переключает видимость значений на экране",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"descr": "Устанавливает первую координату",
|
||||
"params": [
|
||||
"Номер строки первого символа"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"descr": "Устанавливает вторую координату",
|
||||
"params": [
|
||||
"Номер столбца первого символа"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "descr",
|
||||
"descr": "Задает приставку слева от значения",
|
||||
"params": [
|
||||
"Строка"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "id2show",
|
||||
"descr": "Задает ИД элемента, значение которого хотим отображать на экране",
|
||||
"params": [
|
||||
"Имя элемента конфигурации"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8266_1mb": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8266_1mb_ota": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8285_1mb": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8285_1mb_ota": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8266_2mb": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8266_2mb_ota": [
|
||||
"https://github.com/maxint-rd/TM16xx",
|
||||
"adafruit/Adafruit GFX Library @ ^1.11.5",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Экраны",
|
||||
"menuSection": "screens",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -40,6 +40,7 @@
|
||||
"max": "Максимальный порог индикатора на который реагировать.",
|
||||
"idshow": "id элемента конфигурации который нужно повесить индикацию."
|
||||
},
|
||||
"title": "Адресная светодиодная матрица",
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "noShow",
|
||||
@@ -99,6 +100,12 @@
|
||||
"esp32_4mb": [
|
||||
"adafruit/Adafruit NeoPixel@^1.10.6"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"adafruit/Adafruit NeoPixel@^1.10.6"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"adafruit/Adafruit NeoPixel@^1.10.6"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"adafruit/Adafruit NeoPixel@^1.10.6"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -46,7 +46,12 @@
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp32_16mb": [],
|
||||
"esp32s2_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_16mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
"esp8285_1mb": [],
|
||||
|
||||
@@ -3,24 +3,28 @@
|
||||
|
||||
extern IoTGpio IoTgpio;
|
||||
|
||||
|
||||
class ButtonOut : public IoTItem {
|
||||
private:
|
||||
int _pin, _inv;
|
||||
int _pin;
|
||||
bool _inv;
|
||||
|
||||
public:
|
||||
ButtonOut(String parameters): IoTItem(parameters) {
|
||||
jsonRead(parameters, "pin", _pin);
|
||||
jsonRead(parameters, "inv", _inv);
|
||||
_round = 0;
|
||||
|
||||
IoTgpio.pinMode(_pin, OUTPUT);
|
||||
IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD);
|
||||
enableDoByInt = false;
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
//value.valD = IoTgpio.analogRead(_pin);
|
||||
|
||||
int val = _inv?1:0;
|
||||
IoTgpio.digitalWrite(_pin, val);
|
||||
// SerialPrint("I", "ButtonOut","single pulse end");
|
||||
value.valD = 0;
|
||||
regEvent(0, "ButtonOut");
|
||||
enableDoByInt = false;
|
||||
//regEvent(value.valD, "ButtonOut"); //обязательный вызов хотяб один
|
||||
}
|
||||
|
||||
@@ -30,24 +34,40 @@ class ButtonOut : public IoTItem {
|
||||
// param - вектор ("массив") значений параметров переданных вместе с командой: ID.Команда("пар1", 22, 33) -> param[0].ValS = "пар1", param[1].ValD = 22
|
||||
|
||||
if (command == "change") {
|
||||
value.valD = 1 - IoTgpio.digitalRead(_pin);
|
||||
IoTgpio.digitalWrite(_pin, value.valD);
|
||||
value.valD = 1 - (int)value.valD;
|
||||
IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD);
|
||||
regEvent(value.valD, "ButtonOut");
|
||||
}
|
||||
|
||||
else if (command == "pulse") {
|
||||
if (param[0].isDecimal && (param[0].valD != 0)) {
|
||||
value.valD = !_inv?1:0;
|
||||
enableDoByInt = true;
|
||||
// SerialPrint("I", "ButtonOut","single pulse start");
|
||||
regEvent((String)(int)!_inv?1:0, "ButtonOut");
|
||||
suspendNextDoByInt(param[0].valD);
|
||||
IoTgpio.digitalWrite(_pin, !_inv?1:0);
|
||||
}
|
||||
}
|
||||
return {}; // команда поддерживает возвращаемое значения. Т.е. по итогу выполнения команды или общения с внешней системой, можно вернуть значение в сценарий для дальнейшей обработки
|
||||
}
|
||||
|
||||
void setValue(const IoTValue& Value, bool genEvent = true) {
|
||||
value = Value;
|
||||
IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD);
|
||||
if ((value.valD == !_inv?1:0) && (_interval != 0)) {
|
||||
value.valD = !_inv?1:0;
|
||||
enableDoByInt = true;
|
||||
// SerialPrint("I", "ButtonOut","single pulse start");
|
||||
suspendNextDoByInt(_interval);
|
||||
} else {
|
||||
enableDoByInt = false;
|
||||
}
|
||||
regEvent((String)(int)value.valD, "ButtonOut", false, genEvent);
|
||||
IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD);
|
||||
}
|
||||
|
||||
String getValue() {
|
||||
return (String)(int)value.valD;
|
||||
}
|
||||
//=======================================================================================================
|
||||
|
||||
~ButtonOut() {};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -39,13 +39,23 @@
|
||||
"name": "change",
|
||||
"descr": "Инвертирует значение переключателя",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "pulse",
|
||||
"descr": "Генерирует одиночный импульс",
|
||||
"params": ["Длительность (ms)"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp32_16mb": [],
|
||||
"esp32s2_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_16mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
"esp8285_1mb": [],
|
||||
|
||||
@@ -79,16 +79,7 @@ public:
|
||||
|
||||
case 1:
|
||||
// for doByIntervals
|
||||
if (enableDoByInt)
|
||||
{
|
||||
currentMillis = millis();
|
||||
difference = currentMillis - prevMillis;
|
||||
if (difference >= _interval)
|
||||
{
|
||||
prevMillis = millis();
|
||||
this->doByInterval();
|
||||
}
|
||||
}
|
||||
IoTItem::loop();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Пассивный звуковой извещатель",
|
||||
"type": "Writing",
|
||||
"subtype": "Buzzer",
|
||||
"id": "buzzer",
|
||||
"widget": "toggle",
|
||||
"page": "Кнопки",
|
||||
"descr": "Buzzer",
|
||||
"int": 4000,
|
||||
"pin": 14,
|
||||
"freq": 2000,
|
||||
"duration": 1000,
|
||||
"beatLevel": 4,
|
||||
"tempo": 120,
|
||||
"tempoCorrection": 1,
|
||||
"pauseBetween": 0,
|
||||
"transpose": 0,
|
||||
"cycle": 0,
|
||||
"indication": 1,
|
||||
"val": 0
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Пассивный звуковой извещатель",
|
||||
"type": "Writing",
|
||||
"subtype": "Buzzer",
|
||||
"id": "buzzer",
|
||||
"widget": "toggle",
|
||||
"page": "Кнопки",
|
||||
"descr": "Buzzer",
|
||||
"int": 4000,
|
||||
"pin": 14,
|
||||
"freq": 2000,
|
||||
"duration": 1000,
|
||||
"beatLevel": 4,
|
||||
"tempo": 120,
|
||||
"tempoCorrection": 1,
|
||||
"pauseBetween": 0,
|
||||
"transpose": 0,
|
||||
"cycle": 0,
|
||||
"indication": 1,
|
||||
"val": 0
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Alex K",
|
||||
"authorContact": "https://t.me/cmche",
|
||||
@@ -43,11 +43,11 @@
|
||||
"int": "Количество миллисекунд между повторами одиночного сигнала",
|
||||
"pin": "Управляемый пин",
|
||||
"freq": "Частота сигнала, Hz",
|
||||
"duration": "Длительность сигнала, ms",
|
||||
"duration": "Длительность сигнала, ms",
|
||||
"beatLevel": "Долей в такте",
|
||||
"tempo": "Оригинальный темп мелодии, bpm",
|
||||
"tempoCorrection": "Коррекция темпа мелодии",
|
||||
"pauseBetween": "Дополнительная пауза между нот, в долях от длительности ноты",
|
||||
"tempo": "Оригинальный темп мелодии, bpm",
|
||||
"tempoCorrection": "Коррекция темпа мелодии",
|
||||
"pauseBetween": "Дополнительная пауза между нот, в долях от длительности ноты",
|
||||
"transpose": "Транспонирование на количество полутонов. +/-12 - для повышения/понижения на октаву",
|
||||
"cycle": "Повтор мелодии/серии сигналов",
|
||||
"indication": "Индикация в виджет, что идет сигнал, играет мелодия",
|
||||
@@ -57,17 +57,30 @@
|
||||
{
|
||||
"name": "tone",
|
||||
"descr": "Проигрывание одиночного сигнала (без индикации)",
|
||||
"params": ["Частота", "Длительность (ms)"]
|
||||
"params": [
|
||||
"Частота",
|
||||
"Длительность (ms)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tones",
|
||||
"descr": "Проигрывание серии сигналов, до 128",
|
||||
"params": ["Частота 1-го сигнала", "Длительность 1-го сигнала (ms)","Частота 2-го сигала", "Длительность 2-го сигнала", "....итд"]
|
||||
"params": [
|
||||
"Частота 1-го сигнала",
|
||||
"Длительность 1-го сигнала (ms)",
|
||||
"Частота 2-го сигала",
|
||||
"Длительность 2-го сигнала",
|
||||
"....итд"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "melody",
|
||||
"descr": "Проигрывание мелодии, до 256 нот. Кодировка 'YYX.ZZZ'. Научная нотация: YY - обозначение ноты (C,CS,D,DS,E,F,FS,G,GS,A,AS,B), X - номер октавы (0-9), ZZZ - длительность в тысячных долях такта (0-999). Обязательно в двойных кавычках. 'AS4.50' - Ля# 4-й октавы, 1/2 такта. На Github лежит Excel файл для перекодировки.",
|
||||
"params": ["Код 1-ой ноты","Код 2-й ноты"," и тд"]
|
||||
"params": [
|
||||
"Код 1-ой ноты",
|
||||
"Код 2-й ноты",
|
||||
" и тд"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "notone",
|
||||
@@ -76,21 +89,29 @@
|
||||
},
|
||||
{
|
||||
"name": "melodySetting",
|
||||
"descr": "Перенастройка параметров мелодии: Долей в такте - (обычно 4), Оригинальный темп -(40-208 bpm), Коррекция темпа - в k раз быстрее/медленнее, Пауза между нот (стакато) - доля от длительности, Коррекция тональности (транспонирование) - в k раз выше/ниже, Повтор 1/0. Чтобы не изменялось значение вбить любой текст в ковычках ",
|
||||
"params": ["Долей в такте", "Оригинальный темп", "Коррекция темпа", "Пауза между нот", "Коррекция тональности", "Повтор мелодии/серии сигналов"]
|
||||
"descr": "Перенастройка параметров мелодии: Долей в такте - (обычно 4), Оригинальный темп -(40-208 bpm), Коррекция темпа - в k раз быстрее/медленнее, Пауза между нот (стакато) - доля от длительности, Коррекция тональности (транспонирование) - в k раз выше/ниже, Повтор 1/0. Чтобы не изменялось значение вбить любой текст в ковычках ",
|
||||
"params": [
|
||||
"Долей в такте",
|
||||
"Оригинальный темп",
|
||||
"Коррекция темпа",
|
||||
"Пауза между нот",
|
||||
"Коррекция тональности",
|
||||
"Повтор мелодии/серии сигналов"
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
"esp8285_1mb": [],
|
||||
"esp8285_1mb_ota": []
|
||||
"esp8285_1mb_ota": [],
|
||||
"esp8266_2mb": [],
|
||||
"esp8266_2mb_ota": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -12,8 +12,7 @@
|
||||
"descr": "Громкость",
|
||||
"needSave": 0,
|
||||
"val": "0",
|
||||
"round" : 0,
|
||||
|
||||
"round": 0,
|
||||
"step": 1,
|
||||
"stepOnPress": 5,
|
||||
"pins": "4,5,2"
|
||||
@@ -37,7 +36,7 @@
|
||||
"moduleDesc": "модуль для работы с Энкодером. Кнопочный вариант совместим с модулями Multitouch и ButtonIn",
|
||||
"retInfo": "Значение счетчика",
|
||||
"propInfo": {
|
||||
"step" : "Размер шага Энкодера, может принимать значение 0.0001 или 1000",
|
||||
"step": "Размер шага Энкодера, может принимать значение 0.0001 или 1000",
|
||||
"stepOnPress": "Размер шага Энкодера при нажатой кнопке, 0 - отключает учет",
|
||||
"pins": "Подключеные пины (CLK, DT, SW)"
|
||||
}
|
||||
@@ -47,9 +46,18 @@
|
||||
"esp32_4mb": [
|
||||
"gyverlibs/EncButton @ ^2.0"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"gyverlibs/EncButton @ ^2.0"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"gyverlibs/EncButton @ ^2.0"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"gyverlibs/EncButton @ ^2.0"
|
||||
],
|
||||
"esp8266_16mb": [
|
||||
"gyverlibs/EncButton @ ^2.0"
|
||||
],
|
||||
"esp8266_1mb": [
|
||||
"gyverlibs/EncButton @ ^2.0"
|
||||
],
|
||||
|
||||
@@ -3,64 +3,108 @@
|
||||
#include "NTP.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
#include "soc/soc.h" // Disable brownour problems
|
||||
#include "soc/rtc_cntl_reg.h" // Disable brownour problems
|
||||
#include "soc/soc.h" // Disable brownour problems
|
||||
#include "soc/rtc_cntl_reg.h" // Disable brownour problems
|
||||
|
||||
#include "FS.h" // SD Card ESP32
|
||||
#include "SD_MMC.h" // SD Card ESP32
|
||||
#include "FS.h" // SD Card ESP32
|
||||
#include "SD_MMC.h" // SD Card ESP32
|
||||
|
||||
void handleGetPic();
|
||||
|
||||
// ===================
|
||||
// Select camera model
|
||||
// ===================
|
||||
//#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
|
||||
//#define CAMERA_MODEL_ESP_EYE // Has PSRAM
|
||||
//#define CAMERA_MODEL_ESP32S3_EYE // Has PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM
|
||||
// #define CAMERA_MODEL_WROVER_KIT // Has PSRAM
|
||||
// #define CAMERA_MODEL_ESP_EYE // Has PSRAM
|
||||
// #define CAMERA_MODEL_ESP32S3_EYE // Has PSRAM
|
||||
// #define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
|
||||
// #define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM
|
||||
// #define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
|
||||
// #define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM
|
||||
// #define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM
|
||||
#define CAMERA_MODEL_AI_THINKER // Has PSRAM
|
||||
//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM
|
||||
//#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
|
||||
// ** Espressif Internal Boards **
|
||||
//#define CAMERA_MODEL_ESP32_CAM_BOARD
|
||||
//#define CAMERA_MODEL_ESP32S2_CAM_BOARD
|
||||
//#define CAMERA_MODEL_ESP32S3_CAM_LCD
|
||||
// #define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM
|
||||
// #define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
|
||||
// ** Espressif Internal Boards **
|
||||
// #define CAMERA_MODEL_ESP32_CAM_BOARD
|
||||
// #define CAMERA_MODEL_ESP32S2_CAM_BOARD
|
||||
// #define CAMERA_MODEL_ESP32S3_CAM_LCD
|
||||
|
||||
|
||||
#define LED_LEDC_CHANNEL 2 //Using different ledc channel/timer than camera
|
||||
#define LED_LEDC_CHANNEL 2 // Using different ledc channel/timer than camera
|
||||
#define CONFIG_LED_MAX_INTENSITY 255
|
||||
|
||||
#include "camera_pins.h"
|
||||
|
||||
|
||||
IoTItem* globalItem = nullptr;
|
||||
|
||||
|
||||
class EspCam : public IoTItem {
|
||||
private:
|
||||
IoTItem *_camItem = nullptr;
|
||||
camera_fb_t *frame = NULL;
|
||||
class EspCam : public IoTItem
|
||||
{
|
||||
private:
|
||||
bool _useLed, _ticker, _webTicker, _initSD;
|
||||
|
||||
public:
|
||||
public:
|
||||
bool isUsedLed() { return _useLed; }
|
||||
bool isWebTicker() { return _webTicker; }
|
||||
|
||||
EspCam(String parameters): IoTItem(parameters) {
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
|
||||
EspCam(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
|
||||
jsonRead(parameters, "useLed", _useLed); // используем = 1 или нет = 0 подсветку (вспышку)
|
||||
if (_useLed) {
|
||||
jsonRead(parameters, "ticker", _ticker); // тикать = 1 - сообщаем всем, что сделали снимок и он готов
|
||||
jsonRead(parameters, "webTicker", _webTicker); // сообщать всем, что через веб попросили отдать картинку с камеры
|
||||
jsonRead(parameters, "flashOn", _useLed); // используем = 1 или нет = 0 подсветку (вспышку)
|
||||
// globalItem = this; // выносим адрес переменной экземпляра для доступа к данным из обработчика событий веб
|
||||
_camItem = this;
|
||||
initCam();
|
||||
|
||||
HTTP.on("/getpic", HTTP_GET, handleGetPic);
|
||||
|
||||
initSD();
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
// save_picture();
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m)
|
||||
{
|
||||
if (command == "save")
|
||||
{
|
||||
if (param.size() == 1)
|
||||
save_picture(param[0].valS);
|
||||
else
|
||||
save_picture();
|
||||
}
|
||||
else if (command == "flashOn")
|
||||
{
|
||||
ledcSetup(LED_LEDC_CHANNEL, 5000, 8);
|
||||
ledcAttachPin(LED_GPIO_NUM, LED_LEDC_CHANNEL);
|
||||
_useLed = true;
|
||||
}
|
||||
else if (command == "flashOff")
|
||||
{
|
||||
_useLed = false;
|
||||
}
|
||||
else if (command == "sendFoto")
|
||||
{
|
||||
sendFoto();
|
||||
}
|
||||
else if (command == "editFoto")
|
||||
{
|
||||
editFoto();
|
||||
}
|
||||
|
||||
jsonRead(parameters, "ticker", _ticker); // тикать = 1 - сообщаем всем, что сделали снимок и он готов
|
||||
jsonRead(parameters, "webTicker", _webTicker); // сообщать всем, что через веб попросили отдать картинку с камеры
|
||||
globalItem = this; // выносим адрес переменной экземпляра для доступа к данным из обработчика событий веб
|
||||
|
||||
return {};
|
||||
}
|
||||
/*
|
||||
IoTItem *getCAMDriver()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
*/
|
||||
void initCam()
|
||||
{
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector
|
||||
camera_config_t config;
|
||||
config.ledc_channel = LEDC_CHANNEL_0;
|
||||
config.ledc_timer = LEDC_TIMER_0;
|
||||
@@ -83,110 +127,141 @@ class EspCam : public IoTItem {
|
||||
config.xclk_freq_hz = 20000000;
|
||||
config.frame_size = FRAMESIZE_UXGA;
|
||||
config.pixel_format = PIXFORMAT_JPEG; // for streaming
|
||||
//config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition
|
||||
// config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition
|
||||
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
|
||||
config.fb_location = CAMERA_FB_IN_PSRAM;
|
||||
config.jpeg_quality = 12;
|
||||
config.fb_count = 1;
|
||||
|
||||
|
||||
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
|
||||
// for larger pre-allocated frame buffer.
|
||||
|
||||
if(psramFound()){
|
||||
if (psramFound())
|
||||
{
|
||||
config.jpeg_quality = 10;
|
||||
config.fb_count = 2;
|
||||
config.grab_mode = CAMERA_GRAB_LATEST;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Limit the frame size when PSRAM is not available
|
||||
config.frame_size = FRAMESIZE_SVGA;
|
||||
config.fb_location = CAMERA_FB_IN_DRAM;
|
||||
}
|
||||
|
||||
#if defined(CAMERA_MODEL_ESP_EYE)
|
||||
|
||||
#if defined(CAMERA_MODEL_ESP_EYE)
|
||||
pinMode(13, INPUT_PULLUP);
|
||||
pinMode(14, INPUT_PULLUP);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// camera init
|
||||
esp_err_t err = esp_camera_init(&config);
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
Serial.printf("Camera init failed with error 0x%x", err);
|
||||
return;
|
||||
}
|
||||
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
// initial sensors are flipped vertically and colors are a bit saturated
|
||||
if (s->id.PID == OV3660_PID) {
|
||||
s->set_vflip(s, 1); // flip it back
|
||||
s->set_brightness(s, 1); // up the brightness just a bit
|
||||
if (s->id.PID == OV3660_PID)
|
||||
{
|
||||
s->set_vflip(s, 1); // flip it back
|
||||
s->set_brightness(s, 1); // up the brightness just a bit
|
||||
s->set_saturation(s, -2); // lower the saturation
|
||||
}
|
||||
|
||||
#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
|
||||
if (_useLed)
|
||||
{
|
||||
ledcSetup(LED_LEDC_CHANNEL, 5000, 8);
|
||||
ledcAttachPin(LED_GPIO_NUM, LED_LEDC_CHANNEL);
|
||||
}
|
||||
|
||||
#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
|
||||
s->set_vflip(s, 1);
|
||||
s->set_hmirror(s, 1);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(CAMERA_MODEL_ESP32S3_EYE)
|
||||
#if defined(CAMERA_MODEL_ESP32S3_EYE)
|
||||
s->set_vflip(s, 1);
|
||||
#endif
|
||||
|
||||
HTTP.on("/getpic", HTTP_GET, handleGetPic);
|
||||
#endif
|
||||
}
|
||||
|
||||
void initSD()
|
||||
{
|
||||
// Start Micro SD card
|
||||
_initSD = true;
|
||||
Serial.println("Starting SD Card");
|
||||
if(!SD_MMC.begin("/sdcard", true)){
|
||||
if (!SD_MMC.begin("/sdcard", true))
|
||||
{
|
||||
Serial.println("SD Card Mount Failed");
|
||||
_initSD = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
uint8_t cardType = SD_MMC.cardType();
|
||||
if(cardType == CARD_NONE){
|
||||
if (cardType == CARD_NONE)
|
||||
{
|
||||
Serial.println("No SD Card attached");
|
||||
_initSD = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
fs::FS &fs = SD_MMC;
|
||||
fs.mkdir("/photos/");
|
||||
fs.mkdir("/photos");
|
||||
}
|
||||
|
||||
void save_picture(String path = "") {
|
||||
// if (_useLed) digitalWrite(4, HIGH); //Turn on the flash
|
||||
void sendFoto()
|
||||
{
|
||||
if (tlgrmItem)
|
||||
{
|
||||
if (_useLed)
|
||||
ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); // Turn on the flash
|
||||
frame = esp_camera_fb_get();
|
||||
if (!frame)
|
||||
{
|
||||
SerialPrint("E", F("Esp-Cam to Telegram"), "Fail esp-cam initialization");
|
||||
return;
|
||||
}
|
||||
if (_useLed)
|
||||
ledcWrite(LED_LEDC_CHANNEL, 0);
|
||||
(tlgrmItem)->sendFoto((byte *)frame->buf, frame->len, "photo.jpg");
|
||||
esp_camera_fb_return(frame);
|
||||
SerialPrint("i", F("Esp-Cam to Telegram"), "esp_CAM send foto");
|
||||
}
|
||||
}
|
||||
|
||||
// // Take Picture with Camera
|
||||
// fb = esp_camera_fb_get();
|
||||
// if(!fb || fb->len >= PICBUF_SIZE) {
|
||||
// if (fb) {
|
||||
// Serial.printf("Camera capture failed size=%d\n", fb->len);
|
||||
// esp_camera_fb_return(fb);
|
||||
// } else Serial.printf("Camera capture failed\n");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // if (value.extBinInfoSize < fb->len) {
|
||||
// // if (value.extBinInfo) free(value.extBinInfo);
|
||||
// // value.extBinInfo = (uint8_t*)malloc(sizeof(uint8_t) * fb->len);
|
||||
// // }
|
||||
// memcpy(value.extBinInfo, fb->buf, fb->len);
|
||||
// value.extBinInfoSize = fb->len;
|
||||
|
||||
// Serial.printf("try send pic by size=%d", fb->len);
|
||||
|
||||
// if (_useLed) digitalWrite(4, LOW);
|
||||
// if (_ticker) regEvent("shot", "EspCam");
|
||||
// esp_camera_fb_return(fb);
|
||||
void editFoto()
|
||||
{
|
||||
if (tlgrmItem)
|
||||
{
|
||||
if (_useLed)
|
||||
ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); // Turn on the flash
|
||||
frame = esp_camera_fb_get();
|
||||
if (!frame)
|
||||
{
|
||||
SerialPrint("E", F("Esp-Cam to Telegram"), "Fail esp-cam initialization");
|
||||
return;
|
||||
}
|
||||
if (_useLed)
|
||||
ledcWrite(LED_LEDC_CHANNEL, 0);
|
||||
|
||||
(tlgrmItem)->editFoto((byte *)frame->buf, frame->len, "photo.jpg");
|
||||
esp_camera_fb_return(frame);
|
||||
SerialPrint("i", F("Esp-Cam to Telegram"), "esp_CAM edit foto");
|
||||
}
|
||||
}
|
||||
|
||||
void save_picture(String path = "")
|
||||
{
|
||||
// Save picture to microSD card
|
||||
fs::FS &fs = SD_MMC;
|
||||
|
||||
if (path == "") {
|
||||
if (path == "")
|
||||
{
|
||||
path = "/photos/";
|
||||
path += getTodayDateDotFormated();
|
||||
path += "/";
|
||||
path += getTodayDateDotFormated();
|
||||
// path += "/";
|
||||
fs.mkdir(path.c_str());
|
||||
|
||||
char buf[32];
|
||||
@@ -196,79 +271,78 @@ class EspCam : public IoTItem {
|
||||
}
|
||||
Serial.println(path);
|
||||
|
||||
// Take Picture with Camera
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
if (_useLed)
|
||||
ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); // Turn on the flash
|
||||
|
||||
if(!fb) {
|
||||
// Take Picture with Camera
|
||||
frame = esp_camera_fb_get();
|
||||
if (_useLed)
|
||||
ledcWrite(LED_LEDC_CHANNEL, 0); // Turn on the flash
|
||||
|
||||
if (!frame)
|
||||
{
|
||||
Serial.println("Camera capture failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
File file = fs.open(path.c_str(), FILE_WRITE);
|
||||
if(!file){
|
||||
if (!file)
|
||||
{
|
||||
Serial.println("Failed to open file in writing mode");
|
||||
}
|
||||
else {
|
||||
file.write(fb->buf, fb->len); // payload (image), payload length
|
||||
}
|
||||
else
|
||||
{
|
||||
file.write(frame->buf, frame->len); // payload (image), payload length
|
||||
Serial.printf("Saved file to path: %s\n", path.c_str());
|
||||
}
|
||||
file.close();
|
||||
|
||||
//return the frame buffer back to the driver for reuse
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
// return the frame buffer back to the driver for reuse
|
||||
esp_camera_fb_return(frame);
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
//save_picture();
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m) {
|
||||
if (command == "save") {
|
||||
if (param.size() == 1)
|
||||
save_picture(param[0].valS);
|
||||
else
|
||||
save_picture();
|
||||
} else if (command == "ledOn" && param.size() == 1) {
|
||||
ledcSetup(LED_LEDC_CHANNEL, 5000, 8);
|
||||
ledcAttachPin(LED_GPIO_NUM, LED_LEDC_CHANNEL);
|
||||
ledcWrite(LED_LEDC_CHANNEL, param[0].valD);
|
||||
} else if (command == "ledOff") {
|
||||
ledcWrite(LED_LEDC_CHANNEL, 0);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
~EspCam() {
|
||||
//free(value.extBinInfo);
|
||||
globalItem = nullptr;
|
||||
~EspCam()
|
||||
{
|
||||
// free(value.extBinInfo);
|
||||
_camItem = nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
void handleGetPic() {
|
||||
if (!globalItem) return;
|
||||
void handleGetPic()
|
||||
{
|
||||
if (!_camItem)
|
||||
return;
|
||||
|
||||
if (((EspCam*)globalItem)->isUsedLed()) ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); //Turn on the flash
|
||||
if (((EspCam *)_camItem)->isUsedLed())
|
||||
ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); // Turn on the flash
|
||||
|
||||
camera_fb_t* fb = NULL;
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
// camera_fb_t *fb = NULL;
|
||||
frame = esp_camera_fb_get();
|
||||
|
||||
if (((EspCam *)_camItem)->isUsedLed())
|
||||
ledcWrite(LED_LEDC_CHANNEL, 0);
|
||||
|
||||
if (!frame)
|
||||
{
|
||||
HTTP.send(200, "text/json", F("Item EspCam not prepared yet or camera hasn't taken a picture yet"));
|
||||
return;
|
||||
}
|
||||
|
||||
HTTP.send_P(200, "image/jpeg", (char *)fb->buf, fb->len);
|
||||
|
||||
if (((EspCam*)globalItem)->isUsedLed()) ledcWrite(LED_LEDC_CHANNEL, 0);
|
||||
if (((EspCam*)globalItem)->isWebTicker()) globalItem->regEvent("webTakesPhoto", "EspCam");
|
||||
esp_camera_fb_return(fb);
|
||||
HTTP.send_P(200, "image/jpeg", (char *)frame->buf, frame->len);
|
||||
|
||||
if (((EspCam *)_camItem)->isWebTicker())
|
||||
_camItem->regEvent("webTakesPhoto", "EspCam");
|
||||
esp_camera_fb_return(frame);
|
||||
}
|
||||
|
||||
|
||||
void* getAPI_EspCam(String subtype, String param) {
|
||||
if (subtype == F("EspCam")) {
|
||||
void *getAPI_EspCam(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("EspCam"))
|
||||
{
|
||||
return new EspCam(param);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Camera OV2640 (ESPcam)",
|
||||
"type": "Reading",
|
||||
"subtype": "EspCam",
|
||||
"id": "EspCam",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
|
||||
"int": 60,
|
||||
"useLed": 0,
|
||||
"ticker": 0,
|
||||
"webTicker": 0
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Camera OV2640 (ESPcam)",
|
||||
"type": "Reading",
|
||||
"subtype": "EspCam",
|
||||
"id": "EspCam",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 60,
|
||||
"flashOn": 0,
|
||||
"ticker": 0,
|
||||
"webTicker": 0
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorName": "Ilya Belyakov, Mikhail Bubnov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
"authorGit": "https://github.com/biveraxe",
|
||||
"specialThanks": "",
|
||||
"moduleName": "EspCam",
|
||||
"moduleVersion": "2.1",
|
||||
"moduleVersion": "3.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"title": "Camera OV2640 (ESPcam)",
|
||||
"moduleDesc": "Предназначен для специальной платы esp32 со встроенной камерой. Добавляет в прошивку функцию создания фото и сохранения на SD при наличии. По адресу /getpic можно получить текущее фото (работает в том числе без SD карты).",
|
||||
"moduleDesc": "Предназначен для специальной платы esp32 со встроенной камерой. Добавляет в прошивку функцию создания фото и сохранения на SD при наличии. По адресу /getpic можно получить текущее фото (работает в том числе без SD карты). Отправка фото в телеграмм через модуль Telegram_v2",
|
||||
"propInfo": {
|
||||
"int": "Пауза в секундах во время постоянной фотосъемки.",
|
||||
"useLed": "использовать диод подсветки при съемке.",
|
||||
"flashOn": "использовать диод подсветки при съемке. используем = 1 или нет = 0 подсветку (вспышку)",
|
||||
"ticker": "Генерировать(1) или нет(0) событие с интервалом int",
|
||||
"webTicker": "Генерировать(1) или нет(0) событие при обращении через веб-страницу по адресу /getpic."
|
||||
},
|
||||
@@ -42,23 +41,31 @@
|
||||
"descr": "Сохранить снимок на SD",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "sendFoto",
|
||||
"descr": "Отправить фото с esp-CAM в телеграмм",
|
||||
"params": [""]
|
||||
},
|
||||
{
|
||||
"name": "editFoto",
|
||||
"descr": "Отредактировать последнее отправленное фото в телеграмм",
|
||||
"params": [""]
|
||||
},
|
||||
{
|
||||
"name": "ledOn",
|
||||
"descr": "Включить подсветку",
|
||||
"params": ["Яркость 0-255"]
|
||||
"name": "flashOn",
|
||||
"descr": "Включить вспышку",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "ledOff",
|
||||
"descr": "Отключить подсветку",
|
||||
"name": "flashOff",
|
||||
"descr": "Отключить вспышку",
|
||||
"params": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"esp32cam_4mb": [
|
||||
"espressif/esp32-camera @ ^2.0.0"
|
||||
]
|
||||
}
|
||||
|
||||
97
src/modules/exec/Ftp/Ftp.cpp
Normal file
97
src/modules/exec/Ftp/Ftp.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
#include <SimpleFTPServer.h>
|
||||
|
||||
#define DEFAULT_STORAGE_TYPE_ESP32 STORAGE_LITTLEFS
|
||||
|
||||
class FTPModule : public IoTItem
|
||||
{
|
||||
private:
|
||||
String login;
|
||||
String pass;
|
||||
FtpServer ftpSrv; // set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
|
||||
public:
|
||||
FTPModule(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
jsonRead(parameters, F("login"), login);
|
||||
jsonRead(parameters, F("pass"), pass);
|
||||
ftpSrv.setCallback(FTPModule::_callback);
|
||||
ftpSrv.setTransferCallback(FTPModule::_transferCallback);
|
||||
ftpSrv.begin(login.c_str(), pass.c_str(), "Welcome IoTManager FTP server"); // username, password for ftp. (default 21, 50009 for PASV)
|
||||
SerialPrint("I", "FtpServer " + (String)_id, "begin");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
ftpSrv.handleFTP();
|
||||
}
|
||||
|
||||
static void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace)
|
||||
{
|
||||
switch (ftpOperation)
|
||||
{
|
||||
case FTP_CONNECT:
|
||||
SerialPrint("i", "FTP", F("Connected!"));
|
||||
|
||||
break;
|
||||
case FTP_DISCONNECT:
|
||||
SerialPrint("i", "FTP", F("Disconnected!"));
|
||||
break;
|
||||
case FTP_FREE_SPACE_CHANGE:
|
||||
SerialPrint("i", "FTP", "Free space change, free " + (String)freeSpace + " of " + (String)totalSpace);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _transferCallback(FtpTransferOperation ftpOperation, const char *name, unsigned int transferredSize)
|
||||
{
|
||||
switch (ftpOperation)
|
||||
{
|
||||
case FTP_UPLOAD_START:
|
||||
SerialPrint("i","FTP", F("Upload start!"));
|
||||
break;
|
||||
case FTP_UPLOAD:
|
||||
SerialPrint("i","FTP", "Upload of file " + (String)name + " byte " + (String)transferredSize);
|
||||
break;
|
||||
case FTP_TRANSFER_STOP:
|
||||
SerialPrint("i","FTP", F("Finish transfer!"));
|
||||
break;
|
||||
case FTP_TRANSFER_ERROR:
|
||||
SerialPrint("E","FTP", F("Transfer error!"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* FTP_UPLOAD_START = 0,
|
||||
* FTP_UPLOAD = 1,
|
||||
*
|
||||
* FTP_DOWNLOAD_START = 2,
|
||||
* FTP_DOWNLOAD = 3,
|
||||
*
|
||||
* FTP_TRANSFER_STOP = 4,
|
||||
* FTP_DOWNLOAD_STOP = 4,
|
||||
* FTP_UPLOAD_STOP = 4,
|
||||
*
|
||||
* FTP_TRANSFER_ERROR = 5,
|
||||
* FTP_DOWNLOAD_ERROR = 5,
|
||||
* FTP_UPLOAD_ERROR = 5
|
||||
*/
|
||||
}
|
||||
|
||||
~FTPModule(){};
|
||||
};
|
||||
|
||||
void *getAPI_FTPModule(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("ftp"))
|
||||
{
|
||||
return new FTPModule(param);
|
||||
}
|
||||
//}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
45
src/modules/exec/Ftp/modinfo.json
Normal file
45
src/modules/exec/Ftp/modinfo.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "FTP сервер",
|
||||
"type": "Reading",
|
||||
"subtype": "ftp",
|
||||
"id": "ftp",
|
||||
"widget": "nil",
|
||||
"page": "",
|
||||
"descr": "FTP сервер",
|
||||
"login": "admin",
|
||||
"pass": "admin"
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Bubnov Mikhail",
|
||||
"authorContact": "https://t.me/Mit4bmw",
|
||||
"authorGit": "https://github.com/Mit4el",
|
||||
"specialThanks": "",
|
||||
"moduleName": "FTPModule",
|
||||
"moduleVersion": "0.1",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"title": "FTP-сервер",
|
||||
"moduleDesc": "Запускает FTP-сервер на плате esp",
|
||||
"propInfo": {
|
||||
"login": "Логин FTP сервера",
|
||||
"pass": "Пароль FTP сервера"
|
||||
}
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp32_16mb": [],
|
||||
"esp32s2_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_16mb": []
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,12 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
class HttpGet : public IoTItem
|
||||
{
|
||||
public:
|
||||
HttpGet(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
}
|
||||
|
||||
void sendHttpPOST(String url, String msg)
|
||||
{
|
||||
if (WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
class HttpGet : public IoTItem {
|
||||
public:
|
||||
HttpGet(String parameters) : IoTItem(parameters) {}
|
||||
|
||||
void sendHttpPOST(String url, String msg) {
|
||||
if (isNetworkActive()) {
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, url);
|
||||
@@ -23,26 +17,23 @@ public:
|
||||
SerialPrint("<-", F("HttpPOST"), "URL: " + url + ", msg: " + msg);
|
||||
SerialPrint("->", F("HttpPOST"), "URL: " + url + ", server: " + httpResponseCode);
|
||||
|
||||
if (httpResponseCode > 0)
|
||||
{
|
||||
if (httpResponseCode > 0) {
|
||||
value.valS = payload;
|
||||
value.isDecimal = false;
|
||||
SerialPrint("->", F("HttpPOST"), "msg from server: " + (String)payload.c_str());
|
||||
value.valS = payload;
|
||||
regEvent(value.valS, "HttpGet");
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
}
|
||||
void sendHttpGET(String url)
|
||||
{
|
||||
|
||||
void sendHttpGET(String url) {
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
#if defined ESP8266
|
||||
if (!http.begin(client, url))
|
||||
{
|
||||
if (!http.begin(client, url)) {
|
||||
#elif defined ESP32
|
||||
if (!http.begin(url))
|
||||
{
|
||||
if (!http.begin(url)) {
|
||||
#endif
|
||||
|
||||
SerialPrint("I", F("HttpGet"), "connection failed ");
|
||||
@@ -52,31 +43,23 @@ public:
|
||||
String payload = http.getString();
|
||||
SerialPrint("<-", F("HttpGET"), "URL: " + url);
|
||||
SerialPrint("->", F("HttpGET"), "URL: " + url + ", server: " + httpResponseCode);
|
||||
if (httpResponseCode > 0)
|
||||
{
|
||||
if (httpResponseCode > 0) {
|
||||
value.valS = payload;
|
||||
value.isDecimal = false;
|
||||
SerialPrint("->", F("HttpGET"), "msg from server: " + (String)payload.c_str());
|
||||
value.valS = payload;
|
||||
regEvent(value.valS, "HttpGet");
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m)
|
||||
{
|
||||
if (param.size() > 0)
|
||||
{
|
||||
if (command == "get")
|
||||
{
|
||||
if (param.size())
|
||||
{
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m) {
|
||||
if (param.size() > 0) {
|
||||
if (command == "get") {
|
||||
if (param.size()) {
|
||||
sendHttpGET(param[0].valS);
|
||||
}
|
||||
}
|
||||
else if (command == "post")
|
||||
{
|
||||
if (param.size())
|
||||
{
|
||||
} else if (command == "post") {
|
||||
if (param.size()) {
|
||||
sendHttpPOST(param[0].valS, param[1].valS);
|
||||
}
|
||||
}
|
||||
@@ -87,14 +70,10 @@ public:
|
||||
~HttpGet(){};
|
||||
};
|
||||
|
||||
void *getAPI_HttpGet(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("HttpGet"))
|
||||
{
|
||||
void *getAPI_HttpGet(String subtype, String param) {
|
||||
if (subtype == F("HttpGet")) {
|
||||
return new HttpGet(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -41,7 +41,8 @@
|
||||
"name": "post",
|
||||
"descr": "Отправить http запрос методом POST.",
|
||||
"params": [
|
||||
"URL","message"
|
||||
"URL",
|
||||
"message"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -49,6 +50,9 @@
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp32s2_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
|
||||
@@ -9,37 +9,41 @@ extern IoTGpio IoTgpio;
|
||||
class IoTServo : public IoTItem {
|
||||
private:
|
||||
Servo servObj;
|
||||
int _apin, _oldValue;
|
||||
int _locmap1, _locmap2, _locmap3, _locmap4;
|
||||
// int _apin, _oldValue;
|
||||
int _oldValue;
|
||||
// int _locmap1, _locmap2, _locmap3, _locmap4;
|
||||
|
||||
public:
|
||||
IoTServo(String parameters): IoTItem(parameters) {
|
||||
int pin;
|
||||
int pin, minPulseWidth, maxPulseWidth, neutralPulseWidth;
|
||||
jsonRead(parameters, "pin", pin);
|
||||
servObj.attach(pin);
|
||||
jsonRead(parameters, "minPulseWidth", minPulseWidth);
|
||||
jsonRead(parameters, "maxPulseWidth", maxPulseWidth);
|
||||
jsonRead(parameters, "neutralPulseWidth", neutralPulseWidth);
|
||||
servObj.attach(pin, minPulseWidth, maxPulseWidth, neutralPulseWidth);
|
||||
|
||||
jsonRead(parameters, "apin", _apin);
|
||||
if (_apin >= 0) IoTgpio.pinMode(_apin, INPUT);
|
||||
// jsonRead(parameters, "apin", _apin);
|
||||
// if (_apin >= 0) IoTgpio.pinMode(_apin, INPUT);
|
||||
|
||||
String map;
|
||||
jsonRead(parameters, F("amap"), map, false);
|
||||
if (map != "") {
|
||||
_locmap1 = selectFromMarkerToMarker(map, ",", 0).toInt();
|
||||
_locmap2 = selectFromMarkerToMarker(map, ",", 1).toInt();
|
||||
_locmap3 = selectFromMarkerToMarker(map, ",", 2).toInt();
|
||||
_locmap4 = selectFromMarkerToMarker(map, ",", 3).toInt();
|
||||
}
|
||||
// String map;
|
||||
// jsonRead(parameters, F("amap"), map, false);
|
||||
// if (map != "") {
|
||||
// _locmap1 = selectFromMarkerToMarker(map, ",", 0).toInt();
|
||||
// _locmap2 = selectFromMarkerToMarker(map, ",", 1).toInt();
|
||||
// _locmap3 = selectFromMarkerToMarker(map, ",", 2).toInt();
|
||||
// _locmap4 = selectFromMarkerToMarker(map, ",", 3).toInt();
|
||||
// }
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
if (_apin >= 0) {
|
||||
value.valD = map(IoTgpio.analogRead(_apin), _locmap1, _locmap2, _locmap3, _locmap4);
|
||||
if (abs(_oldValue - value.valD) > 5) {
|
||||
_oldValue = value.valD;
|
||||
servObj.write(_oldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
// void doByInterval() {
|
||||
// if (_apin >= 0) {
|
||||
// value.valD = map(IoTgpio.analogRead(_apin), _locmap1, _locmap2, _locmap3, _locmap4);
|
||||
// if (abs(_oldValue - value.valD) > 5) {
|
||||
// _oldValue = value.valD;
|
||||
// servObj.write(_oldValue);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m) {
|
||||
if (command == "rotate") {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Сервопривод",
|
||||
"type": "Writing",
|
||||
"subtype": "IoTServo",
|
||||
"id": "servo",
|
||||
"widget": "range",
|
||||
"page": "servo",
|
||||
"descr": "угол",
|
||||
|
||||
"int": 1,
|
||||
"pin": 12,
|
||||
"apin": -1,
|
||||
"amap": "0, 4096, 0, 180"
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Сервопривод",
|
||||
"type": "Writing",
|
||||
"subtype": "IoTServo",
|
||||
"id": "servo",
|
||||
"widget": "rangeServo",
|
||||
"page": "servo",
|
||||
"descr": "угол",
|
||||
"pin": 12,
|
||||
"minPulseWidth": 544,
|
||||
"maxPulseWidth": 2400,
|
||||
"neutralPulseWidth": 1500,
|
||||
"trackingID": ""
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -31,26 +31,33 @@
|
||||
"title": "Сервопривод",
|
||||
"moduleDesc": "Предназначен для управления сервоприводом по уровню аналогово сигнала.",
|
||||
"propInfo": {
|
||||
"int": "Пауза в секундах между опросами аналогового входа. Если 0, то читаем постоянно",
|
||||
"pin": "Пин, к которому подключен сервопривод",
|
||||
"apin": "Номер GPIO аналогового пина. Если -1, то функция отключена.",
|
||||
"amap": "Настройки преобразования значений аналога в нужный диапазон сервы, имеет смысл, если аналог включен."
|
||||
"minPulseWidth": "Минимальная длина импульса",
|
||||
"maxPulseWidth": "Максимальная длина импульса",
|
||||
"neutralPulseWidth": "Нейтральная длина импульса",
|
||||
"trackingID": "Идентификатор отслеживания значения другого элемента конфигурации, например, чтение аналога"
|
||||
},
|
||||
"retInfo": "Содержит текущее значение поворота",
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "rotate",
|
||||
"descr": "Повернуть привод на значение",
|
||||
"params": ["Числовое значение"]
|
||||
"params": [
|
||||
"Числовое значение"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/RoboticsBrno/ServoESP32"
|
||||
"https://github.com/RoboticsBrno/ServoESP32#v1.0.3"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/RoboticsBrno/ServoESP32#v1.0.3"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/RoboticsBrno/ServoESP32#v1.0.3"
|
||||
],
|
||||
"esp8266_4mb": []
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Расширитель портов Mcp23008",
|
||||
"type": "Reading",
|
||||
"subtype": "Mcp23008",
|
||||
"id": "Mcp",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
|
||||
"int": "0",
|
||||
"addr": "0x20",
|
||||
"index": 1
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Расширитель портов Mcp23008",
|
||||
"type": "Reading",
|
||||
"subtype": "Mcp23008",
|
||||
"id": "Mcp",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": "0",
|
||||
"addr": "0x20",
|
||||
"index": 1
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -35,14 +34,20 @@
|
||||
"index": "Значения от 1 до 4, где при выборе 1 будет нумерация pin 100-115, при выборе 2 200-215 и т.д."
|
||||
}
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"adafruit/Adafruit Mcp23017 Arduino Library@^2.1.0",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"adafruit/Adafruit Mcp23017 Arduino Library@^2.1.0",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"adafruit/Adafruit Mcp23017 Arduino Library@^2.1.0",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"adafruit/Adafruit Mcp23017 Arduino Library@^2.1.0",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Расширитель портов Mcp23017",
|
||||
"type": "Reading",
|
||||
"subtype": "Mcp23017",
|
||||
"id": "Mcp",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
|
||||
"int": "0",
|
||||
"addr": "0x20",
|
||||
"index": 1
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Расширитель портов Mcp23017",
|
||||
"type": "Reading",
|
||||
"subtype": "Mcp23017",
|
||||
"id": "Mcp",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": "0",
|
||||
"addr": "0x20",
|
||||
"index": 1
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -35,14 +34,20 @@
|
||||
"index": "Значения от 1 до 4, где при выборе 1 будет нумерация pin 100-115, при выборе 2 200-215 и т.д."
|
||||
}
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"adafruit/Adafruit MCP23017 Arduino Library@^2.1.0",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"adafruit/Adafruit MCP23017 Arduino Library@^2.1.0",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"adafruit/Adafruit MCP23017 Arduino Library@^2.1.0",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"adafruit/Adafruit MCP23017 Arduino Library@^2.1.0",
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "MP3 плеер",
|
||||
"type": "Reading",
|
||||
"subtype": "Mp3",
|
||||
"id": "mp3",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
|
||||
"int": 1,
|
||||
"pins": "14,12",
|
||||
"volume": 20
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "MP3 плеер",
|
||||
"type": "Reading",
|
||||
"subtype": "Mp3",
|
||||
"id": "mp3",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 1,
|
||||
"pins": "14,12",
|
||||
"volume": 20
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -59,12 +58,17 @@
|
||||
{
|
||||
"name": "volume",
|
||||
"descr": "Установить громкость",
|
||||
"params": ["Значение громкости"]
|
||||
"params": [
|
||||
"Значение громкости"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "playFolder",
|
||||
"descr": "Проиграть файл из папки",
|
||||
"params": ["Номер папки", "Номер файла"]
|
||||
"params": [
|
||||
"Номер папки",
|
||||
"Номер файла"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "play",
|
||||
@@ -83,13 +87,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"dfrobot/DFRobotDFPlayerMini @ ^1.0.5"
|
||||
],
|
||||
"esp32_4mb4f": [
|
||||
"dfrobot/DFRobotDFPlayerMini @ ^1.0.5"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"dfrobot/DFRobotDFPlayerMini @ ^1.0.5"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"dfrobot/DFRobotDFPlayerMini @ ^1.0.5"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -9,7 +9,7 @@
|
||||
"id": "impulse",
|
||||
"widget": "anydataDef",
|
||||
"page": "Кнопки",
|
||||
"descr": "Количество нажаний",
|
||||
"descr": "Количество нажатий",
|
||||
"needSave": 0,
|
||||
"int": 300,
|
||||
"inv": 1,
|
||||
@@ -44,10 +44,14 @@
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
"esp8285_1mb": [],
|
||||
"esp8285_1mb_ota": []
|
||||
"esp8285_1mb_ota": [],
|
||||
"esp8266_2mb": [],
|
||||
"esp8266_2mb_ota": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,16 +53,11 @@ String parseToString(const MyMessage& message) {
|
||||
class MySensorsGate : public IoTItem {
|
||||
private:
|
||||
public:
|
||||
MySensorsGate(String parameters) : IoTItem(parameters) {
|
||||
SerialPrint("i", "MySensors", "Gate initialized");
|
||||
}
|
||||
MySensorsGate(String parameters) : IoTItem(parameters) { SerialPrint("i", "MySensors", "Gate initialized"); }
|
||||
|
||||
void doByInterval() {
|
||||
}
|
||||
void doByInterval() {}
|
||||
|
||||
void loop() {
|
||||
loopMySensorsExecute();
|
||||
}
|
||||
void loop() { loopMySensorsExecute(); }
|
||||
|
||||
~MySensorsGate(){};
|
||||
|
||||
@@ -351,6 +346,10 @@ class MySensorsNode : public IoTItem {
|
||||
int _minutesPassed = 0;
|
||||
String json = "{}";
|
||||
bool dataFromNode = false;
|
||||
// временное решение
|
||||
unsigned long currentMillis;
|
||||
unsigned long prevMillis;
|
||||
unsigned long difference;
|
||||
|
||||
public:
|
||||
MySensorsNode(String parameters) : IoTItem(parameters) {
|
||||
@@ -388,9 +387,7 @@ class MySensorsNode : public IoTItem {
|
||||
}
|
||||
|
||||
// событие когда пользователь подключается приложением или веб интерфейсом к усройству
|
||||
void onMqttWsAppConnectEvent() {
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
void onMqttWsAppConnectEvent() { setNewWidgetAttributes(); }
|
||||
|
||||
void setNewWidgetAttributes() {
|
||||
if (dataFromNode) {
|
||||
@@ -426,4 +423,4 @@ void* getAPI_MySensorsGate(String subtype, String param) {
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -49,6 +49,8 @@
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": []
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": []
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,23 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
#include "classes/IoTGpio.h"
|
||||
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
|
||||
#define PCF8574_I2CADDR_DEFAULT 0x20 ///< DS3502 default I2C address
|
||||
#define PCF8574_I2CADDR_DEFAULT 0x20 ///< DS3502 стандартный I2C адрес
|
||||
|
||||
class Adafruit_PCF8574_mod {
|
||||
public:
|
||||
Adafruit_PCF8574_mod() {};
|
||||
Adafruit_PCF8574_mod() : _pinConfig(0xFF) {}; // По умолчанию все пины настроены как входы
|
||||
|
||||
bool begin(uint8_t i2c_address = PCF8574_I2CADDR_DEFAULT, TwoWire *wire = &Wire) {
|
||||
i2c_dev = new Adafruit_I2CDevice(i2c_address, wire);
|
||||
|
||||
if (!i2c_dev->begin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return i2c_dev->begin();
|
||||
}
|
||||
|
||||
bool digitalWriteByte(uint8_t d) {
|
||||
_writebuf = d;
|
||||
return i2c_dev->write(&_writebuf, 1);
|
||||
return updateRegister();
|
||||
}
|
||||
|
||||
uint8_t digitalReadByte(void) {
|
||||
@@ -32,20 +27,20 @@ class Adafruit_PCF8574_mod {
|
||||
|
||||
bool digitalWrite(int pinnum, bool val) {
|
||||
if (val) {
|
||||
_writebuf |= 1 << pinnum;
|
||||
_writebuf |= (1 << pinnum);
|
||||
} else {
|
||||
_writebuf &= ~(1 << pinnum);
|
||||
}
|
||||
return i2c_dev->write(&_writebuf, 1);
|
||||
return updateRegister(); // Обновляем регистр после изменения состояния пина
|
||||
}
|
||||
|
||||
bool pinMode(int pinnum, uint8_t val) {
|
||||
if ((val == INPUT) || (val == INPUT_PULLUP)) {
|
||||
_writebuf |= 1 << pinnum;
|
||||
_pinConfig |= (1 << pinnum);
|
||||
} else {
|
||||
_writebuf &= ~(1 << pinnum);
|
||||
_pinConfig &= ~(1 << pinnum);
|
||||
}
|
||||
return i2c_dev->write(&_writebuf, 1);
|
||||
return updateRegister(); // Обновляем регистр после изменения конфигурации пина
|
||||
}
|
||||
|
||||
bool digitalRead(int pinnum) {
|
||||
@@ -55,6 +50,12 @@ class Adafruit_PCF8574_mod {
|
||||
|
||||
private:
|
||||
uint8_t _readbuf = 0, _writebuf = 0;
|
||||
uint8_t _pinConfig; // Конфигурация пинов (вход/выход)
|
||||
|
||||
bool updateRegister() {
|
||||
uint8_t outputValue = (_writebuf & ~_pinConfig) | (_pinConfig);
|
||||
return i2c_dev->write(&outputValue, 1); // Отправляем обновленное значение регистра на устройство
|
||||
}
|
||||
|
||||
Adafruit_I2CDevice *i2c_dev;
|
||||
};
|
||||
@@ -66,7 +67,7 @@ class Pcf8574Driver : public IoTGpio {
|
||||
public:
|
||||
Pcf8574Driver(int index, String addr) : IoTGpio(index) {
|
||||
if (!_pcf.begin(hexStringToUint8(addr), &Wire)) {
|
||||
Serial.println("PCF8574 Init Error.");
|
||||
Serial.println("Ошибка инициализации PCF8574."); // Переводим на русский
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +86,8 @@ class Pcf8574Driver : public IoTGpio {
|
||||
void digitalInvert(int pin) {
|
||||
_pcf.digitalWrite(pin, 1 - _pcf.digitalRead(pin));
|
||||
}
|
||||
|
||||
~Pcf8574Driver() {};
|
||||
};
|
||||
|
||||
|
||||
class Pcf8574 : public IoTItem {
|
||||
private:
|
||||
Pcf8574Driver* _driver;
|
||||
@@ -108,7 +106,7 @@ class Pcf8574 : public IoTItem {
|
||||
int index;
|
||||
jsonRead(parameters, "index", index);
|
||||
if (index > 4) {
|
||||
Serial.println("Pcf8574 wrong index. Must be 0 - 4");
|
||||
Serial.println("Неправильный индекс Pcf8574. Должен быть 0 - 4."); // Переводим на русский
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,14 +120,13 @@ class Pcf8574 : public IoTItem {
|
||||
}
|
||||
}
|
||||
|
||||
//возвращает ссылку на экземпляр класса Pcf8574Driver
|
||||
IoTGpio* getGpioDriver() {
|
||||
return _driver;
|
||||
}
|
||||
|
||||
~Pcf8574() {
|
||||
delete _driver;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
void* getAPI_Pcf8574(String subtype, String param) {
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Расширитель портов Pcf8574",
|
||||
"type": "Reading",
|
||||
"subtype": "Pcf8574",
|
||||
"id": "Pcf",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": "0",
|
||||
"addr": "0x20",
|
||||
"index": 1
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Расширитель портов Pcf8574",
|
||||
"type": "Reading",
|
||||
"subtype": "Pcf8574",
|
||||
"id": "Pcf",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": "0",
|
||||
"addr": "0x20",
|
||||
"index": 1
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Serghei Crasnicov",
|
||||
"authorContact": "https://t.me/Serghei63",
|
||||
@@ -27,15 +27,20 @@
|
||||
"int": "Не используется",
|
||||
"addr": "Адрес устройства на шине, обычно 0x20",
|
||||
"index": "Значения от 1 до 4, где при выборе 1 будет нумерация pin 100-115, при выборе 2 200-215 и т.д."
|
||||
}
|
||||
},
|
||||
"title": "Расширитель портов Pcf8574"
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"adafruit/Adafruit BusIO @ ^1.13.2"
|
||||
],
|
||||
|
||||
71
src/modules/exec/Pcf8591/Pcf8591.cpp
Normal file
71
src/modules/exec/Pcf8591/Pcf8591.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
#include "Wire.h"
|
||||
#include <Adafruit_PCF8591.h>
|
||||
|
||||
// Make sure that this is set to the value in volts of VCC
|
||||
#define ADC_REFERENCE_VOLTAGE 3.3
|
||||
|
||||
class Pcf8591 : public IoTItem {
|
||||
int _pin;
|
||||
bool _isRaw;
|
||||
bool _isInited = false;
|
||||
Adafruit_PCF8591 pcf = Adafruit_PCF8591();
|
||||
|
||||
public:
|
||||
Pcf8591(String parameters) : IoTItem(parameters) {
|
||||
String tmp;
|
||||
jsonRead(parameters, "pin", tmp);
|
||||
_pin = tmp.toInt();
|
||||
|
||||
jsonRead(parameters, "mode", tmp);
|
||||
_isRaw = tmp == "raw";
|
||||
|
||||
if (!pcf.begin()) {
|
||||
Serial.println("# Adafruit PCF8591 not found!");
|
||||
_isInited = false;
|
||||
} else
|
||||
|
||||
_isInited = true;
|
||||
|
||||
Serial.println("# Adafruit PCF8591 found");
|
||||
|
||||
pcf.enableDAC(true);
|
||||
|
||||
Serial.println("AIN0, AIN1, AIN2, AIN3");
|
||||
|
||||
}
|
||||
|
||||
uint8_t dac_counter = 0;
|
||||
|
||||
void doByInterval() {
|
||||
|
||||
// Make a triangle wave on the DAC output
|
||||
pcf.analogWrite(dac_counter++);
|
||||
|
||||
if (_isInited) {
|
||||
if (_isRaw)
|
||||
value.valD = pcf.analogRead(_pin); // Чтение АЦП нулевого канала (Вольты)
|
||||
else
|
||||
value.valD = (int_to_volts(pcf.analogRead(_pin), 8, ADC_REFERENCE_VOLTAGE));
|
||||
regEvent(value.valD, "PCF8591");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
float int_to_volts(uint16_t dac_value, uint8_t bits, float logic_level) {
|
||||
return (((float)dac_value / ((1 << bits) - 1)) * logic_level);
|
||||
|
||||
}
|
||||
|
||||
~Pcf8591(){};
|
||||
};
|
||||
|
||||
void *getAPI_Pcf8591(String subtype, String param) {
|
||||
if (subtype == F("Pcf8591")) {
|
||||
return new Pcf8591(param);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
75
src/modules/exec/Pcf8591/modinfo.json
Normal file
75
src/modules/exec/Pcf8591/modinfo.json
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Расширитель портов PCF8591",
|
||||
"type": "Reading",
|
||||
"subtype": "Pcf8591",
|
||||
"id": "Pcf85",
|
||||
"widget": "anydataVlt",
|
||||
"page": "PCF8591",
|
||||
"descr": "PCF_0",
|
||||
"pin": "0",
|
||||
"mode": "volt",
|
||||
"map": "1,255,1,100",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": 2,
|
||||
"int": 7
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Serghei Crasnicov",
|
||||
"authorContact": "https://t.me/Serghei63",
|
||||
"authorGit": "https://github.com/Serghei63",
|
||||
"specialThanks": "",
|
||||
"moduleName": "Pcf8591",
|
||||
"moduleVersion": "1.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"title": "Расширитель 4-х аналоговых портов PCF8591",
|
||||
"moduleDesc": "Позволяет получить относительную величину напряжения на понижающем трансформаторе.",
|
||||
"propInfo": {
|
||||
"pin": "Номер AN, к которому подключен датчик. Допускается 0, 1, 2, 3",
|
||||
"mode": "Режим работы. volt - вывод в вольтах , raw - значения от 0 до 255",
|
||||
"int": "Количество секунд между опросами датчика."
|
||||
}
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
|
||||
"esp32_4mb": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp32_16mb": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp8266_16mb": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp8266_1mb": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp8266_1mb_ota": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp8285_1mb": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp8285_1mb_ota": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp8266_2mb": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
],
|
||||
"esp8266_2mb_ota": [
|
||||
"https://github.com/adafruit/Adafruit_PCF8591"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "PWM ESP32",
|
||||
"type": "Writing",
|
||||
"subtype": "Pwm32",
|
||||
"id": "pwm",
|
||||
"widget": "range",
|
||||
"page": "Кнопки",
|
||||
"descr": "PWM",
|
||||
"int": 0,
|
||||
"pin": 2,
|
||||
"freq": 5000,
|
||||
"ledChannel": 2,
|
||||
"PWM_resolution": 10,
|
||||
"val": 0,
|
||||
"apin": -1
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "PWM ESP32",
|
||||
"type": "Writing",
|
||||
"subtype": "Pwm32",
|
||||
"id": "pwm",
|
||||
"widget": "range",
|
||||
"page": "Кнопки",
|
||||
"descr": "PWM",
|
||||
"int": 0,
|
||||
"pin": 2,
|
||||
"freq": 5000,
|
||||
"ledChannel": 2,
|
||||
"PWM_resolution": 10,
|
||||
"val": 0,
|
||||
"apin": -1
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Avaks",
|
||||
"authorContact": "https://t.me/Avaks",
|
||||
@@ -42,10 +42,10 @@
|
||||
"freq": "Частота"
|
||||
}
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": []
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": []
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "PWM ESP8266",
|
||||
"type": "Writing",
|
||||
"subtype": "Pwm8266",
|
||||
"id": "pwm",
|
||||
"widget": "range",
|
||||
"page": "Кнопки",
|
||||
"descr": "PWM",
|
||||
"int": 0,
|
||||
"pin": 15,
|
||||
"freq": 5000,
|
||||
"val": 0,
|
||||
"apin": -1
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "PWM ESP8266",
|
||||
"type": "Writing",
|
||||
"subtype": "Pwm8266",
|
||||
"id": "pwm",
|
||||
"widget": "range",
|
||||
"page": "Кнопки",
|
||||
"descr": "PWM",
|
||||
"int": 0,
|
||||
"pin": 15,
|
||||
"freq": 5000,
|
||||
"val": 0,
|
||||
"apin": -1
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Avaks",
|
||||
"authorContact": "https://t.me/Avaks",
|
||||
@@ -38,9 +38,7 @@
|
||||
"freq": "Частота"
|
||||
}
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "SD карта",
|
||||
"type": "Writing",
|
||||
"subtype": "SDcard",
|
||||
"id": "sd",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
|
||||
"int": 1
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "SD карта",
|
||||
"type": "Writing",
|
||||
"subtype": "SDcard",
|
||||
"id": "sd",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 1
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -31,12 +30,16 @@
|
||||
"int": "Не используется."
|
||||
}
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"espressif/esp32-camera @ ^2.0.0"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"espressif/esp32-camera @ ^2.0.0"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"espressif/esp32-camera @ ^2.0.0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Доп. функции системы",
|
||||
"type": "Reading",
|
||||
"subtype": "SysExt",
|
||||
"id": "SysExt",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 15
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Доп. функции системы",
|
||||
"type": "Reading",
|
||||
"subtype": "SysExt",
|
||||
"id": "SysExt",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 15
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -30,11 +30,11 @@
|
||||
"int": "Не используется"
|
||||
}
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": []
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Телеграм-Бот",
|
||||
"type": "Writing",
|
||||
"subtype": "Telegram",
|
||||
"id": "tg",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 10,
|
||||
|
||||
"token": "",
|
||||
"autos": 1,
|
||||
"receiveMsg": 0,
|
||||
"chatID": ""
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Телеграм-Бот",
|
||||
"type": "Writing",
|
||||
"subtype": "Telegram",
|
||||
"id": "tg",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 10,
|
||||
"token": "",
|
||||
"autos": 1,
|
||||
"receiveMsg": 0,
|
||||
"chatID": ""
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -38,25 +37,36 @@
|
||||
"chatID": "ИД диалога с контактом. Необходим для отправки сообщений именно вам."
|
||||
},
|
||||
"funcInfo": [
|
||||
{
|
||||
{
|
||||
"name": "sendMsg",
|
||||
"descr": "Отправить сообщение без повторений.",
|
||||
"params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"]
|
||||
"params": [
|
||||
"Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"
|
||||
]
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "sendOftenMsg",
|
||||
"descr": "Отправить сообщение в любом случае, даж если отправляли такое ранее.",
|
||||
"params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"]
|
||||
"params": [
|
||||
"Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"CTBot @2.1.9"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"CTBot @2.1.9"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"CTBot @2.1.9"
|
||||
],
|
||||
"esp32_16mb": [
|
||||
"CTBot @2.1.9"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"CTBot @2.1.9"
|
||||
]
|
||||
|
||||
@@ -13,7 +13,7 @@ class TelegramLT : public IoTItem {
|
||||
}
|
||||
|
||||
void sendTelegramMsg(bool often, String msg) {
|
||||
if (WiFi.status() == WL_CONNECTED && (often || !often && _prevMsg != msg)) {
|
||||
if (isNetworkActive() && (often || !often && _prevMsg != msg)) {
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
http.begin(client, "http://live-control.com/iotm/telegram.php");
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -15,7 +14,6 @@
|
||||
"chatID": ""
|
||||
}
|
||||
],
|
||||
|
||||
"about": {
|
||||
"authorName": "AVAKS",
|
||||
"authorContact": "https://t.me/@avaks_dev",
|
||||
@@ -51,12 +49,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": true,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp32_16mb": [],
|
||||
"esp32s2_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_16mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
"esp8285_1mb": [],
|
||||
@@ -64,4 +65,4 @@
|
||||
"esp8266_2mb": [],
|
||||
"esp8266_2mb_ota": []
|
||||
}
|
||||
}
|
||||
}
|
||||
361
src/modules/exec/Telegram_v2/Telegram_v2.cpp
Normal file
361
src/modules/exec/Telegram_v2/Telegram_v2.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
// #define FB_NO_UNICODE
|
||||
// #define FB_NO_URLENCODE
|
||||
// #define FB_NO_OTA
|
||||
// #define FB_DYNAMIC
|
||||
// #include <GyverGFX.h>
|
||||
// #include <CharPlot.h>
|
||||
// #include "esp_camera.h"
|
||||
|
||||
#include <FastBot.h>
|
||||
#include <vector>
|
||||
|
||||
// FastBot _myBot;
|
||||
FastBot *_myBot = nullptr;
|
||||
FastBot *instanceBot()
|
||||
{
|
||||
if (!_myBot)
|
||||
{
|
||||
_myBot = new FastBot();
|
||||
// ot->begin();
|
||||
}
|
||||
return _myBot;
|
||||
}
|
||||
|
||||
String _token;
|
||||
String _chatID;
|
||||
bool _autos;
|
||||
bool _initSD;
|
||||
|
||||
class Telegram_v2 : public IoTItem
|
||||
{
|
||||
private:
|
||||
bool _receiveMsg;
|
||||
String _prevMsg = "";
|
||||
bool _useLed = false;
|
||||
|
||||
public:
|
||||
Telegram_v2(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
jsonRead(parameters, "token", _token);
|
||||
jsonRead(parameters, "autos", _autos);
|
||||
jsonRead(parameters, "receiveMsg", _receiveMsg);
|
||||
jsonRead(parameters, "chatID", _chatID);
|
||||
instanceBot();
|
||||
_myBot->attach(telegramMsgParse);
|
||||
|
||||
#ifdef ESP32
|
||||
// _myBot->useDNS(true);
|
||||
#endif
|
||||
|
||||
_myBot->setToken(_token);
|
||||
// _myBot->enableUTF8Encoding(true);
|
||||
_myBot->setChatID(_chatID);
|
||||
// _myBot->showMenuText("help","help",false);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (_receiveMsg && isNetworkActive())
|
||||
{
|
||||
_myBot->tick();
|
||||
}
|
||||
// Далее вызов doByInterval для обработки комманд
|
||||
IoTItem::loop();
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m)
|
||||
{
|
||||
if (!isNetworkActive()) return {};
|
||||
if (command == "sendMsg")
|
||||
{
|
||||
if (param.size())
|
||||
{
|
||||
String strTmp;
|
||||
if (param[0].isDecimal)
|
||||
strTmp = param[0].valD;
|
||||
else
|
||||
strTmp = param[0].valS;
|
||||
sendTelegramMsg(false, strTmp);
|
||||
}
|
||||
}
|
||||
else if (command == "sendOftenMsg")
|
||||
{
|
||||
if (param.size())
|
||||
{
|
||||
String strTmp;
|
||||
if (param[0].isDecimal)
|
||||
strTmp = param[0].valD;
|
||||
else
|
||||
strTmp = param[0].valS;
|
||||
sendTelegramMsg(true, strTmp);
|
||||
}
|
||||
}
|
||||
else if (command == "sendPinMsg")
|
||||
{
|
||||
if (param.size())
|
||||
{
|
||||
String strTmp;
|
||||
if (param[0].isDecimal)
|
||||
strTmp = param[0].valD;
|
||||
else
|
||||
strTmp = param[0].valS;
|
||||
_myBot->sendMessage(strTmp, _chatID);
|
||||
_myBot->pinMessage(_myBot->lastBotMsg());
|
||||
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ",pin msg: " + strTmp);
|
||||
}
|
||||
}
|
||||
else if (command == "editMsg")
|
||||
{
|
||||
if (param.size())
|
||||
{
|
||||
String strTmp;
|
||||
if (param[0].isDecimal)
|
||||
strTmp = param[0].valD;
|
||||
else
|
||||
strTmp = param[0].valS;
|
||||
_myBot->editMessage(_myBot->lastBotMsg(), strTmp);
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ",edit msg: " + strTmp);
|
||||
}
|
||||
}
|
||||
else if (command == "sendFile")
|
||||
{
|
||||
if (param.size() && !param[0].isDecimal)
|
||||
{
|
||||
// String path = filepath(filename);
|
||||
auto file = FileFS.open(param[0].valS, FILE_READ);
|
||||
if (!file)
|
||||
{
|
||||
SerialPrint("E", F("Telegram"), "Fail send file: " + param[0].valS);
|
||||
return {};
|
||||
}
|
||||
// File file = LittleFS.open(param[0].valS, "r"); // /test.png
|
||||
// selectToMarkerLast(msg.text, "_")
|
||||
uint8_t res = _myBot->sendFile(file, (FB_FileType)param[1].valD, selectToMarkerLast(param[0].valS, "/"), _chatID);
|
||||
file.close();
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", sendFile: " + param[0].valS + " res: " + String(res));
|
||||
}
|
||||
}
|
||||
|
||||
else if (command == "editFile")
|
||||
{
|
||||
if (param.size() && !param[0].isDecimal)
|
||||
{
|
||||
// String path = filepath(filename);
|
||||
auto file = FileFS.open(param[0].valS, FILE_READ);
|
||||
if (!file)
|
||||
{
|
||||
SerialPrint("E", F("Telegram"), "Fail edit file: " + param[0].valS);
|
||||
return {};
|
||||
}
|
||||
// File file = LittleFS.open(param[0].valS, "r"); // /test.png
|
||||
// selectToMarkerLast(msg.text, "_")
|
||||
uint8_t res = _myBot->editFile(file, (FB_FileType)param[1].valD, selectToMarkerLast(param[0].valS, "/"), _myBot->lastBotMsg(), _chatID);
|
||||
file.close();
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", editFile: " + param[0].valS + " res: " + String(res));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void static telegramMsgParse(FB_msg &msg)
|
||||
{
|
||||
// FB_msg msg;
|
||||
SerialPrint("->", F("Telegram"), "chat ID: " + msg.chatID + ", msg: " + msg.text);
|
||||
// _myBot->setChatID(_chatID);
|
||||
if (_autos)
|
||||
{
|
||||
_chatID = msg.chatID;
|
||||
}
|
||||
if (msg.text.indexOf("set") != -1)
|
||||
{
|
||||
msg.text = deleteBeforeDelimiter(msg.text, "_");
|
||||
generateOrder(selectToMarker(msg.text, "_"), selectToMarkerLast(msg.text, "_"));
|
||||
_myBot->replyMessage("order done", msg.messageID, _chatID);
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", msg: " + String(msg.text));
|
||||
}
|
||||
else if (msg.text.indexOf("get") != -1)
|
||||
{
|
||||
msg.text = deleteBeforeDelimiter(msg.text, "_");
|
||||
IoTItem *item = findIoTItem(msg.text);
|
||||
if (item)
|
||||
{
|
||||
_myBot->replyMessage(item->getValue(), msg.messageID, _chatID);
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", msg: " + String(msg.text));
|
||||
}
|
||||
}
|
||||
|
||||
else if (msg.text.indexOf("all") != -1)
|
||||
{
|
||||
// String list = returnListOfParams();
|
||||
String out;
|
||||
std::vector<float> vctr;
|
||||
for (std::list<IoTItem *>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it)
|
||||
{
|
||||
if ((*it)->iAmLocal)
|
||||
{
|
||||
if (it == IoTItems.begin())
|
||||
{
|
||||
out = "get_" + (*it)->getID();
|
||||
}
|
||||
else
|
||||
{
|
||||
out = out + " \n " + "get_" + (*it)->getID();
|
||||
}
|
||||
vctr.push_back(atoff((*it)->getValue().c_str()));
|
||||
// _myBot->sendMessage((*it)->getID() + ": " + (*it)->getValue(), _chatID);
|
||||
}
|
||||
}
|
||||
_myBot->showMenuText("select Id", out, true);
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + "\n" + out);
|
||||
// _myBot->sendMessage(CharPlot<LINE_X2>(&vctr[0], vctr.size(), 5), _chatID);
|
||||
// SerialPrint("<-", F("Telegram"), CharPlot<LINE_X2>(&vctr[0], vctr.size(), 10));
|
||||
}
|
||||
|
||||
else if (msg.text.indexOf("file") != -1 && msg.chatID == _chatID)
|
||||
{
|
||||
msg.text = deleteBeforeDelimiter(msg.text, "_");
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", get file: " + String(msg.text));
|
||||
auto file = FileFS.open(selectToMarker(msg.text, "_"), FILE_READ); // /test.png
|
||||
if (!file)
|
||||
{
|
||||
SerialPrint("E", F("Telegram"), "Fail send file: " + selectToMarker(msg.text, "_"));
|
||||
return;
|
||||
}
|
||||
int type = atoi(selectToMarkerLast(msg.text, "_").c_str());
|
||||
_myBot->sendFile(file, (FB_FileType)type, selectToMarker(msg.text, "_"), _chatID);
|
||||
file.close();
|
||||
}
|
||||
else if (msg.isFile)
|
||||
{
|
||||
if (msg.text.indexOf("download") != -1 && msg.chatID == _chatID)
|
||||
{
|
||||
downloadFile(msg);
|
||||
}
|
||||
else if (msg.text.indexOf("nextion") != -1 && msg.chatID == _chatID)
|
||||
{
|
||||
for (std::list<IoTItem *>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
|
||||
if ((*it)->getSubtype() == "NextionUpload") {
|
||||
(*it)->uploadNextionTlgrm(msg.fileUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msg.text.indexOf("help") != -1)
|
||||
{
|
||||
_myBot->sendMessage("ID: " + chipId, _chatID);
|
||||
_myBot->sendMessage("chatID: " + _chatID, _chatID);
|
||||
_myBot->sendMessage(F("Wrong order, use /all to get all values, /get_id to get value, /set_id_value to set value, or /file_name_type or send file msg=download"), _chatID);
|
||||
}
|
||||
else
|
||||
{
|
||||
// setValue(msg.text);
|
||||
}
|
||||
}
|
||||
|
||||
void sendTelegramMsg(bool often, String msg)
|
||||
{
|
||||
if (!isNetworkActive()) return;
|
||||
if (often)
|
||||
{
|
||||
_myBot->sendMessage(msg, _chatID);
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", msg: " + msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_prevMsg != msg)
|
||||
{
|
||||
_prevMsg = msg;
|
||||
_myBot->sendMessage(msg, _chatID);
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", msg: " + msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendFoto(uint8_t *buf, uint32_t length, const String &name)
|
||||
{
|
||||
if (!isNetworkActive()) return;
|
||||
_myBot->sendFile(buf, length, FB_PHOTO, name, _chatID);
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", send foto from esp-cam");
|
||||
}
|
||||
|
||||
void editFoto(uint8_t *buf, uint32_t length, const String &name)
|
||||
{
|
||||
if (!isNetworkActive()) return;
|
||||
_myBot->editFile(buf, length, FB_PHOTO, name, _myBot->lastBotMsg(), _chatID);
|
||||
SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", edit foto from esp-cam");
|
||||
}
|
||||
|
||||
int static downloadFile(FB_msg &msg)
|
||||
{
|
||||
int _size = 0;
|
||||
String path = '/' + msg.fileName; // вида /filename.xxx
|
||||
auto file = FileFS.open(path, FILE_WRITE); // открываем для записи
|
||||
// _myBot->sendMessage("Downloading from: " + _chatID + ", file: " + String(msg.fileName), _chatID);
|
||||
if (file)
|
||||
{ // файл открылся/создался
|
||||
HTTPClient http;
|
||||
|
||||
#ifdef ESP8266 // esp8266 требует SSl
|
||||
BearSSL::WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
http.begin(client, msg.fileUrl); // пингуем файл
|
||||
#else // esp32 сама умеет SSL
|
||||
http.begin(msg.fileUrl); // пингуем файл
|
||||
#endif
|
||||
|
||||
if (http.GET() == HTTP_CODE_OK)
|
||||
{ // файл доступен
|
||||
// загружаем в память. Результат > 0 - успешно
|
||||
_size = http.writeToStream(&file);
|
||||
}
|
||||
http.end(); // закрываем соединение
|
||||
file.close(); // закрываем файл
|
||||
|
||||
if (_size == 0)
|
||||
{
|
||||
SerialPrint("E", F("Telegram"), "download error file url: " + msg.fileUrl);
|
||||
_myBot->sendMessage(F("Download Fail"), _chatID);
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("<-", F("Telegram"), "download from: " + _chatID + ", file: " + msg.fileName + " size = " + String(_size) + " byte");
|
||||
_myBot->sendMessage("Download Ok, size = " + String(_size) + " byte", _chatID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("E", F("Telegram"), F("file write error"));
|
||||
_myBot->sendMessage(F("file write error"), _chatID);
|
||||
}
|
||||
return _size;
|
||||
}
|
||||
|
||||
IoTItem *getTlgrmDriver()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
~Telegram_v2()
|
||||
{
|
||||
tlgrmItem = nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
void *getAPI_Telegram_v2(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("Telegram_v2"))
|
||||
{
|
||||
return new Telegram_v2(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
96
src/modules/exec/Telegram_v2/modinfo.json
Normal file
96
src/modules/exec/Telegram_v2/modinfo.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Телеграм-Бот v2",
|
||||
"type": "Writing",
|
||||
"subtype": "Telegram_v2",
|
||||
"id": "tg",
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 10,
|
||||
|
||||
"token": "",
|
||||
"autos": 1,
|
||||
"receiveMsg": 0,
|
||||
"chatID": ""
|
||||
}],
|
||||
|
||||
"about": {
|
||||
"authorName": "Mikhail Bubnov",
|
||||
"authorContact": "https://t.me/Mit4bmw",
|
||||
"authorGit": "https://github.com/Mit4el",
|
||||
"specialThanks": "",
|
||||
"moduleName": "Telegram_v2",
|
||||
"moduleVersion": "1.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 37,
|
||||
"esp8266_4mb": 37
|
||||
},
|
||||
"title": "Телеграм-Бот v2",
|
||||
"moduleDesc": "Добавляет возможность отправлять сообщения от имени бота контакту в Телеграм-чате и получать команды.",
|
||||
"propInfo": {
|
||||
"token": "Токен для авторизации бота в системе Telegram",
|
||||
"autos": "Автоматически(1) или нет(0) запоминать ChatID по входящим сообщениям. Т.е. бот будет информировать тех, кто последний прислал сообщение.",
|
||||
"receiveMsg": "Обрабатывать(1) или нет(0) входящие сообщения.",
|
||||
"chatID": "ИД диалога с контактом. Необходим для отправки сообщений именно вам."
|
||||
},
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "sendMsg",
|
||||
"descr": "Отправить сообщение без повторений.",
|
||||
"params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"]
|
||||
},
|
||||
{
|
||||
"name": "sendOftenMsg",
|
||||
"descr": "Отправить сообщение в любом случае, даж если отправляли такое ранее.",
|
||||
"params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"]
|
||||
},
|
||||
{
|
||||
"name": "sendPinMsg",
|
||||
"descr": "Отправить закрепленное сообщение в любом случае, даж если отправляли такое ранее.",
|
||||
"params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"]
|
||||
},
|
||||
{
|
||||
"name": "editMsg",
|
||||
"descr": "Отредактировать последнее отправленное ботом сообщение.",
|
||||
"params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"]
|
||||
},
|
||||
{
|
||||
"name": "sendFile",
|
||||
"descr": "Отправить файл в телеграмм, с указанием типа файла: 0-фото, 1-аудио, 2-документ, 3-видео, 4-анимация, 5-голос",
|
||||
"params": ["Путь к файлу (/test.png)", "Тип файла/информации (число)"]
|
||||
},
|
||||
{
|
||||
"name": "editFile",
|
||||
"descr": "Отредактировать последний отправленный файл, с указанием типа файла: 0-фото, 1-аудио, 2-документ, 3-видео, 4-анимация, 5-голос",
|
||||
"params": ["Путь к файлу (/test.png)", "Тип файла/информации (число)"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"gyverlibs/FastBot"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"gyverlibs/FastBot"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"gyverlibs/FastBot"
|
||||
],
|
||||
"esp32s2_4mb": [
|
||||
"gyverlibs/FastBot"
|
||||
],
|
||||
"esp32_16mb": [
|
||||
"gyverlibs/FastBot"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"gyverlibs/FastBot"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -150,8 +150,9 @@ public:
|
||||
class ThermostatPID : public IoTItem
|
||||
{
|
||||
private:
|
||||
String _set_id; // заданная температура
|
||||
String _term_id; // термометр
|
||||
String _set_id; // заданная температура
|
||||
String _term_id; // термометр
|
||||
String _term_rezerv_id; // резервный термометр
|
||||
float _int, _KP, _KI, _KD, sp, pv,
|
||||
pv_last = 0, // предыдущая температура
|
||||
ierr = 0, // интегральная погрешность
|
||||
@@ -168,6 +169,7 @@ public:
|
||||
{
|
||||
jsonRead(parameters, "set_id", _set_id);
|
||||
jsonRead(parameters, "term_id", _term_id);
|
||||
jsonRead(parameters, "term_rezerv_id", _term_rezerv_id);
|
||||
jsonRead(parameters, "int", _int);
|
||||
jsonRead(parameters, "KP", _KP);
|
||||
jsonRead(parameters, "KI", _KI);
|
||||
@@ -237,15 +239,40 @@ protected:
|
||||
interim = tmp->getValue();
|
||||
pv = ::atof(interim.c_str());
|
||||
}
|
||||
if (pv < -40 && pv > 120 && !pv) // Решаем что ошибка датчика
|
||||
{
|
||||
if (_term_rezerv_id != "")
|
||||
{
|
||||
tmp = findIoTItem(_term_rezerv_id); // используем резервный
|
||||
if (tmp)
|
||||
{
|
||||
interim = tmp->getValue();
|
||||
pv = ::atof(interim.c_str());
|
||||
if (pv < -40 && pv > 120 && !pv)
|
||||
pv = 0;
|
||||
}
|
||||
else
|
||||
pv = 0;
|
||||
}
|
||||
else
|
||||
pv = 0;
|
||||
}
|
||||
if (sp && pv)
|
||||
{
|
||||
value.valD = pid(sp, pv, pv_last, ierr, _int);
|
||||
value.valS = (String)(int)pid(sp, pv, pv_last, ierr, _int);
|
||||
regEvent(value.valS, "ThermostatPID", false, true);
|
||||
}
|
||||
// value.valD = pid(sp, pv, pv_last, ierr, _int);
|
||||
// value.valS = (String)(int)value.valD;
|
||||
regEvent(pid(sp, pv, pv_last, ierr, _int), "ThermostatPID", false, true);
|
||||
}
|
||||
else
|
||||
regEvent(0, "ThermostatPID", false, true);
|
||||
pv_last = pv;
|
||||
}
|
||||
|
||||
// временное решение
|
||||
unsigned long currentMillis;
|
||||
unsigned long prevMillis;
|
||||
unsigned long difference;
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (enableDoByInt)
|
||||
@@ -327,8 +354,8 @@ private:
|
||||
public:
|
||||
ThermostatETK(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
jsonRead(parameters, "set_id", _set_id);
|
||||
jsonRead(parameters, "term_id", _term_id);
|
||||
// jsonRead(parameters, "set_id", _set_id);
|
||||
// jsonRead(parameters, "term_id", _term_id);
|
||||
jsonRead(parameters, "iv_k", _iv_k);
|
||||
jsonRead(parameters, "outside_id", _outside_id);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"menuSection": "Исполнительные устройства",
|
||||
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -35,6 +34,7 @@
|
||||
"map": "1,100,1,100",
|
||||
"set_id": "",
|
||||
"term_id": "",
|
||||
"term_rezerv_id": "",
|
||||
"rele": "",
|
||||
"KP": 5.0,
|
||||
"KI": 50,
|
||||
@@ -73,7 +73,6 @@
|
||||
"outside_id": ""
|
||||
}
|
||||
],
|
||||
|
||||
"about": {
|
||||
"authorName": "AVAKS",
|
||||
"authorContact": "https://t.me/@avaks_dev",
|
||||
@@ -106,30 +105,38 @@
|
||||
{
|
||||
"name": "enable",
|
||||
"descr": "включить / выключить термостатирование (режим AUTO) применим к PID и Гистере́зис ",
|
||||
"params": ["thermostat.enable(1) - вкл, thermostat.enable(0) - выкл, "]
|
||||
"params": [
|
||||
"thermostat.enable(1) - вкл, thermostat.enable(0) - выкл, "
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "KP",
|
||||
"descr": "Пропорциональный коэффициент PID .",
|
||||
"params": ["thermostat.KP(1) - задает значение коэффициента"]
|
||||
"params": [
|
||||
"thermostat.KP(1) - задает значение коэффициента"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "KI",
|
||||
"descr": "Интегральный коэффициент PID .",
|
||||
"params": ["thermostat.KI(1) - задает значение коэффициента"]
|
||||
"params": [
|
||||
"thermostat.KI(1) - задает значение коэффициента"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "KD",
|
||||
"descr": "Дифференциальный коэффициент PID .",
|
||||
"params": ["thermostat.KD(1) - задает значение коэффициента"]
|
||||
"params": [
|
||||
"thermostat.KD(1) - задает значение коэффициента"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
@@ -138,4 +145,4 @@
|
||||
"esp8266_2mb": [],
|
||||
"esp8266_2mb_ota": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,11 @@
|
||||
"descr": "Отправить значение в топик MQTT",
|
||||
"params": ["Топик", "Значение"]
|
||||
},
|
||||
{
|
||||
"name": "mqttIsConnect",
|
||||
"descr": "Получить состояние подключения к MQTT",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "getHours",
|
||||
"descr": "Получить текущее число часов. Если время не получено из сети Интернет или внешнего RTC, то условие пропускается",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "A02 Дальность",
|
||||
"type": "Reading",
|
||||
"type": "Reading",
|
||||
"subtype": "A02Distance",
|
||||
"id": "dist",
|
||||
"widget": "anydataCm",
|
||||
@@ -15,16 +15,17 @@
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Bubnov Mikhail",
|
||||
"authorContact": "https://t.me/Mitchel",
|
||||
"authorContact": "https://t.me/Mit4bmw",
|
||||
"authorGit": "https://github.com/Mit4el",
|
||||
"exampleURL": "https://iotmanager.org/wiki",
|
||||
"specialThanks": "",
|
||||
"moduleName": "A02Distance",
|
||||
"moduleVersion": "0.1",
|
||||
"moduleDesc": "A0221AU, A02YYUW Ультразвуковой датчик. Позволяет получить дальность с ультрозвуковых датчиков A0221AU, A02YYUW",
|
||||
"moduleDesc": "Позволяет получить дальность с ультрозвуковых датчиков A0221AU, A02YYUW",
|
||||
"propInfo": {
|
||||
"int": "Количество секунд между опросами датчика."
|
||||
}
|
||||
},
|
||||
"title": "A0221AU, A02YYUW Ультразвуковой датчик дальности"
|
||||
},
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "Acs712 Ток",
|
||||
"type": "Reading",
|
||||
"type": "Reading",
|
||||
"subtype": "Acs712",
|
||||
"id": "amp",
|
||||
"widget": "anydataAmp",
|
||||
@@ -13,15 +13,15 @@
|
||||
"pin": 39,
|
||||
"int": 5,
|
||||
"rms": 1,
|
||||
"vref": 5000,
|
||||
"vref": 5000,
|
||||
"sens": 100,
|
||||
"adczero" : 512,
|
||||
"btn-setZero": "nil"
|
||||
"adczero": 512,
|
||||
"btn-setZero": "nil"
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Bubnov Mikhail",
|
||||
"authorContact": "https://t.me/Mitchel",
|
||||
"authorContact": "https://t.me/Mit4bmw",
|
||||
"authorGit": "https://github.com/Mit4el",
|
||||
"exampleURL": "https://iotmanager.org/wiki",
|
||||
"specialThanks": "",
|
||||
@@ -34,13 +34,17 @@
|
||||
"rms": "1 - подсчет средне-квадратического тока (переменный), 0 - подсчет средне-арифмитического тока (постоянный)",
|
||||
"vref": "Vref (мВ) - Опороное наряжение питания Acs712, по умолчанию = 5000мВ",
|
||||
"sens": "Чувствительность датчика тока: 5A = 185mВ/A , 20A = 100mВ/A , 30A = 66mВ/A ",
|
||||
"adczero" : "Переменная калибровки нулевого значения отсчетов АЦП при нулевой нагрузке. Для ESP8266 - 512, Для ESP32 -2048, это 2.5В = 0А (1,65 с делителем) для Acs712 20A и 30A при стабильном токе 5В",
|
||||
"btn-setZero": "Кнопка калибровки нулевого значения отсчетов АЦП при нулевой нагрузке. Нагрузка в момент калибровки должна быть отключена! После перезагрузки будет установлено в значение по умолчанию adczero. Для сохранение смотрим лог, и изменияем adczero"
|
||||
}
|
||||
"adczero": "Переменная калибровки нулевого значения отсчетов АЦП при нулевой нагрузке. Для ESP8266 - 512, Для ESP32 -2048, это 2.5В = 0А (1,65 с делителем) для Acs712 20A и 30A при стабильном токе 5В",
|
||||
"btn-setZero": "Кнопка калибровки нулевого значения отсчетов АЦП при нулевой нагрузке. Нагрузка в момент калибровки должна быть отключена! После перезагрузки будет установлено в значение по умолчанию adczero. Для сохранение смотрим лог, и изменияем adczero"
|
||||
},
|
||||
"title": "Acs712 Датчик тока"
|
||||
},
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp32s2_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -44,6 +44,12 @@
|
||||
"esp32_4mb": [
|
||||
"adafruit/Adafruit ADS1X15 @ ^2.3.0"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"adafruit/Adafruit ADS1X15 @ ^2.3.0"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"adafruit/Adafruit ADS1X15 @ ^2.3.0"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"adafruit/Adafruit ADS1X15 @ ^2.3.0"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -12,7 +12,7 @@
|
||||
"descr": "AHTXX Температура",
|
||||
"int": 15,
|
||||
"addr": "0x38",
|
||||
"shtType":1,
|
||||
"shtType": 1,
|
||||
"round": 1
|
||||
},
|
||||
{
|
||||
@@ -26,7 +26,7 @@
|
||||
"descr": "AHTXX Влажность",
|
||||
"int": 15,
|
||||
"addr": "0x38",
|
||||
"shtType":1,
|
||||
"shtType": 1,
|
||||
"round": 1
|
||||
}
|
||||
],
|
||||
@@ -59,8 +59,26 @@
|
||||
"esp32_4mb": [
|
||||
"https://github.com/enjoyneering/AHTxx.git"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/enjoyneering/AHTxx.git"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/enjoyneering/AHTxx.git"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/enjoyneering/AHTxx.git"
|
||||
],
|
||||
"esp8266_1mb": [
|
||||
"https://github.com/enjoyneering/AHTxx.git"
|
||||
],
|
||||
"esp8266_1mb_ota": [
|
||||
"https://github.com/enjoyneering/AHTxx.git"
|
||||
],
|
||||
"esp8285_1mb": [
|
||||
"https://github.com/enjoyneering/AHTxx.git"
|
||||
],
|
||||
"esp8285_1mb_ota": [
|
||||
"https://github.com/enjoyneering/AHTxx.git"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -64,13 +64,7 @@ class AnalogAdc : public IoTItem {
|
||||
_avgSumm = _avgSumm + IoTgpio.analogRead(_pin);
|
||||
_avgCount++;
|
||||
}
|
||||
|
||||
currentMillis = millis();
|
||||
difference = currentMillis - prevMillis;
|
||||
if (difference >= _interval) {
|
||||
prevMillis = millis();
|
||||
this->doByInterval();
|
||||
}
|
||||
IoTItem::loop();
|
||||
}
|
||||
|
||||
~AnalogAdc(){};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -42,6 +42,8 @@
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
|
||||
"configItem": [{
|
||||
"global": 0,
|
||||
"name": "Cенсор освещенность Bh1750",
|
||||
"type": "Reading",
|
||||
"subtype": "Bh1750",
|
||||
"id": "Bh1750",
|
||||
"widget": "anydata",
|
||||
"page": "Сенсоры",
|
||||
"descr": "Освещённость",
|
||||
"round": 1,
|
||||
"int": 15
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Cенсор освещенность Bh1750",
|
||||
"type": "Reading",
|
||||
"subtype": "Bh1750",
|
||||
"id": "Bh1750",
|
||||
"widget": "anydata",
|
||||
"page": "Сенсоры",
|
||||
"descr": "Освещённость",
|
||||
"round": 1,
|
||||
"int": 15
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Ilya Belyakov",
|
||||
"authorContact": "https://t.me/Biveraxe",
|
||||
@@ -37,6 +37,12 @@
|
||||
"esp32_4mb": [
|
||||
"BH1750"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"BH1750"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"BH1750"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"BH1750"
|
||||
]
|
||||
|
||||
@@ -1,27 +1,196 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP32
|
||||
#include <NimBLEDevice.h>
|
||||
#define BLE_PART1
|
||||
#define BLE_PART2
|
||||
#include <decoder.h>
|
||||
#include <vector>
|
||||
|
||||
// Создаем переменную для хранения данных с датчиков bluetooth
|
||||
StaticJsonDocument<JSON_BUFFER_SIZE * 4> BLEbuffer;
|
||||
JsonObject extBLEdata = BLEbuffer.to<JsonObject>();
|
||||
// StaticJsonDocument<JSON_BUFFER_SIZE * 4> BLEbuffer;
|
||||
// DynamicJsonDocument extBLEdata(JSON_BUFFER_SIZE * 4);
|
||||
// JsonObject extBLEdata = BLEbuffer.to<JsonObject>();
|
||||
class BleSens;
|
||||
std::vector<BleSens *> BleSensArray;
|
||||
|
||||
BLEScan *pBLEScan;
|
||||
TheengsDecoder decoder;
|
||||
StaticJsonDocument<512> doc;
|
||||
class BleSens : public IoTItem
|
||||
{
|
||||
private:
|
||||
// описание параметров передаваемых из настроек датчика из веба
|
||||
String _MAC;
|
||||
String _sensor;
|
||||
int timeRecv = 0;
|
||||
int _minutesPassed = 0;
|
||||
String json = "{}";
|
||||
int orange = 0;
|
||||
int red = 0;
|
||||
int offline = 0;
|
||||
int _int;
|
||||
bool dataFromNode = false;
|
||||
|
||||
public:
|
||||
String whoIAm(/*String &mac, String &sens*/)
|
||||
{
|
||||
// mac = _MAC;
|
||||
// sens = _sensor;
|
||||
return _MAC;
|
||||
}
|
||||
|
||||
void setBLEdata(JsonObject extBLEdata)
|
||||
{
|
||||
if (_sensor == "last")
|
||||
{
|
||||
timeRecv = extBLEdata[_sensor].as<int>();
|
||||
char *s;
|
||||
s = TimeToString(millis() / 1000 - timeRecv / 1000);
|
||||
value.isDecimal = 0;
|
||||
if (timeRecv > 0)
|
||||
{
|
||||
value.valS = s;
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
else
|
||||
{
|
||||
value.valS = "";
|
||||
}
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
else
|
||||
{
|
||||
String valStr = extBLEdata[_sensor].as<String>();
|
||||
if (valStr != "null")
|
||||
{
|
||||
if (value.isDecimal == isDigitDotCommaStr(valStr))
|
||||
{
|
||||
value.isDecimal = 1;
|
||||
value.valD = valStr.toFloat();
|
||||
regEvent(value.valD, _id);
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
else
|
||||
{
|
||||
value.isDecimal = 0;
|
||||
value.valS = valStr;
|
||||
regEvent(value.valS, _id);
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
char *TimeToString(unsigned long t)
|
||||
{
|
||||
static char str[12];
|
||||
long h = t / 3600;
|
||||
t = t % 3600;
|
||||
int m = t / 60;
|
||||
int s = t % 60;
|
||||
sprintf(str, "%02ld:%02d:%02d", h, m, s);
|
||||
return str;
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
if (_sensor == "last")
|
||||
{
|
||||
char *s;
|
||||
s = TimeToString(millis() / 1000 - timeRecv / 1000);
|
||||
value.isDecimal = 0;
|
||||
if (timeRecv > 0)
|
||||
{
|
||||
value.valS = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.valS = "";
|
||||
}
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
_minutesPassed++;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
void onMqttWsAppConnectEvent()
|
||||
{
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
void setNewWidgetAttributes()
|
||||
{
|
||||
|
||||
int minutes_ = _minutesPassed * _int / 60;
|
||||
jsonWriteStr(json, F("info"), prettyMinutsTimeout(minutes_));
|
||||
if (dataFromNode)
|
||||
{
|
||||
if (orange != 0 && red != 0 && offline != 0)
|
||||
{
|
||||
if (minutes_ < orange)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), "");
|
||||
}
|
||||
if (minutes_ >= orange && minutes_ < red)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), F("orange")); // сделаем виджет оранжевым
|
||||
}
|
||||
if (minutes_ >= red && minutes_ < offline)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), F("red")); // сделаем виджет красным
|
||||
}
|
||||
if (minutes_ >= offline)
|
||||
{
|
||||
jsonWriteStr(json, F("info"), F("offline"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonWriteStr(json, F("info"), F("awaiting"));
|
||||
}
|
||||
sendSubWidgetsValues(_id, json);
|
||||
}
|
||||
|
||||
BleSens(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_MAC = jsonReadStr(parameters, "MAC");
|
||||
_sensor = jsonReadStr(parameters, "sensor");
|
||||
jsonRead(parameters, F("orange"), orange);
|
||||
jsonRead(parameters, F("red"), red);
|
||||
jsonRead(parameters, F("offline"), offline);
|
||||
jsonRead(parameters, F("int"), _int);
|
||||
dataFromNode = false;
|
||||
BleSensArray.push_back(this);
|
||||
}
|
||||
|
||||
~BleSens(){};
|
||||
};
|
||||
|
||||
//=======================================================================================================
|
||||
|
||||
/** Callback to process the results of the last scan or restart it */
|
||||
void scanEndedCB(NimBLEScanResults results)
|
||||
{
|
||||
int count = results.getCount();
|
||||
SerialPrint("i", F("BLE"), "Scan done! "); // +"Devices found: " + String(count));
|
||||
// pBLEScan->clearResults();
|
||||
}
|
||||
|
||||
class BleScan : public IoTItem, BLEAdvertisedDeviceCallbacks
|
||||
{
|
||||
private:
|
||||
//описание параметров передаваемых из настроек датчика из веба
|
||||
// описание параметров передаваемых из настроек датчика из веба
|
||||
int _scanDuration;
|
||||
String _filter;
|
||||
bool _debug;
|
||||
|
||||
StaticJsonDocument<512> doc;
|
||||
BLEScan *pBLEScan;
|
||||
TheengsDecoder decoder;
|
||||
|
||||
public:
|
||||
//=======================================================================================================
|
||||
std::string convertServiceData(std::string deviceServiceData)
|
||||
{
|
||||
int serviceDataLength = (int)deviceServiceData.length();
|
||||
@@ -67,169 +236,91 @@ public:
|
||||
|
||||
if (decoder.decodeBLEJson(BLEdata))
|
||||
{
|
||||
|
||||
BLEdata.remove("manufacturerdata");
|
||||
BLEdata.remove("servicedata");
|
||||
|
||||
String mac_address = BLEdata["id"].as<const char *>();
|
||||
String mac_address = BLEdata["mac"].as<const char *>();
|
||||
if (mac_address == "")
|
||||
{
|
||||
BLEdata["mac"] = BLEdata["id"];
|
||||
mac_address = BLEdata["id"].as<const char *>();
|
||||
}
|
||||
mac_address.replace(":", "");
|
||||
|
||||
if (_filter != "")
|
||||
if (_debug < 2)
|
||||
{
|
||||
if (BLEdata[_filter])
|
||||
BLEdata.remove("manufacturerdata");
|
||||
BLEdata.remove("servicedata");
|
||||
BLEdata.remove("type");
|
||||
BLEdata.remove("cidc");
|
||||
BLEdata.remove("acts");
|
||||
BLEdata.remove("cont");
|
||||
BLEdata.remove("track");
|
||||
BLEdata.remove("id");
|
||||
}
|
||||
// дописываем время прихода пакета данных
|
||||
BLEdata["last"] = millis();
|
||||
if (_debug)
|
||||
{
|
||||
if ((_filter != "" && BLEdata[_filter]) || _filter == "")
|
||||
{
|
||||
for (JsonPair kv : BLEdata)
|
||||
// for (JsonPair kv : BLEdata)
|
||||
// {
|
||||
// String val = BLEdata.as<String>();
|
||||
String output;
|
||||
if (_debug < 2)
|
||||
{
|
||||
extBLEdata[mac_address][kv.key()] = BLEdata[kv.key()];
|
||||
BLEdata.remove("servicedatauuid");
|
||||
}
|
||||
serializeJson(BLEdata, output);
|
||||
SerialPrint("i", F("BLE"), mac_address + " " + output);
|
||||
//}
|
||||
}
|
||||
|
||||
// дописываем время прихода пакета данных
|
||||
extBLEdata[mac_address]["last"] = millis();
|
||||
}
|
||||
SerialPrint("i", F("BLE"), "found: " + String(BLEdata["mac"].as<const char *>()));
|
||||
}
|
||||
else
|
||||
|
||||
// Перебираем все зарегистрированные сенсоры BleSens
|
||||
for (std::vector<BleSens *>::iterator it = BleSensArray.begin();
|
||||
it != BleSensArray.end(); ++it)
|
||||
{
|
||||
for (JsonPair kv : BLEdata)
|
||||
{
|
||||
extBLEdata[mac_address][kv.key()] = BLEdata[kv.key()];
|
||||
}
|
||||
// дописываем время прихода пакета данных
|
||||
extBLEdata[mac_address]["last"] = millis();
|
||||
// Если это данные для нужного сенсора (по его МАКУ)
|
||||
if ((*it)->whoIAm() == mac_address)
|
||||
// то передаем ему json, дальше он сам разберется
|
||||
(*it)->setBLEdata(BLEdata);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
BleScan(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_scanDuration = jsonReadInt(parameters, "scanDuration");
|
||||
_filter = jsonReadStr(parameters, "filter");
|
||||
jsonRead(parameters, "debug", _debug);
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); // create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(this);
|
||||
pBLEScan->setActiveScan(false); // active scan uses more power, but get results faster
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99); // less or equal setInterval value
|
||||
pBLEScan->setMaxResults(0); // do not store the scan results, use callback only.
|
||||
}
|
||||
|
||||
// doByInterval()
|
||||
void doByInterval()
|
||||
{
|
||||
if (pBLEScan->isScanning() == false)
|
||||
{
|
||||
SerialPrint("i", F("BLE"), "Start Scanning...");
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); // create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(this);
|
||||
pBLEScan->setActiveScan(false); // active scan uses more power, but get results faster
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99); // less or equal setInterval value
|
||||
if (_scanDuration > 0)
|
||||
{
|
||||
SerialPrint("i", F("BLE"), "Start Scanning...");
|
||||
pBLEScan->start(_scanDuration, scanEndedCB, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================================================
|
||||
|
||||
// doByInterval()
|
||||
void doByInterval()
|
||||
{
|
||||
|
||||
if (_scanDuration > 0)
|
||||
{
|
||||
BLEScanResults foundDevices = pBLEScan->start(_scanDuration, true);
|
||||
int count = foundDevices.getCount();
|
||||
SerialPrint("i", F("BLE"), "Devices found: " + String(count));
|
||||
SerialPrint("i", F("BLE"), "Scan done!");
|
||||
pBLEScan->clearResults();
|
||||
}
|
||||
for (JsonPair kv : extBLEdata)
|
||||
{
|
||||
String val = extBLEdata[kv.key()].as<String>();
|
||||
SerialPrint("i", F("BLE"), _id + " " + kv.key().c_str() + " " + val);
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================================================
|
||||
|
||||
~BleScan(){};
|
||||
~BleScan() { BleSensArray.clear(); };
|
||||
};
|
||||
|
||||
class BleSens : public IoTItem
|
||||
{
|
||||
private:
|
||||
//описание параметров передаваемых из настроек датчика из веба
|
||||
String _MAC;
|
||||
String _sensor;
|
||||
|
||||
public:
|
||||
//=======================================================================================================
|
||||
char *TimeToString(unsigned long t)
|
||||
{
|
||||
static char str[12];
|
||||
long h = t / 3600;
|
||||
t = t % 3600;
|
||||
int m = t / 60;
|
||||
int s = t % 60;
|
||||
sprintf(str, "%02ld:%02d:%02d", h, m, s);
|
||||
return str;
|
||||
}
|
||||
|
||||
BleSens(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_MAC = jsonReadStr(parameters, "MAC");
|
||||
_sensor = jsonReadStr(parameters, "sensor");
|
||||
}
|
||||
|
||||
//=======================================================================================================
|
||||
|
||||
// doByInterval()
|
||||
void doByInterval()
|
||||
{
|
||||
if (_sensor == "last")
|
||||
{
|
||||
int valInt = extBLEdata[_MAC][_sensor].as<int>();
|
||||
char *s;
|
||||
s = TimeToString(millis() / 1000 - valInt / 1000);
|
||||
value.isDecimal = 0;
|
||||
if (valInt > 0)
|
||||
{
|
||||
value.valS = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.valS = "";
|
||||
}
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
else
|
||||
{
|
||||
String valStr = extBLEdata[_MAC][_sensor].as<String>();
|
||||
if (valStr != "null")
|
||||
{
|
||||
if (value.isDecimal = isDigitDotCommaStr(valStr))
|
||||
{
|
||||
value.isDecimal = 1;
|
||||
value.valD = valStr.toFloat();
|
||||
regEvent(value.valD, _id);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.isDecimal = 0;
|
||||
value.valS = valStr;
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value.isDecimal = 0;
|
||||
value.valS = "";
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
}
|
||||
}
|
||||
//=======================================================================================================
|
||||
|
||||
~BleSens(){};
|
||||
};
|
||||
#endif
|
||||
|
||||
// Заглушка для ESP8266
|
||||
#ifdef ESP8266
|
||||
class Ble : public IoTItem
|
||||
{
|
||||
private:
|
||||
public:
|
||||
Ble(String parameters) : IoTItem(parameters) {}
|
||||
};
|
||||
#endif
|
||||
//=======================================================================================================
|
||||
|
||||
void *getAPI_Ble(String subtype, String param)
|
||||
{
|
||||
@@ -245,4 +336,4 @@ void *getAPI_Ble(String subtype, String param)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "bluetooth сканер",
|
||||
"num": 1,
|
||||
"type": "Reading",
|
||||
"subtype": "BleScan",
|
||||
"id": "BleScan",
|
||||
@@ -12,11 +11,11 @@
|
||||
"descr": "",
|
||||
"int": 135,
|
||||
"scanDuration": 10,
|
||||
"filter": "servicedatauuid"
|
||||
"filter": "servicedatauuid",
|
||||
"debug": 1
|
||||
},
|
||||
{
|
||||
"name": "bluetooth датчик",
|
||||
"num": 1,
|
||||
"type": "Reading",
|
||||
"subtype": "BleSens",
|
||||
"id": "BleSens",
|
||||
@@ -24,32 +23,41 @@
|
||||
"page": "Сенсоры",
|
||||
"descr": "",
|
||||
"needSave": 0,
|
||||
"int": 30,
|
||||
"global": 0,
|
||||
"round": 1,
|
||||
"int": 60,
|
||||
"orange": 60,
|
||||
"red": 120,
|
||||
"offline": 180,
|
||||
"MAC": "",
|
||||
"sensor": ""
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "AVAKS",
|
||||
"authorContact": "https://t.me/@avaks_dev",
|
||||
"authorGit": "https://github.com/avaksru",
|
||||
"authorName": "AVAKS, v3 - Mit4bmw",
|
||||
"authorContact": "https://t.me/@avaks, https://t.me/Mit4bmw",
|
||||
"authorGit": "https://github.com/avaksru, https://github.com/Mit4el",
|
||||
"specialThanks": "@Serghei63",
|
||||
"moduleName": "Ble",
|
||||
"moduleVersion": "1.0",
|
||||
"moduleVersion": "3.1",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 1261449,
|
||||
"esp32_4mb": 314692,
|
||||
"esp8266_4mb": 0
|
||||
},
|
||||
"subTypes": ["BleSens", "BleScan"],
|
||||
"subTypes": [
|
||||
"BleSens",
|
||||
"BleScan"
|
||||
],
|
||||
"title": "Сканер Bluetooth",
|
||||
"moduleDesc": "Позволяет получить данные с Bluetooth часов и термометров Mijia, Xiaomi, Cleargrass, ...",
|
||||
"moduleDesc": "Позволяет получить данные с Bluetooth часов и термометров и.т.д. Полный список (учитывать отстование нашей версии библиотеки) https://decoder.theengs.io/devices/devices.html Наш перечень в файле lib/decoder/devices.h Здесь полный перечень датчиков, для уменьшения размера модуля рекомендуется использовать модули Ble_part1 и Ble_part2",
|
||||
"propInfo": {
|
||||
"round": "Округление после запятой.",
|
||||
"int": "Интервал сканирования BLE окружения (BleScan) / Интервал отправки собранной телеметрии в MQTT (BleSens)",
|
||||
"orange": "количество минут после которого окрасить виджет в оранжевый цвет",
|
||||
"red": "количество минут после которого окрасить виджет в красный цвет",
|
||||
"offline": "количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится",
|
||||
"int": "Интервал сканирования BLE окружения (BleScan) / В BleSens темп обновления времнени поступления данных, сами даные обновляются по мене сканирования/поступления",
|
||||
"scanDuration": "Длительность сканирования ",
|
||||
"filter": "Позволяет установить фильтр по параметру передаваемому датчиком. Данные будут считываться только с датчиков у которых есть передаваемый параметр указанный в фильтре",
|
||||
"filter": "Позволяет установить фильтр по параметру передаваемому датчиком. Влияет только на вывод лога при debug=1, что бы было легче найти датчики, если много устройств в эфире",
|
||||
"MAC": "MAC адрес беспроводного датчика",
|
||||
"sensor": "Тип сенсора: температура / влажность / время / ... "
|
||||
}
|
||||
@@ -57,8 +65,22 @@
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/h2zero/NimBLE-Arduino.git",
|
||||
"https://github.com/avaksru/decoder.git"
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32_16mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32s3_16mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32c3m_4mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
338
src/modules/sensors/Ble_part1/Ble_p1.cpp
Normal file
338
src/modules/sensors/Ble_part1/Ble_p1.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
#include <Arduino.h>
|
||||
#include <NimBLEDevice.h>
|
||||
#define BLE_PART1
|
||||
// #define BLE_PART2
|
||||
#include <decoder.h>
|
||||
#include <vector>
|
||||
|
||||
// Создаем переменную для хранения данных с датчиков bluetooth
|
||||
// StaticJsonDocument<JSON_BUFFER_SIZE * 4> BLEbuffer;
|
||||
// DynamicJsonDocument extBLEdata(JSON_BUFFER_SIZE * 4);
|
||||
// JsonObject extBLEdata = BLEbuffer.to<JsonObject>();
|
||||
class BleSens;
|
||||
std::vector<BleSens *> BleSensArray;
|
||||
|
||||
class BleSens : public IoTItem
|
||||
{
|
||||
private:
|
||||
// описание параметров передаваемых из настроек датчика из веба
|
||||
String _MAC;
|
||||
String _sensor;
|
||||
int timeRecv = 0;
|
||||
int _minutesPassed = 0;
|
||||
String json = "{}";
|
||||
int orange = 0;
|
||||
int red = 0;
|
||||
int offline = 0;
|
||||
int _int;
|
||||
bool dataFromNode = false;
|
||||
|
||||
public:
|
||||
String whoIAm(/*String &mac, String &sens*/)
|
||||
{
|
||||
// mac = _MAC;
|
||||
// sens = _sensor;
|
||||
return _MAC;
|
||||
}
|
||||
|
||||
void setBLEdata(JsonObject extBLEdata)
|
||||
{
|
||||
if (_sensor == "last")
|
||||
{
|
||||
timeRecv = extBLEdata[_sensor].as<int>();
|
||||
char *s;
|
||||
s = TimeToString(millis() / 1000 - timeRecv / 1000);
|
||||
value.isDecimal = 0;
|
||||
if (timeRecv > 0)
|
||||
{
|
||||
value.valS = s;
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
else
|
||||
{
|
||||
value.valS = "";
|
||||
}
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
else
|
||||
{
|
||||
String valStr = extBLEdata[_sensor].as<String>();
|
||||
if (valStr != "null")
|
||||
{
|
||||
if (value.isDecimal == isDigitDotCommaStr(valStr))
|
||||
{
|
||||
value.isDecimal = 1;
|
||||
value.valD = valStr.toFloat();
|
||||
regEvent(value.valD, _id);
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
else
|
||||
{
|
||||
value.isDecimal = 0;
|
||||
value.valS = valStr;
|
||||
regEvent(value.valS, _id);
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
char *TimeToString(unsigned long t)
|
||||
{
|
||||
static char str[12];
|
||||
long h = t / 3600;
|
||||
t = t % 3600;
|
||||
int m = t / 60;
|
||||
int s = t % 60;
|
||||
sprintf(str, "%02ld:%02d:%02d", h, m, s);
|
||||
return str;
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
if (_sensor == "last")
|
||||
{
|
||||
char *s;
|
||||
s = TimeToString(millis() / 1000 - timeRecv / 1000);
|
||||
value.isDecimal = 0;
|
||||
if (timeRecv > 0)
|
||||
{
|
||||
value.valS = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.valS = "";
|
||||
}
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
_minutesPassed++;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
void onMqttWsAppConnectEvent()
|
||||
{
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
void setNewWidgetAttributes()
|
||||
{
|
||||
|
||||
int minutes_ = _minutesPassed * _int / 60;
|
||||
jsonWriteStr(json, F("info"), prettyMinutsTimeout(minutes_));
|
||||
if (dataFromNode)
|
||||
{
|
||||
if (orange != 0 && red != 0 && offline != 0)
|
||||
{
|
||||
if (minutes_ < orange)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), "");
|
||||
}
|
||||
if (minutes_ >= orange && minutes_ < red)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), F("orange")); // сделаем виджет оранжевым
|
||||
}
|
||||
if (minutes_ >= red && minutes_ < offline)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), F("red")); // сделаем виджет красным
|
||||
}
|
||||
if (minutes_ >= offline)
|
||||
{
|
||||
jsonWriteStr(json, F("info"), F("offline"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonWriteStr(json, F("info"), F("awaiting"));
|
||||
}
|
||||
sendSubWidgetsValues(_id, json);
|
||||
}
|
||||
|
||||
BleSens(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_MAC = jsonReadStr(parameters, "MAC");
|
||||
_sensor = jsonReadStr(parameters, "sensor");
|
||||
jsonRead(parameters, F("orange"), orange);
|
||||
jsonRead(parameters, F("red"), red);
|
||||
jsonRead(parameters, F("offline"), offline);
|
||||
jsonRead(parameters, F("int"), _int);
|
||||
dataFromNode = false;
|
||||
BleSensArray.push_back(this);
|
||||
}
|
||||
|
||||
~BleSens(){};
|
||||
};
|
||||
|
||||
//=======================================================================================================
|
||||
|
||||
/** Callback to process the results of the last scan or restart it */
|
||||
void scanEndedCB(NimBLEScanResults results)
|
||||
{
|
||||
int count = results.getCount();
|
||||
SerialPrint("i", F("BLE"), "Scan done! "); // +"Devices found: " + String(count));
|
||||
// pBLEScan->clearResults();
|
||||
}
|
||||
|
||||
class BleScan : public IoTItem, BLEAdvertisedDeviceCallbacks
|
||||
{
|
||||
private:
|
||||
// описание параметров передаваемых из настроек датчика из веба
|
||||
int _scanDuration;
|
||||
String _filter;
|
||||
bool _debug;
|
||||
|
||||
StaticJsonDocument<512> doc;
|
||||
BLEScan *pBLEScan;
|
||||
TheengsDecoder decoder;
|
||||
|
||||
public:
|
||||
std::string convertServiceData(std::string deviceServiceData)
|
||||
{
|
||||
int serviceDataLength = (int)deviceServiceData.length();
|
||||
char spr[2 * serviceDataLength + 1];
|
||||
for (int i = 0; i < serviceDataLength; i++)
|
||||
sprintf(spr + 2 * i, "%.2x", (unsigned char)deviceServiceData[i]);
|
||||
spr[2 * serviceDataLength] = 0;
|
||||
return spr;
|
||||
}
|
||||
|
||||
void onResult(BLEAdvertisedDevice *advertisedDevice)
|
||||
{
|
||||
JsonObject BLEdata = doc.to<JsonObject>();
|
||||
String mac_adress_ = advertisedDevice->getAddress().toString().c_str();
|
||||
mac_adress_.toUpperCase();
|
||||
BLEdata["id"] = (char *)mac_adress_.c_str();
|
||||
|
||||
if (advertisedDevice->haveName())
|
||||
{
|
||||
BLEdata["name"] = (char *)advertisedDevice->getName().c_str();
|
||||
}
|
||||
if (advertisedDevice->haveManufacturerData())
|
||||
{
|
||||
char *manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t *)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
|
||||
BLEdata["manufacturerdata"] = manufacturerdata;
|
||||
free(manufacturerdata);
|
||||
}
|
||||
if (advertisedDevice->haveRSSI())
|
||||
BLEdata["rssi"] = (int)advertisedDevice->getRSSI();
|
||||
if (advertisedDevice->haveTXPower())
|
||||
BLEdata["txpower"] = (int8_t)advertisedDevice->getTXPower();
|
||||
if (advertisedDevice->haveServiceData())
|
||||
{
|
||||
int serviceDataCount = advertisedDevice->getServiceDataCount();
|
||||
for (int j = 0; j < serviceDataCount; j++)
|
||||
{
|
||||
std::string service_data = convertServiceData(advertisedDevice->getServiceData(j));
|
||||
BLEdata["servicedata"] = (char *)service_data.c_str();
|
||||
std::string serviceDatauuid = advertisedDevice->getServiceDataUUID(j).toString();
|
||||
BLEdata["servicedatauuid"] = (char *)serviceDatauuid.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if (decoder.decodeBLEJson(BLEdata))
|
||||
{
|
||||
String mac_address = BLEdata["mac"].as<const char *>();
|
||||
if (mac_address == "")
|
||||
{
|
||||
BLEdata["mac"] = BLEdata["id"];
|
||||
mac_address = BLEdata["id"].as<const char *>();
|
||||
}
|
||||
mac_address.replace(":", "");
|
||||
if (_debug < 2)
|
||||
{
|
||||
BLEdata.remove("manufacturerdata");
|
||||
BLEdata.remove("servicedata");
|
||||
BLEdata.remove("type");
|
||||
BLEdata.remove("cidc");
|
||||
BLEdata.remove("acts");
|
||||
BLEdata.remove("cont");
|
||||
BLEdata.remove("track");
|
||||
BLEdata.remove("id");
|
||||
}
|
||||
// дописываем время прихода пакета данных
|
||||
BLEdata["last"] = millis();
|
||||
if (_debug)
|
||||
{
|
||||
if ((_filter != "" && BLEdata[_filter]) || _filter == "")
|
||||
{
|
||||
// for (JsonPair kv : BLEdata)
|
||||
// {
|
||||
// String val = BLEdata.as<String>();
|
||||
String output;
|
||||
if (_debug < 2)
|
||||
{
|
||||
BLEdata.remove("servicedatauuid");
|
||||
}
|
||||
serializeJson(BLEdata, output);
|
||||
SerialPrint("i", F("BLE"), mac_address + " " + output);
|
||||
//}
|
||||
}
|
||||
|
||||
SerialPrint("i", F("BLE"), "found: " + String(BLEdata["mac"].as<const char *>()));
|
||||
}
|
||||
|
||||
// Перебираем все зарегистрированные сенсоры BleSens
|
||||
for (std::vector<BleSens *>::iterator it = BleSensArray.begin();
|
||||
it != BleSensArray.end(); ++it)
|
||||
{
|
||||
// Если это данные для нужного сенсора (по его МАКУ)
|
||||
if ((*it)->whoIAm() == mac_address)
|
||||
// то передаем ему json, дальше он сам разберется
|
||||
(*it)->setBLEdata(BLEdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BleScan(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_scanDuration = jsonReadInt(parameters, "scanDuration");
|
||||
_filter = jsonReadStr(parameters, "filter");
|
||||
jsonRead(parameters, "debug", _debug);
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); // create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(this);
|
||||
pBLEScan->setActiveScan(false); // active scan uses more power, but get results faster
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99); // less or equal setInterval value
|
||||
pBLEScan->setMaxResults(0); // do not store the scan results, use callback only.
|
||||
}
|
||||
|
||||
// doByInterval()
|
||||
void doByInterval()
|
||||
{
|
||||
if (pBLEScan->isScanning() == false)
|
||||
{
|
||||
if (_scanDuration > 0)
|
||||
{
|
||||
SerialPrint("i", F("BLE"), "Start Scanning...");
|
||||
pBLEScan->start(_scanDuration, scanEndedCB, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~BleScan() { BleSensArray.clear(); };
|
||||
};
|
||||
|
||||
//=======================================================================================================
|
||||
|
||||
void *getAPI_Ble_part1(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("BleScan_p1"))
|
||||
{
|
||||
return new BleScan(param);
|
||||
}
|
||||
else if (subtype == F("BleSens_p1"))
|
||||
{
|
||||
return new BleSens(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
86
src/modules/sensors/Ble_part1/modinfo.json
Normal file
86
src/modules/sensors/Ble_part1/modinfo.json
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "bluetooth сканер",
|
||||
"type": "Reading",
|
||||
"subtype": "BleScan_p1",
|
||||
"id": "BleScan",
|
||||
"widget": "na",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 135,
|
||||
"scanDuration": 10,
|
||||
"filter": "servicedatauuid",
|
||||
"debug": 1
|
||||
},
|
||||
{
|
||||
"name": "bluetooth датчик",
|
||||
"type": "Reading",
|
||||
"subtype": "BleSens_p1",
|
||||
"id": "BleSens",
|
||||
"widget": "anydataDef",
|
||||
"page": "Сенсоры",
|
||||
"descr": "",
|
||||
"needSave": 0,
|
||||
"int": 30,
|
||||
"global": 0,
|
||||
"round": 1,
|
||||
"orange": 60,
|
||||
"red": 120,
|
||||
"offline": 180,
|
||||
"MAC": "",
|
||||
"sensor": ""
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "AVAKS, v3 - Mit4bmw",
|
||||
"authorContact": "https://t.me/@avaks, https://t.me/Mit4bmw",
|
||||
"authorGit": "https://github.com/avaksru, https://github.com/Mit4el",
|
||||
"specialThanks": "@Serghei63",
|
||||
"moduleName": "Ble_part1",
|
||||
"moduleVersion": "3.1",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 262796,
|
||||
"esp8266_4mb": 0
|
||||
},
|
||||
"subTypes": [
|
||||
"BleSens_p1",
|
||||
"BleScan_p1"
|
||||
],
|
||||
"title": "Сканер Bluetooth, часть 1",
|
||||
"moduleDesc": "Часть 1 популярных Bluetooth датчиков. Позволяет получить данные с Bluetooth часов и термометров Mijia, Xiaomi, Cleargrass, Qingping, Inkbird. Разделение на части сделано для уменьшения размера модуля. Обе части вместе не использовать! для всех датчиков модуль Ble! Полный список (учитывать отстование нашей версии библиотеки) https://decoder.theengs.io/devices/devices.html Наш перечень в файле lib/decoder/devices.h",
|
||||
"propInfo": {
|
||||
"round": "Округление после запятой.",
|
||||
"orange": "количество минут после которого окрасить виджет в оранжевый цвет",
|
||||
"red": "количество минут после которого окрасить виджет в красный цвет",
|
||||
"offline": "количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится",
|
||||
"int": "Интервал сканирования BLE окружения (BleScan) / В BleSens темп обновления времнени поступления данных, сами даные обновляются по мене сканирования/поступления",
|
||||
"scanDuration": "Длительность сканирования ",
|
||||
"filter": "Позволяет установить фильтр по параметру передаваемому датчиком. Влияет только на вывод лога при debug=1, что бы было легче найти датчики, если много устройств в эфире",
|
||||
"MAC": "MAC адрес беспроводного датчика",
|
||||
"sensor": "Тип сенсора: температура / влажность / время / ... "
|
||||
}
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32_16mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32s3_16mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32c3m_4mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
]
|
||||
}
|
||||
}
|
||||
339
src/modules/sensors/Ble_part2/Ble_p2.cpp
Normal file
339
src/modules/sensors/Ble_part2/Ble_p2.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
#include <Arduino.h>
|
||||
#include <NimBLEDevice.h>
|
||||
// #define BLE_PART1
|
||||
#define BLE_PART2
|
||||
#include <decoder.h>
|
||||
#include <vector>
|
||||
|
||||
// Создаем переменную для хранения данных с датчиков bluetooth
|
||||
// StaticJsonDocument<JSON_BUFFER_SIZE * 4> BLEbuffer;
|
||||
// DynamicJsonDocument extBLEdata(JSON_BUFFER_SIZE * 4);
|
||||
// JsonObject extBLEdata = BLEbuffer.to<JsonObject>();
|
||||
class BleSens;
|
||||
std::vector<BleSens *> BleSensArray;
|
||||
|
||||
class BleSens : public IoTItem
|
||||
{
|
||||
private:
|
||||
// описание параметров передаваемых из настроек датчика из веба
|
||||
String _MAC;
|
||||
String _sensor;
|
||||
int timeRecv = 0;
|
||||
int _minutesPassed = 0;
|
||||
String json = "{}";
|
||||
int orange = 0;
|
||||
int red = 0;
|
||||
int offline = 0;
|
||||
int _int;
|
||||
bool dataFromNode = false;
|
||||
|
||||
public:
|
||||
String whoIAm(/*String &mac, String &sens*/)
|
||||
{
|
||||
// mac = _MAC;
|
||||
// sens = _sensor;
|
||||
return _MAC;
|
||||
}
|
||||
|
||||
void setBLEdata(JsonObject extBLEdata)
|
||||
{
|
||||
if (_sensor == "last")
|
||||
{
|
||||
timeRecv = extBLEdata[_sensor].as<int>();
|
||||
char *s;
|
||||
s = TimeToString(millis() / 1000 - timeRecv / 1000);
|
||||
value.isDecimal = 0;
|
||||
if (timeRecv > 0)
|
||||
{
|
||||
value.valS = s;
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
else
|
||||
{
|
||||
value.valS = "";
|
||||
}
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
else
|
||||
{
|
||||
String valStr = extBLEdata[_sensor].as<String>();
|
||||
if (valStr != "null")
|
||||
{
|
||||
if (value.isDecimal == isDigitDotCommaStr(valStr))
|
||||
{
|
||||
value.isDecimal = 1;
|
||||
value.valD = valStr.toFloat();
|
||||
regEvent(value.valD, _id);
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
else
|
||||
{
|
||||
value.isDecimal = 0;
|
||||
value.valS = valStr;
|
||||
regEvent(value.valS, _id);
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
char *TimeToString(unsigned long t)
|
||||
{
|
||||
static char str[12];
|
||||
long h = t / 3600;
|
||||
t = t % 3600;
|
||||
int m = t / 60;
|
||||
int s = t % 60;
|
||||
sprintf(str, "%02ld:%02d:%02d", h, m, s);
|
||||
return str;
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
if (_sensor == "last")
|
||||
{
|
||||
char *s;
|
||||
s = TimeToString(millis() / 1000 - timeRecv / 1000);
|
||||
value.isDecimal = 0;
|
||||
if (timeRecv > 0)
|
||||
{
|
||||
value.valS = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.valS = "";
|
||||
}
|
||||
regEvent(value.valS, _id);
|
||||
}
|
||||
_minutesPassed++;
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
void onMqttWsAppConnectEvent()
|
||||
{
|
||||
setNewWidgetAttributes();
|
||||
}
|
||||
void setNewWidgetAttributes()
|
||||
{
|
||||
|
||||
int minutes_ = _minutesPassed * _int / 60;
|
||||
jsonWriteStr(json, F("info"), prettyMinutsTimeout(minutes_));
|
||||
if (dataFromNode)
|
||||
{
|
||||
if (orange != 0 && red != 0 && offline != 0)
|
||||
{
|
||||
if (minutes_ < orange)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), "");
|
||||
}
|
||||
if (minutes_ >= orange && minutes_ < red)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), F("orange")); // сделаем виджет оранжевым
|
||||
}
|
||||
if (minutes_ >= red && minutes_ < offline)
|
||||
{
|
||||
jsonWriteStr(json, F("color"), F("red")); // сделаем виджет красным
|
||||
}
|
||||
if (minutes_ >= offline)
|
||||
{
|
||||
jsonWriteStr(json, F("info"), F("offline"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonWriteStr(json, F("info"), F("awaiting"));
|
||||
}
|
||||
sendSubWidgetsValues(_id, json);
|
||||
}
|
||||
|
||||
BleSens(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_MAC = jsonReadStr(parameters, "MAC");
|
||||
_sensor = jsonReadStr(parameters, "sensor");
|
||||
jsonRead(parameters, F("orange"), orange);
|
||||
jsonRead(parameters, F("red"), red);
|
||||
jsonRead(parameters, F("offline"), offline);
|
||||
jsonRead(parameters, F("int"), _int);
|
||||
dataFromNode = false;
|
||||
BleSensArray.push_back(this);
|
||||
}
|
||||
|
||||
~BleSens(){};
|
||||
};
|
||||
|
||||
//=======================================================================================================
|
||||
|
||||
/** Callback to process the results of the last scan or restart it */
|
||||
void scanEndedCB(NimBLEScanResults results)
|
||||
{
|
||||
int count = results.getCount();
|
||||
SerialPrint("i", F("BLE"), "Scan done! "); // +"Devices found: " + String(count));
|
||||
// pBLEScan->clearResults();
|
||||
}
|
||||
|
||||
class BleScan : public IoTItem, BLEAdvertisedDeviceCallbacks
|
||||
{
|
||||
private:
|
||||
// описание параметров передаваемых из настроек датчика из веба
|
||||
int _scanDuration;
|
||||
String _filter;
|
||||
bool _debug;
|
||||
|
||||
StaticJsonDocument<512> doc;
|
||||
BLEScan *pBLEScan;
|
||||
TheengsDecoder decoder;
|
||||
|
||||
public:
|
||||
std::string convertServiceData(std::string deviceServiceData)
|
||||
{
|
||||
int serviceDataLength = (int)deviceServiceData.length();
|
||||
char spr[2 * serviceDataLength + 1];
|
||||
for (int i = 0; i < serviceDataLength; i++)
|
||||
sprintf(spr + 2 * i, "%.2x", (unsigned char)deviceServiceData[i]);
|
||||
spr[2 * serviceDataLength] = 0;
|
||||
return spr;
|
||||
}
|
||||
|
||||
void onResult(BLEAdvertisedDevice *advertisedDevice)
|
||||
{
|
||||
JsonObject BLEdata = doc.to<JsonObject>();
|
||||
String mac_adress_ = advertisedDevice->getAddress().toString().c_str();
|
||||
mac_adress_.toUpperCase();
|
||||
BLEdata["id"] = (char *)mac_adress_.c_str();
|
||||
|
||||
if (advertisedDevice->haveName())
|
||||
{
|
||||
BLEdata["name"] = (char *)advertisedDevice->getName().c_str();
|
||||
}
|
||||
if (advertisedDevice->haveManufacturerData())
|
||||
{
|
||||
char *manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t *)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
|
||||
BLEdata["manufacturerdata"] = manufacturerdata;
|
||||
free(manufacturerdata);
|
||||
}
|
||||
if (advertisedDevice->haveRSSI())
|
||||
BLEdata["rssi"] = (int)advertisedDevice->getRSSI();
|
||||
if (advertisedDevice->haveTXPower())
|
||||
BLEdata["txpower"] = (int8_t)advertisedDevice->getTXPower();
|
||||
if (advertisedDevice->haveServiceData())
|
||||
{
|
||||
int serviceDataCount = advertisedDevice->getServiceDataCount();
|
||||
for (int j = 0; j < serviceDataCount; j++)
|
||||
{
|
||||
std::string service_data = convertServiceData(advertisedDevice->getServiceData(j));
|
||||
BLEdata["servicedata"] = (char *)service_data.c_str();
|
||||
std::string serviceDatauuid = advertisedDevice->getServiceDataUUID(j).toString();
|
||||
BLEdata["servicedatauuid"] = (char *)serviceDatauuid.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if (decoder.decodeBLEJson(BLEdata))
|
||||
{
|
||||
String mac_address = BLEdata["mac"].as<const char *>();
|
||||
if (mac_address == "")
|
||||
{
|
||||
BLEdata["mac"] = BLEdata["id"];
|
||||
mac_address = BLEdata["id"].as<const char *>();
|
||||
}
|
||||
mac_address.replace(":", "");
|
||||
if (_debug < 2)
|
||||
{
|
||||
BLEdata.remove("manufacturerdata");
|
||||
BLEdata.remove("servicedata");
|
||||
BLEdata.remove("type");
|
||||
BLEdata.remove("cidc");
|
||||
BLEdata.remove("acts");
|
||||
BLEdata.remove("cont");
|
||||
BLEdata.remove("track");
|
||||
BLEdata.remove("id");
|
||||
}
|
||||
|
||||
// дописываем время прихода пакета данных
|
||||
BLEdata["last"] = millis();
|
||||
if (_debug)
|
||||
{
|
||||
if ((_filter != "" && BLEdata[_filter]) || _filter == "")
|
||||
{
|
||||
// for (JsonPair kv : BLEdata)
|
||||
// {
|
||||
// String val = BLEdata.as<String>();
|
||||
String output;
|
||||
if (_debug < 2)
|
||||
{
|
||||
BLEdata.remove("servicedatauuid");
|
||||
}
|
||||
serializeJson(BLEdata, output);
|
||||
SerialPrint("i", F("BLE"), mac_address + " " + output);
|
||||
//}
|
||||
}
|
||||
|
||||
SerialPrint("i", F("BLE"), "found: " + String(BLEdata["mac"].as<const char *>()));
|
||||
}
|
||||
|
||||
// Перебираем все зарегистрированные сенсоры BleSens
|
||||
for (std::vector<BleSens *>::iterator it = BleSensArray.begin();
|
||||
it != BleSensArray.end(); ++it)
|
||||
{
|
||||
// Если это данные для нужного сенсора (по его МАКУ)
|
||||
if ((*it)->whoIAm() == mac_address)
|
||||
// то передаем ему json, дальше он сам разберется
|
||||
(*it)->setBLEdata(BLEdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BleScan(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_scanDuration = jsonReadInt(parameters, "scanDuration");
|
||||
_filter = jsonReadStr(parameters, "filter");
|
||||
jsonRead(parameters, "debug", _debug);
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); // create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(this);
|
||||
pBLEScan->setActiveScan(false); // active scan uses more power, but get results faster
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99); // less or equal setInterval value
|
||||
pBLEScan->setMaxResults(0); // do not store the scan results, use callback only.
|
||||
}
|
||||
|
||||
// doByInterval()
|
||||
void doByInterval()
|
||||
{
|
||||
if (pBLEScan->isScanning() == false)
|
||||
{
|
||||
if (_scanDuration > 0)
|
||||
{
|
||||
SerialPrint("i", F("BLE"), "Start Scanning...");
|
||||
pBLEScan->start(_scanDuration, scanEndedCB, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~BleScan() { BleSensArray.clear(); };
|
||||
};
|
||||
|
||||
//=======================================================================================================
|
||||
|
||||
void *getAPI_Ble_part2(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("BleScan_p2"))
|
||||
{
|
||||
return new BleScan(param);
|
||||
}
|
||||
else if (subtype == F("BleSens_p2"))
|
||||
{
|
||||
return new BleSens(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
86
src/modules/sensors/Ble_part2/modinfo.json
Normal file
86
src/modules/sensors/Ble_part2/modinfo.json
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "bluetooth сканер",
|
||||
"type": "Reading",
|
||||
"subtype": "BleScan_p2",
|
||||
"id": "BleScan",
|
||||
"widget": "na",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"int": 135,
|
||||
"scanDuration": 10,
|
||||
"filter": "servicedatauuid",
|
||||
"debug": 1
|
||||
},
|
||||
{
|
||||
"name": "bluetooth датчик",
|
||||
"type": "Reading",
|
||||
"subtype": "BleSens_p2",
|
||||
"id": "BleSens",
|
||||
"widget": "anydataDef",
|
||||
"page": "Сенсоры",
|
||||
"descr": "",
|
||||
"needSave": 0,
|
||||
"int": 30,
|
||||
"global": 0,
|
||||
"round": 1,
|
||||
"orange": 60,
|
||||
"red": 120,
|
||||
"offline": 180,
|
||||
"MAC": "",
|
||||
"sensor": ""
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "AVAKS, v3 - Mit4bmw",
|
||||
"authorContact": "https://t.me/@avaks, https://t.me/Mit4bmw",
|
||||
"authorGit": "https://github.com/avaksru, https://github.com/Mit4el",
|
||||
"specialThanks": "@Serghei63",
|
||||
"moduleName": "Ble_part2",
|
||||
"moduleVersion": "3.1",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 288972,
|
||||
"esp8266_4mb": 0
|
||||
},
|
||||
"subTypes": [
|
||||
"BleSens_p2",
|
||||
"BleScan_p2"
|
||||
],
|
||||
"title": "Сканер Bluetooth, часть 2",
|
||||
"moduleDesc": "Часть 2 Bluetooth датчиков. Позволяет получить данные с Bluetooth датчиков, кроме Mijia, Xiaomi, Cleargrass, Qingping, Inkbird. Обе части вместе не использовать! для всех датчиков модуль Ble! Полный список (учитывать отстование нашей версии библиотеки) https://decoder.theengs.io/devices/devices.html Наш перечень в файле lib/decoder/devices.h",
|
||||
"propInfo": {
|
||||
"round": "Округление после запятой.",
|
||||
"orange": "количество минут после которого окрасить виджет в оранжевый цвет",
|
||||
"red": "количество минут после которого окрасить виджет в красный цвет",
|
||||
"offline": "количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится",
|
||||
"int": "Интервал сканирования BLE окружения (BleScan) / В BleSens темп обновления времнени поступления данных, сами даные обновляются по мене сканирования/поступления",
|
||||
"scanDuration": "Длительность сканирования ",
|
||||
"filter": "Позволяет установить фильтр по параметру передаваемому датчиком. Влияет только на вывод лога при debug=1, что бы было легче найти датчики, если много устройств в эфире",
|
||||
"MAC": "MAC адрес беспроводного датчика",
|
||||
"sensor": "Тип сенсора: температура / влажность / время / ... "
|
||||
}
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32_16mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32s3_16mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
],
|
||||
"esp32c3m_4mb": [
|
||||
"https://github.com/Mit4el/NimBLE-Arduino.git"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -83,6 +83,12 @@
|
||||
"esp32_4mb": [
|
||||
"adafruit/Adafruit BME280 Library"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"adafruit/Adafruit BME280 Library"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"adafruit/Adafruit BME280 Library"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"adafruit/Adafruit BME280 Library"
|
||||
],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -55,6 +55,12 @@
|
||||
"esp32_4mb": [
|
||||
"adafruit/Adafruit BMP280 Library"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"adafruit/Adafruit BMP280 Library"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"adafruit/Adafruit BMP280 Library"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"adafruit/Adafruit BMP280 Library"
|
||||
],
|
||||
@@ -64,9 +70,6 @@
|
||||
"esp8266_1mb_ota": [
|
||||
"adafruit/Adafruit BMP280 Library"
|
||||
],
|
||||
"esp8266_2mb": [
|
||||
"adafruit/Adafruit BMP280 Library"
|
||||
],
|
||||
"esp8285_1mb": [
|
||||
"adafruit/Adafruit BMP280 Library"
|
||||
],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -36,6 +36,8 @@
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -55,6 +55,12 @@
|
||||
"esp32_4mb": [
|
||||
"beegee-tokyo/DHT sensor library for ESPx"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"beegee-tokyo/DHT sensor library for ESPx"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"beegee-tokyo/DHT sensor library for ESPx"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"beegee-tokyo/DHT sensor library for ESPx"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -42,6 +42,12 @@
|
||||
"esp32_4mb": [
|
||||
"https://github.com/milesburton/Arduino-Temperature-Control-Library"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/milesburton/Arduino-Temperature-Control-Library"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/milesburton/Arduino-Temperature-Control-Library"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/milesburton/Arduino-Temperature-Control-Library"
|
||||
],
|
||||
|
||||
178
src/modules/sensors/Ds2423/Ds2423.cpp
Normal file
178
src/modules/sensors/Ds2423/Ds2423.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
#include <DS2423.h>
|
||||
#include <OneWire.h>
|
||||
#include <map>
|
||||
|
||||
typedef uint8_t DeviceAddressDS2423[8];
|
||||
// глобальные списки необходимы для хранения объектов об активных линиях 1-wire используемых разными датчиками из модуля. Ключ - номер пина
|
||||
std::map<int, OneWire *> oneWireDS2423Array;
|
||||
|
||||
// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку
|
||||
OneWire *instanceOneWire_DS2423(uint8_t ONE_WIRE_PIN)
|
||||
{
|
||||
// учитываем, что библиотека может работать с несколькими линиями на разных пинах, поэтому инициируем библиотеку, если линия ранее не использовалась
|
||||
if (oneWireDS2423Array.find(ONE_WIRE_PIN) == oneWireDS2423Array.end())
|
||||
oneWireDS2423Array[ONE_WIRE_PIN] = new OneWire((uint8_t)ONE_WIRE_PIN);
|
||||
return oneWireDS2423Array[ONE_WIRE_PIN];
|
||||
}
|
||||
|
||||
// Определяем адрес.
|
||||
bool getDeviceAddressDS2423(uint8_t pin, uint8_t *deviceAddress, int index)
|
||||
{
|
||||
OneWire *_wire = instanceOneWire_DS2423(pin);
|
||||
uint8_t depth = 0;
|
||||
_wire->reset_search();
|
||||
while (depth <= index && _wire->search(deviceAddress))
|
||||
{
|
||||
if (depth == index && _wire->crc8((uint8_t *)deviceAddress, 7) == deviceAddress[7])
|
||||
return true;
|
||||
depth++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
class Ds2423a : public IoTItem
|
||||
{
|
||||
private:
|
||||
// описание параметров передаваемых из настроек датчика из веба
|
||||
String _addr;
|
||||
int _pin;
|
||||
int _index;
|
||||
DS2423 *ds2423;
|
||||
DeviceAddressDS2423 _deviceAddress;
|
||||
|
||||
public:
|
||||
Ds2423a(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
|
||||
jsonRead(parameters, "pin", _pin);
|
||||
jsonRead(parameters, "index", _index, false);
|
||||
jsonRead(parameters, "addr", _addr, false);
|
||||
|
||||
// Определяем адрес. Если параметр addr не установлен, то узнаем адрес по индексу
|
||||
if (_addr == "")
|
||||
{
|
||||
if (getDeviceAddressDS2423(_pin, _deviceAddress, _index))
|
||||
{
|
||||
char addrStr[20] = "";
|
||||
hex2string(_deviceAddress, 8, addrStr);
|
||||
SerialPrint("I", "Sensor " + (String)_id, "index: " + (String)_index + " addr: " + String(addrStr));
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("E", "Sensor " + (String)_id, "index: " + (String)_index + " addres not search");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string2hex(_addr.c_str(), _deviceAddress);
|
||||
}
|
||||
|
||||
ds2423 = new DS2423(instanceOneWire_DS2423(_pin), _deviceAddress);
|
||||
ds2423->begin(DS2423_COUNTER_A | DS2423_COUNTER_B);
|
||||
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
ds2423->update();
|
||||
if (ds2423->isError())
|
||||
{
|
||||
Serial.println("Error reading counter");
|
||||
}
|
||||
else
|
||||
{
|
||||
value.valD = ds2423->getCount(DS2423_COUNTER_A);
|
||||
// if (value.valD != -127)
|
||||
regEvent(value.valD, "Ds2423a"); // обязательный вызов для отправки результата работы
|
||||
// else
|
||||
// SerialPrint("E", "Sensor Ds2423a", "Error");
|
||||
}
|
||||
}
|
||||
//=======================================================================================================
|
||||
|
||||
~Ds2423a(){};
|
||||
};
|
||||
|
||||
class Ds2423b : public IoTItem
|
||||
{
|
||||
private:
|
||||
// описание параметров передаваемых из настроек датчика из веба
|
||||
String _addr;
|
||||
int _pin;
|
||||
int _index;
|
||||
DS2423 *ds2423;
|
||||
DeviceAddressDS2423 _deviceAddress;
|
||||
|
||||
public:
|
||||
Ds2423b(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
jsonRead(parameters, "pin", _pin);
|
||||
jsonRead(parameters, "index", _index, false);
|
||||
jsonRead(parameters, "addr", _addr, false);
|
||||
|
||||
// Определяем адрес. Если параметр addr не установлен, то узнаем адрес по индексу
|
||||
if (_addr == "")
|
||||
{
|
||||
if (getDeviceAddressDS2423(_pin, _deviceAddress, _index))
|
||||
{
|
||||
char addrStr[20] = "";
|
||||
hex2string(_deviceAddress, 8, addrStr);
|
||||
SerialPrint("I", "Sensor " + (String)_id, "index: " + (String)_index + " addr: " + String(addrStr));
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialPrint("E", "Sensor " + (String)_id, "index: " + (String)_index + " addres not search");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string2hex(_addr.c_str(), _deviceAddress);
|
||||
}
|
||||
|
||||
ds2423 = new DS2423(instanceOneWire_DS2423(_pin), _deviceAddress);
|
||||
ds2423->begin(DS2423_COUNTER_A | DS2423_COUNTER_B);
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
ds2423->update();
|
||||
|
||||
if (ds2423->isError())
|
||||
{
|
||||
Serial.println("Error reading counter");
|
||||
}
|
||||
else
|
||||
{
|
||||
// запускаем опрос измерений у всех датчиков на линии
|
||||
value.valD = ds2423->getCount(DS2423_COUNTER_B);
|
||||
// if (value.valD != -127)
|
||||
regEvent(value.valD, "Ds2423b"); // обязательный вызов для отправки результата работы
|
||||
// else
|
||||
// SerialPrint("E", "Sensor Ds2423b", "Error");
|
||||
}
|
||||
}
|
||||
//=======================================================================================================
|
||||
|
||||
~Ds2423b(){};
|
||||
};
|
||||
|
||||
void *getAPI_Ds2423(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("Ds2423a"))
|
||||
{
|
||||
return new Ds2423a(param);
|
||||
}
|
||||
else if (subtype == F("Ds2423b"))
|
||||
{
|
||||
return new Ds2423b(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
80
src/modules/sensors/Ds2423/modinfo.json
Normal file
80
src/modules/sensors/Ds2423/modinfo.json
Normal file
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"menuSection": "executive_devices",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "DS2423 счетчик 1",
|
||||
"type": "Reading",
|
||||
"subtype": "Ds2423a",
|
||||
"id": "dscounta",
|
||||
"widget": "anydataDef",
|
||||
"page": "Счетчики",
|
||||
"descr": "DS1 V",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"pin": 4,
|
||||
"index": 0,
|
||||
"addr": "",
|
||||
"int": 10,
|
||||
"round": 0,
|
||||
"needSave": 0
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"name": "DS2423 счетчик 2",
|
||||
"type": "Reading",
|
||||
"subtype": "Ds2423b",
|
||||
"id": "dscountb",
|
||||
"widget": "anydataDef",
|
||||
"page": "Счетчики",
|
||||
"descr": "DS2 V",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"pin": 4,
|
||||
"index": 0,
|
||||
"addr": "",
|
||||
"int": 10,
|
||||
"round": 0,
|
||||
"needSave": 0
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Serghei Crasnicov",
|
||||
"authorContact": "https://t.me/Serghei63",
|
||||
"authorGit": "https://github.com/Serghei63",
|
||||
"specialThanks": "Bubnov Mikhail @Mit4bmw",
|
||||
"moduleName": "Ds2423",
|
||||
"moduleVersion": "1.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"title": "Cчетчик ds2423",
|
||||
"moduleDesc": "Позволяет получить значения с Ds2423.",
|
||||
"propInfo": {
|
||||
"pin": "GPIO номер, к которому подключена шина данных датчиков.",
|
||||
"index": "Порядковый номер датчика на шине.",
|
||||
"addr": "Адрес датчика на шине для точной идентификации. Если оставить пустым, то попробует найти по индексу и пину и Можно скопировать из консоли.",
|
||||
"int": "Количество секунд между опросами датчика."
|
||||
}
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/jbechter/arduino-onewire-DS2423",
|
||||
"paulstoffregen/OneWire @ ^2.3.7"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/jbechter/arduino-onewire-DS2423",
|
||||
"paulstoffregen/OneWire @ ^2.3.7"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/jbechter/arduino-onewire-DS2423",
|
||||
"paulstoffregen/OneWire @ ^2.3.7"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/jbechter/arduino-onewire-DS2423",
|
||||
"paulstoffregen/OneWire @ ^2.3.7"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -62,6 +62,12 @@
|
||||
"esp32_4mb": [
|
||||
"openenergymonitor/EmonLib@1.1.0"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"openenergymonitor/EmonLib@1.1.0"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"openenergymonitor/EmonLib@1.1.0"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"openenergymonitor/EmonLib@1.1.0"
|
||||
]
|
||||
|
||||
243
src/modules/sensors/ExampleModule/ExampleModule.cpp
Normal file
243
src/modules/sensors/ExampleModule/ExampleModule.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
//=======================================================================================================
|
||||
// Это файл сенсора, в нем осуществляется чтение сенсора.
|
||||
// для добавления сенсора вам нужно скопировать этот файл и заменить в нем текст ExamleModule на название вашего сенсора
|
||||
// Название должно быть уникальным, коротким и отражать суть сенсора.
|
||||
|
||||
// Обязательные библиотеки из ядра IoTM
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
//! Здесь подключаем стороннюю библиотеку при необходимости (ExternalLibrary заменить)
|
||||
// #include <ExternalLibrary.h>
|
||||
#include "ExternalLibrary.h" //удалить, только для примера. Внешние библиотеки правильно в <>
|
||||
|
||||
//! Объяевляем класс IoTGpio для работы с GPIO
|
||||
extern IoTGpio IoTgpio;
|
||||
|
||||
//=========================================================================================================
|
||||
//=========================================================================================================
|
||||
// Объявление сторонней библиотекит с использованием глобавльных объектов
|
||||
//=======================================================================================================
|
||||
//! Объявляем стороннюю библиотеку при необходимости (ExternalLibrary заменить)
|
||||
// !!! ЗДЕСЬ И ДАЛЕЕ libXX НАЗВАТЬ уникальным именем)
|
||||
ExternalLibrary *libXX = nullptr;
|
||||
|
||||
// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку
|
||||
// instanceLibXX НАЗВАТЬ по наименованию модуля (instanceДатчикХ)
|
||||
// ПРИ НЕОБХДИМОСТИ передаем любые нужные параметры для инициализации библиотеки (в данном случае PIN)
|
||||
//
|
||||
// !!! ВЫзвать данную функцию нужно хотябы один раз,
|
||||
// но в каждом конструкторе класса модуля ExampleModule_A, ExampleModule_B и т.д.
|
||||
// или можно вывывать постоянно при обращении к библиотеке, типа: instanceLibXX().READ_LIB_DATA_OTHER();
|
||||
//
|
||||
// !!!!!! В деструкторах ~ExampleModule_B() и ~ExampleModule_A() надо УДАЛЯТЬ объект libXX, ЕСЛИ в функцию instanceLibXX чтото передается.
|
||||
// Удаляем класс библиотеки, а то при переконфигурации в нем не поменяются PIN и дугие параметры передаваемые в библиотеку.
|
||||
// P.S. Не для всех, если используется map или vector то лучше не надо.
|
||||
|
||||
ExternalLibrary *instanceLibXX(int pin)
|
||||
{
|
||||
if (!libXX)
|
||||
{ // Если библиотека ранее инициализировалась, то просто вернем указатель
|
||||
// Инициализируем библиотеку
|
||||
libXX = new ExternalLibrary();
|
||||
libXX->begin(pin); // При необходимости делаем begin библиотеке
|
||||
}
|
||||
return libXX;
|
||||
}
|
||||
|
||||
//=========================================================================================================
|
||||
//=========================================================================================================
|
||||
// Первый класс модуля для определения 1-го элемента (параметра)
|
||||
// Служит для запроса и отображения парметра/элемента датчика
|
||||
// IoTManager система модульная: один парметр - один элемент (класс)
|
||||
//
|
||||
//=========================================================================================================
|
||||
//=========================================================================================================
|
||||
// ребенок - родитель
|
||||
class ExampleModule_A : public IoTItem
|
||||
{
|
||||
private:
|
||||
//=======================================================================================================
|
||||
// Секция переменных.
|
||||
// Это секция где Вы можете объявлять переменные и объекты arduino библиотек, что бы
|
||||
// впоследствии использовать их в loop и setup
|
||||
unsigned int _pin;
|
||||
// пользовательская переменная, в данном случае для считывания аналогового сигнала
|
||||
int adc;
|
||||
|
||||
public:
|
||||
//=======================================================================================================
|
||||
// setup()
|
||||
// это аналог setup из arduino. Здесь вы можете выполнять методы инициализации сенсора.
|
||||
// Такие как ...begin и подставлять в них параметры полученные из web интерфейса.
|
||||
// Все параметры хранятся в перемененной parameters, вы можете прочитать любой параметр используя jsonRead функции:
|
||||
// jsonReadStr, jsonReadBool, jsonReadInt
|
||||
ExampleModule_A(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
// Читаем пользовательскую переменную PIN, должна быть объявлена в в modeinfo.json
|
||||
_pin = jsonReadInt(parameters, "pin");
|
||||
// другой вариант чтения парметров модуля
|
||||
jsonRead(parameters, F("int"), _interval, false);
|
||||
}
|
||||
|
||||
//=======================================================================================================
|
||||
// doByInterval() - основная функция периодической работы
|
||||
// это аналог loop из arduino, но вызываемый каждые int секунд, заданные в настройках. Здесь вы должны выполнить чтение вашего сенсора
|
||||
// а затем выполнить regEvent - это регистрация произошедшего события чтения
|
||||
// здесь так же доступны все переменные из секции переменных, и полученные в setup
|
||||
// если у сенсора несколько величин то делайте несколько regEvent
|
||||
// не используйте delay - помните, что данный loop общий для всех модулей. Если у вас планируется длительная операция, постарайтесь разбить ее на порции
|
||||
// и выполнить за несколько тактов
|
||||
void doByInterval()
|
||||
{
|
||||
// Пример получения данных из библиотеки, где READ_LIB_DATA_OTHER - функция Вашей библиотеки
|
||||
value.valD = instanceLibXX(_pin)->READ_LIB_DATA_OTHER();
|
||||
// Здесь Наменование произвольным но понятным к какому модулю относится
|
||||
regEvent(value.valD, "ExampleModule"); // обязательный вызов хотяб один для регистрации события в ядре IoTM
|
||||
}
|
||||
|
||||
//=======================================================================================================
|
||||
// loop(), если не нужно переопределять, удалить.
|
||||
// полный аналог loop() из arduino. Нужно помнить, что все модули имеют равный поочередный доступ к центральному loop(), поэтому, необходимо следить
|
||||
// за задержками в алгоритме и не создавать пауз. Кроме того, данная версия перегружает родительскую, поэтому doByInterval() отключается, если
|
||||
// не повторить механизм расчета интервалов.
|
||||
void loop()
|
||||
{
|
||||
// Пример получения данных с аналоговым датчиком
|
||||
adc = IoTgpio.analogRead(_pin);
|
||||
|
||||
// Блок вызова doByInterval, так как если определили loop, то сам он не вызовится
|
||||
IoTItem::loop();
|
||||
}
|
||||
|
||||
~ExampleModule_A()
|
||||
{
|
||||
// Удаляем класс библиотеки, а то при переконфигурации в нем не поменяются PIN и дугие параметры передаваемые в библиотеку.
|
||||
delete libXX;
|
||||
libXX = nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
//=========================================================================================================
|
||||
//=========================================================================================================
|
||||
// Второй класс модуля для определения 2-го элемента (параметра)
|
||||
// Делается по аналогии с первым классом, служит для запроса и отображения другого парметра если их несколько с одного датчика
|
||||
// IoTManager система модульная: один парметр - один элемент (класс)
|
||||
//
|
||||
// Содержит описание дополнительных методов onModuleOrder и execute
|
||||
//=========================================================================================================
|
||||
//=========================================================================================================
|
||||
class ExampleModule_B : public IoTItem
|
||||
{
|
||||
private:
|
||||
// Пользовательские переменные
|
||||
unsigned int _pin;
|
||||
|
||||
public:
|
||||
ExampleModule_B(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
// Читаем пользовательскую переменную PIN, должна быть объявлена в в modeinfo.json
|
||||
_pin = jsonReadInt(parameters, "pin");
|
||||
// Можно инициализировать библиотеку один раз, а потом используем указатель
|
||||
instanceLibXX(_pin);
|
||||
libXX->READ_LIB_DATA_OTHER();
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
// Пример получения данных из библиотеки, где READ_LIB_DATA_OTHER - функция Вашей библиотеки
|
||||
value.valD = libXX->READ_LIB_DATA_OTHER();
|
||||
// Здесь Наменование произвольным но понятным к какому модулю относится
|
||||
regEvent(value.valD, "ExampleModule"); // обязательный вызов хотяб один для регистрации события в ядре IoTM
|
||||
}
|
||||
|
||||
//================ обработка кнопок из конфигурации ===================
|
||||
// Хук (переопределение виртуальной функции) для обработки кнопки (в value будут данные с собственной панели ввода)
|
||||
// Что бы кнопка была без поля ввода, нужно в modeinfo.json указать "btn-Example": nil
|
||||
void onModuleOrder(String &key, String &value)
|
||||
{
|
||||
if (key == "Example") // название кнопки btn-Example
|
||||
{
|
||||
SerialPrint("i", F("Sensor ExampleModule"), "User run calibration " + value);
|
||||
// ЧТО ТО Делаем
|
||||
}
|
||||
}
|
||||
|
||||
//================ обработка команд из сценария===================
|
||||
// Хук (переопределение виртуальной функции) для обработки команды из сценария (в param будут даныые переданные в функции в сценарии)
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m)
|
||||
{
|
||||
if (command == "expampleFunc")
|
||||
{
|
||||
if (param.size())
|
||||
{
|
||||
// Забираем данные из другого модуля по его ID
|
||||
// Если в сценарии передадим id модуля
|
||||
String value = getItemValue(param[0].valS);
|
||||
// Что то делаем с этим параметром
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (command == "expample2")
|
||||
{
|
||||
if (param.size() == 2)
|
||||
{
|
||||
SerialPrint("i", F("Sensor ExampleModule"), "expample2: " + param[0].valS + ", " + param[1].valS);
|
||||
// Передаем полученные данные на дальнейшую обработку
|
||||
// foo(param[0].valS, param[1].valS);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (command == "expampleAny")
|
||||
{
|
||||
if (param.size() >= 1)
|
||||
{
|
||||
int sizeOfParam = param.size();
|
||||
for (unsigned int i = 0; i < sizeOfParam; i++)
|
||||
{
|
||||
SerialPrint("i", F("Sensor ExampleModule"), "expampleAny: " + param[i].valS);
|
||||
// Что то делаем с каждым принятым значением
|
||||
// foo(param[i].valD);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void foo(String logid, String value)
|
||||
{
|
||||
// Прсото пример кокой-то функции
|
||||
}
|
||||
|
||||
~ExampleModule_B()
|
||||
{
|
||||
// Удаляем класс библиотеки, а то при переконфигурации в нем не поменяются PIN и дугие параметры передаваемые в библиотеку.
|
||||
delete libXX;
|
||||
libXX = nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
//=========================================================================================================
|
||||
//=========================================================================================================
|
||||
// Функция для связи модуля с ядром IoTM
|
||||
// !!! ЗДЕСЬ getAPI_ИМЯ ИМЯ должно совпадать с "moduleName" из modeinfo.json
|
||||
// после замены названия сенсора, на функцию можно не обращать внимания
|
||||
// если сенсор предполагает использование общего объекта библиотеки для нескольких экземпляров сенсора, то в данной функции необходимо предусмотреть
|
||||
// создание и контроль соответствующих глобальных переменных
|
||||
//=========================================================================================================
|
||||
//=========================================================================================================
|
||||
void *getAPI_ExampleModule(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("ExampleModule_A"))
|
||||
{ // !!! ЗДЕСЬ subtype ДОЛЖЕН СОВПАДАТЬ С subtype из modeinfo.json
|
||||
return new ExampleModule_A(param);
|
||||
}
|
||||
else if (subtype == F("ExampleModule_B"))
|
||||
{ // !!! ЗДЕСЬ subtype ДОЛЖЕН СОВПАДАТЬ С subtype из modeinfo.json
|
||||
return new ExampleModule_B(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
38
src/modules/sensors/ExampleModule/ExternalLibrary.h
Normal file
38
src/modules/sensors/ExampleModule/ExternalLibrary.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
/*******************************************************************************
|
||||
** **
|
||||
** УДАЛИТЬ ЭТО ПРОСТО ПРИМЕР БИБЛИОТЕКИ **
|
||||
** **
|
||||
*******************************************************************************/
|
||||
|
||||
class ExternalLibrary
|
||||
{
|
||||
private:
|
||||
/* data */
|
||||
public:
|
||||
ExternalLibrary(/* args */);
|
||||
|
||||
~ExternalLibrary();
|
||||
void begin (int pin);
|
||||
float READ_LIB_DATA_OTHER();
|
||||
};
|
||||
|
||||
ExternalLibrary::ExternalLibrary(/* args */)
|
||||
{
|
||||
}
|
||||
|
||||
ExternalLibrary::~ExternalLibrary()
|
||||
{
|
||||
}
|
||||
|
||||
void ExternalLibrary::begin(int pin)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
float ExternalLibrary::READ_LIB_DATA_OTHER()
|
||||
{
|
||||
static float f = 0;
|
||||
return f++;
|
||||
}
|
||||
90
src/modules/sensors/ExampleModule/modinfo.json
Normal file
90
src/modules/sensors/ExampleModule/modinfo.json
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Пример датчика А",
|
||||
"type": "Reading",
|
||||
"subtype": "ExampleModule_A",
|
||||
"id": "Tmp",
|
||||
"widget": "anydataTmp",
|
||||
"page": "Сенсоры",
|
||||
"descr": "Температура",
|
||||
"int": 15,
|
||||
"pin": "32",
|
||||
"round": 1
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"name": "Пример датчика Б",
|
||||
"type": "Reading",
|
||||
"subtype": "ExampleModule_B",
|
||||
"id": "Press",
|
||||
"widget": "anydataMm",
|
||||
"page": "Сенсоры",
|
||||
"descr": "Давление",
|
||||
"int": 15,
|
||||
"pin": "32",
|
||||
"round": 1,
|
||||
"btn-Example": 100
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "NAME",
|
||||
"authorContact": "https://t.me/NAME",
|
||||
"authorGit": "https://github.com/NAME",
|
||||
"exampleURL": "https://iotmanager.org/wiki",
|
||||
"specialThanks": "",
|
||||
"moduleName": "ExampleModule",
|
||||
"moduleVersion": "1.0",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
},
|
||||
"title": "Название модуля",
|
||||
"moduleDesc": "Описание модуля. Что позволяет получить. Особенности работы",
|
||||
"propInfo": {
|
||||
"pin": "Аналоговый GPIO номер, к которому подключен датчик.",
|
||||
"int": "Количество секунд между опросами датчика",
|
||||
"btn-Example": "Кнопка Example. В поле указать ......"
|
||||
},
|
||||
"funcInfo": [
|
||||
{
|
||||
"name": "expampleFunc",
|
||||
"descr": "Пример функции вызываемой из сценария. Принимает Id другого модуля и смотрит его значение",
|
||||
"params": [
|
||||
"ID стороннего модуля"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "expample2",
|
||||
"descr": "Второй Пример функции вызываемой из сценария.",
|
||||
"params": [
|
||||
"Описание педедаваемого параметра",
|
||||
"параметр 2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "expampleAny",
|
||||
"descr": "Третий Пример функции вызываемой из сценария. С неограниченным числом параметров",
|
||||
"params": [
|
||||
"Описание педедаваемых параметров"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"defActive": false,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp32s2_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
"esp8285_1mb": [],
|
||||
"esp8285_1mb_ota": [],
|
||||
"esp8266_2mb": [],
|
||||
"esp8266_2mb_ota": []
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,28 @@ private:
|
||||
int red = 0;
|
||||
int offline = 0;
|
||||
bool dataFromNode = false;
|
||||
String _topic = "";
|
||||
bool _isJson;
|
||||
bool _debug;
|
||||
bool sendOk = false;
|
||||
|
||||
public:
|
||||
ExternalMQTT(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
_MAC = jsonReadStr(parameters, "MAC");
|
||||
_sensor = jsonReadStr(parameters, "sensor");
|
||||
jsonRead(parameters, F("orange"), orange);
|
||||
jsonRead(parameters, F("red"), red);
|
||||
jsonRead(parameters, F("offline"), offline);
|
||||
_topic = jsonReadStr(parameters, "topic");
|
||||
jsonRead(parameters, "isJson", _isJson);
|
||||
// jsonRead(parameters, "addPrefix", _addPrefix);
|
||||
jsonRead(parameters, "debug", _debug);
|
||||
dataFromNode = false;
|
||||
if (mqttIsConnect())
|
||||
{
|
||||
sendOk = true;
|
||||
mqttSubscribeExternal(_topic);
|
||||
}
|
||||
}
|
||||
char *TimeToString(unsigned long t)
|
||||
{
|
||||
@@ -38,46 +50,71 @@ public:
|
||||
{
|
||||
if (msg.indexOf("HELLO") == -1)
|
||||
{
|
||||
|
||||
// SerialPrint("i", "onMqttRecive", "Прилетело " + topic);
|
||||
// SerialPrint("i", "onMqttRecive", "Прилетело " + msg);
|
||||
String dev = selectToMarkerLast(topic, "/");
|
||||
dev.toUpperCase();
|
||||
dev.replace(":", "");
|
||||
if (_MAC == "")
|
||||
if (_topic != topic)
|
||||
{
|
||||
SerialPrint("i", "onMqttRecive", dev + " --> " + msg);
|
||||
}
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
DeserializationError error = deserializeJson(doc, msg);
|
||||
if (error)
|
||||
{
|
||||
SerialPrint("E", F("onMqttRecive"), error.f_str());
|
||||
}
|
||||
JsonObject jsonObject = doc.as<JsonObject>();
|
||||
|
||||
for (JsonPair kv : jsonObject)
|
||||
{
|
||||
String key = kv.key().c_str();
|
||||
String val = kv.value();
|
||||
if (_MAC == dev && _sensor == key)
|
||||
if (_debug)
|
||||
{
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setValue(val);
|
||||
// setNewWidgetAttributes();
|
||||
SerialPrint("i", "ExternalMQTT", _id + " not equal: " + topic + " msg: " + msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Serial.println("Key: " + key);
|
||||
// Serial.println("Value: " + val);
|
||||
if (_isJson)
|
||||
{
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
DeserializationError error = deserializeJson(doc, msg);
|
||||
if (error)
|
||||
{
|
||||
SerialPrint("E", F("ExternalMQTT"), error.f_str());
|
||||
}
|
||||
JsonObject jsonObject = doc.as<JsonObject>();
|
||||
|
||||
for (JsonPair kv : jsonObject)
|
||||
{
|
||||
String key = kv.key().c_str();
|
||||
String val = kv.value();
|
||||
if (_debug)
|
||||
{
|
||||
SerialPrint("i", "ExternalMQTT", "Received MAC: " + dev + " key=" + key + " val=" + val);
|
||||
}
|
||||
if (_sensor == key)
|
||||
{
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setValue(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_debug)
|
||||
{
|
||||
SerialPrint("i", "ExternalMQTT", "Received MAC: " + dev + " val=" + msg);
|
||||
}
|
||||
dataFromNode = true;
|
||||
_minutesPassed = 0;
|
||||
setValue(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getMqttExterSub()
|
||||
{
|
||||
return _topic;
|
||||
}
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
_minutesPassed++;
|
||||
setNewWidgetAttributes();
|
||||
if (mqttIsConnect() && !sendOk)
|
||||
{
|
||||
sendOk = true;
|
||||
mqttSubscribeExternal(_topic);
|
||||
}
|
||||
}
|
||||
void onMqttWsAppConnectEvent()
|
||||
{
|
||||
@@ -107,6 +144,7 @@ public:
|
||||
if (_minutesPassed >= offline)
|
||||
{
|
||||
jsonWriteStr(json, F("info"), F("offline"));
|
||||
SerialPrint("i", "ExternalMQTT", _id + " - offline");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +154,6 @@ public:
|
||||
}
|
||||
sendSubWidgetsValues(_id, json);
|
||||
}
|
||||
|
||||
~ExternalMQTT(){};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -11,23 +10,24 @@
|
||||
"widget": "",
|
||||
"page": "",
|
||||
"descr": "",
|
||||
"MAC": "",
|
||||
"sensor": "",
|
||||
"topic": "",
|
||||
"isJson": 1,
|
||||
"round": "",
|
||||
"orange": 60,
|
||||
"red": 120,
|
||||
"offline": 180,
|
||||
"int": 60
|
||||
"int": 60,
|
||||
"debug": 0
|
||||
}
|
||||
],
|
||||
|
||||
"about": {
|
||||
"authorName": "AVAKS",
|
||||
"authorContact": "https://t.me/@avaks_dev",
|
||||
"authorContact": "https://t.me/@avaks",
|
||||
"authorGit": "https://github.com/avaksru",
|
||||
"specialThanks": "",
|
||||
"moduleName": "ExternalMQTT",
|
||||
"moduleVersion": "1",
|
||||
"moduleVersion": "1.2",
|
||||
"usedRam": {
|
||||
"esp32_4mb": 15,
|
||||
"esp8266_4mb": 15
|
||||
@@ -40,15 +40,20 @@
|
||||
"orange": "количество минут после которого окрасить виджет в оранжевый цвет",
|
||||
"red": "количество минут после которого окрасить виджет в красный цвет",
|
||||
"offline": "количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится",
|
||||
"MAC": "MAC адрес беспроводного датчика",
|
||||
"sensor": "Тип сенсора: температура / влажность / время / ... "
|
||||
"sensor": "Тип сенсора: температура / влажность / время / ... Он же ключ в json пакете ",
|
||||
"topic":"Подписаться на произвольный топик (полный путь), в модуле данный топик буде проверяться с отправителем, например homed/fd/zigbee/temp",
|
||||
"addPrefix":"1 (число), будет добавлен стандартный префикс из настроек, 0 - не добавлять префикс",
|
||||
"isJson":"1 - ожидаем в топике json, 0 - в топике просто значение (при 0 поле sensor заполнять не требуется)",
|
||||
"debug":"1 - выводить дополнительный лог в сериал"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32s2_4mb": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
@@ -57,4 +62,4 @@
|
||||
"esp8266_2mb": [],
|
||||
"esp8266_2mb_ota": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -87,6 +87,12 @@
|
||||
"esp32_4mb": [
|
||||
"kosme/arduinoFFT@^1.5.6"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"kosme/arduinoFFT@^1.5.6"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"kosme/arduinoFFT@^1.5.6"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"kosme/arduinoFFT@^1.5.6"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -52,6 +52,12 @@
|
||||
"esp32_4mb": [
|
||||
"https://github.com/JonasGMorsch/GY-21.git"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/JonasGMorsch/GY-21.git"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/JonasGMorsch/GY-21.git"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/JonasGMorsch/GY-21.git"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -55,6 +55,12 @@
|
||||
"esp32_4mb": [
|
||||
"ClosedCube HDC1080"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"ClosedCube HDC1080"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"ClosedCube HDC1080"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"ClosedCube HDC1080"
|
||||
]
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
|
||||
"configItem": [{
|
||||
"name": "HX710 Cенсор давления",
|
||||
"type": "Reading",
|
||||
"subtype": "Hx710",
|
||||
"id": "hxp",
|
||||
"widget": "anydataDef",
|
||||
"page": "Давление",
|
||||
"descr": "HX press",
|
||||
"int": 15,
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": 1,
|
||||
"data": 14,
|
||||
"clock": 15
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "HX710 Cенсор давления",
|
||||
"type": "Reading",
|
||||
"subtype": "Hx710",
|
||||
"id": "hxp",
|
||||
"widget": "anydataDef",
|
||||
"page": "Давление",
|
||||
"descr": "HX press",
|
||||
"int": 15,
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": 1,
|
||||
"data": 14,
|
||||
"clock": 15
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Serghei Crasnicov",
|
||||
"authorContact": "https://t.me/Serghei63",
|
||||
@@ -44,13 +44,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"https://github.com/kurimawxx00/hx710B_pressure_sensor"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"https://github.com/kurimawxx00/hx710B_pressure_sensor"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"https://github.com/kurimawxx00/hx710B_pressure_sensor"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"https://github.com/kurimawxx00/hx710B_pressure_sensor"
|
||||
]
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
|
||||
"configItem": [{
|
||||
"name": "HX711 Cенсор весов",
|
||||
"type": "Reading",
|
||||
"subtype": "Hx711",
|
||||
"id": "hx",
|
||||
"widget": "anydataDef",
|
||||
"page": "Весы",
|
||||
"descr": "HX вес",
|
||||
"int": 15,
|
||||
"map": "1024,1024,1,100",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": 1,
|
||||
"data": 3,
|
||||
"clock": 2,
|
||||
"chan": 2
|
||||
}],
|
||||
|
||||
{
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"name": "HX711 Cенсор весов",
|
||||
"type": "Reading",
|
||||
"subtype": "Hx711",
|
||||
"id": "hx",
|
||||
"widget": "anydataDef",
|
||||
"page": "Весы",
|
||||
"descr": "HX вес",
|
||||
"int": 15,
|
||||
"map": "1024,1024,1,100",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": 1,
|
||||
"data": 3,
|
||||
"clock": 2,
|
||||
"chan": 2
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
"authorName": "Serghei Crasnicov",
|
||||
"authorContact": "https://t.me/Serghei63",
|
||||
@@ -48,7 +48,9 @@
|
||||
{
|
||||
"name": "sleepMode",
|
||||
"descr": "Перевести в режим сна",
|
||||
"params": ["=1 режим сна, =0 проснуться"]
|
||||
"params": [
|
||||
"=1 режим сна, =0 проснуться"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
@@ -57,13 +59,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"defActive": false,
|
||||
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [
|
||||
"GyverHX711@1.2"
|
||||
],
|
||||
"esp32_4mb3f": [
|
||||
"GyverHX711@1.2"
|
||||
],
|
||||
"esp32cam_4mb": [
|
||||
"GyverHX711@1.2"
|
||||
],
|
||||
"esp8266_4mb": [
|
||||
"GyverHX711@1.2"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"menuSection": "Сенсоры",
|
||||
"menuSection": "sensors",
|
||||
"configItem": [
|
||||
{
|
||||
"global": 0,
|
||||
@@ -41,6 +41,8 @@
|
||||
"defActive": true,
|
||||
"usedLibs": {
|
||||
"esp32_4mb": [],
|
||||
"esp32_4mb3f": [],
|
||||
"esp32cam_4mb": [],
|
||||
"esp8266_4mb": [],
|
||||
"esp8266_1mb": [],
|
||||
"esp8266_1mb_ota": [],
|
||||
|
||||
@@ -1,136 +1,242 @@
|
||||
/******************************************************************
|
||||
Used Adafruit INA219 Current Sensor
|
||||
Support for INA219
|
||||
https://github.com/adafruit/Adafruit_INA219
|
||||
Used GyverINA Current Sensor
|
||||
Support for INA219 INA226
|
||||
https://github.com/GyverLibs/GyverINA
|
||||
|
||||
adapted for version 4dev @Serghei63
|
||||
adapted for version 4dev @Mit4bmw
|
||||
******************************************************************/
|
||||
|
||||
#include "Global.h"
|
||||
#include "classes/IoTItem.h"
|
||||
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_INA219.h>
|
||||
#include <GyverINA.h>
|
||||
#include <map>
|
||||
|
||||
Adafruit_INA219 ina219;
|
||||
|
||||
float shuntvoltage = 0;
|
||||
float busvoltage = 0;
|
||||
float current_mA = 0;
|
||||
float loadvoltage = 0;
|
||||
float power_mW = 0;
|
||||
|
||||
// shuntvoltage = ina219.getShuntVoltage_mV(); // Получение напряжение на шунте
|
||||
// busvoltage = ina219.getBusVoltage_V(); // Получение значение напряжения V
|
||||
// current_mA = ina219.getCurrent_mA(); // Получение значение тока в мА
|
||||
// power_mW = ina219.getPower_mW(); // Получение значение мощности
|
||||
// loadvoltage = busvoltage + (shuntvoltage / 1000); // Расчет напряжение на нагрузки
|
||||
|
||||
class Ina219loadvoltage : public IoTItem {
|
||||
public:
|
||||
Ina219loadvoltage(String parameters) : IoTItem(parameters) {
|
||||
|
||||
// Wire.begin(2,0); // Инициализация шины I2C для модуля E01
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
|
||||
loadvoltage = busvoltage + (shuntvoltage / 1000);
|
||||
value.valD = loadvoltage;
|
||||
|
||||
regEvent(value.valD, "Ina219loadvoltage");
|
||||
}
|
||||
|
||||
~Ina219loadvoltage(){};
|
||||
// Структура для хранения настроек датчика
|
||||
struct Ina219Value
|
||||
{
|
||||
float shunt = 0;
|
||||
float maxV = 0;
|
||||
};
|
||||
|
||||
class Ina219busvoltage : public IoTItem {
|
||||
public:
|
||||
Ina219busvoltage(String parameters) : IoTItem(parameters) {
|
||||
// глобальные списки необходимы для хранения Модуля Настроек. Ключ - адрес
|
||||
std::map<uint8_t, Ina219Value *> ina219SettingArray;
|
||||
|
||||
// Wire.begin(2,0); // Инициализация шины I2C для модуля E01
|
||||
// глобальные списки необходимы для хранения объектов используемых разными датчиками из модуля. Ключ - адрес
|
||||
std::map<uint8_t, INA219 *> ina219Array;
|
||||
|
||||
// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку
|
||||
INA219 *instanceIna219(uint8_t ADDR)
|
||||
{
|
||||
/** default I2C address **/
|
||||
if (ADDR == 0)
|
||||
ADDR = 0x40; // 1000000 (A0+A1=GND)
|
||||
|
||||
// учитываем, что библиотека может работать с несколькими линиями на разных пинах, поэтому инициируем библиотеку, если линия ранее не использовалась
|
||||
if (ina219Array.find(ADDR) == ina219Array.end())
|
||||
{
|
||||
if (ina219SettingArray.find(ADDR) != ina219SettingArray.end())
|
||||
ina219Array[ADDR] = new INA219(ina219SettingArray[ADDR]->shunt, ina219SettingArray[ADDR]->maxV, (uint8_t)ADDR);
|
||||
else
|
||||
ina219Array[ADDR] = new INA219(0.1f, 3.2f, (uint8_t)ADDR); // Стандартные значения для модуля INA219 (0.1 Ом, 3.2А, адрес 0x40)
|
||||
ina219Array[ADDR]->begin();
|
||||
// ina219ValueArray[ADDR] = new Ina219Value;
|
||||
}
|
||||
return ina219Array[ADDR];
|
||||
}
|
||||
|
||||
class Ina219voltage : public IoTItem
|
||||
{
|
||||
private:
|
||||
uint8_t _addr = 0;
|
||||
|
||||
public:
|
||||
Ina219voltage(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
String sAddr;
|
||||
jsonRead(parameters, "addr", sAddr);
|
||||
if (sAddr == "")
|
||||
scanI2C();
|
||||
else
|
||||
_addr = hexStringToUint8(sAddr);
|
||||
}
|
||||
|
||||
void doByInterval() {
|
||||
|
||||
busvoltage = ina219.getBusVoltage_V();
|
||||
value.valD = busvoltage;
|
||||
|
||||
regEvent(value.valD, "Ina219busvoltage");
|
||||
void doByInterval()
|
||||
{
|
||||
regEvent(instanceIna219(_addr)->getVoltage(), "Ina219voltage");
|
||||
}
|
||||
|
||||
~Ina219busvoltage(){};
|
||||
~Ina219voltage(){};
|
||||
};
|
||||
|
||||
class Ina219shuntvoltage : public IoTItem
|
||||
{
|
||||
private:
|
||||
uint8_t _addr = 0;
|
||||
|
||||
class Ina219curr : public IoTItem {
|
||||
public:
|
||||
Ina219curr(String parameters) : IoTItem(parameters) {
|
||||
|
||||
// Wire.begin(2,0); // Инициализация шины I2C для модуля E01
|
||||
public:
|
||||
Ina219shuntvoltage(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
String sAddr;
|
||||
jsonRead(parameters, "addr", sAddr);
|
||||
if (sAddr == "")
|
||||
scanI2C();
|
||||
else
|
||||
_addr = hexStringToUint8(sAddr);
|
||||
}
|
||||
void doByInterval() {
|
||||
|
||||
current_mA = ina219.getCurrent_mA();
|
||||
value.valD = current_mA;
|
||||
|
||||
regEvent(value.valD, "Ina219curr");
|
||||
}
|
||||
|
||||
~Ina219curr(){};
|
||||
};
|
||||
|
||||
class Ina219shuntvoltage : public IoTItem {
|
||||
public:
|
||||
Ina219shuntvoltage(String parameters) : IoTItem(parameters) {
|
||||
|
||||
// Wire.begin(2,0); // Инициализация шины I2C для модуля E01
|
||||
}
|
||||
void doByInterval() {
|
||||
|
||||
shuntvoltage = ina219.getShuntVoltage_mV();
|
||||
value.valD = shuntvoltage;
|
||||
|
||||
regEvent(value.valD, "Ina219shuntvoltage");
|
||||
void doByInterval()
|
||||
{
|
||||
regEvent(instanceIna219(_addr)->getShuntVoltage(), "Ina219shuntvoltage");
|
||||
}
|
||||
|
||||
~Ina219shuntvoltage(){};
|
||||
};
|
||||
|
||||
class Power_mW : public IoTItem {
|
||||
public:
|
||||
Power_mW(String parameters) : IoTItem(parameters) {
|
||||
class Ina219curr : public IoTItem
|
||||
{
|
||||
private:
|
||||
uint8_t _addr = 0;
|
||||
|
||||
// Wire.begin(2,0); // Инициализация шины I2C для модуля E01
|
||||
public:
|
||||
Ina219curr(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
String sAddr;
|
||||
jsonRead(parameters, "addr", sAddr);
|
||||
if (sAddr == "")
|
||||
scanI2C();
|
||||
else
|
||||
_addr = hexStringToUint8(sAddr);
|
||||
}
|
||||
void doByInterval() {
|
||||
|
||||
power_mW = ina219.getPower_mW();
|
||||
value.valD = power_mW;
|
||||
|
||||
regEvent(value.valD, "Ina219power"); // TODO: найти способ понимания ошибки получения данных
|
||||
|
||||
void doByInterval()
|
||||
{
|
||||
regEvent(instanceIna219(_addr)->getCurrent(), "Ina219curr");
|
||||
}
|
||||
|
||||
~Power_mW(){};
|
||||
~Ina219curr(){};
|
||||
};
|
||||
void* getAPI_Ina219(String subtype, String param) {
|
||||
if (subtype == F("Ina219curr")) {
|
||||
ina219.begin();
|
||||
|
||||
class Ina219Power : public IoTItem
|
||||
{
|
||||
private:
|
||||
uint8_t _addr = 0;
|
||||
|
||||
public:
|
||||
Ina219Power(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
String sAddr;
|
||||
jsonRead(parameters, "addr", sAddr);
|
||||
if (sAddr == "")
|
||||
scanI2C();
|
||||
else
|
||||
_addr = hexStringToUint8(sAddr);
|
||||
}
|
||||
void doByInterval()
|
||||
{
|
||||
regEvent(instanceIna219(_addr)->getPower(), "Ina219power"); // TODO: найти способ понимания ошибки получения данных
|
||||
}
|
||||
|
||||
~Ina219Power(){};
|
||||
};
|
||||
|
||||
class Ina219Setting : public IoTItem
|
||||
{
|
||||
private:
|
||||
uint8_t _addr = 0;
|
||||
int adjClbr = 0;
|
||||
int resol = 1;
|
||||
|
||||
|
||||
public:
|
||||
Ina219Setting(String parameters) : IoTItem(parameters)
|
||||
{
|
||||
String sAddr;
|
||||
jsonRead(parameters, "addr", sAddr);
|
||||
jsonRead(parameters, "adjClbr", adjClbr);
|
||||
jsonRead(parameters, "resol", resol);
|
||||
|
||||
|
||||
if (sAddr == "")
|
||||
scanI2C();
|
||||
else
|
||||
_addr = hexStringToUint8(sAddr);
|
||||
|
||||
ina219SettingArray[_addr] = new Ina219Value;
|
||||
jsonRead(parameters, "shunt", ina219SettingArray[_addr]->shunt);
|
||||
jsonRead(parameters, "maxV", ina219SettingArray[_addr]->maxV);
|
||||
|
||||
instanceIna219(_addr)->adjCalibration(adjClbr);
|
||||
if (resol == 1)
|
||||
resol = 0b0011;
|
||||
else
|
||||
resol += 0b0111;
|
||||
|
||||
instanceIna219(_addr)->setResolution(INA219_VBUS, resol); // Напряжение в 12ти битном режиме
|
||||
instanceIna219(_addr)->setResolution(INA219_VSHUNT, resol); // Ток в 12ти битном режиме
|
||||
}
|
||||
|
||||
void onModuleOrder(String &key, String &value)
|
||||
{
|
||||
if (key == "getClbr")
|
||||
{
|
||||
SerialPrint("i", F("Ina219"), "addr: " + String(_addr) + ", Value Calibration:" + instanceIna219(_addr)->getCalibration());
|
||||
}
|
||||
}
|
||||
|
||||
IoTValue execute(String command, std::vector<IoTValue> ¶m)
|
||||
{
|
||||
if (command == "sleep")
|
||||
{
|
||||
if (param.size() == 1)
|
||||
{
|
||||
if (param[0].valD == 0)
|
||||
instanceIna219(_addr)->sleep(false);
|
||||
if (param[0].valD == 1)
|
||||
instanceIna219(_addr)->sleep(true);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
/*
|
||||
else if (command == "getCalibration")
|
||||
{
|
||||
SerialPrint("i", F("Ina219"), "addr: " + String(_addr) + ", Value Calibration:" + instanceIna219(_addr)->getCalibration());
|
||||
return {};
|
||||
}*/
|
||||
return {};
|
||||
}
|
||||
|
||||
~Ina219Setting(){};
|
||||
};
|
||||
|
||||
void *getAPI_Ina219(String subtype, String param)
|
||||
{
|
||||
if (subtype == F("Ina219curr"))
|
||||
{
|
||||
return new Ina219curr(param);
|
||||
} else if (subtype == F("Ina219shuntvoltage")) {
|
||||
ina219.begin();
|
||||
}
|
||||
else if (subtype == F("Ina219shuntvoltage"))
|
||||
{
|
||||
return new Ina219shuntvoltage(param);
|
||||
} else if (subtype == F("power_mW")) {
|
||||
ina219.begin();
|
||||
return new Power_mW(param);
|
||||
} else if (subtype == F("Ina219busvoltage")) {
|
||||
ina219.begin();
|
||||
return new Ina219busvoltage(param);
|
||||
} else if (subtype == F("Ina219loadvoltage")) {
|
||||
ina219.begin();
|
||||
return new Ina219loadvoltage(param);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
else if (subtype == F("Ina219power"))
|
||||
{
|
||||
return new Ina219Power(param);
|
||||
}
|
||||
else if (subtype == F("Ina219voltage"))
|
||||
{
|
||||
return new Ina219voltage(param);
|
||||
}
|
||||
else if (subtype == F("Ina219setting"))
|
||||
{
|
||||
return new Ina219Setting(param);
|
||||
// Ina219Setting *ptr = new Ina219Setting(param);
|
||||
// ina219SettingArray[ptr->getAddr()] = ptr;
|
||||
// return ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user