AAOffice_kitchen/AAOffice_kitchen.ino

626 lines
21 KiB
C++
Raw 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>
//Наша кнопочка при нажатии на которую произойдет вызов wifi менеджера и перезагрузка в станцию
#define TRIGGER_PIN 39
#define safetyPin 33
#define buzzerPin 23
#define VersionSW 2
extern const unsigned char image_arrow[77], image_x[77], image_error[77], image_wateok[77], image_connect[77], image_wifi[77];
//const unsigned int VersionSW = 5; //65536 Версия прошивки
byte errorID = 0; //Сбор и вывод ошибок
//5
WiFiManager wm; // обьект менеджера
WiFiManagerParameter custom_field;
Preferences OTApreferences; //Обьект хранения настроек хеша прошивки
StaticJsonDocument<200> doc;
WiFiClient espClient;
PubSubClient MqttClient(espClient);
IPAddress IpMqtt;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "0.ua.pool.ntp.org", 7200, 60000); //Собственно сервер времени смещение и частоат запроса, но он вручную
const PROGMEM char *willmess = "{\"conn\":\"err\"}";
const PROGMEM char *mqttHostName = "cctv.automation.art"; //Хостнейм брокера 192.168.89.210 cctv.automation.art:8889
unsigned int mqttPort = 8889; // 1883 //Порт брокера 1883
const PROGMEM char *mqttLogin = NULL, //Логин пароль - необходимо сменить код при connect()
*mqttPass = NULL;
const char *mqttIPHost; //тут хранится IP хоста по хостнейму
unsigned long timingUpdate, timingReqSensor, timingSendMqtt; //Таймеры для millis()
int PROGMEM nextM5Update = 450000; //каждые 7.5 минут запрос обновления с сервера //опрос датчиков раз в 10 секунд
short int PROGMEM nextMqttSend = 500; //Отправка
String getMacAddress();
String macc = getMacAddress();
bool buzFlag = true;
bool mqttSendFlag = false;
bool safetyStateNow = true, safetyStateOld = true;
int reqCounter = 0;
short int rssi = 0;
bool flagHIGH = true;
char bufTopic[140], bufWillTopic[150];
volatile short int debounce = 0;
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);
delay(50);
// Wire.begin(25, 21); //Пины для I2c на ATOM
M5.dis.setBrightness(30);
pinMode(safetyPin, INPUT_PULLDOWN); //Пин датчика для работы
pinMode(buzzerPin, OUTPUT);
digitalWrite(buzzerPin, LOW);
attachInterrupt(digitalPinToInterrupt(safetyPin), detectWaterOn, CHANGE);
// LOW прерывание будет запущено, если на контакте будет значение «LOW»
// HIGH прерывание будет запущено, если на контакте будет значение «HIGH»
// CHANGE прерывание будет запущено, если значение на контакте изменится (например, с «LOW» на «HIGH» или с «HIGH» на «LOW»)
// FALLING прерывание будет запущено, если значение на контакте изменится с «HIGH» на «LOW»
// RISING прерывание будет запущено, если значение на контакте изменится с «LOW» на «HIGH»
WiFi.mode(WIFI_STA);
Serial.setDebugOutput(true);
String topicTemp = "aastudio/" + macc + "/data/0";
String willTopicTemp = "aastudio/" + macc + "/status";
// String angleTopicTemp = topicTemp + "/setangle";
Serial.println(topicTemp);
Serial.println(willTopicTemp);
////Serial.println(angleTopicTemp);
topicTemp.toCharArray(bufTopic, topicTemp.length() + 1);
willTopicTemp.toCharArray(bufWillTopic, willTopicTemp.length() + 1);
//angleTopicTemp.toCharArray(bufAngleTopic, angleTopicTemp.length() + 1);
disPreSet((uint8_t *)image_connect, 0, 0);
//Serial.println("\n Starting station");
pinMode(TRIGGER_PIN, INPUT);
// wm.resetSettings(); // wipe settings
// add a custom input field
//int customFieldLength = 40;
// new (&custom_field) WiFiManagerParameter("customfieldid", "Custom Field Label", "Custom Field Value", customFieldLength,"placeholder=\"Custom Field Placeholder\"");
// test custom html input type(checkbox)
// new (&custom_field) WiFiManagerParameter("customfieldid", "Custom Field Label", "Custom Field Value", customFieldLength,"placeholder=\"Custom Field Placeholder\" type=\"checkbox\""); // custom html type
// test custom html(radio)
const char *custom_radio_str = "<br/><label for='customfieldid'>Custom Field Label</label><input type='radio' name='customfieldid' value='1' checked> One<br><input type='radio' name='customfieldid' value='2'> Two<br><input type='radio' name='customfieldid' value='3'> Three";
new (&custom_field) WiFiManagerParameter(custom_radio_str); // custom html input
wm.addParameter(&custom_field);
wm.setSaveParamsCallback(saveParamCallback);
// custom menu via array or vector
// menu tokens, "wifi","wifinoscan","info","param","close","sep","erase","restart","exit" (sep is seperator) (if param is in menu, params will not show up in wifi page!)
// const char* menu[] = {"wifi","info","param","sep","restart","exit"};
// wm.setMenu(menu,6);
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(60); // 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(10); // 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.setBreakAfterConfig(true); // always exit configportal even if wifi save fails
wm.setWiFiAutoReconnect(true); // if true, enable autoreconnecting
bool res;
// res = wm.autoConnect(); // auto generated AP name from chipid
// res = wm.autoConnect("AutoConnectAP"); // anonymous ap
res = wm.autoConnect("KitchenM5Portal", "12345678"); // Подключение к анонимной точке доступа
if (!res)
{
Serial.println("Failed to connect or hit timeout");
//ESP.restart();
}
else
{
Serial.println("connected - OK");
}
//Запрос IP сервера MQTT и установка сервера
setMqttServer();
reqNtpTime();
}
//Установка сервера и порта
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)
{
delay(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();
ESP.restart();
}
String getParam(String name)
{
String value;
if (wm.server->hasArg(name))
{
value = wm.server->arg(name);
}
return value;
}
void saveParamCallback()
{
//Serial.\println("[CALLBACK] saveParamCallback fired");
//Serial.\println("PARAM customfieldid = " + getParam("customfieldid"));
getParam("customfieldid");
}
//Обновление прошивки, происходит проверка и загрузка
//Делается 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); //Вывод JSON строки в консоль
JsonData.toCharArray(resultString, JsonData.length() + 1);
MqttClient.publish(bufTopic, resultString, true);
}
if (sendStatus == true)
{
char *conn;
switch (statusConn)
{
case 1:
conn = "on";
break;
case 2:
conn = "off";
break;
case 3:
conn = "slp";
break;
default:
break;
}
doc["conn"] = conn;
doc["rssi"] = rssi;
doc["bsid"] = WiFi.BSSIDstr();
//doc["lasterror"] = errorID;
doc["ts"] = timeNow;
doc["sv"] = VersionSW;
JsonData = "";
serializeJson(doc, JsonData);
Serial.println(JsonData); //Вывод JSON строки в консоль
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())
{
Serial.println("MQTT reconnect...");
circle++;
if(circle == 2)
{
disPreSet((uint8_t *)image_error, 0, 0);
break;
}
const char *clientId = macc.c_str();
//macc += String(random(0xffff), HEX);
if(MqttClient.connect(clientId, 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;
delay(1000);
}
}
}
void loop()
{
M5.update();
if (buzFlag == true)
{
disPreSet((uint8_t *)image_arrow, 0, 0);
safetyStateNow = false;
flagHIGH = true;
buzAlarm(true);
Serial.println(" buzFlag == true ");
}
if (digitalRead(safetyPin) == HIGH && flagHIGH == true)
{
Serial.println(" digitalRead(safetyPin) == HIGH ");
buzFlag = false;
buzAlarm(false);
safetyStateNow = true;
disPreSet((uint8_t *)image_wateok, 0, 0);
flagHIGH = false;
attachInterrupt(digitalPinToInterrupt(safetyPin), detectWaterOn, CHANGE);
// LOW прерывание будет запущено, если на контакте будет значение «LOW»
// HIGH прерывание будет запущено, если на контакте будет значение «HIGH»
// CHANGE прерывание будет запущено, если значение на контакте изменится (например, с «LOW» на «HIGH» или с «HIGH» на «LOW»)
// FALLING прерывание будет запущено, если значение на контакте изменится с «HIGH» на «LOW»
// RISING прерывание будет запущено, если значение на контакте изменится с «LOW» на «HIGH»
}
if (safetyStateNow != safetyStateOld)
{
SendMqttReq(true, true, 1);
safetyStateOld = safetyStateNow;
}
if (WiFi.status() == WL_CONNECTED)
{
if (!MqttClient.connected())
{
errorID = 7;
reconnectMqtt();
}
}
else
{
Serial.println(WiFi.status());
disPreSet((uint8_t *)image_wifi, 0, 0);
errorID = 6;
delay(3000);
ESP.restart();
}
// 40 секунд и происходит сброс настроек WIFI
if (M5.Btn.wasReleasefor(40000))
{
checkButton();
}
if (M5.Btn.wasReleasefor(2000))
{
buzAlarm(false);
}
//По таймеру запруск обновления прошивки
if (millis() - timingUpdate > nextM5Update)
{
reqNtpTime();
OTAUpdate();
// //Serial.print("OTAUpdate() - ");
// //Serial.println(millis());
timingUpdate = millis();
}
//Таймер отправки данных в брокер
MqttClient.loop();
}
int disconnectMQTT()
{
SendMqttReq(false, true, 2);
MqttClient.disconnect();
return 0;
}
// disPreSet((uint8_t *)image_wateok,0,0);
int disPreSet(uint8_t *image_what, int8_t x, int8_t y)
{
// M5.dis.fillpix(0x000000);
M5.dis.clear();
delay(100);
M5.dis.displaybuff(image_what, x, y);
delay(100);
return 0;
}
int buzAlarm(bool buzFlag)
{
Serial.print("Buzzer: ");
Serial.println(buzFlag);
if (buzFlag == true)
{
digitalWrite(buzzerPin, HIGH);
delay(100);
digitalWrite(buzzerPin, LOW);
delay(100);
digitalWrite(buzzerPin, HIGH);
delay(200);
digitalWrite(buzzerPin, LOW);
delay(200);
}
else
{
digitalWrite(buzzerPin, LOW);
}
return 0;
}