/* Питание 3,3 В Логические уровни 3,3 В Точность измерения влажности ± 2% Точность измерения температуры ± 0,2 °C Разрешающая способность показаний датчика HDC1080 14 бит Диапазон измерения концентрации CO2: 400 ... 8192 ppm Диапазон измерения концентрации летучих органических веществ 0 ... 1187 ppb */ #include "esp_sleep.h" #include "driver/rtc_io.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 30 #define GPIO_WAK 23 #define CCS811_ADDR 0x5B //Default I2C Address //#define CCS811_ADDR 0x5A //Alternate I2C Address unsigned int VersionSW = 225; //65536 Версия прошивки //15 - добавлено то, се, забыл вообще дописать что добавленоSerial //19 - вывод в консоль всех действий, ошибки с обновлнеием - починил, прияногое мигание светодиодом, тест для поиска metrics. //21 - убран мак из вывода в топике //22 - поправлено поиск сервера по metrics local. //23 - добавленн BSID //24 - полный передел всего 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 = "cctv.automation.art"; //Хостнейм брокера metrics.local cctv.automation.art //192.168.89.210 unsigned int mqttPort = 8889; //Порт брокера 1883 8889 String getMacAddress(); String macc = getMacAddress(); String topicTemp = "aastudio/" + macc + "/data/0"; String willTopicTemp = "aastudio/" + macc + "/status"; const PROGMEM char *mqttLogin = "login", *mqttPass = "password"; char bufTopic[140]; char bufWillTopic[150]; const char *mqttIPHost; RTC_DATA_ATTR unsigned long timingUpdate, timingSendMqtt; //Таймеры для millis() int PROGMEM nextM5Update = 450000; //каждые 7.5 минут запрос обновления с сервера int PROGMEM nextReqSensor = 10000; //опрос датчиков раз в 10 секунд int PROGMEM nextMqttSend = 60000; //Отправка //Поправочные коефициенты для датчиков //[24A160474D14, 5002919F5450, 5002918A38CC] //tcoeff[0,1,2] //hcoeff[0,1,2] float PROGMEM tcoeff[3] = {-11.18, -9.62, -10.6}; //Температура float PROGMEM hcoeff[3] = {13.77, 14.07, 15.56}; //Влажность float callibrationT = 0, callibrationH = 0; float TempAv, HumAv, Eco2Av, TvocAv; byte errorID = 0; long rssi = 0; #define NUM_AVER 5 float averageT, averageH, averageECO, averageTVOC; // перем. среднего float valArrayT[NUM_AVER], valArrayH[NUM_AVER], valArrayECO[NUM_AVER], valArrayTVOC[NUM_AVER]; // массив byte idxT = 0, idxH = 0, idxECO = 0, idxTVOC = 0; bool mqttSendFlag = false; int reqCounter = 0; bool flagblink = true; //Настройки void setup() { 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); // gpio_hold_en(pinWak); // gpio_deep_sleep_hold_en(); // rtc_gpio_set_level (pin_MOSFET, 1); // GPIO ВЫСОКИЙ // esp_sleep_pd_config (ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // esp_sleep_enable_timer_wakeup (60 * 1000 * 1000); // esp_deep_sleep_start (); pinMode(TRIGGER_PIN, INPUT); hdc1080.begin(0x40); //14 бит температура и влажность hdc1080.setResolution(HDC1080_RESOLUTION_11BIT, HDC1080_RESOLUTION_11BIT); while (!ccs.begin() && !ccs.available()) //The device's I2C address is 0x5A { Serial.println("Failed BEGIN And Failed available "); } //ccs.setTempOffset(8.6); //Установка коеффициентов каллибровки по MAC адресу // SetCallibrationCoeff(); delay(15); WiFi.mode(WIFI_STA); Serial.begin(115200); Serial.setDebugOutput(true); topicTemp.toCharArray(bufTopic, topicTemp.length() + 1); willTopicTemp.toCharArray(bufWillTopic, willTopicTemp.length() + 1); Serial.println(bufTopic); Serial.println(bufWillTopic); // wm.resetSettings(); // wipe settings // const char *custom_radio_str = "
One
Two
Three"; // new (&custom_field) WiFiManagerParameter(custom_radio_str); // custom html input // wm.addParameter(&custom_field); // wm.setSaveParamsCallback(saveParamCallback); std::vector menu = {"wifi", "info", "param", "sep", "restart", "exit"}; wm.setMenu(menu); wm.setClass("invert"); //wm.setConfigPortalTimeout(60); 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; } //Функция получения данных из MQTT если мы подпишемся на топики // void callback(char *topic, byte *payload, unsigned int length) // { // ////////////Serial.print("Message arrived ["); // ////////////Serial.print(topic); // //////////Serial.print("] "); // for (int i = 0; i < length; i++) // { // //////////Serial.print((char)payload[i]); // } // //////////Serial.println(); // } //Запрос времени NTP и установка локлаьного времени 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; } // { // String value; // if (wm.server->hasArg(name)) // { // value = wm.server->arg(name); // } // return value; // } //Обновление прошивки, происходит проверка и загрузка void OTAUpdate() { Serial.println("OTAUpdate() START"); bool flagOTA = false; String keyOTA; String payload; if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String serverPath = "http://Dmeteosence.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"); //После update ничего не происходит, такая вот особенность. //Если все прошло хорошо, перезагрузка на новую прошивку // Если указано значение true, модуль ESP перезагрузится после успешного завершения обновления. В случае ложного , модуль не перезагружается автоматически. Загруженная новая прошивка остается в стадии обновления флэш-памяти на модуле ESP. // Процесс загрузки во время следующего запуска модуля путем сброса скопирует обновленное микропрограммное обеспечение в фактическую область программы, и запустится новая программа эскиза. Значение по умолчанию верно. ////////Serial.print("ret "); Serial.println(ret); 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; } } } //Функция для индикации Led void ledset(char color = 't', bool blink = false) { M5.dis.setBrightness(30); //Половина яркости switch (color) { case 'g': M5.dis.drawpix(0, 0xf00000); //Зеленый break; case 'r': M5.dis.drawpix(0, 0x00f000); //Красный break; case 'b': M5.dis.drawpix(0, 0x0000f0); //Синий break; case 'w': M5.dis.drawpix(0, 0x707070); //Белый break; default: M5.dis.clear(); 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); } //Установка коефициентов калибровки void SetCallibrationCoeff() { // ////////Serial.print("Mac: "); // ////////Serial.println(getMacAddress()); //[24A160474D14, 5002919F5450, 5002918A38CC] //tcoeff[0,1,2] //hcoeff[0,1,2] if (macc == "24A160474D14") { callibrationT = tcoeff[0]; callibrationH = hcoeff[0]; } if (macc == "5002919F5450") { callibrationT = tcoeff[1]; callibrationH = hcoeff[1]; } if (macc == "5002918A38CC") { callibrationT = tcoeff[2]; callibrationH = hcoeff[2]; } } //Запрос данных с датчиков. void reqSensorData() { delay(15); float hdc1080Temp = 0, hdc1080Hum = 0; int eco2 = 0, tvoc = 0; for (int g = 0; g <= 10; g++) { delay(1000); if (ccs.available()) { if (!ccs.readData()) { // ccs.readAlgorithmResults(); eco2 = ccs.geteCO2(); tvoc = ccs.getTVOC(); Serial.println(ccs.geteCO2()); Serial.println(ccs.getTVOC()); Serial.print("baseline: "); Serial.println(ccs.getBaseline()); Serial.print("CurrentSelected: "); Serial.println(ccs.getCurrentSelected()); Serial.print("RawADCreading: "); Serial.println(ccs.getRawADCreading()); } else { Serial.println("==========ECO TVOC NONE READ"); } } else { eco2 = 0; tvoc = 0; Serial.println("==========ECO TVOC NONE AVAILABLE"); errorID = 5; } } hdc1080Temp = hdc1080.readTemperature(); hdc1080Hum = hdc1080.readHumidity(); if (hdc1080Temp < -40 || hdc1080Temp > 60) { errorID = 6; } if (hdc1080Hum < 0 || hdc1080Temp > 100) { errorID = 7; } Serial.println("Original data: "); Serial.print(hdc1080.readTemperature()); Serial.print(" "); Serial.println(hdc1080.readHumidity()); Serial.println("Calibration data: "); Serial.print(hdc1080Temp); Serial.print(" "); Serial.println(hdc1080Hum); TempAv = hdc1080Temp; HumAv = hdc1080Hum; 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["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("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["lasterror"] = errorID; 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(); //const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage ////Serial.println(clientId); if (MqttClient.connect(clientId, bufWillTopic, 2, true, willmess)) { Serial.println("MqttClient.connect() - OK"); errorID = 8; 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(3000); } } } void loop() { //Проверка старта сервера M5.update(); if (!MqttClient.connected()) { reconnectMqtt(); ledset('w', true); } //Если не определен IP то и не будет отправки. // 40 секунд и происходит сброс настроек WIFI if (M5.Btn.isPressed()) { ledset('r', true); checkButton(); } if (millis() - timingUpdate > nextM5Update) { reqNtpTime(); OTAUpdate(); timingUpdate = millis(); } reqSensorData(); ledset('g', true); SendMqttReq(true, true, 3); MqttClient.loop(); disconnectMQTT(); delay(500); goToSleep(); } int disconnectMQTT() { MqttClient.disconnect(); return 0; } int goToSleep() { ledset(); WiFi.disconnect(true); WiFi.mode(WIFI_OFF); // adc_power_off(); esp_wifi_stop(); // rtc_gpio_init(pinWak); // rtc_gpio_set_direction(pinWak, RTC_GPIO_MODE_OUTPUT_ONLY); // rtc_gpio_set_level(pinWak, 0); esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); esp_deep_sleep_start(); }