/* Питание 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 // 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 = 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 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(); }