/* Управление сервоприводом MQTT */ #include // https://github.com/tzapu/WiFiManager #include //Сохранение настроек хеша прошивки #include #include //Библиотека ОТА обновлений #include "M5Atom.h" //Библиотека атома для функции Led и Кнопки, можно упразднить и убрать #include //Udp клиент #include //NTP запрос времени #include //Внутреннее время #include //Библиотека дял I2C #include //Mtqq #include //Упакова в JSon - удобная библиотека #include #include #include #include #define ONE_WIRE_BUS 26 //Наша кнопочка при нажатии на которую произойдет вызов wifi менеджера и перезагрузка в станцию #define TRIGGER_PIN 39 #define SERVO_PIN 33 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); DeviceAddress insideThermometer; unsigned int VersionSW = 8; //65536 Версия прошивки //2 поиск по metrics local //3 - //4 - тестовая для проверки загрузки прошивки //5 - логин и пароль //7- температура //8 атач Servo servoAir; int posServo = 0; int posServoLast = 0; byte errorID = 0; WiFiManager wm; // обьект менеджера WiFiManagerParameter custom_field; Preferences OTApreferences; //Обьект хранения настроек хеша прошивки String JsonData; StaticJsonDocument<200> doc, docResult; WiFiClient espClient; PubSubClient MqttClient(espClient); WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "192.168.89.210", 0, 30000); //Собственно сервер времени смещение и частоат запроса, но он вручную float tempC=0; const char *spacePref = "ota-config"; const PROGMEM char *mqttHostName = "metrics"; //Хостнейм брокера 192.168.89.210 cctv.automation.art:8889 unsigned int mqttPort = 1883; //Порт брокера 1883 const PROGMEM char *mqttLogin = "AA_Lab", //Логин пароль - необходимо сменить код при connect() *mqttPass = "automation.art$"; const char *mqttIPHost; //тут хранится IP хоста по хостнейму unsigned long timingUpdate,timingUpdateDS, timingReqSensor, timingSendMqtt, timingAjaxUpdate; //Таймеры для millis() int PROGMEM nextM5Update = 60000; //450000 //каждые 7.5 минут запрос обновления с сервера int PROGMEM nextM5UpdateDS = 10000; //450000 //каждые 7.5 минут запрос обновления с сервера String getMacAddress(); String macc = getMacAddress(); bool mqttSendFlag = false; int reqCounter = 0; long rssi = 0; byte MapAngle, SetAngle, SpeedAngle = 10; char bufTopic[140], bufWillTopic[150], bufAngleTopic[150]; void ledset(char color, bool blink = false) { M5.dis.setBrightness(60); //Половина яркости 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; } } void printTemperature(DeviceAddress deviceAddress) { float tempC = sensors.getTempC(deviceAddress); if(tempC == DEVICE_DISCONNECTED_C) { Serial.println("Error: Could not read temperature data"); return; } Serial.print("Temp C: "); Serial.print(tempC); } //Настройки void setup() { M5.begin(true, false, true); servoAir.attach(SERVO_PIN); sensors.begin(); sensors.setResolution(insideThermometer, 9); pinMode(SERVO_PIN, OUTPUT); digitalWrite(SERVO_PIN, LOW); String topicTemp = "aastudio/" + macc; String willTopicTemp = topicTemp + "/status"; String angleTopicTemp = topicTemp + "/setangle"; Serial.println(topicTemp); Serial.println(willTopicTemp); Serial.println(angleTopicTemp); topicTemp.toCharArray(bufTopic, topicTemp.length() + 1); willTopicTemp.toCharArray(bufWillTopic, willTopicTemp.length() + 1); angleTopicTemp.toCharArray(bufAngleTopic, angleTopicTemp.length() + 1); WiFi.mode(WIFI_STA); Serial.begin(115200); Serial.setDebugOutput(true); delay(100); Serial.println("\n Starting station"); pinMode(TRIGGER_PIN, INPUT); //int customFieldLength = 40; 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(30); // auto close configportal after n seconds wm.setWiFiAutoReconnect(true); bool res; res = wm.autoConnect("AirAnglePortal", "12345678"); // Подключение к анонимной точке доступа if (!res) { Serial.println("Failed to connect or hit timeout"); ESP.restart(); } else { Serial.println("connected client - OK"); } //Запрос IP сервера MQTT и установка сервера setMqttServer(); reqNtpTime(); reqNtpTime(); firstStart(); } //Установка сервера и порта void setMqttServer() { mdns_init(); IPAddress IpMqtt, ipaddr; ipaddr = MDNS.queryHost(mqttHostName); // .local omitted // Serial.println(ipaddr.toString()); if (ipaddr.toString() == "0.0.0.0") { Serial.println("Trying again to resolve mDNS"); ledset('r'); int err = WiFi.hostByName(mqttHostName, IpMqtt); if (err == 1) { delay(50); setServCall(IpMqtt); ledset('w'); } else { Serial.print("Error code hostByName(): "); Serial.println(err); ledset('r'); } } else { setServCall(ipaddr); ledset('w'); } } void setServCall(IPAddress SetIpaddr) { MqttClient.setServer(SetIpaddr, mqttPort); MqttClient.setCallback(callback); } void firstStart() { OTApreferences.begin(spacePref, true); posServo = OTApreferences.getString("stateangle").toInt(); SetAngle = OTApreferences.getString("percent").toInt(); SpeedAngle = OTApreferences.getString("us").toInt(); OTApreferences.end(); Serial.println(SpeedAngle); Serial.println(SetAngle); Serial.println(posServo); moveServo((int)SetAngle, (int)SpeedAngle, posServo); SendMqttReq(); } void moveServo(int SetAngleTmp, int SpeedAngleTmp, int posServoTmp) { if ((SetAngleTmp >= 0 && SetAngleTmp <= 100) && (SpeedAngleTmp >= 0 && SpeedAngleTmp <= 50)) { MapAngle = map(SetAngleTmp, 0, 100, 2, 178); // 544 мкс — для 0° и 2400 мкс — для 180°. if (posServoTmp < MapAngle) { for (int tok = posServoTmp; tok <= MapAngle; tok++) { servoAir.write(tok); delay(SpeedAngleTmp); } } else { for (int tok = posServoTmp; tok >= MapAngle; tok--) { servoAir.write(tok); delay(SpeedAngleTmp); } } //servoAir.write(SetAngle); posServo = (int)MapAngle; SpeedAngle = SpeedAngleTmp; SetAngle = SetAngleTmp; OTApreferences.begin(spacePref, false); OTApreferences.putString("stateangle", String(posServo)); OTApreferences.putString("us", String(SpeedAngle)); OTApreferences.putString("percent", String(SetAngle)); Serial.println(SpeedAngle); Serial.println(SetAngle); Serial.println(posServo); OTApreferences.end(); // servoAir.detach(); } } //Функция получения данных из MQTT если мы подпишемся на топики void callback(char *topic, byte *payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); char jsonResult[250]; for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); jsonResult[i] = (char)payload[i]; } DeserializationError error = deserializeJson(docResult, jsonResult); if (error) { Serial.print(F("deserializeJson() failed: ")); //Serial.println(String(error)); return; } // {"percent":50,"speed":20} //сделать проверку на байті SetAngle = docResult["percent"]; SpeedAngle = docResult["speed"]; //0-100 // byte isTimer = docResult["timer"]; // byte isTimerAngle = docResult["timerangle"]; // Serial.println(SetAngle); moveServo(SetAngle, SpeedAngle, posServo); } // Serial.println(isTimer); // Serial.println(isTimerAngle); //Запрос времени NTP и установка локлаьного времени void reqNtpTime() { timeClient.update(); setTime(timeClient.getEpochTime()); // Serial.println(timeClient.getEpochTime()); // Serial.println("<=ntp====now=>"); //time_t t = now(); // Serial.println(t); } //Нажатие кнопки для сброса void checkButton() { disconnectMQTT(); wm.resetSettings(); ESP.restart(); } void saveParamCallback() { Serial.println("[CALLBACK] saveParamCallback fired"); Serial.println("PARAM customfieldid = " + getParam("customfieldid")); } String getParam(String name) { String value; if (wm.server->hasArg(name)) { value = wm.server->arg(name); } return value; } //Обновление прошивки, происходит проверка и загрузка //Делается Get запрос на хостинг проверяется хеш, если хеш void OTAUpdate() { Serial.println("OTAUpdate()"); bool flagOTA = false; String keyOTA; String payload; OTApreferences.begin(spacePref, false); if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String serverPath = "http://meteosence.s-host.net/airqa/airquality.php?meteopas=6v567t5pDU2UDH7eBydb&mac=" + macc + "&meteodata=gethash"; http.begin(serverPath.c_str()); int httpResponseCode = http.GET(); if (httpResponseCode > 0) { Serial.print("HTTP Response code: "); Serial.println(httpResponseCode); payload = http.getString(); Serial.println(payload); if (payload != "errno" || payload != "errfi") { 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); Serial.println("flagOTA = true;"); } } else { Serial.println("Hosting return error HASH or error REQUEST"); } } else { Serial.print("Error code HTTP: "); Serial.println(httpResponseCode); } // Free resources http.end(); } else { Serial.println("WiFi Disconnected"); } if (flagOTA == true) { flagOTA = false; Serial.println("flagOTA = false;"); ledset('b'); t_httpUpdate_return ret = ESPhttpUpdate.update("http://meteosence.s-host.net/airqa/servo/servoatoms.bin"); //После update ничего не происходит, такая вот особенность. //Если все прошло хорошо, перезагрузка на новую прошивку 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"); delay(3000); ESP.restart(); break; case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); delay(3000); ESP.restart(); break; case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); delay(3000); ESP.restart(); break; } } OTApreferences.end(); } //Получение мак адреса 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); } //Отправка данных по MQTT void SendMqttReq() { ledset('g'); Serial.println("Data in SendMqttReq()"); rssi = WiFi.RSSI(); // doc["mac"] = String(getMacAddress()); doc["swver"] = VersionSW; servoAir.attach(SERVO_PIN); doc["anglestate"] = posServo; servoAir.detach(); doc["percent"] = SetAngle; doc["rssi"] = rssi; doc["t"]=tempC; doc["time"] = now(); char resultString[200]; String JsonData = ""; serializeJson(doc, JsonData); Serial.println(JsonData); //Вывод JSON строки в консоль JsonData.toCharArray(resultString, JsonData.length() + 1); if (mqttSendFlag == true) { MqttClient.publish(bufTopic, resultString, true); //Serial.println("SentToTopic - ok"); } } //Переподключение при петери связи с MQTT //10 раз проверили и вернулись в общий цикл что бы вдруг что втянуть обновления void reconnectMqtt() { //clientId += String(random(0xffff), HEX); //clientId.c_str() //MqttClient.connect(macc.c_str(), mqttLogin, mqttPass byte circle = 0; while (!MqttClient.connected()) { ledset('r'); Serial.println("MQTT reconnect..."); circle++; if (circle == 2) { break; } const char *clientId = macc.c_str(); //macc += String(random(0xffff), HEX); if (MqttClient.connect(clientId, mqttLogin, mqttPass, bufWillTopic, 2, true, "OFFLINE")) //mqttLogin, mqttPass { MqttClient.publish(bufTopic, "CONNECTED", true); MqttClient.publish(bufWillTopic, "ONLINE", true); MqttClient.subscribe(bufAngleTopic); mqttSendFlag = true; } 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(2000); } } } void loop() { //Проверка старта сервера M5.update(); if (!MqttClient.connected()) { reconnectMqtt(); } // 40 секунд и происходит сброс настроек WIFI if (M5.Btn.wasReleasefor(40000)) { checkButton(); } //По таймеру запруск обновления прошивки if (millis() - timingUpdate > nextM5Update) { reqNtpTime(); OTAUpdate(); Serial.print("OTAUpdate() "); // Serial.println(millis()); timingUpdate = millis(); } if (posServo != posServoLast) { SendMqttReq(); posServoLast = posServo; } if (millis() - timingUpdateDS > nextM5UpdateDS) { sensors.requestTemperatures(); SendMqttReq(); tempC = sensors.getTempCByIndex(0); // Check if reading was successful if(tempC != DEVICE_DISCONNECTED_C) { Serial.print("Temperature for the device 1 (index 0) is: "); Serial.println(tempC); } else { Serial.println("Error: Could not read temperature data"); } timingUpdateDS = millis(); } MqttClient.loop(); } int disconnectMQTT() { MqttClient.publish(bufTopic, "DISCONNECTED", true); MqttClient.disconnect(); return 0; }