M5Atom_airqa/M5Atom_airqa.ino

685 lines
18 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.

/*
Питание 3,3 В
Логические уровни 3,3 В
Точность измерения влажности ± 2%
Точность измерения температуры ± 0,2 °C
Разрешающая способность показаний датчика HDC1080 14 бит
Диапазон измерения концентрации CO2: 400 ... 8192 ppm
Диапазон измерения концентрации летучих органических веществ 0 ... 1187 ppb
*/
/***************************************************************************
Датчики воздуха
24A1605423AC - light
5002918A38CC
5002919F5450
24A160474D14
24A160542B80
***************************************************************************/
#include "esp_sleep.h"
#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 "ClosedCube_HDC1080.h" //Температура влажность
#include "Adafruit_CCS811.h" //eco2 Tvoc
//#include "SparkFunCCS811.h"
#include <PubSubClient.h> //Mtqq
#include <ArduinoJson.h> //Упакова в JSon - удобная библиотека
#include <ESPmDNS.h>
//Наша кнопочка при нажатии на которую произойдет вызов wifi менеджера и перезагрузка в станцию
#define TRIGGER_PIN 39
#define uS_TO_S_FACTOR 1000000
#define TIME_TO_SLEEP 60
#define GPIO_WAK 23
#define CCS811_ADDR 0x5B //Default I2C Address
//#define CCS811_ADDR 0x5A //Alternate I2C Address
unsigned int VersionSW = 30; //65536 Версия прошивки
//15 - добавлено то, се, забыл вообще дописать что добавленоSerial
//19 - вывод в консоль всех действий, ошибки с обновлнеием - починил, прияногое мигание светодиодом, тест для поиска metrics.
//21 - убран мак из вывода в топике
//22 - поправлено поиск сервера по metrics local.
//23 - добавленн BSID
//24 - полный передел всего
//25 - новые топики, спящий режим
//26 - тест обновления сука - работает
//27 - таймаут станции без wifi 40 сек
//28 - калибровка вернулась
//29 - baseline выведен и отключен
//30 - новое освещение оранжевый цвет
gpio_num_t pinWak = GPIO_NUM_23;
WiFiManager wm; // обьект менеджера
WiFiManagerParameter custom_field;
Preferences OTApreferences; //Обьект хранения настроек хеша прошивки
Adafruit_CCS811 ccs;
ClosedCube_HDC1080 hdc1080;
StaticJsonDocument<200> doc, other;
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 char *mqttHostName = "metrics"; //Хостнейм брокера metrics.local cctv.automation.art //192.168.89.210
unsigned int mqttPort = 1883; //Порт брокера 1883 8889
String getMacAddress();
String macc = getMacAddress();
String topicTemp = "aastudio/sens/" + macc + "/data/0";
String willTopicTemp = "aastudio/sens/" + macc + "/status";
const PROGMEM char *mqttLogin = "login", *mqttPass = "password";
char bufTopic[140];
char bufWillTopic[150];
const char *mqttIPHost;
RTC_DATA_ATTR byte countOta = 0;
int PROGMEM nextM5Update = 450000; //каждые 7.5 минут запрос обновления с сервера
int PROGMEM nextReqSensor = 10000; //опрос датчиков раз в 10 секунд
int PROGMEM nextMqttSend = 60000; //Отправка
float TempAv, HumAv, Eco2Av, TvocAv;
unsigned int BaselineTvoc=0;
byte errorID = 0;
long rssi = 0;
byte idxT = 0, idxH = 0, idxECO = 0, idxTVOC = 0;
bool mqttSendFlag = false;
int reqCounter = 0;
bool flagblink = true;
void ledset(byte color = 0, bool blink = false)
{
M5.dis.setBrightness(30); //Половина яркости
switch (color)
{
case 2:
M5.dis.drawpix(0, 0xf00000); //Зеленый
break;
case 1:
M5.dis.drawpix(0, 0x00ff00); //Красный
break;
case 3:
M5.dis.drawpix(0, 0x0000ff); //Синий
break;
case 4:
M5.dis.drawpix(0, 0x707070); //Белый
break;
default:
M5.dis.clear();
break;
}
}
//Настройки
void setup()
{
Serial.begin(115200);
// Serial.setDebugOutput(true);
btStop();
// esp_bt_controller_disable();
// adc_power_on();
M5.begin(true, false, true);
Wire.begin(25, 21); //Пины для I2c на ATOM
pinMode(GPIO_WAK, OUTPUT); //Пин датчика для работы
digitalWrite(GPIO_WAK, LOW);
pinMode(TRIGGER_PIN, INPUT);
hdc1080.begin(0x40);
hdc1080.setResolution(HDC1080_RESOLUTION_11BIT, HDC1080_RESOLUTION_11BIT);
while (!ccs.begin() && !ccs.available()) //The device's I2C address is 0x5A
{
ledset(1, true);
}
//ccs.setTempOffset(8.6);
// ccs.setBaseline(21634);
//Установка коеффициентов каллибровки по MAC адресу
// SetCallibrationCoeff();
delay(15);
WiFi.mode(WIFI_STA);
topicTemp.toCharArray(bufTopic, topicTemp.length() + 1);
willTopicTemp.toCharArray(bufWillTopic, willTopicTemp.length() + 1);
Serial.println(bufTopic);
Serial.println(bufWillTopic);
// wm.resetSettings(); // wipe settings
std::vector<const char *> menu = {"wifi", "info", "param", "sep", "restart", "exit"};
wm.setMenu(menu);
wm.setClass("invert");
wm.setConfigPortalTimeout(40);
wm.setMinimumSignalQuality(20);
wm.setScanDispPerc(true);
wm.setWiFiAutoReconnect(true);
bool res;
res = wm.autoConnect("AirQaPortal", "12345678"); // Подключение к анонимной точке доступа
if (!res)
{
////////////Serial.println("Failed to connect or hit timeout");
ESP.restart();
}
else
{
////////////Serial.println("Server AirQaPortal start");
}
//Запрос IP сервера MQTT и установка сервера
setMqttServer();
reqNtpTime();
}
//Установка сервера и порта
void setMqttServer()
{
mdns_init();
IPAddress IpMqtt, ipaddr;
ipaddr = MDNS.queryHost(mqttHostName);
if (ipaddr.toString() == "0.0.0.0")
{
Serial.println("MDNS.queryHost==0.0.0.0");
int err = WiFi.hostByName(mqttHostName, IpMqtt);
if (err == 1)
{
setServCall(IpMqtt);
}
else
{
Serial.print("Error code hostByName(): ");
Serial.println(err);
}
}
else
{
setServCall(ipaddr);
}
}
int setServCall(IPAddress SetIpaddr)
{
//////Serial.println("setServCall");
//////Serial.println(SetIpaddr);
MqttClient.setServer(SetIpaddr, mqttPort);
// MqttClient.setCallback(callback);
////////Serial.println("Set MQTT Server - OK") ;
return 0;
}
int reqNtpTime()
{
if (timeClient.update())
{
uint32_t timeEpoch = timeClient.getEpochTime();
setTime(timeEpoch);
Serial.print(timeEpoch);
Serial.print(" <=ntp== ==device=> ");
Serial.println(now());
if (timeEpoch < 100000)
{
Serial.println("Error Time");
}
}
return 0;
}
int checkButton()
{
Serial.println("Button Pressed to RESET");
wm.resetSettings();
ESP.restart();
//////////Serial.println("Starting config portal");
//wm.setConfigPortalTimeout(120);
// if (!wm.startConfigPortal("AirQaPortal", "12345678")) //Логин и пароль точки доступа
// {
// Serial.println("failed to connect or hit timeout");
// ESP.restart();
// }
// else
// {
// Serial.println("Clien connected to AP");
// }
return 0;
}
void OTAUpdate()
{
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!OTAUpdate() START!!!!!!!!!!!!!!!!!!!!!!!!!!");
bool flagOTA = false;
String keyOTA;
String payload;
if (WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
String serverPath = "http://meteosence.s-host.net/airqa/airquality.php?meteopas=e93gme9hAt9nSWaV&mac=" + macc + "&meteodata=gethash";
Serial.println(serverPath);
http.begin(serverPath.c_str());
int httpResponseCode = http.GET();
if (httpResponseCode > 0)
{
Serial.println(httpResponseCode);
payload = http.getString();
Serial.println(payload);
if (payload != "errno" || payload != "errfi")
{
OTApreferences.begin("ota-config");
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);
}
OTApreferences.end();
}
else
{
Serial.print("Host error: ");
Serial.println(payload);
errorID = 3;
}
}
else
{
Serial.println(httpResponseCode);
errorID = 2;
}
// Free resources
http.end();
}
else
{
////////Serial.println("WHY WiFi is Disconnected??");
}
if (flagOTA == true)
{
// flagOTA = false;
disconnectMQTT();
ESPhttpUpdate.rebootOnUpdate(true);
t_httpUpdate_return ret = ESPhttpUpdate.update("http://meteosence.s-host.net/airqa/airatoms.bin");
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;
}
}
}
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);
}
int SetCallibrationCoeff(String MacThat)
{
// 24A1605423AC - light
// 5002918A38CC
// 5002919F5450
// 24A160474D14
// 24A160542B80
if (MacThat == "24A1605423AC")
{
TempAv += -6.91;
HumAv += 7.95;
}
if (MacThat == "5002918A38CC")
{
TempAv += -5.72;
HumAv += 12.74;
}
if (MacThat == "5002919F5450")
{
TempAv += -6.57;
HumAv += 12.89;
}
if (MacThat == "24A160474D14")
{
TempAv += -7.14;
HumAv += 11.84;
}
if (MacThat == "24A160542B80")
{
TempAv += -7.37;
HumAv += 8.5;
}
return 0;
}
//Запрос данных с датчиков.
void reqSensorData()
{
float hdc1080Temp = 0, hdc1080Hum = 0;
int eco2 = 0, tvoc = 0;
hdc1080Temp = hdc1080.readTemperature();
hdc1080Hum = hdc1080.readHumidity();
if (hdc1080Temp < -40 || hdc1080Temp > 60)
{
errorID = 6;
}
if (hdc1080Hum < 0 || hdc1080Temp > 100)
{
errorID = 7;
}
char outstr[6];
dtostrf(hdc1080Temp, 5, 2, outstr);
TempAv = atof(outstr);
dtostrf(hdc1080Hum, 5, 2, outstr);
HumAv = atof(outstr);
for (int g = 0; g <= 5; g++)
{
delay(1000);
if (ccs.available())
{
if (!ccs.readData())
{
ccs.setEnvironmentalData(hdc1080Hum-55,hdc1080Temp-25);
eco2 = ccs.geteCO2();
tvoc = ccs.getTVOC();
BaselineTvoc=ccs.getBaseline();
Serial.println(eco2);
Serial.println(tvoc);
}
else
{
Serial.println("==========ECO TVOC NONE READ");
}
}
else
{
eco2 = 0;
tvoc = 0;
Serial.println("==========ECO TVOC NONE AVAILABLE");
errorID = 5;
}
}
Eco2Av = eco2;
TvocAv = tvoc;
rssi = WiFi.RSSI();
}
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)
{
doc["t"] = TempAv;
doc["h"] = HumAv;
doc["eco"] = (int)Eco2Av;
doc["tvoc"] = (int)TvocAv;
doc["blt"]= BaselineTvoc;
if (macc == "24A1605423AC")
{
doc["ligh"] = (int)map(analogRead(33), 0, 4095, 0, 100);
}
doc["ts"] = timeNow;
serializeJson(doc, JsonData);
doc.remove("t");
doc.remove("h");
doc.remove("eco");
doc.remove("tvoc");
doc.remove("blt");
if (macc == "24A1605423AC")
{
doc.remove("ligh");
}
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)
{
const 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["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);
}
}
return 0;
}
void reconnectMqtt()
{
byte circle = 0;
while (!MqttClient.connected())
{
Serial.print("MQTT reconnect...");
circle++;
if (circle == 2)
{
break;
}
String maccrandom = macc + String(random(0xffff), HEX);
const char *clientId = maccrandom.c_str();
if (MqttClient.connect(clientId, bufWillTopic, 2, true, willmess))
{
Serial.println("MqttClient.connect() - OK");
mqttSendFlag = true;
SendMqttReq(false, 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
*/
mqttSendFlag = false;
delay(1000);
}
}
}
void loop()
{
//Проверка старта сервера
M5.update();
if (!MqttClient.connected())
{
reconnectMqtt();
ledset(1, true);
}
if (countOta >= 10)
{
// reqNtpTime();
OTAUpdate();
countOta = 0;
}
countOta++;
reqSensorData();
SetCallibrationCoeff(macc);
SendMqttReq(true, true, 3);
MqttClient.loop();
disconnectMQTT();
delay(500);
if (M5.Btn.pressedFor(1000))
{
ledset(4, true);
checkButton();
}
goToSleep();
}
int disconnectMQTT()
{
MqttClient.disconnect();
return 0;
}
int goToSleep()
{
ledset(3, true);
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
esp_wifi_stop();
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start();
}