diff --git a/M5Atom_airqa.ino b/M5Atom_airqa.ino index 4da8078..8d1a2ea 100644 --- a/M5Atom_airqa.ino +++ b/M5Atom_airqa.ino @@ -1,8 +1,28 @@ +/* +Питание 3,3 В +Логические уровни 3,3 В +Точность измерения влажности ± 2% +Точность измерения температуры ± 0,2 °C +Разрешающая способность показаний датчика HDC1080 14 бит +Диапазон измерения концентрации CO2: 400 ... 8192 ppm +Диапазон измерения концентрации летучих органических веществ 0 ... 1187 ppb +*/ + #include // https://github.com/tzapu/WiFiManager #include //Сохранение настроек хеша прошивки #include #include +#include "M5Atom.h" + +#include + +#include +#include "ClosedCube_HDC1080.h" +#include "Adafruit_CCS811.h" //The device's I2C address is 0x5A + +#include +#include //Наша кнопочка при нажатии на которую произойдет вызов wifi менеджера и перезагрузка в станцию #define TRIGGER_PIN 39 @@ -11,11 +31,73 @@ WiFiManager wm; // обьект менеджера WiFiManagerParameter custom_field; Preferences OTApreferences; -unsigned long timingUpdate; +Ticker RGBWTicker; +Adafruit_CCS811 ccs; +ClosedCube_HDC1080 hdc1080; +String JsonData; +StaticJsonDocument<200> doc; + +WiFiClient espClient; +PubSubClient MqttClient(espClient); +IPAddress IpMqtt; + +const PROGMEM char *mqttHostName = "cctv.automation.art"; +unsigned int mqttPort = 8889; +const PROGMEM char *topicName = "/aastudio"; // +mac адресс девайса +const PROGMEM char *mqttLogin = "login", + *mqttPass = "password"; +const char *mqttIPHost; + +unsigned long timingUpdate, timingReqSensor, timingSendMqtt; 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.22, -9.62, -11.03}; +float PROGMEM hcoeff[3] = {12.30, 13.34, 15.18}; + +float callibrationT = 0, callibrationH = 0; +String macc = ""; +float TempAv, HumAv, Eco2Av, TvocAv; + +#define NUM_AVER 6 +float averageT; // перем. среднего +float valArrayT[NUM_AVER]; // массив +byte idxT = 0; + +float averageH; // перем. среднего +float valArrayH[NUM_AVER]; // массив +byte idxH = 0; + +float averageECO; // перем. среднего +float valArrayECO[NUM_AVER]; // массив +byte idxECO = 0; + +float averageTVOC; // перем. среднего +float valArrayTVOC[NUM_AVER]; // массив +byte idxTVOC = 0; + +bool mqttSendFlag = false; + +// float arrT[5] = {0}, arrH[5] = {0}; +// int arrECO[5] = {0}, tvoc[5] = {0}; +int reqCounter = 0; +bool flagblink = true; void setup() { + M5.begin(true, false, true); + delay(50); + Wire.begin(25, 21); + + pinMode(23, OUTPUT); + digitalWrite(23, LOW); + WiFi.mode(WIFI_STA); Serial.begin(115200); Serial.setDebugOutput(true); @@ -69,40 +151,82 @@ void setup() //if you get here you have connected to the WiFi Serial.println("connected..."); } + + if (!ccs.begin()) //The device's I2C address is 0x5A + { + Serial.println("Failed to start sensor CCS811! "); + } + + ccs.setTempOffset(8.6); + + hdc1080.begin(0x40); //14 бит температура и влажность + hdc1080.setResolution(HDC1080_RESOLUTION_14BIT, HDC1080_RESOLUTION_14BIT); + + //Установка коеффициентов каллибровки по MAC адресу + SetCallibrationCoeff(); + //Запрос IP сервера MQTT и установка сервера + setMqttServer(); +} + +void setMqttServer() +{ + Serial.println("gethostbyname: "); + int err = WiFi.hostByName(mqttHostName, IpMqtt); + if (err == 1) + { + Serial.print("Ip address by Name: "); + Serial.println(IpMqtt); + + delay(50); + + MqttClient.setServer(IpMqtt, mqttPort); + MqttClient.setCallback(callback); + + } + else + { + Serial.print("Error code gethostbyname: "); + Serial.println(err); + } +} + +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(); } void checkButton() { + Serial.println("Button Pressed"); + delay(3000); if (digitalRead(TRIGGER_PIN) == LOW) { - delay(50); - if (digitalRead(TRIGGER_PIN) == LOW) - { - Serial.println("Button Pressed"); - delay(3000); - if (digitalRead(TRIGGER_PIN) == LOW) - { - Serial.println("Button Held"); - Serial.println("Erasing Config, restarting"); - wm.resetSettings(); - ESP.restart(); - } + Serial.println("Button Held"); + Serial.println("Erasing Config, restarting"); + wm.resetSettings(); + ESP.restart(); + } - // start portal w delay - Serial.println("Starting config portal"); - wm.setConfigPortalTimeout(120); + // start portal w delay + Serial.println("Starting config portal"); + wm.setConfigPortalTimeout(120); - if (!wm.startConfigPortal("AirQaPOrtal", "12345678")) - { - Serial.println("failed to connect or hit timeout"); - delay(3000); - // ESP.restart(); - } - else - { - Serial.println("connected.."); - } - } + if (!wm.startConfigPortal("AirQaPortal", "12345678")) + { + Serial.println("failed to connect or hit timeout"); + delay(3000); + // ESP.restart(); + } + else + { + Serial.println("CLIENT connect"); } } @@ -214,14 +338,276 @@ void OTAUpdate() OTApreferences.end(); } +void ledset(char color, bool blink = false) +{ + switch (color) + { + case 'g': + M5.dis.drawpix(0, 0xf00000); //Зеленый + ledset('r', true); + 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; + } + if (blink == true) + { //Таймер для LED + RGBWTicker.attach_ms(1000, ledBlinkTimer); + } + else + { + RGBWTicker.detach(); + } +} + +void ledBlinkTimer() +{ + if (flagblink == true) + { + M5.dis.drawpix(0, 0xf00000); + flagblink = false; + } + else + { + M5.dis.clear(); + flagblink = true; + } +} + +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] + + macc = getMacAddress(); + + 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]; + } +} + +float middleArifmT(float newVal) +{ // принимает новое значение + valArrayT[idxT] = newVal; // пишем каждый раз в новую ячейку + if (++idxT >= NUM_AVER) + idxT = 0; // перезаписывая самое старое значение + averageT = 0; // обнуляем среднее + for (int i = 0; i < NUM_AVER; i++) + { + averageT += valArrayT[i]; // суммируем + } + averageT /= NUM_AVER; // делим + return averageT; // возвращаем +} + +float middleArifmH(float newVal) +{ // принимает новое значение + valArrayH[idxH] = newVal; // пишем каждый раз в новую ячейку + if (++idxH >= NUM_AVER) + idxH = 0; // перезаписывая самое старое значение + averageH = 0; // обнуляем среднее + for (int i = 0; i < NUM_AVER; i++) + { + averageH += valArrayH[i]; // суммируем + } + averageH /= NUM_AVER; // делим + return averageH; // возвращаем +} + +float middleArifmECO(float newVal) +{ // принимает новое значение + valArrayECO[idxECO] = newVal; // пишем каждый раз в новую ячейку + if (++idxECO >= NUM_AVER) + idxECO = 0; // перезаписывая самое старое значение + averageECO = 0; // обнуляем среднее + for (int i = 0; i < NUM_AVER; i++) + { + averageECO += valArrayECO[i]; // суммируем + } + averageECO /= NUM_AVER; // делим + return averageECO; // возвращаем +} + +float middleArifmTVOC(float newVal) +{ // принимает новое значение + valArrayTVOC[idxTVOC] = newVal; // пишем каждый раз в новую ячейку + if (++idxTVOC >= NUM_AVER) + idxTVOC = 0; // перезаписывая самое старое значение + averageTVOC = 0; // обнуляем среднее + for (int i = 0; i < NUM_AVER; i++) + { + averageTVOC += valArrayTVOC[i]; // суммируем + } + averageTVOC /= NUM_AVER; // делим + return averageTVOC; // возвращаем +} + +void reqSensorData() +{ + float hdc1080Temp = 0, hdc1080Hum = 0; + int eco2 = 0, tvoc = 0; + if (ccs.available()) + { + if (!ccs.readData()) + { + eco2 = ccs.geteCO2(); + tvoc = ccs.getTVOC(); + } + else + { + eco2 = 0; + tvoc = 0; + } + } + + hdc1080Temp = hdc1080.readTemperature() + callibrationT; + hdc1080Hum = hdc1080.readHumidity() + callibrationH; + + TempAv = middleArifmT(hdc1080Temp); + HumAv = middleArifmH(hdc1080Hum); + Eco2Av = middleArifmECO(eco2); + TvocAv = middleArifmTVOC(tvoc); +} + +void SendMqttReq() +{ + char HumInt[8], TempInt[8]; + + dtostrf(HumAv * 100, 4, 0, HumInt); + dtostrf(TempAv * 100, 4, 0, TempInt); + + doc["mac"] = String(getMacAddress()); + doc["t"] = String(TempInt); + doc["h"] = String(HumInt); + doc["eco"] = (int)Eco2Av; + doc["tvoc"] = (int)TvocAv; + doc["time"]=millis(); + + char resultString[200]; + String JsonData = ""; + serializeJson(doc, JsonData); + Serial.println(JsonData); //Вывод JSON строки в консоль + JsonData.toCharArray(resultString, JsonData.length() + 1); + + String finishTopic = ""; + finishTopic = String(topicName) + "/" + macc; + + Serial.println(finishTopic); + + if (mqttSendFlag == true) + { + MqttClient.publish(finishTopic.c_str(), resultString); + Serial.println("SentToTopic - ok"); + } +} + +void reconnectMqtt() +{ + Serial.print("Attempting MQTT connection..."); + //clientId += String(random(0xffff), HEX); + //clientId.c_str() + //MqttClient.connect(macc.c_str(), mqttLogin, mqttPass + + const char *clientId = macc.c_str(); + //macc += String(random(0xffff), HEX); + + if (MqttClient.connect("AtomClient")) + { + String finishTopic = ""; + finishTopic = String(topicName) + "/" + macc; + // MqttClient.publish(finishTopic.c_str(), "Reconnect NOW"); + Serial.print("Connect MQTT OK"); + // client.subscribe("inTopic"); + mqttSendFlag = true; + } + else + { + Serial.print("failed, rc="); + Serial.print(MqttClient.state()); + mqttSendFlag = false; + } +} + void loop() { //Проверка старта сервера - checkButton(); + M5.update(); + MqttClient.loop(); + //Если не определен IP то и не будет отправки. + + if (!MqttClient.connected()) + { //Если клиент отключился, переподключение + reconnectMqtt(); + } + + // 40 секунд и происходит сброс настроек WIFI + if (M5.Btn.wasReleasefor(40000)) + { + ledset('r', true); + checkButton(); + } //По таймеру запруск обновления - проверка хеша прошивки на сервере + if (millis() - timingUpdate > nextM5Update) { OTAUpdate(); + + // Serial.print("OTAUpdate() - "); + // Serial.println(millis()); + timingUpdate = millis(); } -} \ No newline at end of file + + if (millis() - timingReqSensor > nextReqSensor) + { + reqSensorData(); + + // Serial.print("reqSensorData() - "); + // Serial.println(millis()); + + timingReqSensor = millis(); + } + + if (millis() - timingSendMqtt > nextMqttSend) + { + SendMqttReq(); + timingSendMqtt = millis(); + } +}