/* Управление сервоприводом MQTT */ #include #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 //Наша кнопочка при нажатии на которую произойдет вызов wifi менеджера и перезагрузка в станцию #define TRIGGER_PIN 39 unsigned int VersionSW = 1; //65536 Версия прошивки 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, "0.ua.pool.ntp.org", 7200, 60000); //Собственно сервер времени смещение и частоат запроса, но он вручную const char *spacePref = "ota-config"; const PROGMEM char *mqttHostName = "cctv.automation.art"; //Хостнейм брокера 192.168.89.210 cctv.automation.art:8889 unsigned int mqttPort = 8889; //Порт брокера 1883 const PROGMEM char *mqttLogin = NULL, //Логин пароль - необходимо сменить код при connect() *mqttPass = NULL; const char *mqttIPHost; //тут хранится IP хоста по хостнейму unsigned long timingUpdate, timingReqSensor, timingSendMqtt, timingAjaxUpdate; //Таймеры для millis() int PROGMEM nextM5Update = 450000; //каждые 7.5 минут запрос обновления с сервера int PROGMEM nextReqSensor = 10000; //опрос датчиков раз в 10 секунд int PROGMEM nextMqttSend = 3000; 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 setup() { M5.begin(true, false, true); M5.Power.begin(); M5.Lcd.clear(WHITE); M5.Lcd.setTextSize(4); M5.Lcd.setTextColor(BLACK); pinMode(25, OUTPUT); digitalWrite(25, LOW); servoAir.attach(25); 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(90); // 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(); } //Установка сервера и порта 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"); int err = WiFi.hostByName(mqttHostName, IpMqtt); if (err == 1) { delay(50); setServCall(IpMqtt); } else { Serial.print("Error code hostByName(): "); Serial.println(err); } } else { setServCall(ipaddr); } } void setServCall(IPAddress SetIpaddr) { MqttClient.setServer(SetIpaddr, mqttPort); MqttClient.setCallback(callback); } 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(MapAngle)); OTApreferences.putString("percent", String(SpeedAngle)); OTApreferences.putString("us", String(SetAngle)); Serial.print("OTApreferences.getString(stateangle): "); Serial.println(OTApreferences.getString("stateangle")); OTApreferences.end(); drawLcd(); } } void drawLcd() { M5.Lcd.clear(WHITE); M5.Lcd.setCursor(20, 50); M5.Lcd.setTextColor(BLACK); M5.Lcd.print(posServo); M5.Lcd.println("<"); M5.Lcd.setCursor(20, 120); M5.Lcd.setTextColor(BLACK); M5.Lcd.print(SetAngle); M5.Lcd.println("%"); M5.Lcd.setCursor(150, 120); M5.Lcd.setTextColor(BLACK); M5.Lcd.print(SpeedAngle); M5.Lcd.println("uS"); } //Функция получения данных из 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=1234567890&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;"); t_httpUpdate_return ret = ESPhttpUpdate.update("http://meteosence.s-host.net/airqa/safeatoms.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(); } void ledset(char color, bool blink = false) { // M5.dis.setBrightness(120); //Половина яркости 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); } //Отправка данных по MQTT void SendMqttReq() { Serial.println("Data in SendMqttReq()"); rssi = WiFi.RSSI(); // doc["mac"] = String(getMacAddress()); doc["swver"] = VersionSW; doc["anglestate"] = String(servoAir.read()); doc["percent"] = SetAngle; doc["rssi"] = rssi; doc["lasterror"] = errorID; 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()) { Serial.println("MQTT reconnect..."); circle++; if (circle == 2) { break; } const char *clientId = macc.c_str(); //macc += String(random(0xffff), HEX); if (MqttClient.connect(clientId, bufWillTopic, 2, true, "OFFLINE")) //mqttLogin, mqttPass { MqttClient.publish(bufTopic, "CONNECT", 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(3000); } } } void loop() { //Проверка старта сервера M5.update(); if (!MqttClient.connected()) { reconnectMqtt(); } if (M5.BtnA.wasPressed()) { if (SetAngle >= 0 && SetAngle < 100) { SetAngle += 1; moveServo(SetAngle, SpeedAngle, posServo); } } if (M5.BtnB.wasPressed()) { if (SetAngle > 0 && SetAngle <= 100) { SetAngle -= 1; moveServo(SetAngle, SpeedAngle, posServo); } } if (M5.BtnC.wasPressed()) { SpeedAngle += 5; if (SpeedAngle > 50) { SpeedAngle = 0; } drawLcd(); } //Если не определен IP то и не будет отправки. // 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; } MqttClient.loop(); } int disconnectMQTT() { MqttClient.publish(bufTopic, "DISCONNECTED", true); MqttClient.disconnect(); return 0; }