AAOffice_kitchen/AAOffice_kitchen.ino

687 lines
21 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
Отправка данных на MQTT про обновление
*/
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <Preferences.h> //Сохранение настроек хеша прошивки
#include <HTTPClient.h>
#include <ESP32httpUpdate.h> //Библиотека ОТА обновлений
#include "M5Atom.h" //Библиотека атома для функции Led и Кнопки, можно упразднить и убрать
#include <WiFiUdp.h> //Udp клиент
#include <NTPClient.h> //NTP запрос времени
#include <TimeLib.h> //Внутреннее время
#include <Wire.h> //Библиотека дял I2C
#include <PubSubClient.h> //Mtqq
#include <ArduinoJson.h> //Упакова в JSon - удобная библиотека
#include <ESPmDNS.h>
#include <Ticker.h>
//Наша кнопочка при нажатии на которую произойдет вызов wifi менеджера и перезагрузка в станцию
#define TRIGGER_PIN 39
#define safetyPin 33
#define buzzerPin 23
#define VersionSW 7
//3 - версия с прошивкой.
//5 - изменение картинок
//7 - изменение рисунков
extern const unsigned char image_arrow[77], image_error[77], image_wateok[77], image_connect[77], image_wifi[77];
//const unsigned int VersionSW = 5; //65536 Версия прошивки
byte errorID = 0; //Сбор и вывод ошибок
Ticker BuzTicker;
WiFiManager wm; // обьект менеджера
//WiFiManagerParameter custom_field;
Preferences OTApreferences; //Обьект хранения настроек хеша прошивки
StaticJsonDocument<200> doc;
WiFiClient espClient;
PubSubClient MqttClient(espClient);
IPAddress IpMqtt(192, 168, 89, 210);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "192.168.89.210", 0, 60000); //Собственно сервер времени смещение и частоат запроса, но он вручную
const char *wmhostname = "KitchenWater";
const PROGMEM char *willmess = "{\"conn\":\"err\"}";
//const PROGMEM char *mqttHostName = "192.168.0.111"; //Хостнейм брокера 192.168.89.210 cctv.automation.art:8889
unsigned int mqttPort = 1883; // 1883 //Порт брокера 1883
const PROGMEM char *mqttLogin = "AA_Lab", *mqttPass = "automation.art$";
const char *mqttIPHost;
//тут хранится IP хоста по хостнейму
unsigned long timingUpdate, timingSendStatus; //Таймеры для millis()
int PROGMEM nextM5Update = 18050000; //раз в 4 чаасов //каждые 7.5 минут запрос обновления с сервера //опрос датчиков раз в 10 секунд
int PROGMEM nextSendStatus = 10000;
String getMacAddress();
String macc = getMacAddress();
bool buzFlag = false;
bool mqttSendFlag = false;
bool safetyStateNow = true, safetyStateOld = true;
int reqCounter = 0;
short int rssi = 0;
bool flagHIGH = false;
char bufTopic[140], bufWillTopic[150];
volatile short int debounce = 0;
TaskHandle_t Task1;
TaskHandle_t Task2;
void Task1code(void *pvParameters);
void Task2code(void *pvParameters);
void buzAlarm();
int SendMqttReq(bool, bool, byte);
void IRAM_ATTR detectWaterOn()
{
if (millis() - debounce >= 100 && digitalRead(safetyPin) == LOW)
{
debounce = millis();
buzFlag = true;
detachInterrupt(digitalPinToInterrupt(safetyPin));
}
}
//Настройки
void setup()
{
Serial.begin(115200);
M5.begin(true, false, true);
vTaskDelay(50);
// Wire.begin(25, 21); //Пины для I2c на ATOM
M5.dis.setBrightness(50);
disPreSet((uint8_t *)image_connect, 0, 0);
pinMode(safetyPin, INPUT); //Пин датчика для работы
pinMode(buzzerPin, OUTPUT);
digitalWrite(buzzerPin, LOW);
detectWaterOn();
Serial.println("--------");
Serial.println(digitalRead(safetyPin));
Serial.println("--------");
xTaskCreatePinnedToCore(
Task1code, /* Функция задачи */
"Task1", /* Название задачи */
3024, /* Размер стека задачи */
NULL, /* Параметр задачи */
0, /* Приоритет задачи */
&Task1, /* Идентификатор задачи, чтобы ее можно было отслеживать */
0); /* Ядро для выполнения задачи (0) */
vTaskDelay(100);
// Создаем задачу с кодом из функции Task2code(),
// с приоритетом 1 и выполняемую на ядре 1:
xTaskCreatePinnedToCore(
Task2code, /* Функция задачи */
"Task2", /* Название задачи */
7024, /* Размер стека задачи */
NULL, /* Параметр задачи */
1, /* Приоритет задачи */
&Task2, /* Идентификатор задачи, чтобы ее можно было отслеживать */
1); /* Ядро для выполнения задачи (1) */
vTaskDelay(100);
attachInterrupt(digitalPinToInterrupt(safetyPin), detectWaterOn, CHANGE);
// LOW прерывание будет запущено, если на контакте будет значение «LOW»
// HIGH прерывание будет запущено, если на контакте будет значение «HIGH»
// CHANGE прерывание будет запущено, если значение на контакте изменится (например, с «LOW» на «HIGH» или с «HIGH» на «LOW»)
// FALLING прерывание будет запущено, если значение на контакте изменится с «HIGH» на «LOW»
// RISING прерывание будет запущено, если значение на контакте изменится с «LOW» на «HIGH»
// Serial.setDebugOutput(true);
String topicTemp = "aastudio/sens/" + macc + "/data/0";
String willTopicTemp = "aastudio/sens/" + macc + "/status";
Serial.println(topicTemp);
Serial.println(willTopicTemp);
topicTemp.toCharArray(bufTopic, topicTemp.length() + 1);
willTopicTemp.toCharArray(bufWillTopic, willTopicTemp.length() + 1);
//angleTopicTemp.toCharArray(bufAngleTopic, angleTopicTemp.length() + 1);
pinMode(TRIGGER_PIN, INPUT);
// wm.resetSettings(); // wipe settings
std::vector<const char *> menu = {"wifi", "info", "param", "sep", "restart", "exit"};
wm.setMenu(menu);
// set dark theme
wm.setClass("invert");
//set static ip
// wm.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); // set static ip,gw,sn
// wm.setShowStaticFields(true); // force show static ip fields
// wm.setShowDnsFields(true); // force show dns field always
// wm.setConnectTimeout(20); // how long to try to connect for before continuing
wm.setConfigPortalTimeout(35); // auto close configportal after n seconds
// wm.setCaptivePortalEnable(false); // disable captive portal redirection
// wm.setAPClientCheck(true); // avoid timeout if client connected to softap
// wifi scan settings
// wm.setRemoveDuplicateAPs(false); // do not remove duplicate ap names (true)
wm.setMinimumSignalQuality(15); // set min RSSI (percentage) to show in scans, null = 8%
// wm.setShowInfoErase(false); // do not show erase button on info page
wm.setScanDispPerc(true); // show RSSI as percentage not graph icons
//wm.setConfigPortalBlocking(false);
// wm.setBreakAfterConfig(true); // always exit configportal even if wifi save fails
wm.setWiFiAutoReconnect(true); // if true, enable autoreconnecting
//wm.setHostname(wmhostname);
bool res;
res = wm.autoConnect("KitchenM5Portal", "12345678");
if (!res)
{
Serial.println("Failed to connect or hit timeout");
//ESP.restart();
}
else
{
Serial.println("WIFI - OK");
}
reqNtpTime();
setServCall(IpMqtt);
if (!MqttClient.connected())
{
reconnectMqtt();
}
else
{
SendMqttReq(true, true, 1);
}
}
// Установка сервера и порта
// void setMqttServer()
// {
// mdns_init();
// IPAddress IpMqtt, ipaddr;
// ipaddr = MDNS.queryHost(mqttHostName); // .local omitted
// // //Serial.println(ipaddr.toString());
// if (ipaddr.toString() == "0.0.0.0")
// {
// Serial.println("Trying again to resolve mDNS");
// int err = WiFi.hostByName(mqttHostName, IpMqtt);
// if (err == 1)
// {
// vTaskDelay(50);
// setServCall(IpMqtt);
// }
// else
// {
// errorID = 1;
// Serial.print("Error code hostByName(): ");
// Serial.println(err);
// }
// }
// else
// {
// setServCall(ipaddr);
// }
// }
void setServCall(IPAddress SetIpaddr)
{
MqttClient.setServer(SetIpaddr, mqttPort);
// MqttClient.setCallback(callback);
}
//Функция получения данных из MQTT если мы подпишемся на топики
// void callback(char *topic, byte *payload, unsigned int length)
// {
// //Serial.print("Message arrived [");
// //Serial.print(topic);
// //Serial.print("] ");
// for (int i = 0; i < length; i++)
// {
// //Serial.print((char)payload[i]);
// }
// //Serial.println();
// }
//Запрос времени NTP и установка локлаьного времени
void reqNtpTime()
{
timeClient.update();
setTime(timeClient.getEpochTime());
Serial.println(timeClient.getEpochTime());
Serial.println("<=ntp====now=>");
time_t t = now();
Serial.println(t);
}
//Нажатие кнопки для сброса
void checkButton()
{
Serial.println("Button RESET Pressed");
disconnectMQTT();
wm.resetSettings();
//
wm.startConfigPortal();
// ESP.restart();
}
String getParam(String name)
{
String value;
if (wm.server->hasArg(name))
{
value = wm.server->arg(name);
}
return value;
}
//Обновление прошивки, происходит проверка и загрузка
//Делается Get запрос на хостинг проверяется хеш, если хеш
void OTAUpdate()
{
////Serial.\println("OTAUpdate()");
bool flagOTA = false;
String keyOTA;
String payload;
OTApreferences.begin("ota-config");
if (WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
String serverPath = "http://meteosence.s-host.net/airqa/airquality.php?meteopas=345sd4fJfj5Jjfm594d45de&mac=" + macc + "&meteodata=gethash";
http.begin(serverPath.c_str());
int httpResponseCode = http.GET();
if (httpResponseCode > 0)
{
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
payload = http.getString();
Serial.println(payload);
if (payload != "errno" || payload != "errfi")
{
keyOTA = OTApreferences.getString("md5HashOTA");
if (keyOTA.length() <= 0)
{
OTApreferences.putString("md5HashOTA", "undifined");
}
keyOTA = OTApreferences.getString("md5HashOTA");
if (payload != keyOTA)
{
flagOTA = true;
OTApreferences.putString("md5HashOTA", payload);
//Serial.println("flagOTA = true;");
}
}
else
{
//Serial.println("Hosting return error HASH or error REQUEST");
errorID = 2;
}
}
else
{
errorID = 3;
Serial.print("Error code HTTP: ");
Serial.println(httpResponseCode);
}
// Free resources
http.end();
}
else
{
errorID = 4;
Serial.println("WiFi Disconnected");
}
if (flagOTA == true)
{
flagOTA = false;
//Serial.println("flagOTA = false;");
t_httpUpdate_return ret = ESPhttpUpdate.update("http://meteosence.s-host.net/airqa/kitchen/kithenatoms.bin");
//После update ничего не происходит, такая вот особенность.
//Если все прошло хорошо, перезагрузка на новую прошивку
//Serial.print("ret ");
//Serial.println(ret);
switch (ret)
{
case HTTP_UPDATE_FAILED:
//Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
//Serial.println(ESPhttpUpdate.getLastError());
//Serial.println(ESPhttpUpdate.getLastErrorString().c_str());
//Serial.println("HTTP_UPDATE_FAILD Error");
ESP.restart();
break;
case HTTP_UPDATE_NO_UPDATES:
//Serial.println("HTTP_UPDATE_NO_UPDATES");
ESP.restart();
break;
case HTTP_UPDATE_OK:
//Serial.println("HTTP_UPDATE_OK");
ESP.restart();
break;
}
}
OTApreferences.end();
}
//Получение мак адреса
String getMacAddress()
{
uint8_t baseMac[6];
esp_read_mac(baseMac, ESP_MAC_WIFI_STA);
char baseMacChr[18] = {0};
sprintf(baseMacChr, "%02X%02X%02X%02X%02X%02X", baseMac[0], baseMac[1], baseMac[2], baseMac[3], baseMac[4], baseMac[5]);
return String(baseMacChr);
}
//Отправка данных по MQTT
int SendMqttReq(bool sendVal = true, bool sendStatus = true, byte statusConn = 1)
{
if (mqttSendFlag == true)
{
char resultString[200];
String JsonData = "";
rssi = WiFi.RSSI();
unsigned long timeNow = now();
if (sendVal == true)
{
//Serial.println("Data in SendMqttReq()");
// doc["mac"] = String(getMacAddress());
doc["val"] = (int)digitalRead(safetyPin);
doc["ts"] = timeNow;
serializeJson(doc, JsonData);
doc.remove("val");
doc.remove("ts");
doc.clear();
doc.garbageCollect();
Serial.println(JsonData);
JsonData.toCharArray(resultString, JsonData.length() + 1);
MqttClient.publish(bufTopic, resultString, true);
}
if (sendStatus == true)
{
const char *conn;
switch (statusConn)
{
case 1:
conn = "on";
break;
case 2:
conn = "off";
break;
case 3:
conn = "slp";
break;
case 4:
conn = "upd";
break;
default:
break;
}
doc["conn"] = conn;
doc["rssi"] = rssi;
doc["bsid"] = WiFi.BSSIDstr();
//doc["lasterror"] = errorID;
doc["ts"] = timeNow;
doc["exp"] = timeNow+15;
doc["sv"] = VersionSW;
JsonData = "";
serializeJson(doc, JsonData);
Serial.println(JsonData);
doc.clear();
doc.garbageCollect();
JsonData.toCharArray(resultString, JsonData.length() + 1);
MqttClient.publish(bufWillTopic, resultString, true);
Serial.println("SentToTopic - ok");
}
}
return 0;
}
//Переподключение при петери связи с MQTT
//10 раз проверили и вернулись в общий цикл что бы вдруг что втянуть обновления
void reconnectMqtt()
{
//clientId += String(random(0xffff), HEX);
//clientId.c_str()
//MqttClient.connect(macc.c_str(), mqttLogin, mqttPass
byte circle = 0;
while (!MqttClient.connected())
{
const char *clientId = macc.c_str();
if (MqttClient.connect(clientId, mqttLogin, mqttPass, bufWillTopic, 2, true, willmess))
{
mqttSendFlag = true;
SendMqttReq(true, true, 1);
}
else
{
Serial.print("MqttClient.state() = ");
Serial.println(MqttClient.state());
/*
-4 : MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time
-3 : MQTT_CONNECTION_LOST - the network connection was broken
-2 : MQTT_CONNECT_FAILED - the network connection failed
-1 : MQTT_DISCONNECTED - the client is disconnected cleanly
0 : MQTT_CONNECTED - the client is connected
1 : MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT
2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier
3 : MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection
4 : MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected
5 : MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect
*/
errorID = 5;
mqttSendFlag = false;
}
circle++;
if (circle == 3)
{
break;
}
}
}
void loop()
{
}
int disconnectMQTT()
{
Serial.println("Disconnect MQTT server...");
SendMqttReq(false, true, 2);
MqttClient.disconnect();
return 0;
}
int disPreSet(uint8_t *image_what, int8_t x, int8_t y)
{
// M5.dis.fillpix(0x000000);
// M5.dis.clear();
// vTaskDelay(100);
M5.dis.displaybuff(image_what, x, y);
vTaskDelay(500);
return 0;
}
//ядро 0 ядро
void Task1code(void *pvParameters)
{
for (;;)
{
if (buzFlag == true && flagHIGH == false)
{
safetyStateNow = false;
flagHIGH = true;
BuzTicker.attach_ms(900, buzAlarm);
Serial.println(" buzFlag == true && flagHIGH == false ");
}
if (digitalRead(safetyPin) == HIGH && flagHIGH == true)
{
Serial.println(" digitalRead(safetyPin) == HIGH && flagHIGH == true");
buzFlag = false;
safetyStateNow = true;
BuzTicker.detach();
flagHIGH = false;
attachInterrupt(digitalPinToInterrupt(safetyPin), detectWaterOn, CHANGE);
}
if (M5.Btn.pressedFor(90000))
{
checkButton();
}
if (M5.Btn.wasReleasefor(2000))
{
BuzTicker.detach();
}
MqttClient.loop();
vTaskDelay(10);
}
}
// loop 1 ядро
void Task2code(void *pvParameters)
{
for (;;)
{
M5.update();
// wm.process();
if (WiFi.status() != WL_CONNECTED)
{
disPreSet((uint8_t *)image_wifi, 0, 0);
}
// if (WiFi.status() != WL_CONNECTED)
// {
// Serial.println(WiFi.status());
// disPreSet((uint8_t *)image_wifi, 0, 0);
// errorID = 6;
// delay(3000);
// ESP.restart();
// }
if (safetyStateNow != safetyStateOld)
{
Serial.println("safetyStateNow != safetyStateOld");
SendMqttReq(true, true, 1);
safetyStateOld = safetyStateNow;
}
if (buzFlag == false)
{
disPreSet((uint8_t *)image_wateok, 0, 0);
}
else
{
disPreSet((uint8_t *)image_arrow, 0, 0);
}
if (millis() - timingSendStatus > nextSendStatus)
{
SendMqttReq(false, true, 1);
timingSendStatus = millis();
}
//По таймеру запруск обновления прошивки
if (millis() - timingUpdate > nextM5Update)
{
SendMqttReq(false, true, 4);
reqNtpTime();
OTAUpdate();
SendMqttReq(false, true, 1);
timingUpdate = millis();
}
//Таймер отправки данных в брокер
if (!MqttClient.connected())
{
errorID = 7;
disPreSet((uint8_t *)image_error, 0, 0);
reconnectMqtt();
}
vTaskDelay(10);
}
}
void buzAlarm()
{
digitalWrite(buzzerPin, HIGH);
vTaskDelay(100);
digitalWrite(buzzerPin, LOW);
vTaskDelay(100);
digitalWrite(buzzerPin, HIGH);
vTaskDelay(200);
digitalWrite(buzzerPin, LOW);
}