/* Питание 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 // https://github.com/tzapu/WiFiManager #include //Сохранение настроек хеша прошивки #include #include //Библиотека ОТА обновлений #include "M5Atom.h" //Библиотека атома для функции Led и Кнопки, можно упразднить и убрать #include //Udp клиент #include //NTP запрос времени #include //Внутреннее время #include //Библиотека дял I2C #include "ClosedCube_HDC1080.h" //Температура влажность //#include "Adafruit_CCS811.h" //eco2 Tvoc //#include "SparkFunCCS811.h" #include "ccs811.h" #include //Mtqq #include //Упакова в JSon - удобная библиотека #include //Наша кнопочка при нажатии на которую произойдет вызов 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 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(); }