2021-03-15 22:30:39 +02:00
/*
О т п р а в к а д а н н ы х н а MQTT п р о о б н о в л е н и е
*/
2021-03-19 23:13:43 +02:00
2021-03-15 22:30:39 +02:00
# 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 ; // обьект менеджера
2021-03-19 23:13:43 +02:00
//WiFiManagerParameter custom_field;
2021-03-15 22:30:39 +02:00
Preferences OTApreferences ; //Обьект хранения настроек хеша прошивки
StaticJsonDocument < 200 > doc ;
WiFiClient espClient ;
PubSubClient MqttClient ( espClient ) ;
IPAddress IpMqtt ;
WiFiUDP ntpUDP ;
NTPClient timeClient ( ntpUDP , " 0.ua.pool.ntp.org " , 7200 , 60000 ) ; //Собственно сервер времени смещение и частоат запроса, но он вручную
2021-03-19 23:13:43 +02:00
const char * wmhostname = " KitchenWater " ;
2021-03-15 22:30:39 +02:00
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»
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)
2021-03-19 23:13:43 +02:00
//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);
2021-03-15 22:30:39 +02:00
// 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
2021-03-19 23:13:43 +02:00
wm . setClass ( " invert " ) ;
2021-03-15 22:30:39 +02:00
//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
2021-03-19 23:13:43 +02:00
// wm.setConfigPortalTimeout(60); // auto close configportal after n seconds
2021-03-15 22:30:39 +02:00
// 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
2021-03-19 23:13:43 +02:00
wm . setScanDispPerc ( true ) ; // show RSSI as percentage not graph icons
2021-03-15 22:30:39 +02:00
// wm.setBreakAfterConfig(true); // always exit configportal even if wifi save fails
2021-03-19 23:13:43 +02:00
// wm.setWiFiAutoReconnect(true); // if true, enable autoreconnecting
// wm.setConfigPortalBlocking(false);
wm . setHostname ( wmhostname ) ;
wm . setConfigPortalBlocking ( false ) ;
2021-03-15 22:30:39 +02:00
bool res ;
2021-03-19 23:13:43 +02:00
// res = wm.autoConnect("KitchenM5Portal", "12345678");
// if (!res)
// {
// Serial.println("Failed to connect or hit timeout");
// //ESP.restart();
// }
// else
// {
// Serial.println("connected - OK");
// }
2021-03-15 22:30:39 +02:00
//Запрос 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 ( ) ;
2021-03-19 23:13:43 +02:00
//
wm . startConfigPortal ( ) ;
// ESP.restart();
2021-03-15 22:30:39 +02:00
}
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 ) ; //Вывод JSON строки в консоль
JsonData . toCharArray ( resultString , JsonData . length ( ) + 1 ) ;
MqttClient . publish ( bufTopic , resultString , true ) ;
}
if ( sendStatus = = true )
{
2021-03-19 23:13:43 +02:00
const char * conn ;
2021-03-15 22:30:39 +02:00
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
2021-03-19 23:13:43 +02:00
if ( M5 . Btn . wasReleasefor ( 10000 ) )
{
2021-03-15 22:30:39 +02:00
checkButton ( ) ;
}
if ( M5 . Btn . wasReleasefor ( 2000 ) )
{
buzAlarm ( false ) ;
}
//По таймеру запруск обновления прошивки
if ( millis ( ) - timingUpdate > nextM5Update )
{
reqNtpTime ( ) ;
OTAUpdate ( ) ;
// //Serial.print("OTAUpdate() - ");
// //Serial.println(millis());
timingUpdate = millis ( ) ;
}
//Таймер отправки данных в брокер
2021-03-19 23:13:43 +02:00
2021-03-15 22:30:39 +02:00
MqttClient . loop ( ) ;
2021-03-19 23:13:43 +02:00
wm . process ( ) ;
2021-03-15 22:30:39 +02:00
}
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 )
2021-03-19 23:13:43 +02:00
{
// digitalWrite(buzzerPin, HIGH);
// delay(100);
// digitalWrite(buzzerPin, LOW);
// delay(100);
// digitalWrite(buzzerPin, HIGH);
// delay(200);
// digitalWrite(buzzerPin, LOW);
// delay(200);
2021-03-15 22:30:39 +02:00
}
else
{
digitalWrite ( buzzerPin , LOW ) ;
}
return 0 ;
}