M5Atom_airqa/M5Atom_airqa.ino

756 lines
22 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.

/*
Питание 3,3 В
Логические уровни 3,3 В
Точность измерения влажности ± 2%
Точность измерения температуры ± 0,2 °C
Разрешающая способность показаний датчика HDC1080 14 бит
Диапазон измерения концентрации CO2: 400 ... 8192 ppm
Диапазон измерения концентрации летучих органических веществ 0 ... 1187 ppb
*/
/***************************************************************************
Датчики воздуха
24A1605423AC - light
5002918A38CC
5002919F5450
24A160474D14
24A160542B80
Белая 2B80
Шумная 38CC
У вас 23AC
Офис 5450
Кухня 4D14
***************************************************************************/
#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 "ccs811.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 = 54; //65536 Версия прошивки
//15 - добавлено то, се, забыл вообще дописать что добавлено Serial
//19 - вывод в консоль всех действий, ошибки с обновлнеием - починил, прияногое мигание светодиодом, тест для поиска metrics.
//21 - убран мак из вывода в топике
//22 - поправлено поиск сервера по metrics local.
//23 - добавленн BSID
//24 - полный передел всего
//25 - новые топики, спящий режим и куча иземенний связанных со спящим режимом.
//26 - тест обновления да сук - работает
//27 - таймаут станции без wifi 40 сек
//28 - калибровка вернулась
//29 - baseline выведен из 24х часов и отключен
//30 - новое освещение оранжевый цвет который нихера не оранжевый
//31 - фиксим время
//32 - рестарт без wifi
//33 - бейслайн и куча изменений по переподключению mqtt
//34 - правка с опросом, бесконечный цикл
//35 - закоменчен вывод ошибок available
//36 - настройка mqtt по ip и режимы датчика - неудачная загрузка 27.03
//37-38 - сохранение ip metrics, и чтение из памяти, убрана базовая линия. Логин пароль. Фиксим получение временя 29.03
//39 - новая функция преобразования строку в ip объект.
//41 - уменьшил код на 7%. Перезалив. Пропали 3 из 5, но не отвалились, ходят за обновлениями, все успешно
//42 - Новая функция получения данных с CO2. Что-то не вяжется с либой.
//43 - Больше задержек для чтения i2c
//44 - Новая либа твок "maarten-pennings" Спаркфан и Адафрут суки - с ошибками изначально пишут на всех форумах.
//46 - Смена локального сервера времени, хрони на одроиде. Установка базовых линий по новой.
//48 - алгоритм фильтрации
//50 - нахуй хост менеджер, смена качества
//52 - убрал базовые линии ЕСО2.
//53 - базовые линии вернулись на место. Обновление по старту css811 7 измерений вместо 10
WiFiManager wm; // обьект менеджера
WiFiManagerParameter custom_field;
Preferences OTApreferences; //Обьект хранения настроек хеша прошивки
//Adafruit_CCS811 ccs;
CCS811 ccs811(23);
ClosedCube_HDC1080 hdc1080;
StaticJsonDocument<200> doc;
WiFiClient espClient;
PubSubClient MqttClient(espClient);
//IPAddress IpMqtt(192,168,89,210);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "192.168.89.210", 0, 20000); //Собственно сервер времени смещение и частоат запроса, но он вручную
const PROGMEM char *willmess = "{\"conn\":\"err\"}";
//Хостнейм брокера metrics.local cctv.automation.art //192.168.89.210
IPAddress ipaddrBro(192, 168, 89, 210);
unsigned int mqttPort = 1883; //Порт брокера 1883 8889
const char *prefArea = "ota-config";
String getMacAddress();
String macc = getMacAddress();
String topicTemp = "aastudio/sens/" + macc + "/data/0";
String willTopicTemp = "aastudio/sens/" + macc + "/status";
const PROGMEM char *mqttLogin = "AA_Lab", *mqttPass = "automation.art$";
char bufTopic[140];
char bufWillTopic[150];
const char *mqttIPHost;
RTC_DATA_ATTR byte countOta = 0;
// RTC_DATA_ATTR wakPinSet=LOW;
float TempAv, HumAv, Eco2Av, TvocAv;
unsigned int BaselineTvoc = 0;
long rssi = 0;
byte idxT = 0, idxH = 0, idxECO = 0, idxTVOC = 0;
unsigned int cntWhile = 4;
bool mqttSendFlag = false;
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()
{
pinMode(GPIO_WAK, OUTPUT); //Пин датчика для работы
digitalWrite(GPIO_WAK, LOW);
delay(10);
Serial.begin(115200);
Serial.setDebugOutput(true);
M5.begin(true, false, true);
Wire.begin(25, 21); //Пины для I2c на ATOM
hdc1080.begin(0x40);
hdc1080.setResolution(HDC1080_RESOLUTION_11BIT, HDC1080_RESOLUTION_11BIT);
startCcs811(macc);
pinMode(TRIGGER_PIN, INPUT);
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 = wm.autoConnect("AirQaPortal", "12345678"); // Подключение к анонимной точке доступа
if (!res)
{
ESP.restart();
}
//Запрос IP сервера MQTT и установка сервера
// MqttClient.setServer(ipaddrBro, mqttPort);
if (reqNtpTime() == 1)
{
reqNtpTime();
}
}
//Установка сервера и порта
int reqNtpTime()
{
if (timeClient.update())
{
uint32_t timeEpoch = timeClient.getEpochTime();
if (timeEpoch < 1600000000)
{
Serial.println("Error reqNtpTime() Time");
return 1;
}
setTime(timeEpoch);
// Serial.print(timeEpoch);
// Serial.print(" <=ntp== ==device=> ");
// Serial.println(now());
}
return 0;
}
int checkButton()
{
Serial.println("Button Pressed to RESET");
OTApreferences.begin(prefArea);
OTApreferences.clear();
OTApreferences.end();
wm.resetSettings();
ESP.restart();
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(prefArea);
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);
}
}
else
{
Serial.println(httpResponseCode);
}
// 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 startCcs811(String MacThat)
{
unsigned int coefBaseline = 0;
ccs811.set_i2cdelay(50);
bool ok = ccs811.begin();
delay(100);
if (!ok)
{
Serial.println("setup: CCS811 begin FAILED");
httpSerial("CCS811_failed_begin");
return 1;
}
// Serial.print("setup: hardware version: ");
// Serial.println(ccs811.hardware_version(), HEX);
// Serial.print("setup: bootloader version: ");
// Serial.println(ccs811.bootloader_version(), HEX);
// Serial.print("setup: application version: ");
// Serial.println(ccs811.application_version(), HEX);
ok = ccs811.start(CCS811_MODE_1SEC);
if (!ok)
{
Serial.println("setup: CCS811 start FAILED");
httpSerial("CCS811_failed_start");
return 2;
}
if (MacThat == "24A1605423AC")
{
coefBaseline = 25725;
}
if (MacThat == "5002918A38CC")
{
coefBaseline = 29814;
}
if (MacThat == "5002919F5450")
{
coefBaseline = 13431;
}
if (MacThat == "24A160474D14")
{
coefBaseline = 21632;
}
if (MacThat == "24A160542B80")
{
coefBaseline = 13440;
}
if (coefBaseline != 0)
{
ccs811.set_baseline(coefBaseline);
}
return 0;
}
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;
hdc1080Temp = hdc1080.readTemperature();
hdc1080Hum = hdc1080.readHumidity();
delay(50);
char outstr[6];
dtostrf(hdc1080Temp, 5, 2, outstr);
TempAv = atof(outstr);
dtostrf(hdc1080Hum, 5, 2, outstr);
HumAv = atof(outstr);
int counAvail = 0;
ledset(4, false);
bool ccsflag = true;
uint16_t eco2N, etvocN, errstat, raw, baseline;
while (ccsflag)
{
ccs811.read(&eco2N, &etvocN, &errstat, &raw);
ccs811.get_baseline(&baseline);
// Print measurement results based on status
// if (errstat == CCS811_ERRSTAT_OK)
// {
// // Serial.print("CCS811: ");
// // Serial.print("eco2=");
// // Serial.print(eco2N);
// // Serial.print(" ppm ");
// // Serial.print("etvoc=");
// // Serial.print(etvocN);
// // Serial.print(" ppb ");
// // Serial.print(" baseline= ");
// // Serial.print(baseline);
// // Serial.println();
// }
// if (errstat == CCS811_ERRSTAT_OK_NODATA)
// {
// // Serial.println("CCS811: waiting for (new) data");
// }
// else
if (errstat & CCS811_ERRSTAT_I2CFAIL)
{
httpSerial("CCS811_i2c_error");
}
else
{
// Serial.print("CCS811: errstat=");
// Serial.print(errstat, HEX);
// Serial.print("=");
// Serial.println();
httpSerial("errstat="+String(ccs811.errstat_str(errstat)));
}
counAvail++;
if (counAvail >= 8)
{
ccsflag = false;
}
delay(1200);
}
ledset(2, false);
Eco2Av = eco2N;
TvocAv = etvocN;
BaselineTvoc = baseline;
rssi = WiFi.RSSI();
}
int SendMqttReq(bool sendVal = true, bool sendStatus = true, byte statusConn = 1)
{
if (mqttSendFlag == true)
{
ledset(2, 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;
case 4:
conn = "upd";
break;
default:
break;
}
doc["conn"] = conn;
doc["rssi"] = rssi;
doc["bsid"] = WiFi.BSSIDstr();
doc["ts"] = timeNow;
doc["exp"] = timeNow + 60 + 17;
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())
{
circle++;
if (circle == 4)
{
break;
}
MqttClient.setServer(ipaddrBro, mqttPort);
const char *clientId = macc.c_str();
if (MqttClient.connect(clientId, mqttLogin, mqttPass, bufWillTopic, 2, true, willmess))
{
Serial.println("Mqttconnect - OK");
mqttSendFlag = true;
SendMqttReq(false, true, 1);
}
else
{
Serial.print("Mqtt.state() = ");
Serial.println(MqttClient.state());
httpSerial("MqttState:" + String(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(500);
}
}
}
int httpSerial(String serialmess)
{
OTApreferences.begin("ota-config");
if (WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
String serverPath = "http://meteosence.s-host.net/serial/log.php?mac=" + macc + "&logmess=" + serialmess;
http.begin(serverPath.c_str());
int httpResponseCode = http.GET();
if (httpResponseCode > 0)
{
// Serial.print("HTTP Response code: ");
// Serial.println(httpResponseCode);
String payload = http.getString();
if (payload == "false")
{
Serial.println("Failed write to Http.Serial");
}
}
}
return 0;
}
void loop()
{
//Проверка старта сервера
M5.update();
if (!MqttClient.connected())
{
Serial.print("MQTT reconnect..");
reconnectMqtt();
}
MqttClient.loop();
reqSensorData();
SetCallibrationCoeff(macc);
SendMqttReq(true, true, 3);
if (M5.Btn.pressedFor(1000))
{
httpSerial("Reset_in_button");
ledset(4, true);
checkButton();
}
if (countOta >= 10)
{
// reqNtpTime();
countOta = 0;
SendMqttReq(false, true, 4);
OTAUpdate();
SendMqttReq(false, true, 1);
}
countOta++;
startCcs811(macc);
goToSleep();
}
int disconnectMQTT()
{
MqttClient.disconnect();
return 0;
}
int goToSleep()
{
ledset(3, true);
disconnectMQTT();
delay(500);
WiFi.disconnect(true);
delay(50);
WiFi.mode(WIFI_OFF);
delay(50);
esp_wifi_stop();
delay(100);
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
delay(100);
esp_deep_sleep_start();
}