Merge branch 'IoTManagerProject:ver4dev' into ver4dev

This commit is contained in:
Al
2023-11-24 21:16:12 +01:00
committed by GitHub
341 changed files with 27535 additions and 3331 deletions

View File

@@ -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;
}

View File

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

View File

@@ -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> &param) { // будет возможным использовать, когда сценарии запустятся
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 {

View File

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

View File

@@ -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> &param)
@@ -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(){};
};

View File

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

View 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> &param) { // будет возможным использовать, когда сценарии запустятся
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;
}
}

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

View 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> &param) {
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;
}
}

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

View 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);
}

View 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;
};

View 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;
}
}

View 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": []
}
}

View 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;
}
}

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

View File

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

View File

@@ -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": [],

View File

@@ -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() {};
};

View File

@@ -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": [],

View File

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

View File

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

View File

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

View File

@@ -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> &param)
{
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> &param) {
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;
}
}

View File

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

View 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;
}

View 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": []
}
}

View File

@@ -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> &param)
{
if (param.size() > 0)
{
if (command == "get")
{
if (param.size())
{
IoTValue execute(String command, std::vector<IoTValue> &param) {
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;
}
}

View File

@@ -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": [],

View File

@@ -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> &param) {
if (command == "rotate") {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
}
}
}

View File

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

View File

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

View File

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

View 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;
}
}

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

View File

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

View File

@@ -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": [],

View File

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

View File

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

View File

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

View File

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

View File

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

View 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> &param)
{
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;
}
}

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

View File

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

View File

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

View File

@@ -69,6 +69,11 @@
"descr": "Отправить значение в топик MQTT",
"params": ["Топик", "Значение"]
},
{
"name": "mqttIsConnect",
"descr": "Получить состояние подключения к MQTT",
"params": []
},
{
"name": "getHours",
"descr": "Получить текущее число часов. Если время не получено из сети Интернет или внешнего RTC, то условие пропускается",

View File

@@ -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": {

View File

@@ -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": [],

View File

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

View File

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

View File

@@ -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(){};

View File

@@ -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": [],

View File

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

View File

@@ -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;
}
}
}

View File

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

View 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;
}
}

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

View 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;
}
}

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

View File

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

View File

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

View File

@@ -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": [],

View File

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

View File

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

View 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;
}
}

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

View File

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

View 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> &param)
{
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;
}
}

View 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++;
}

View 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": []
}
}

View File

@@ -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(){};
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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": [],

View File

@@ -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> &param)
{
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;
}
}

View File

@@ -1,99 +1,141 @@
{
"menuSection": "Сенсоры",
"configItem": [{
"global": 0,
"name": "INA219 Tок",
"type": "Reading",
"subtype": "Ina219curr",
"id": "Ina219current",
"widget": "anydatamAmp",
"page": "INA 219",
"descr": "219 Датчик тока",
"int": 10
},
{
"global": 0,
"name": "INA219 Напряжение",
"type": "Reading",
"subtype": "Ina219busvoltage",
"id": "Ina219busvoltage",
"widget": "anydataVlt",
"page": "INA 219",
"descr": "219 Датчик напряжения",
"int": 10
},
{
"global": 0,
"name": "INA219 Мощность",
"type": "Reading",
"subtype": "power_mW",
"id": "Ina219power",
"widget": "anydatamWtt",
"page": "INA 219",
"descr": "219 Мощность",
"int": 10
},
{
"global": 0,
"name": "INA219 Напряжение нагрузки",
"type": "Reading",
"subtype": "Ina219loadvoltage",
"id": "Ina219loadvoltage",
"widget": "anydataVlt",
"page": "INA 219",
"descr": "219 Напряжение нагрузки",
"int": 10
},
{
"global": 0,
"name": "INA219 Шунт",
"type": "Reading",
"subtype": "Ina219shuntvoltage",
"id": "Ina219shuntvoltage",
"widget": "anydatamVlt",
"page": "INA 219",
"descr": "219 Напряжение шунта",
"int": 10
}],
"about": {
"authorName": "Serghei Crasnicov",
"authorContact": "https://t.me/Serghei63",
"authorGit": "https://github.com/Serghei63",
"specialThanks": "Дмитрий , Serg",
"moduleName": "Ina219",
"moduleVersion": "1.0",
"usedRam": {
"esp32_4mb": 15,
"esp8266_4mb": 15
},
"subTypes": [
"Ina219curr",
"Ina219busvoltage",
"power_mW",
"Ina219loadvoltage",
"Ina219shuntvoltage"
],
"title": "Милливатметр постоянного тока",
"moduleDesc": "Измеряет постоянный ток до 3.2 ампера, напряжение до 26 вольт и мощность на нагрузке",
"propInfo": {
"int": "Количество секунд между опросами датчика."
}
"menuSection": "sensors",
"configItem": [
{
"global": 0,
"name": "INA219 Tок",
"type": "Reading",
"subtype": "Ina219curr",
"id": "ina219_A",
"widget": "anydataAmp",
"page": "INA 219",
"descr": "Сила тока",
"addr": "0x40",
"plus": 0,
"multiply": 1,
"round": 3,
"int": 10
},
"defActive": false,
"usedLibs": {
"esp32_4mb": [
"https://github.com/adafruit/Adafruit_INA219.git"
],
"esp8266_4mb": [
"https://github.com/adafruit/Adafruit_INA219.git"
]
{
"global": 0,
"name": "INA219 Напряжение",
"type": "Reading",
"subtype": "Ina219voltage",
"id": "ina219_V",
"widget": "anydataVlt",
"page": "INA 219",
"descr": "Напряжения",
"addr": "0x40",
"plus": 0,
"multiply": 1,
"round": 3,
"int": 10
},
{
"global": 0,
"name": "INA219 Мощность",
"type": "Reading",
"subtype": "Ina219power",
"id": "ina219_W",
"widget": "anydataWt",
"page": "INA 219",
"descr": "Мощность",
"addr": "0x40",
"plus": 0,
"multiply": 1,
"round": 3,
"int": 10
},
{
"global": 0,
"name": "INA219 Шунт",
"type": "Reading",
"subtype": "Ina219shuntvoltage",
"id": "ina219_Vsh",
"widget": "anydataVlt",
"page": "INA 219",
"descr": "Напряжение шунта",
"addr": "0x40",
"plus": 0,
"multiply": 1,
"round": 3,
"int": 10
},
{
"global": 0,
"name": "INA219 Настройки",
"type": "Reading",
"subtype": "Ina219setting",
"id": "ina219_set",
"widget": "nil",
"page": "",
"descr": "",
"addr": "0x40",
"shunt": 0.1,
"maxV": 3.2,
"adjClbr": 0,
"resol": 4,
"btn-getClbr": "nil"
}
}
],
"about": {
"authorName": "Serghei Crasnicov",
"authorContact": "https://t.me/Serghei63",
"authorGit": "https://github.com/Serghei63",
"specialThanks": "Дмитрий, Serg, v2.0 - Mitchel @Mit4bmw",
"moduleName": "Ina219",
"moduleVersion": "2.0",
"usedRam": {
"esp32_4mb": 15,
"esp8266_4mb": 15
},
"subTypes": [
"Ina219curr",
"Ina219voltage",
"Ina219power",
"Ina219shuntvoltage",
"Ina219setting"
],
"title": "Милливатметр постоянного тока",
"moduleDesc": "Измеряет постоянный ток до 3.2 ампера, напряжение до 26 вольт и мощность на нагрузке. Модуль INA219 Настройки - для изменении настроек нужен постоянно в конфигурации, должен стоять перед рдугими модулями с тем же адресом, без него работает на значенях по умолчанию",
"propInfo": {
"int": "Количество секунд между опросами датчика.",
"addr": "Адрес датчика на шине, обычно 0x40. Если оставить поле пустым, то запуститься сканер I2C и подключение к адресу 0x40",
"shunt": "Сопротивление шунта, штатно 0.1Ом. Изменить если его перепаяли",
"maxV": "Максимальный ожидаемый ток, штатно 3.2А, для указаного шунта",
"adjClbr": "Задать смещение (подкрутить) калибровочное значение на указанное значение. -20 = Уменьшить калибровочное значение на 20",
"resol": "Установка режима усреднения для измерения напряжения и тока, рекомендуется для повышения стабильности показаний на шумной нагрузке. Варианты 1(без усреднения),2,4,8,16,32,64,128",
"btn-getClbr": "Кнопка запроса текущей калибровки, выводится в лог"
},
"funcInfo": [
{
"name": "sleep",
"descr": "INA219 Настройки. Установка / снятие режима сна датчика INA219",
"params": [
"1- вкл сна/ 0-выкл сна"
]
}
]
},
"defActive": false,
"usedLibs": {
"esp32_4mb": [
"https://github.com/GyverLibs/GyverINA"
],
"esp32_4mb3f": [
"https://github.com/GyverLibs/GyverINA"
],
"esp32cam_4mb": [
"https://github.com/GyverLibs/GyverINA"
],
"esp32s2_4mb": [
"https://github.com/GyverLibs/GyverINA"
],
"esp8266_4mb": [
"https://github.com/GyverLibs/GyverINA"
],
"esp8266_16mb": [
"https://github.com/GyverLibs/GyverINA"
]
}
}

View File

@@ -0,0 +1,237 @@
/******************************************************************
Used GyverINA Current Sensor
Support for INA219 INA226
https://github.com/GyverLibs/GyverINA
adapted for version 4dev @Mit4bmw
******************************************************************/
#include "Global.h"
#include "classes/IoTItem.h"
#include <Wire.h>
#include <GyverINA.h>
#include <map>
// Структура для хранения настроек датчика
struct Ina226Value
{
float shunt = 0;
float maxV = 0;
};
// глобальные списки необходимы для хранения Модуля Настроек. Ключ - адрес
std::map<uint8_t, Ina226Value *> ina226SettingArray;
// глобальные списки необходимы для хранения объектов используемых разными датчиками из модуля. Ключ - адрес
std::map<uint8_t, INA226 *> ina226Array;
// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку
INA226 *instanceIna226(uint8_t ADDR)
{
/** default I2C address **/
if (ADDR == 0)
ADDR = 0x40; // 1000000 (A0+A1=GND)
// учитываем, что библиотека может работать с несколькими линиями на разных пинах, поэтому инициируем библиотеку, если линия ранее не использовалась
if (ina226Array.find(ADDR) == ina226Array.end())
{
if (ina226SettingArray.find(ADDR) != ina226SettingArray.end())
ina226Array[ADDR] = new INA226(ina226SettingArray[ADDR]->shunt, ina226SettingArray[ADDR]->maxV, (uint8_t)ADDR);
else
ina226Array[ADDR] = new INA226(0.1f, 0.8f, (uint8_t)ADDR); // Стандартные значения для модуля INA226 (0.1 Ом, 0.8А, адрес 0x40)
ina226Array[ADDR]->begin();
// ina226ValueArray[ADDR] = new Ina226Value;
}
return ina226Array[ADDR];
}
class Ina226voltage : public IoTItem
{
private:
uint8_t _addr = 0;
public:
Ina226voltage(String parameters) : IoTItem(parameters)
{
String sAddr;
jsonRead(parameters, "addr", sAddr);
if (sAddr == "")
scanI2C();
else
_addr = hexStringToUint8(sAddr);
}
void doByInterval()
{
regEvent(instanceIna226(_addr)->getVoltage(), "Ina226voltage");
}
~Ina226voltage(){};
};
class Ina226shuntvoltage : public IoTItem
{
private:
uint8_t _addr = 0;
public:
Ina226shuntvoltage(String parameters) : IoTItem(parameters)
{
String sAddr;
jsonRead(parameters, "addr", sAddr);
if (sAddr == "")
scanI2C();
else
_addr = hexStringToUint8(sAddr);
}
void doByInterval()
{
regEvent(vinstanceIna226(_addr)->getShuntVoltage(), "Ina226shuntvoltage");
}
~Ina226shuntvoltage(){};
};
class Ina226curr : public IoTItem
{
private:
uint8_t _addr = 0;
public:
Ina226curr(String parameters) : IoTItem(parameters)
{
String sAddr;
jsonRead(parameters, "addr", sAddr);
if (sAddr == "")
scanI2C();
else
_addr = hexStringToUint8(sAddr);
}
void doByInterval()
{
regEvent(instanceIna226(_addr)->getCurrent(), "Ina226curr");
}
~Ina226curr(){};
};
class Ina226Power : public IoTItem
{
private:
uint8_t _addr = 0;
public:
Ina226Power(String parameters) : IoTItem(parameters)
{
String sAddr;
jsonRead(parameters, "addr", sAddr);
if (sAddr == "")
scanI2C();
else
_addr = hexStringToUint8(sAddr);
}
void doByInterval()
{
regEvent(instanceIna226(_addr)->getPower(), "Ina226power"); // TODO: найти способ понимания ошибки получения данных
}
~Ina226Power(){};
};
class Ina226Setting : public IoTItem
{
private:
uint8_t _addr = 0;
int adjClbr = 0;
int resol = 1;
public:
Ina226Setting(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);
ina226SettingArray[_addr] = new Ina226Value;
jsonRead(parameters, "shunt", ina226SettingArray[_addr]->shunt);
jsonRead(parameters, "maxV", ina226SettingArray[_addr]->maxV);
instanceIna226(_addr)->adjCalibration(adjClbr);
instanceIna226(_addr)->setAveraging(resol); // Напряжение в 12ти битном режиме
}
void onModuleOrder(String &key, String &value)
{
if (key == "getClbr")
{
SerialPrint("i", F("Ina226"), "addr: " + String(_addr) + ", Value Calibration:" + instanceIna226(_addr)->getCalibration());
}
}
IoTValue execute(String command, std::vector<IoTValue> &param)
{
if (command == "sleep")
{
if (param.size() == 1)
{
if (param[0].valD == 0)
instanceIna226(_addr)->sleep(false);
if (param[0].valD == 1)
instanceIna226(_addr)->sleep(true);
return {};
}
}
/*
else if (command == "getCalibration")
{
SerialPrint("i", F("Ina226"), "addr: " + String(_addr) + ", Value Calibration:" + instanceIna226(_addr)->getCalibration());
return {};
}*/
return {};
}
~Ina226Setting(){};
};
void *getAPI_Ina226(String subtype, String param)
{
if (subtype == F("Ina226curr"))
{
return new Ina226curr(param);
}
else if (subtype == F("Ina226shuntvoltage"))
{
return new Ina226shuntvoltage(param);
}
else if (subtype == F("Ina226power"))
{
return new Ina226Power(param);
}
else if (subtype == F("Ina226voltage"))
{
return new Ina226voltage(param);
}
else if (subtype == F("Ina226setting"))
{
return new Ina226Setting(param);
// Ina226Setting *ptr = new Ina226Setting(param);
// ina226SettingArray[ptr->getAddr()] = ptr;
// return ptr;
}
else
{
return nullptr;
}
}

View File

@@ -0,0 +1,141 @@
{
"menuSection": "sensors",
"configItem": [
{
"global": 0,
"name": "INA226 Tок",
"type": "Reading",
"subtype": "Ina226curr",
"id": "ina226_A",
"widget": "anydataAmp",
"page": "INA 226",
"descr": "Сила тока",
"addr": "0x40",
"plus": 0,
"multiply": 1,
"round": 3,
"int": 10
},
{
"global": 0,
"name": "INA226 Напряжение",
"type": "Reading",
"subtype": "Ina226voltage",
"id": "ina226_V",
"widget": "anydataVlt",
"page": "INA 226",
"descr": "Напряжения",
"addr": "0x40",
"plus": 0,
"multiply": 1,
"round": 3,
"int": 10
},
{
"global": 0,
"name": "INA226 Мощность",
"type": "Reading",
"subtype": "Ina226power",
"id": "ina226_W",
"widget": "anydataWt",
"page": "INA 226",
"descr": "Мощность",
"addr": "0x40",
"plus": 0,
"multiply": 1,
"round": 3,
"int": 10
},
{
"global": 0,
"name": "INA226 Шунт",
"type": "Reading",
"subtype": "Ina226shuntvoltage",
"id": "ina226_Vsh",
"widget": "anydataVlt",
"page": "INA 226",
"descr": "Напряжение шунта",
"addr": "0x40",
"plus": 0,
"multiply": 1,
"round": 3,
"int": 10
},
{
"global": 0,
"name": "INA226 Настройки",
"type": "Reading",
"subtype": "Ina226setting",
"id": "ina226_set",
"widget": "nil",
"page": "",
"descr": "",
"addr": "0x40",
"shunt": 0.1,
"maxV": 3.2,
"adjClbr": 0,
"resol": 4,
"btn-getClbr": "nil"
}
],
"about": {
"authorName": "Serghei Crasnicov",
"authorContact": "https://t.me/Serghei63",
"authorGit": "https://github.com/Serghei63",
"specialThanks": "v2.0 - Mitchel @Mit4bmw",
"moduleName": "Ina226",
"moduleVersion": "2.0",
"usedRam": {
"esp32_4mb": 15,
"esp8266_4mb": 15
},
"subTypes": [
"Ina226curr",
"Ina226voltage",
"Ina226power",
"Ina226shuntvoltage",
"Ina226setting"
],
"title": "Милливатметр постоянного тока",
"moduleDesc": "Стандартные значения для модуля INA226 (Сопротивление шунта - 0.1 Ом, Максимальный ожидаемый ток - 0.8 А, Адрес на шине I2c - 0x40). Модуль INA226 Настройки - для изменении настроек нужен постоянно в конфигурации, должен стоять перед рдугими модулями с тем же адресом, без него работает на значенях по умолчанию",
"propInfo": {
"int": "Количество секунд между опросами датчика.",
"addr": "Адрес датчика на шине, обычно 0x40. Если оставить поле пустым, то запуститься сканер I2C и подключение к адресу 0x40",
"shunt": "Сопротивление шунта, штатно 0.1Ом. Изменить если его перепаяли",
"maxV": "Максимальный ожидаемый ток, штатно 0.8А, для указаного шунта",
"adjClbr": "Задать смещение (подкрутить) калибровочное значение на указанное значение. -20 = Уменьшить калибровочное значение на 20",
"resol": "Установка режима усреднения (колическва замеров) для измерения напряжения и тока, рекомендуется для повышения стабильности показаний на шумной нагрузке. Пропорционально увеличивает время оцифровки. Варианты 0(без усреднения), от 1 до 7 - соответстввует 4,16,64,128,256,512,1024",
"btn-getClbr": "Кнопка запроса текущей калибровки, выводится в лог"
},
"funcInfo": [
{
"name": "sleep",
"descr": "INA226 Настройки. Установка / снятие режима сна датчика INA226",
"params": [
"1- вкл сна/ 0-выкл сна"
]
}
]
},
"defActive": false,
"usedLibs": {
"esp32_4mb": [
"https://github.com/GyverLibs/GyverINA"
],
"esp32_4mb3f": [
"https://github.com/GyverLibs/GyverINA"
],
"esp32cam_4mb": [
"https://github.com/GyverLibs/GyverINA"
],
"esp32s2_4mb": [
"https://github.com/GyverLibs/GyverINA"
],
"esp8266_4mb": [
"https://github.com/GyverLibs/GyverINA"
],
"esp8266_16mb": [
"https://github.com/GyverLibs/GyverINA"
]
}
}

View File

@@ -1,5 +1,5 @@
{
"menuSection": "Сенсоры",
"menuSection": "sensors",
"configItem": [
{
"global": 0,
@@ -39,6 +39,12 @@
"esp32_4mb": [
"https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino"
],
"esp32_4mb3f": [
"https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino"
],
"esp32cam_4mb": [
"https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino"
],
"esp8266_4mb": [
"https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino"
]

View File

@@ -1,5 +1,5 @@
{
"menuSection": "Сенсоры",
"menuSection": "sensors",
"configItem": [
{
"global": 0,
@@ -41,6 +41,9 @@
"esp32_4mb": [
"adafruit/MAX6675 library"
],
"esp32_4mb3f": [
"adafruit/MAX6675 library"
],
"esp8266_4mb": [
"adafruit/MAX6675 library"
]

View File

@@ -1,5 +1,5 @@
{
"menuSection": "Сенсоры",
"menuSection": "sensors",
"configItem": [
{
"global": 0,
@@ -73,11 +73,17 @@
"ABC": "Автокалибровка. По умолчанию включена. Раз в сутки на 20 мин. надо выставлять на свежий воздух.",
"pin": "пин получения значений по ШИМ. Esp8266: GPIO 15 - D8, ESP32: GPIO 21, > MHZ19: PWM желтый провод",
"maxRetriesNotAvailable": "Максимальное количество попыток опроса сенсора по ШИМ. (может задерживать контроллер)"
}
},
"title": "Mhz19 Датчик уровеня концентрации CO2"
},
"defActive": false,
"usedLibs": {
"esp32_4mb": [],
"esp32_4mb3f": [],
"esp32s2_4mb": [
"plerup/EspSoftwareSerial"
],
"esp32cam_4mb": [],
"esp8266_4mb": []
}
}

View File

@@ -0,0 +1,75 @@
/******************************************************************
Simple library for NTC thermistors
https://github.com/GyverLibs/GyverNTC
adapted for version 4 @Serghei63
******************************************************************/
#include "Global.h"
#include "classes/IoTItem.h"
class NTCt : public IoTItem {
private:
unsigned int _pin;
float R1 = 10000.0; // voltage divider resistor value
float Beta = 3950.0; // Beta value
float To = 25.; // Temperature Celsius
float Ro = 10000.0; // Resistance of Thermistor at 25 degree Celsius
float adcMax = 1023.0;
float Vs = 3.3;
public:
NTCt(String parameters): IoTItem(parameters) {
_pin = jsonReadInt(parameters, "pin");
R1 = jsonReadInt(parameters, "R1"); // voltage divider resistor value
Beta = jsonReadInt(parameters, "Beta"); // Beta value
To = jsonReadInt(parameters, "T0"); // Temperature degree Celsius
Ro = jsonReadInt(parameters, "R0"); // Resistance of Thermistor at 25 degree Celsius
Vs = jsonReadInt(parameters, "Vs"); // Resistance of Thermistor at 25 degree Celsius
To = To+273.15;
#if defined ESP8266
adcMax = 1023.0;
#elif defined ESP32
adcMax = 4095.0;
#endif
}
void doByInterval() {
float Vout, Rt = 0;
float T = 0;
float adc = 0;
for (int i = 0; i < 10; i++)
{
adc += IoTgpio.analogRead(_pin);
}
adc /= 10;
Vout = adc * Vs/adcMax;
Rt = R1 * Vout / (Vs - Vout);
T = 1/(1/To + log(Rt/Ro)/Beta); // Temperature in Kelvin
value.valD = T - 273.15; // Celsius
SerialPrint("i", F("Ntc"), "adc = " + String(adc)+ " ,Vout = " + String(Vout)+ " ,T = " + String(value.valD));
if (String(value.valD) != "nan") regEvent(value.valD, "Ntc");
else
SerialPrint("E", "Ntc", "Error");
}
~NTCt() {};
};
void* getAPI_Ntc(String subtype, String param) {
if (subtype == F("Ntc")) {
return new NTCt(param);
} else {
return nullptr;
}
}

View File

@@ -0,0 +1,64 @@
{
"menuSection": "sensors",
"configItem": [
{
"global": 0,
"name": "Cенсор температуры NTC",
"type": "Reading",
"subtype": "Ntc",
"id": "Ntctmp",
"widget": "anydataTmp",
"page": "Сенсоры",
"descr": "NTC Температура",
"needSave": 0,
"val": "0",
"int": 15,
"pin": "35",
"R1": "10000",
"R0": "10000",
"Beta": "3950.0",
"T0": "25",
"Vs": "3.3",
"round": 1
}
],
"about": {
"authorName": "Serghei Crasnicov",
"authorContact": "https://t.me/Serghei63",
"authorGit": "https://github.com/Serghei63",
"specialThanks": "https://t.me/Mit4bmw",
"moduleName": "Ntc",
"moduleVersion": "1.1",
"usedRam": {
"esp32_4mb": 15,
"esp8266_4mb": 15
},
"subTypes": [
"Ntc"
],
"title": "Модуль датчика Ntc",
"moduleDesc": "",
"retInfo": "",
"propInfo": {
"pin": "Аналоговый пин (для esp8266 = 0, для esp32 алаоговый gpio, например 35)",
"R1": "Сопротивление подтягивающего резистора, должен быть равен сопротивлению термистера",
"Vs": "Напряжение питания датчика, Для точности измерить и ввести своё, по умолчанию 3.3В",
"R0": "Сопротивление термистора при температуре То, например 10 КОм при 25С",
"T0": "Базовая температура, температура измерения сопротивление термистора (Rterm), обычно 25С",
"Beta": "Beta термистора"
}
},
"defActive": false,
"usedLibs": {
"esp32_4mb": [],
"esp32_4mb3f": [],
"esp32cam_4mb": [],
"esp8266_4mb": [],
"esp8266_1mb": [],
"esp8266_1mb_ota": [],
"esp8285_1mb": [],
"esp8285_1mb_ota": [],
"esp8266_2mb": [],
"esp8266_2mb_ota": []
}
}

View File

@@ -1,5 +1,5 @@
{
"menuSection": "Сенсоры",
"menuSection": "sensors",
"configItem": [
{
"global": 0,
@@ -115,8 +115,8 @@
"Pzem004pf",
"Pzem004cmd"
],
"title": "Счетчик электроэнергии PZEM 004 t версии 3.0 (с модбасом). Возможно подключение трех счетчиков к одной esp для трехфазных сетей. Для этого нужно настроить разные адреса modbus в платах pzem",
"moduleDesc": "Считает потраченную электроэнергию, измеряет напряжение, частоту, силу тока и прочие параметры",
"title": "Счетчик электроэнергии PZEM 004 t версии 3.0 (с модбасом)",
"moduleDesc": "Считает потраченную электроэнергию, измеряет напряжение, частоту, силу тока и прочие параметры. Возможно подключение трех счетчиков к одной esp для трехфазных сетей. Для этого нужно настроить разные адреса modbus в платах pzem",
"propInfo": {
"addr": "Адрес modbus",
"int": "Количество секунд между опросами датчика. Желателно устанавливать одинаковые интервалы для параметров (для одного адреса Pzem) что опрос происходил один раз, остальные из 500мс буфера.",
@@ -128,6 +128,8 @@
"defActive": true,
"usedLibs": {
"esp32_4mb": [],
"esp32_4mb3f": [],
"esp32cam_4mb": [],
"esp8266_4mb": [],
"esp8266_1mb": [],
"esp8266_1mb_ota": [],

View File

@@ -1,5 +1,5 @@
{
"menuSection": "Сенсоры",
"menuSection": "sensors",
"configItem": [
{
"global": 0,
@@ -64,6 +64,12 @@
"esp32_4mb": [
"rc-switch @ ^2.6.4"
],
"esp32_4mb3f": [
"rc-switch @ ^2.6.4"
],
"esp32cam_4mb": [
"rc-switch @ ^2.6.4"
],
"esp8266_4mb": [
"rc-switch @ ^2.6.4"
],

View File

@@ -39,18 +39,18 @@ class RTC : public IoTItem {
return this;
}
ulong getRtcUnixTime() {
return _watch->gettimeUnix();
unsigned long getRtcUnixTime() {
return _watch->gettimeUnix() - jsonReadInt(settingsFlashJson, F("timezone")) * 60 * 60;
}
void onModuleOrder(String &key, String &value) {
if (key == "setUTime") {
char *stopstring;
ulong ut = strtoul(value.c_str(), &stopstring, 10);
unsigned long ut = strtoul(value.c_str(), &stopstring, 10);
_watch->settimeUnix(ut);
SerialPrint("i", F("RTC"), "Устанавливаем время: " + value);
} else if (key == "setSysTime") {
_watch->settimeUnix(unixTime);
_watch->settimeUnix(unixTime + jsonReadInt(settingsFlashJson, F("timezone")) * 60 * 60);
SerialPrint("i", F("RTC"), F("Запоминаем системное время"));
}
}
@@ -63,6 +63,42 @@ class RTC : public IoTItem {
valTmp.valS = _watch->gettime(param[0].valS + " ");
return valTmp;
}
} else if (command == "setUnixTime") {
if (param.size() == 1) {
long ut = strtoul(param[0].valS.c_str(), nullptr, 10);
_watch->settimeUnix(ut);
return {};
}
} else if (command == "setTime") {
if (param.size() == 6) {
_watch->settime(param[0].valD, param[1].valD, param[2].valD, param[3].valD, param[4].valD, param[5].valD); //сек, мин, час, день, мес, год
return {};
}
} else if (command == "getTimeFloat") {
if (param.size() == 1) {
IoTValue valTmp;
_watch->gettime();
valTmp.isDecimal = true;
String type = param[0].valS;
if (type == "H") {
valTmp.valD = static_cast<float>(_watch->Hours);
} else if (type == "i") {
valTmp.valD = static_cast<float>(_watch->minutes);
} else if (type == "s") {
valTmp.valD = static_cast<float>(_watch->seconds);
} else if (type == "w") {
valTmp.valD = static_cast<float>(_watch->weekday);
} else if (type == "d") {
valTmp.valD = static_cast<float>(_watch->day);
} else if (type == "m") {
valTmp.valD = static_cast<float>(_watch->month);
} else if (type == "Y") {
valTmp.valD = static_cast<float>(_watch->year);
} else {
return {}; // Если переданный тип не поддерживается
}
return valTmp;
}
}
return {};

View File

@@ -1,5 +1,5 @@
{
"menuSection": "Сенсоры",
"menuSection": "sensors",
"configItem": [
{
"global": 0,
@@ -18,7 +18,7 @@
"ticker": 0,
"int": 5,
"btn-setUTime": "0",
"btn-setSysTime": "nil"
"btn-setSysTime": "nil"
}
],
"about": {
@@ -51,19 +51,44 @@
{
"name": "getTime",
"descr": "Получить строковое значение времени по указанному формату.",
"params": ["Формат как у функции date() в PHP"]
"params": [
"Формат как у функции date() в PHP"
]
},
{
"name": "setTime",
"descr": "Установить время через сценарии в формате сек, мин, час, день, мес, год.",
"params": [
"сек", "мин", "час", "день", "мес", "год"
]
},
{
"name": "setUnixTime",
"descr": "Установить время через сценарий в формате юникстайм",
"params": [
"rtc.setUnixTime('46489234') - параметр в виде строки"
]
},
{
"name": "getTimeFloat",
"descr": "Получить числовое значение времени по указанному формату",
"params": [
"s - Вернуть секунды, i - Вернуть минуты, H - Вернуть часы в 24-часовом формате, d - Вернуть день месяца, w - Вернуть день недели, m - Вернуть месяц, Y - Вернуть год(4 знака)"
]
}
]
},
"defActive": true,
"usedLibs": {
"esp32_4mb": ["https://github.com/tremaru/iarduino_RTC"],
"esp8266_4mb": ["https://github.com/tremaru/iarduino_RTC"],
"esp8266_1mb": ["https://github.com/tremaru/iarduino_RTC"],
"esp8266_1mb_ota": ["https://github.com/tremaru/iarduino_RTC"],
"esp8285_1mb": ["https://github.com/tremaru/iarduino_RTC"],
"esp8285_1mb_ota": ["https://github.com/tremaru/iarduino_RTC"],
"esp8266_2mb": ["https://github.com/tremaru/iarduino_RTC"],
"esp8266_2mb_ota": ["https://github.com/tremaru/iarduino_RTC"]
"esp32_4mb": [],
"esp32_4mb3f": [],
"esp32cam_4mb": [],
"esp8266_4mb": [],
"esp8266_1mb": [],
"esp8266_1mb_ota": [],
"esp8285_1mb": [],
"esp8285_1mb_ota": [],
"esp8266_2mb": [],
"esp8266_2mb_ota": []
}
}

View File

@@ -1,19 +1,19 @@
{
"menuSection": "Сенсоры",
"menuSection": "sensors",
"configItem": [
{
"name": "(S8) Cенсор качества воздуха",
"num": 3,
"type": "Reading",
"subtype": "S8co",
"id": "s8co",
"widget": "anydataPpm",
"page": "Сенсоры",
"descr": "S8_CO2",
"int": 15,
"round": 1,
"rxPin": 13,
"txPin": 15
"name": "(S8) Cенсор качества воздуха",
"num": 3,
"type": "Reading",
"subtype": "S8co",
"id": "s8co",
"widget": "anydataPpm",
"page": "Сенсоры",
"descr": "S8_CO2",
"int": 15,
"round": 1,
"rxPin": 13,
"txPin": 15
}
],
"about": {
@@ -35,9 +35,8 @@
},
"defActive": true,
"usedLibs": {
"esp32_4mb": [
],
"esp8266_4mb": [
]
"esp32_4mb": [],
"esp32_4mb3f": [],
"esp8266_4mb": []
}
}

View File

@@ -0,0 +1,432 @@
#include "Global.h"
#include "classes/IoTItem.h"
#include <SensirionI2CScd4x.h>
#include <SensirionCore.h>
#include <Wire.h>
// SensirionI2CScd4x scd4x;
SensirionI2CScd4x *scd4x = nullptr; // create an object of the CSD40 class
char errorMessageScd4x[256];
uint16_t errorCodeScd4x;
void printUint16Hex(uint16_t value)
{
Serial.print(value < 4096 ? "0" : "");
Serial.print(value < 256 ? "0" : "");
Serial.print(value < 16 ? "0" : "");
Serial.print(value, HEX);
}
void printSerialNumber(uint16_t serial0, uint16_t serial1, uint16_t serial2)
{
Serial.print("Serial: 0x");
printUint16Hex(serial0);
printUint16Hex(serial1);
printUint16Hex(serial2);
Serial.println();
}
// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку
SensirionI2CScd4x *instanceScd4x()
{
if (!scd4x)
{ // Если библиотека ранее инициализировалась, т о просто вернем указатель
// Инициализируем библиотеку
scd4x = new SensirionI2CScd4x();
Wire.begin();
scd4x->begin(Wire);
//Останавливаем периодический опрос датчика вбиблиотеке для запроса Сер.номера (на всякий случай)
// stop potentially previously started measurement
errorCodeScd4x = instanceScd4x()->stopPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
Serial.println(errorMessageScd4x);
}
//Запрашиваем и выводим серийный номер датчика
uint16_t serial0;
uint16_t serial1;
uint16_t serial2;
errorCodeScd4x = instanceScd4x()->getSerialNumber(serial0, serial1, serial2);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute getSerialNumber(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
else
{
printSerialNumber(serial0, serial1, serial2);
}
//Обратно стартуем периодический опрос датчика библиотекой (по описанию библиотеки каждые 5сек)
// Start Measurement
errorCodeScd4x = instanceScd4x()->startPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
Serial.println("Waiting for first measurement... ");
}
return scd4x;
}
class Scd40co2 : public IoTItem
{
private:
bool lowPeriodic = true;
bool autoCalibration = true;
//int targetCo2 = 0;
public:
Scd40co2(String parameters) : IoTItem(parameters)
{
jsonRead(parameters, F("lowPeriodic"), lowPeriodic);
jsonRead(parameters, F("autoCalibration"), autoCalibration);
// jsonRead(parameters, F("targetCo2"), targetCo2);
settingParameters();
}
void doByInterval()
{
// Read Measurement
uint16_t co2 = 0;
float temperature = 0.0f;
float humidity = 0.0f;
bool isDataReady = false;
//Запрашиваем библиотеку о готовности отправить запрос
errorCodeScd4x = instanceScd4x()->getDataReadyFlag(isDataReady);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute getDataReadyFlag(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
return;
}
if (!isDataReady)
{
return;
}
//Если все нормально забираем у библиотеки данные
errorCodeScd4x = instanceScd4x()->readMeasurement(co2, temperature, humidity);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute readMeasurement(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
else if (co2 == 0)
{
Serial.println("Invalid sample detected, skipping.");
}
else
{
Serial.print("Co2:");
Serial.print(co2);
Serial.print("\t");
// Serial.print("Temperature:");
// Serial.print(temperature);
// Serial.print("\t");
// Serial.print("Humidity:");
// Serial.println(humidity);
}
// value.valD = scd4x.readMeasurement(temperature);
value.valD = co2;
if (value.valD < 5000)
regEvent(value.valD, "Scd40co2");
else
SerialPrint("E", "Sensor Scd40co2", "Error", _id);
}
//Хук для обработки кнопки
void onModuleOrder(String &key, String &value)
{
if (key == "Recalibration")//название кнопки btn-Recalibration
{
SerialPrint("i", F("Sensor Scd40co2"), "User run calibration, targetCo2: " + value);
Recalibration(value.toInt());
}
}
void Recalibration(int targetCo2)
{
//Останавливаем периодический опрос датчика вбиблиотеке для запроса Сер.номера (на всякий случай)
// stop potentially previously started measurement
errorCodeScd4x = instanceScd4x()->stopPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
Serial.println(errorMessageScd4x);
}
delay(500); // Из описания performForcedRecalibration 2. Stop periodic measurement. Wait 500 ms.
uint16_t frcCorrection;
errorCodeScd4x = instanceScd4x()->performForcedRecalibration(targetCo2, frcCorrection);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute performForcedRecalibration(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
else
{
Serial.println("performForcedRecalibration(): OK!");
SerialPrint("i", F("Sensor Scd40co2"), "Calibration is OK, frcCorrection: " + String(frcCorrection));
}
//Обратно стартуем периодический опрос датчика библиотекой (по описанию библиотеки каждые 5сек)
// Start Measurement
errorCodeScd4x = instanceScd4x()->startPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
Serial.println("Waiting for first measurement... ");
}
void settingParameters()
{
//Останавливаем периодический опрос датчика вбиблиотеке для запроса Сер.номера (на всякий случай)
// stop potentially previously started measurement
errorCodeScd4x = instanceScd4x()->stopPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
Serial.println(errorMessageScd4x);
}
errorCodeScd4x = instanceScd4x()->startLowPowerPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute startLowPowerPeriodicMeasurement(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
else
{
Serial.println("startLowPowerPeriodicMeasurement(): OK!");
}
errorCodeScd4x = instanceScd4x()->setAutomaticSelfCalibration((uint16_t)autoCalibration);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute setAutomaticSelfCalibration(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
else
{
Serial.println("setAutomaticSelfCalibration(): OK!");
}
//Обратно стартуем периодический опрос датчика библиотекой (по описанию библиотеки каждые 5сек)
// Start Measurement
errorCodeScd4x = instanceScd4x()->startPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
Serial.println("Waiting for first measurement... ");
}
~Scd40co2(){};
};
class Scd40t : public IoTItem
{
private:
int offsetT;
public:
Scd40t(String parameters) : IoTItem(parameters)
{
jsonRead(parameters, F("offset"), offsetT);
setTemperatureOffset();
}
void doByInterval()
{
// Read Measurement
uint16_t co2 = 0;
float temperature = 0.0f;
float humidity = 0.0f;
bool isDataReady = false;
errorCodeScd4x = instanceScd4x()->getDataReadyFlag(isDataReady);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute getDataReadyFlag(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
return;
}
if (!isDataReady)
{
return;
}
errorCodeScd4x = instanceScd4x()->readMeasurement(co2, temperature, humidity);
if (errorCodeScd4x)
{
Serial.print("errorCodeScd4x trying to execute readMeasurement(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
else if (co2 == 0)
{
Serial.println("Invalid sample detected, skipping.");
}
else
{
// Serial.print("Co2:");
// Serial.print(co2);
// Serial.print("\t");
Serial.print("Temperature:");
Serial.print(temperature);
Serial.print("\t");
// Serial.print("Humidity:");
// Serial.println(humidity);
}
// value.valD = scd4x.readMeasurement(temperature);
value.valD = temperature;
if (value.valD < 124)
regEvent(value.valD, "Scd40t");
else
SerialPrint("E", "Sensor Scd40t", "Error", _id);
}
void setTemperatureOffset()
{
//Останавливаем периодический опрос датчика вбиблиотеке для запроса Сер.номера (на всякий случай)
// stop potentially previously started measurement
errorCodeScd4x = instanceScd4x()->stopPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
Serial.println(errorMessageScd4x);
}
errorCodeScd4x = instanceScd4x()->setTemperatureOffset((uint16_t)offsetT);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute setTemperatureOffset(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
else
{
Serial.println("setTemperatureOffset(): OK!");
}
//Обратно стартуем периодический опрос датчика библиотекой (по описанию библиотеки каждые 5сек)
// Start Measurement
errorCodeScd4x = instanceScd4x()->startPeriodicMeasurement();
if (errorCodeScd4x)
{
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
Serial.println("Waiting for first measurement... ");
}
~Scd40t(){};
};
class Scd40h : public IoTItem
{
public:
Scd40h(String parameters) : IoTItem(parameters)
{
}
void doByInterval()
{
// Read Measurement
uint16_t co2 = 0;
float temperature = 0.0f;
float humidity = 0.0f;
bool isDataReady = false;
errorCodeScd4x = instanceScd4x()->getDataReadyFlag(isDataReady);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute getDataReadyFlag(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
return;
}
if (!isDataReady)
{
return;
}
errorCodeScd4x = instanceScd4x()->readMeasurement(co2, temperature, humidity);
if (errorCodeScd4x)
{
Serial.print("Error trying to execute readMeasurement(): ");
errorToString(errorCodeScd4x, errorMessageScd4x, 256);
Serial.println(errorMessageScd4x);
}
else if (co2 == 0)
{
Serial.println("Invalid sample detected, skipping.");
}
else
{
// Serial.print("Co2:");
// Serial.print(co2);
// Serial.print("\t");
// Serial.print("Temperature:");
// Serial.print(temperature);
// Serial.print("\t");
Serial.print("Humidity:");
Serial.println(humidity);
Serial.print("\t");
}
// value.valD = scd4x.readMeasurement(temperature);
value.valD = humidity;
if (value.valD < 100)
regEvent(value.valD, "Scd40h");
else
SerialPrint("E", "Sensor Scd40h", "Error", _id);
}
~Scd40h(){};
};
void *getAPI_Scd40(String subtype, String param)
{
if (subtype == F("Scd40co2"))
{
// hdc1080.begin(0x40);
// scd4x->begin(Wire);
// scd4x.begin(Wire);
return new Scd40co2(param);
}
else if (subtype == F("Scd40t"))
{
// hdc1080.begin(0x40);
// scd4x->begin(Wire);
return new Scd40t(param);
}
else if (subtype == F("Scd40h"))
{
// hdc1080.begin(0x40);
// scd4x->begin(Wire);
return new Scd40h(param);
// } else {
// return nullptr;
}
return nullptr;
}

Some files were not shown because too many files have changed in this diff Show More