2021-01-19 18:26:06 +02:00
/*
П и т а н и е 3 , 3 В
Л о г и ч е с к и е у р о в н и 3 , 3 В
Т о ч н о с т ь и з м е р е н и я в л а ж н о с т и ± 2 %
Т о ч н о с т ь и з м е р е н и я т е м п е р а т у р ы ± 0 , 2 ° C
Р а з р е ш а ю щ а я с п о с о б н о с т ь п о к а з а н и й д а т ч и к а HDC1080 14 б и т
Д и а п а з о н и з м е р е н и я к о н ц е н т р а ц и и CO2 : 400 . . . 8192 ppm
Д и а п а з о н и з м е р е н и я к о н ц е н т р а ц и и л е т у ч и х о р г а н и ч е с к и х в е щ е с т в 0 . . . 1187 ppb
*/
2021-01-14 23:27:49 +02:00
# include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
# include <Preferences.h> //Сохранение настроек хеша прошивки
# include <HTTPClient.h>
# include <ESP32httpUpdate.h>
2021-01-19 18:26:06 +02:00
# include "M5Atom.h"
# include <Ticker.h>
# include <Wire.h>
# include "ClosedCube_HDC1080.h"
# include "Adafruit_CCS811.h" //The device's I2C address is 0x5A
# include <PubSubClient.h>
# include <ArduinoJson.h>
2021-01-14 23:27:49 +02:00
//Наша кнопочка при нажатии на которую произойдет вызов wifi менеджера и перезагрузка в станцию
# define TRIGGER_PIN 39
WiFiManager wm ; // обьект менеджера
WiFiManagerParameter custom_field ;
Preferences OTApreferences ;
2021-01-19 18:26:06 +02:00
Ticker RGBWTicker ;
Adafruit_CCS811 ccs ;
ClosedCube_HDC1080 hdc1080 ;
String JsonData ;
StaticJsonDocument < 200 > doc ;
WiFiClient espClient ;
PubSubClient MqttClient ( espClient ) ;
IPAddress IpMqtt ;
const PROGMEM char * mqttHostName = " cctv.automation.art " ;
unsigned int mqttPort = 8889 ;
const PROGMEM char * topicName = " /aastudio " ; // +mac адресс девайса
const PROGMEM char * mqttLogin = " login " ,
* mqttPass = " password " ;
const char * mqttIPHost ;
unsigned long timingUpdate , timingReqSensor , timingSendMqtt ;
2021-01-14 23:27:49 +02:00
int PROGMEM nextM5Update = 450000 ; //каждые 7.5 минут запрос обновления с сервера
2021-01-19 18:26:06 +02:00
int PROGMEM nextReqSensor = 10000 ; //опрос датчиков раз в 10 секунд
int PROGMEM nextMqttSend = 60000 ;
//Поправочные коефициенты
//[24A160474D14, 5002919F5450, 5002918A38CC]
//tcoeff[0,1,2]
//hcoeff[0,1,2]
float PROGMEM tcoeff [ 3 ] = { - 11.22 , - 9.62 , - 11.03 } ;
float PROGMEM hcoeff [ 3 ] = { 12.30 , 13.34 , 15.18 } ;
float callibrationT = 0 , callibrationH = 0 ;
String macc = " " ;
float TempAv , HumAv , Eco2Av , TvocAv ;
# define NUM_AVER 6
float averageT ; // перем. среднего
float valArrayT [ NUM_AVER ] ; // массив
byte idxT = 0 ;
float averageH ; // перем. среднего
float valArrayH [ NUM_AVER ] ; // массив
byte idxH = 0 ;
float averageECO ; // перем. среднего
float valArrayECO [ NUM_AVER ] ; // массив
byte idxECO = 0 ;
float averageTVOC ; // перем. среднего
float valArrayTVOC [ NUM_AVER ] ; // массив
byte idxTVOC = 0 ;
bool mqttSendFlag = false ;
// float arrT[5] = {0}, arrH[5] = {0};
// int arrECO[5] = {0}, tvoc[5] = {0};
int reqCounter = 0 ;
bool flagblink = true ;
2021-01-14 23:27:49 +02:00
void setup ( )
{
2021-01-19 18:26:06 +02:00
M5 . begin ( true , false , true ) ;
delay ( 50 ) ;
Wire . begin ( 25 , 21 ) ;
pinMode ( 23 , OUTPUT ) ;
digitalWrite ( 23 , LOW ) ;
2021-01-14 23:27:49 +02:00
WiFi . mode ( WIFI_STA ) ;
Serial . begin ( 115200 ) ;
Serial . setDebugOutput ( true ) ;
delay ( 3000 ) ;
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 ( 30 ) ; // 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(20); // 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
bool res ;
// res = wm.autoConnect(); // auto generated AP name from chipid
// res = wm.autoConnect("AutoConnectAP"); // anonymous ap
res = wm . autoConnect ( " AutoConnectAP " , " password " ) ; // password protected ap
if ( ! res )
{
Serial . println ( " Failed to connect or hit timeout " ) ;
// ESP.restart();
}
else
{
//if you get here you have connected to the WiFi
Serial . println ( " connected... " ) ;
}
2021-01-19 18:26:06 +02:00
if ( ! ccs . begin ( ) ) //The device's I2C address is 0x5A
{
Serial . println ( " Failed to start sensor CCS811! " ) ;
}
ccs . setTempOffset ( 8.6 ) ;
hdc1080 . begin ( 0x40 ) ; //14 бит температура и влажность
hdc1080 . setResolution ( HDC1080_RESOLUTION_14BIT , HDC1080_RESOLUTION_14BIT ) ;
//Установка коеффициентов каллибровки по MAC адресу
SetCallibrationCoeff ( ) ;
//Запрос IP сервера MQTT и установка сервера
setMqttServer ( ) ;
}
void setMqttServer ( )
{
Serial . println ( " gethostbyname: " ) ;
int err = WiFi . hostByName ( mqttHostName , IpMqtt ) ;
if ( err = = 1 )
{
Serial . print ( " Ip address by Name: " ) ;
Serial . println ( IpMqtt ) ;
delay ( 50 ) ;
MqttClient . setServer ( IpMqtt , mqttPort ) ;
MqttClient . setCallback ( callback ) ;
}
else
{
Serial . print ( " Error code gethostbyname: " ) ;
Serial . println ( err ) ;
}
}
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 ( ) ;
2021-01-14 23:27:49 +02:00
}
void checkButton ( )
{
2021-01-19 18:26:06 +02:00
Serial . println ( " Button Pressed " ) ;
delay ( 3000 ) ;
2021-01-14 23:27:49 +02:00
if ( digitalRead ( TRIGGER_PIN ) = = LOW )
{
2021-01-19 18:26:06 +02:00
Serial . println ( " Button Held " ) ;
Serial . println ( " Erasing Config, restarting " ) ;
wm . resetSettings ( ) ;
ESP . restart ( ) ;
}
2021-01-14 23:27:49 +02:00
2021-01-19 18:26:06 +02:00
// start portal w delay
Serial . println ( " Starting config portal " ) ;
wm . setConfigPortalTimeout ( 120 ) ;
2021-01-14 23:27:49 +02:00
2021-01-19 18:26:06 +02:00
if ( ! wm . startConfigPortal ( " AirQaPortal " , " 12345678 " ) )
{
Serial . println ( " failed to connect or hit timeout " ) ;
delay ( 3000 ) ;
// ESP.restart();
}
else
{
Serial . println ( " CLIENT connect " ) ;
2021-01-14 23:27:49 +02:00
}
}
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 " ) ) ;
}
void OTAUpdate ( )
{
Serial . println ( " OTAUpdate() " ) ;
//http://meteosence.s-host.net/meteosence.php?meteopas=PdF4apD4i95xR5&meteodata=gethash
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=e93gme9hAt9nSWaV&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 ) ;
keyOTA = OTApreferences . getString ( " md5HashOTA " ) ;
if ( keyOTA . length ( ) < = 0 )
{
OTApreferences . putString ( " md5HashOTA " , " asshole " ) ;
}
keyOTA = OTApreferences . getString ( " md5HashOTA " ) ;
if ( payload ! = keyOTA )
{
flagOTA = true ;
OTApreferences . putString ( " md5HashOTA " , payload ) ;
Serial . println ( " flagOTA = true; " ) ;
}
}
else
{
Serial . print ( " Error code: " ) ;
Serial . println ( httpResponseCode ) ;
}
// Free resources
http . end ( ) ;
}
else
{
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/airatoms.bin " ) ;
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 " ) ;
delay ( 3000 ) ;
ESP . restart ( ) ;
break ;
case HTTP_UPDATE_NO_UPDATES :
Serial . println ( " HTTP_UPDATE_NO_UPDATES " ) ;
delay ( 3000 ) ;
ESP . restart ( ) ;
break ;
case HTTP_UPDATE_OK :
Serial . println ( " HTTP_UPDATE_OK " ) ;
delay ( 3000 ) ;
ESP . restart ( ) ;
break ;
}
}
OTApreferences . end ( ) ;
}
2021-01-19 18:26:06 +02:00
void ledset ( char color , bool blink = false )
{
switch ( color )
{
case ' g ' :
M5 . dis . drawpix ( 0 , 0xf00000 ) ; //Зеленый
ledset ( ' r ' , true ) ;
break ;
case ' r ' :
M5 . dis . drawpix ( 0 , 0x00f000 ) ; //Красный
break ;
case ' b ' :
M5 . dis . drawpix ( 0 , 0x0000f0 ) ; //Синий
break ;
case ' w ' :
M5 . dis . drawpix ( 0 , 0x707070 ) ; //Белый
break ;
default :
M5 . dis . clear ( ) ;
break ;
}
if ( blink = = true )
{ //Таймер для LED
RGBWTicker . attach_ms ( 1000 , ledBlinkTimer ) ;
}
else
{
RGBWTicker . detach ( ) ;
}
}
void ledBlinkTimer ( )
{
if ( flagblink = = true )
{
M5 . dis . drawpix ( 0 , 0xf00000 ) ;
flagblink = false ;
}
else
{
M5 . dis . clear ( ) ;
flagblink = true ;
}
}
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 ) ;
}
void SetCallibrationCoeff ( )
{
// Serial.print("Mac: ");
// Serial.println(getMacAddress());
//[24A160474D14, 5002919F5450, 5002918A38CC]
//tcoeff[0,1,2]
//hcoeff[0,1,2]
macc = getMacAddress ( ) ;
if ( macc = = " 24A160474D14 " )
{
callibrationT = tcoeff [ 0 ] ;
callibrationH = hcoeff [ 0 ] ;
}
if ( macc = = " 5002919F5450 " )
{
callibrationT = tcoeff [ 1 ] ;
callibrationH = hcoeff [ 1 ] ;
}
if ( macc = = " 5002918A38CC " )
{
callibrationT = tcoeff [ 2 ] ;
callibrationH = hcoeff [ 2 ] ;
}
}
float middleArifmT ( float newVal )
{ // принимает новое значение
valArrayT [ idxT ] = newVal ; // пишем каждый раз в новую ячейку
if ( + + idxT > = NUM_AVER )
idxT = 0 ; // перезаписывая самое старое значение
averageT = 0 ; // обнуляем среднее
for ( int i = 0 ; i < NUM_AVER ; i + + )
{
averageT + = valArrayT [ i ] ; // суммируем
}
averageT / = NUM_AVER ; // делим
return averageT ; // возвращаем
}
float middleArifmH ( float newVal )
{ // принимает новое значение
valArrayH [ idxH ] = newVal ; // пишем каждый раз в новую ячейку
if ( + + idxH > = NUM_AVER )
idxH = 0 ; // перезаписывая самое старое значение
averageH = 0 ; // обнуляем среднее
for ( int i = 0 ; i < NUM_AVER ; i + + )
{
averageH + = valArrayH [ i ] ; // суммируем
}
averageH / = NUM_AVER ; // делим
return averageH ; // возвращаем
}
float middleArifmECO ( float newVal )
{ // принимает новое значение
valArrayECO [ idxECO ] = newVal ; // пишем каждый раз в новую ячейку
if ( + + idxECO > = NUM_AVER )
idxECO = 0 ; // перезаписывая самое старое значение
averageECO = 0 ; // обнуляем среднее
for ( int i = 0 ; i < NUM_AVER ; i + + )
{
averageECO + = valArrayECO [ i ] ; // суммируем
}
averageECO / = NUM_AVER ; // делим
return averageECO ; // возвращаем
}
float middleArifmTVOC ( float newVal )
{ // принимает новое значение
valArrayTVOC [ idxTVOC ] = newVal ; // пишем каждый раз в новую ячейку
if ( + + idxTVOC > = NUM_AVER )
idxTVOC = 0 ; // перезаписывая самое старое значение
averageTVOC = 0 ; // обнуляем среднее
for ( int i = 0 ; i < NUM_AVER ; i + + )
{
averageTVOC + = valArrayTVOC [ i ] ; // суммируем
}
averageTVOC / = NUM_AVER ; // делим
return averageTVOC ; // возвращаем
}
void reqSensorData ( )
{
float hdc1080Temp = 0 , hdc1080Hum = 0 ;
int eco2 = 0 , tvoc = 0 ;
if ( ccs . available ( ) )
{
if ( ! ccs . readData ( ) )
{
eco2 = ccs . geteCO2 ( ) ;
tvoc = ccs . getTVOC ( ) ;
}
else
{
eco2 = 0 ;
tvoc = 0 ;
}
}
hdc1080Temp = hdc1080 . readTemperature ( ) + callibrationT ;
hdc1080Hum = hdc1080 . readHumidity ( ) + callibrationH ;
TempAv = middleArifmT ( hdc1080Temp ) ;
HumAv = middleArifmH ( hdc1080Hum ) ;
Eco2Av = middleArifmECO ( eco2 ) ;
TvocAv = middleArifmTVOC ( tvoc ) ;
}
void SendMqttReq ( )
{
char HumInt [ 8 ] , TempInt [ 8 ] ;
dtostrf ( HumAv * 100 , 4 , 0 , HumInt ) ;
dtostrf ( TempAv * 100 , 4 , 0 , TempInt ) ;
doc [ " mac " ] = String ( getMacAddress ( ) ) ;
doc [ " t " ] = String ( TempInt ) ;
doc [ " h " ] = String ( HumInt ) ;
doc [ " eco " ] = ( int ) Eco2Av ;
doc [ " tvoc " ] = ( int ) TvocAv ;
doc [ " time " ] = millis ( ) ;
char resultString [ 200 ] ;
String JsonData = " " ;
serializeJson ( doc , JsonData ) ;
Serial . println ( JsonData ) ; //Вывод JSON строки в консоль
JsonData . toCharArray ( resultString , JsonData . length ( ) + 1 ) ;
String finishTopic = " " ;
finishTopic = String ( topicName ) + " / " + macc ;
Serial . println ( finishTopic ) ;
if ( mqttSendFlag = = true )
{
MqttClient . publish ( finishTopic . c_str ( ) , resultString ) ;
Serial . println ( " SentToTopic - ok " ) ;
}
}
void reconnectMqtt ( )
{
Serial . print ( " Attempting MQTT connection... " ) ;
//clientId += String(random(0xffff), HEX);
//clientId.c_str()
//MqttClient.connect(macc.c_str(), mqttLogin, mqttPass
const char * clientId = macc . c_str ( ) ;
//macc += String(random(0xffff), HEX);
if ( MqttClient . connect ( " AtomClient " ) )
{
String finishTopic = " " ;
finishTopic = String ( topicName ) + " / " + macc ;
// MqttClient.publish(finishTopic.c_str(), "Reconnect NOW");
Serial . print ( " Connect MQTT OK " ) ;
// client.subscribe("inTopic");
mqttSendFlag = true ;
}
else
{
Serial . print ( " failed, rc= " ) ;
Serial . print ( MqttClient . state ( ) ) ;
mqttSendFlag = false ;
}
}
2021-01-14 23:27:49 +02:00
void loop ( )
{
//Проверка старта сервера
2021-01-19 18:26:06 +02:00
M5 . update ( ) ;
MqttClient . loop ( ) ;
//Если не определен IP то и не будет отправки.
if ( ! MqttClient . connected ( ) )
{ //Если клиент отключился, переподключение
reconnectMqtt ( ) ;
}
// 40 секунд и происходит с б р о с настроек WIFI
if ( M5 . Btn . wasReleasefor ( 40000 ) )
{
ledset ( ' r ' , true ) ;
checkButton ( ) ;
}
2021-01-14 23:27:49 +02:00
//По таймеру запруск обновления - проверка хеша прошивки на сервере
2021-01-19 18:26:06 +02:00
2021-01-14 23:27:49 +02:00
if ( millis ( ) - timingUpdate > nextM5Update )
{
OTAUpdate ( ) ;
2021-01-19 18:26:06 +02:00
// Serial.print("OTAUpdate() - ");
// Serial.println(millis());
2021-01-14 23:27:49 +02:00
timingUpdate = millis ( ) ;
}
2021-01-19 18:26:06 +02:00
if ( millis ( ) - timingReqSensor > nextReqSensor )
{
reqSensorData ( ) ;
// Serial.print("reqSensorData() - ");
// Serial.println(millis());
timingReqSensor = millis ( ) ;
}
if ( millis ( ) - timingSendMqtt > nextMqttSend )
{
SendMqttReq ( ) ;
timingSendMqtt = millis ( ) ;
}
}