/* Питание 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 Кухня 4D41 ***************************************************************************/ #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 //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 = 43; //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 WiFiManager wm; // обьект менеджера WiFiManagerParameter custom_field; Preferences OTApreferences; //Обьект хранения настроек хеша прошивки Adafruit_CCS811 ccs; ClosedCube_HDC1080 hdc1080; StaticJsonDocument<200> doc; WiFiClient espClient; PubSubClient MqttClient(espClient); //IPAddress IpMqtt(192,168,89,210); WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "0.ua.pool.ntp.org", 0, 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 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; 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; } } IPAddress str2IP(String str) { IPAddress ret(getIpBlock(0, str), getIpBlock(1, str), getIpBlock(2, str), getIpBlock(3, str)); return ret; } int getIpBlock(int index, String str) { char separator = '.'; int found = 0; int strIndex[] = {0, -1}; int maxIndex = str.length() - 1; for (int i = 0; i <= maxIndex && found <= index; i++) { if (str.charAt(i) == separator || i == maxIndex) { found++; strIndex[0] = strIndex[1] + 1; strIndex[1] = (i == maxIndex) ? i + 1 : i; } } return found > index ? str.substring(strIndex[0], strIndex[1]).toInt() : 0; } int setMqttServer(const char *mqttHostNameF, unsigned int mqttPortF) { mdns_init(); IPAddress ipaddr; OTApreferences.begin("ota-config"); String keyIpMqtt = OTApreferences.getString("mqttip", "0"); //Если нет IP в preferences Serial.print("keyIpMqtt: "); Serial.println(keyIpMqtt); if (keyIpMqtt == "0") { //Запрос по хостнейму .local ipaddr = MDNS.queryHost(mqttHostNameF, 4000); //Таймаут 4 секунды //Если не получили по хостнейму МДНС Serial.print("mdns: "); Serial.println(ipaddr); if (ipaddr.toString() == "0.0.0.0") { //Запрос к ДНС по адресу сайта int err = WiFi.hostByName(mqttHostNameF, ipaddr); ////real internal timeout in lwip library is 14[s] //Если получили по хостнейму if (err == 1) { Serial.print("hostByName: "); Serial.println(ipaddr); MqttClient.setServer(ipaddr, mqttPortF); OTApreferences.putString("mqttip", ipaddr.toString()); } else { Serial.print("All method not FOUND IP"); } } else { //Если все хорошо OTApreferences.putString("mqttip", ipaddr.toString()); MqttClient.setServer(ipaddr, mqttPortF); } } else { MqttClient.setServer(str2IP(keyIpMqtt), mqttPortF); } OTApreferences.end(); return 0; } //Настройки void setup() { Serial.begin(115200); Serial.setDebugOutput(true); 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); for (int t = 0; t <= cntWhile; t++) { if (ccs.begin()) { break; } ledset(1, false); } //ccs.setTempOffset(8.6); //ccs.setBaseline(25725); 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 и установка сервера setMqttServer(mqttHostName, 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 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(); delay(50); char outstr[6]; dtostrf(hdc1080Temp, 5, 2, outstr); TempAv = atof(outstr); dtostrf(hdc1080Hum, 5, 2, outstr); HumAv = atof(outstr); int counAvail = 0; byte countRead = 0; ledset(4, false); bool ccsflag = true; while (ccsflag) { if (ccs.available()) { ccs.setEnvironmentalData(hdc1080Hum - 55, hdc1080Temp - 25); if (ccs.readData() == 0) { countRead++; if (countRead >= 7) { ccsflag = false; Serial.println(counAvail); } delay(1050); } else { Serial.println("=======ECO TVOC NONE READ"); } } counAvail++; if (counAvail >= 6000) { ccsflag = false; } } Serial.print("counAvail: "); Serial.println(counAvail); ledset(2, false); Eco2Av = ccs.geteCO2(); TvocAv = ccs.getTVOC(); BaselineTvoc = ccs.getBaseline(); Serial.print(eco2); Serial.print(" "); Serial.print(tvoc); Serial.print(" "); Serial.println(BaselineTvoc); 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["tsexp"] = timeNow + 60 + 11; 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 == 7) { break; } String maccrandom = macc + String(random(0xffff), HEX); const char *clientId = maccrandom.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()); /* -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); } } } 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)) { ledset(4, true); checkButton(); } if (countOta >= 10) { // reqNtpTime(); countOta = 0; SendMqttReq(false, true, 4); OTAUpdate(); SendMqttReq(false, true, 1); } countOta++; 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(); }