dudeden
Published © GPL3+

Upgrading a Window Cleaning Robot from IR Remote to Wi-Fi Co

Upgrading an IR-controlled window cleaning robot with Wi-Fi, MQTT, and Home Assistant integration using a WeMos D1 Mini.

IntermediateWork in progress1 hour11
Upgrading a Window Cleaning Robot from IR Remote to Wi-Fi Co

Things used in this project

Hardware components

Wemos D1 Mini
Espressif Wemos D1 Mini
×1
Resistor 1k ohm
Resistor 1k ohm
×1
Resistor 22.1k ohm
Resistor 22.1k ohm
×1
Optocoupler, Transistor Output
Optocoupler, Transistor Output
PC817
×1
1N4007 – High Voltage, High Current Rated Diode
1N4007 – High Voltage, High Current Rated Diode
MB10S, DB107, 1N4148
×1
General Purpose Transistor NPN
General Purpose Transistor NPN
2N2222, 2N3904, BC547
×1
Hook Up Wire Kit, 22 AWG
Hook Up Wire Kit, 22 AWG
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Multitool, Screwdriver
Multitool, Screwdriver
Mastech MS8217 Autorange Digital Multimeter
Digilent Mastech MS8217 Autorange Digital Multimeter

Story

Read more

Code

Final Firmware

Arduino
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <PubSubClient.h>
#include <ArduinoOTA.h>
#include <LittleFS.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>

#define IR_OUT_PIN D2
#define MOTOR_PIN  D5

#define FW_VERSION "1.3"

char mqtt_host[40] = "";
char mqtt_port[6]  = "1883";
char mqtt_user[32] = "";
char mqtt_pass[32] = "";
char mqtt_base[50] = "window_cleaner";

WiFiClient espClient;
PubSubClient mqtt(espClient);
ESP8266WebServer server(80);
ESP8266HTTPUpdateServer httpUpdater;

String topicCmd;
String topicState;
String topicMotor;
String topicAvailability;
String topicFinishDelaySet;
String topicFinishDelayState;

bool shouldSaveConfig = false;
String robotState = "idle";

bool motorRunning = false;
bool lastMotorRunning = false;
unsigned long motorStopSince = 0;

unsigned long finishDelayMs = 10000;

// ================= RAW COMMANDS =================

const uint16_t raw_up_auto[67] = {
  9104,4382,690,440,690,442,690,442,692,440,686,444,686,446,688,444,686,446,
  688,1566,692,1566,688,1566,690,1564,688,1568,688,1566,690,1566,690,1564,
  690,444,684,1568,690,446,686,444,682,1570,686,448,680,452,684,448,
  684,1570,684,450,682,1572,682,1572,682,452,680,1572,682,1574,684,1572,682
};

const uint16_t raw_left_auto[67] = {
  9094,4394,680,450,678,454,678,454,678,454,678,454,678,454,678,454,676,456,
  678,1576,678,1576,678,1578,680,1576,678,1576,676,1580,678,1578,678,1576,
  678,456,674,1578,678,454,676,1576,676,1578,676,458,672,460,672,458,
  674,1580,674,458,668,1584,672,460,672,460,672,1582,672,1582,644,1612,644
};

const uint16_t raw_right_auto[67] = {
  9042,4442,630,500,630,502,630,502,628,504,628,504,630,502,630,502,630,502,
  630,1624,630,1626,630,1626,630,1624,630,1624,630,1624,632,1624,632,1624,
  632,502,630,1624,632,1624,632,1624,634,1622,632,500,632,502,630,500,
  632,1622,632,500,632,500,632,500,630,500,632,1622,634,1622,632,1622,632
};

const uint16_t raw_water_auto[67] = {
  9050,4434,640,490,638,494,638,494,638,494,638,494,638,494,638,494,638,494,
  638,1616,640,1616,638,1616,640,1616,638,1616,640,1616,640,1614,640,1614,
  638,1616,640,494,638,494,638,494,638,494,638,492,638,494,638,492,
  638,492,636,1616,638,1616,638,1616,640,1614,640,1616,640,1614,640,1616,640
};

const uint16_t raw_water_manual[67] = {
  9052,4426,644,486,642,488,644,488,642,488,644,488,644,488,644,488,642,488,
  644,1608,646,1608,676,1580,646,1608,676,1580,674,1580,676,1578,676,1578,
  676,456,678,1574,676,458,676,454,678,454,676,456,678,454,674,456,
  678,1574,680,452,676,1576,680,1574,680,1574,680,1574,680,1574,678,1576,678
};

const uint16_t raw_up_manual[67] = {
  9022,4460,576,554,576,556,576,556,576,556,576,556,576,556,576,556,576,554,
  576,1678,578,1678,578,1678,578,1678,578,1678,578,1678,578,1678,578,1678,
  576,1678,578,554,576,1678,578,554,578,556,576,554,578,554,576,554,
  660,472,576,1676,662,470,660,1594,660,1594,660,1594,578,1678,578,1676,660
};

const uint16_t raw_left_manual[67] = {
  9048,4434,638,492,638,494,638,494,636,494,638,494,636,494,638,494,638,494,
  638,1614,642,1612,644,1612,644,1610,644,1612,644,1610,648,1608,646,1608,
  648,1608,646,1608,676,1578,678,456,646,484,676,456,676,456,676,454,
  676,456,674,456,676,456,678,1574,678,1576,678,1576,680,1574,680,1576,680
};

const uint16_t raw_right_manual[67] = {
  9040,4438,632,498,632,500,630,500,630,500,630,500,630,502,630,500,632,498,
  630,1622,634,1620,634,1620,636,1620,636,1620,636,1616,636,1618,636,1618,
  638,1616,638,494,636,496,636,1616,638,496,634,496,636,494,664,466,
  636,494,666,1588,638,1616,666,468,636,1618,668,1588,666,1588,670,1584,670
};

const uint16_t raw_down_manual[67] = {
  9100,4378,694,438,690,442,690,440,688,442,690,440,690,440,688,444,688,442,
  688,1564,692,1562,692,1564,692,1564,690,1564,692,1562,692,1564,692,1562,
  692,1564,690,1564,690,444,686,1566,688,1568,690,442,684,448,686,444,
  682,448,684,448,682,1568,686,446,684,446,682,1570,684,1570,686,1568,684
};

const uint16_t raw_pause_play[67] = {
  9038,4440,628,504,626,504,626,504,628,502,628,504,628,504,626,504,628,504,
  626,1626,628,1626,628,1626,628,1626,630,1624,628,1626,630,1624,630,1624,
  628,504,626,504,628,502,628,1624,630,502,628,504,630,502,628,504,
  626,1626,630,1624,630,1624,630,502,630,1624,630,1624,628,1626,628,1626,630
};


void saveConfigCallback() {
  shouldSaveConfig = true;
}

void saveConfig() {
  File f = LittleFS.open("/config.txt", "w");
  if (!f) return;

  f.println(mqtt_host);
  f.println(mqtt_port);
  f.println(mqtt_user);
  f.println(mqtt_pass);
  f.println(mqtt_base);
  f.println(finishDelayMs);
  f.close();
}

void loadConfig() {
  if (!LittleFS.exists("/config.txt")) return;

  File f = LittleFS.open("/config.txt", "r");
  if (!f) return;

  String s;
  s = f.readStringUntil('\n'); s.trim(); s.toCharArray(mqtt_host, sizeof(mqtt_host));
  s = f.readStringUntil('\n'); s.trim(); s.toCharArray(mqtt_port, sizeof(mqtt_port));
  s = f.readStringUntil('\n'); s.trim(); s.toCharArray(mqtt_user, sizeof(mqtt_user));
  s = f.readStringUntil('\n'); s.trim(); s.toCharArray(mqtt_pass, sizeof(mqtt_pass));
  s = f.readStringUntil('\n'); s.trim(); s.toCharArray(mqtt_base, sizeof(mqtt_base));

  s = f.readStringUntil('\n');
  s.trim();
  if (s.length() > 0) finishDelayMs = s.toInt();
  if (finishDelayMs < 1000 || finishDelayMs > 600000) finishDelayMs = 10000;

  f.close();
}


void publishState() {
  if (!mqtt.connected()) return;

  mqtt.publish(topicState.c_str(), robotState.c_str(), true);
  mqtt.publish(topicMotor.c_str(), motorRunning ? "ON" : "OFF", true);
  mqtt.publish(topicFinishDelayState.c_str(), String(finishDelayMs / 1000).c_str(), true);
}

void setRobotState(String newState) {
  if (robotState == newState) return;
  robotState = newState;
  publishState();
}

void setFinishDelaySeconds(int sec) {
  if (sec < 1) sec = 1;
  if (sec > 600) sec = 600;

  finishDelayMs = sec * 1000UL;
  saveConfig();
  publishState();
}

// ================= IR =================

void sendRawToOut(const uint16_t *data, uint16_t len) {
  noInterrupts();

  for (uint16_t i = 0; i < len; i++) {
    digitalWrite(IR_OUT_PIN, (i & 1) ? LOW : HIGH);
    delayMicroseconds(data[i]);
  }

  digitalWrite(IR_OUT_PIN, LOW);
  interrupts();
  delay(80);
}

bool sendCommand(String cmd) {
  cmd.trim();

  if (cmd == "up_auto") {
    sendRawToOut(raw_up_auto, 67);
    setRobotState("cleaning");
  } else if (cmd == "left_auto") {
    sendRawToOut(raw_left_auto, 67);
    setRobotState("cleaning");
  } else if (cmd == "right_auto") {
    sendRawToOut(raw_right_auto, 67);
    setRobotState("cleaning");
  } else if (cmd == "water_auto") {
    sendRawToOut(raw_water_auto, 67);
  } else if (cmd == "water_manual") {
    sendRawToOut(raw_water_manual, 67);
  } else if (cmd == "up_manual") {
    sendRawToOut(raw_up_manual, 67);
    setRobotState("cleaning");
  } else if (cmd == "left_manual") {
    sendRawToOut(raw_left_manual, 67);
    setRobotState("cleaning");
  } else if (cmd == "right_manual") {
    sendRawToOut(raw_right_manual, 67);
    setRobotState("cleaning");
  } else if (cmd == "down_manual") {
    sendRawToOut(raw_down_manual, 67);
    setRobotState("cleaning");
  } else if (cmd == "pause") {
    sendRawToOut(raw_pause_play, 67);
  } else {
    return false;
  }

  publishState();
  return true;
}


void updateMotorStatus() {
  motorRunning = (digitalRead(MOTOR_PIN) == LOW);

  if (motorRunning != lastMotorRunning) {
    lastMotorRunning = motorRunning;
    publishState();

    if (motorRunning) {
      motorStopSince = 0;
      if (robotState != "cleaning") setRobotState("cleaning");
    } else {
      motorStopSince = millis();
    }
  }

  if (robotState == "cleaning" && !motorRunning && motorStopSince > 0) {
    if (millis() - motorStopSince > finishDelayMs) {
      setRobotState("finished");
      motorStopSince = 0;
    }
  }
}

void publishButtonDiscovery(const char* id, const char* name, const char* payload, const char* icon) {
  String chip = String(ESP.getChipId(), HEX);
  String configTopic = "homeassistant/button/window_cleaner_" + String(id) + "/config";

  String json = "{";
  json += "\"name\":\"" + String(name) + "\",";
  json += "\"unique_id\":\"window_cleaner_" + chip + "_" + String(id) + "\",";
  json += "\"command_topic\":\"" + topicCmd + "\",";
  json += "\"payload_press\":\"" + String(payload) + "\",";
  json += "\"icon\":\"" + String(icon) + "\",";
  json += "\"availability_topic\":\"" + topicAvailability + "\",";
  json += "\"device\":{\"identifiers\":[\"window_cleaner_" + chip + "\"],";
  json += "\"name\":\"Робот мойщик окон\",";
  json += "\"manufacturer\":\"DIY\",";
  json += "\"model\":\"WeMos D1 mini IR OUT\",";
  json += "\"sw_version\":\"" FW_VERSION "\"}}";

  mqtt.publish(configTopic.c_str(), json.c_str(), true);
}

void publishSensorDiscovery() {
  String chip = String(ESP.getChipId(), HEX);

  String stateConfig = "homeassistant/sensor/window_cleaner_state/config";
  String stateJson = "{";
  stateJson += "\"name\":\"Состояние\",";
  stateJson += "\"unique_id\":\"window_cleaner_" + chip + "_state\",";
  stateJson += "\"state_topic\":\"" + topicState + "\",";
  stateJson += "\"icon\":\"mdi:robot-vacuum\",";
  stateJson += "\"availability_topic\":\"" + topicAvailability + "\",";
  stateJson += "\"device\":{\"identifiers\":[\"window_cleaner_" + chip + "\"],";
  stateJson += "\"name\":\"Робот мойщик окон\",";
  stateJson += "\"manufacturer\":\"DIY\",";
  stateJson += "\"model\":\"WeMos D1 mini IR OUT\",";
  stateJson += "\"sw_version\":\"" FW_VERSION "\"}}";

  mqtt.publish(stateConfig.c_str(), stateJson.c_str(), true);

  String motorConfig = "homeassistant/binary_sensor/window_cleaner_motor/config";
  String motorJson = "{";
  motorJson += "\"name\":\"Мотор работает\",";
  motorJson += "\"unique_id\":\"window_cleaner_" + chip + "_motor\",";
  motorJson += "\"state_topic\":\"" + topicMotor + "\",";
  motorJson += "\"payload_on\":\"ON\",";
  motorJson += "\"payload_off\":\"OFF\",";
  motorJson += "\"device_class\":\"running\",";
  motorJson += "\"availability_topic\":\"" + topicAvailability + "\",";
  motorJson += "\"device\":{\"identifiers\":[\"window_cleaner_" + chip + "\"],";
  motorJson += "\"name\":\"Робот мойщик окон\",";
  motorJson += "\"manufacturer\":\"DIY\",";
  motorJson += "\"model\":\"WeMos D1 mini IR OUT\",";
  motorJson += "\"sw_version\":\"" FW_VERSION "\"}}";

  mqtt.publish(motorConfig.c_str(), motorJson.c_str(), true);

  String delayConfig = "homeassistant/number/window_cleaner_finish_delay/config";
  String delayJson = "{";
  delayJson += "\"name\":\"Задержка завершения\",";
  delayJson += "\"unique_id\":\"window_cleaner_" + chip + "_finish_delay\",";
  delayJson += "\"command_topic\":\"" + topicFinishDelaySet + "\",";
  delayJson += "\"state_topic\":\"" + topicFinishDelayState + "\",";
  delayJson += "\"min\":1,";
  delayJson += "\"max\":600,";
  delayJson += "\"step\":1,";
  delayJson += "\"unit_of_measurement\":\"s\",";
  delayJson += "\"icon\":\"mdi:timer-outline\",";
  delayJson += "\"availability_topic\":\"" + topicAvailability + "\",";
  delayJson += "\"device\":{\"identifiers\":[\"window_cleaner_" + chip + "\"],";
  delayJson += "\"name\":\"Робот мойщик окон\",";
  delayJson += "\"manufacturer\":\"DIY\",";
  delayJson += "\"model\":\"WeMos D1 mini IR OUT\",";
  delayJson += "\"sw_version\":\"" FW_VERSION "\"}}";

  mqtt.publish(delayConfig.c_str(), delayJson.c_str(), true);
}

void publishDiscovery() {
  publishButtonDiscovery("up_auto", "UP Auto", "up_auto", "mdi:arrow-up-bold");
  publishButtonDiscovery("left_auto", "Left Auto", "left_auto", "mdi:arrow-left-bold");
  publishButtonDiscovery("right_auto", "Right Auto", "right_auto", "mdi:arrow-right-bold");
  publishButtonDiscovery("water_auto", "Water Auto", "water_auto", "mdi:water");
  publishButtonDiscovery("water_manual", "Water Manual", "water_manual", "mdi:water-outline");
  publishButtonDiscovery("up_manual", "Вверх", "up_manual", "mdi:arrow-up-bold-circle");
  publishButtonDiscovery("left_manual", "Влево", "left_manual", "mdi:arrow-left-bold-circle");
  publishButtonDiscovery("right_manual", "Вправо", "right_manual", "mdi:arrow-right-bold-circle");
  publishButtonDiscovery("down_manual", "Вниз", "down_manual", "mdi:arrow-down-bold-circle");
  publishButtonDiscovery("pause", "Пауза / Старт", "pause", "mdi:play-pause");

  publishSensorDiscovery();
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  String msg;
  for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
  msg.trim();

  String t = String(topic);

  if (t == topicCmd) {
    sendCommand(msg);
  } else if (t == topicFinishDelaySet) {
    setFinishDelaySeconds(msg.toInt());
  }
}

void reconnectMqtt() {
  if (mqtt.connected()) return;
  if (strlen(mqtt_host) == 0) return;

  String clientId = "wemos_window_cleaner_";
  clientId += String(ESP.getChipId(), HEX);

  bool ok;

  if (strlen(mqtt_user) > 0) {
    ok = mqtt.connect(clientId.c_str(), mqtt_user, mqtt_pass,
                      topicAvailability.c_str(), 0, true, "offline");
  } else {
    ok = mqtt.connect(clientId.c_str(),
                      topicAvailability.c_str(), 0, true, "offline");
  }

  if (ok) {
    mqtt.publish(topicAvailability.c_str(), "online", true);
    mqtt.subscribe(topicCmd.c_str());
    mqtt.subscribe(topicFinishDelaySet.c_str());
    publishDiscovery();
    publishState();
  }
}

String pageHeader(String title) {
  return String(
    "<!DOCTYPE html><html><head><meta charset='utf-8'>"
    "<meta name='viewport' content='width=device-width,initial-scale=1'>"
    "<title>") + title + "</title>"
    "<style>"
    "body{font-family:Arial;background:#111;color:#eee;margin:0;padding:16px}"
    ".card{max-width:520px;margin:auto;background:#1b1b1b;padding:16px;border-radius:14px}"
    "a.btn,button{display:block;width:100%;box-sizing:border-box;margin:8px 0;padding:14px;border:0;border-radius:10px;background:#2d7ef7;color:white;font-size:18px;text-align:center;text-decoration:none}"
    ".grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin:12px 0}"
    ".grid a.btn{margin:0}"
    "input{width:100%;box-sizing:border-box;padding:10px;margin:6px 0 12px;border-radius:8px;border:0}"
    ".muted{color:#aaa;font-size:14px}"
    ".ok{color:#7CFC00}"
    "</style></head><body><div class='card'><h2>" + title + "</h2>";
}

String pageFooter() {
  return "</div></body></html>";
}

void handleRoot() {
  String html = pageHeader("Робот мойщик окон");

  html += "<p class='muted'>IP: " + WiFi.localIP().toString() + "</p>";
  html += "<p>State: <b>" + robotState + "</b></p>";
  html += "<p>Motor: <b>" + String(motorRunning ? "RUN" : "STOP") + "</b></p>";
  html += "<p>Finish delay: <b>" + String(finishDelayMs / 1000) + " сек</b></p>";

  html += "<a class='btn' href='/cmd?c=up_auto'>UP Auto</a>";
  html += "<a class='btn' href='/cmd?c=left_auto'>Left Auto</a>";
  html += "<a class='btn' href='/cmd?c=right_auto'>Right Auto</a>";
  html += "<a class='btn' href='/cmd?c=water_auto'>Water Auto</a>";
  html += "<a class='btn' href='/cmd?c=water_manual'>Water Manual</a>";

  html += "<div class='grid'>";
  html += "<div></div><a class='btn' href='/cmd?c=up_manual'>▲</a><div></div>";
  html += "<a class='btn' href='/cmd?c=left_manual'>◀</a>";
  html += "<a class='btn' href='/cmd?c=pause'>⏯</a>";
  html += "<a class='btn' href='/cmd?c=right_manual'>▶</a>";
  html += "<div></div><a class='btn' href='/cmd?c=down_manual'>▼</a><div></div>";
  html += "</div>";

  html += "<a class='btn' href='/settings'>Настройки</a>";
  html += "<a class='btn' href='/update'>OTA через браузер</a>";
  html += "<a class='btn' href='/resetwifi'>Сброс Wi-Fi/MQTT</a>";

  html += pageFooter();
  server.send(200, "text/html; charset=utf-8", html);
}

void handleCmd() {
  if (!server.hasArg("c")) {
    server.send(400, "text/plain; charset=utf-8", "Нет команды");
    return;
  }

  String cmd = server.arg("c");
  bool ok = sendCommand(cmd);

  String html = pageHeader("Команда");
  html += ok ? "<p class='ok'>Отправлено: <b>" + cmd + "</b></p>"
             : "<p>Ошибка: <b>" + cmd + "</b></p>";
  html += "<a class='btn' href='/'>Назад</a>";
  html += pageFooter();

  server.send(200, "text/html; charset=utf-8", html);
}

void handleSettings() {
  String html = pageHeader("Настройки");

  html += "<form action='/save' method='post'>";
  html += "MQTT host:<input name='host' value='" + String(mqtt_host) + "'>";
  html += "MQTT port:<input name='port' value='" + String(mqtt_port) + "'>";
  html += "MQTT user:<input name='user' value='" + String(mqtt_user) + "'>";
  html += "MQTT password:<input name='pass' value='" + String(mqtt_pass) + "' type='password'>";
  html += "MQTT base topic:<input name='base' value='" + String(mqtt_base) + "'>";
  html += "Задержка завершения, сек:<input name='finish_delay' value='" + String(finishDelayMs / 1000) + "'>";
  html += "<button type='submit'>Сохранить и перезагрузить</button>";
  html += "</form>";
  html += "<a class='btn' href='/'>Назад</a>";

  html += pageFooter();
  server.send(200, "text/html; charset=utf-8", html);
}

void handleSave() {
  server.arg("host").toCharArray(mqtt_host, sizeof(mqtt_host));
  server.arg("port").toCharArray(mqtt_port, sizeof(mqtt_port));
  server.arg("user").toCharArray(mqtt_user, sizeof(mqtt_user));
  server.arg("pass").toCharArray(mqtt_pass, sizeof(mqtt_pass));
  server.arg("base").toCharArray(mqtt_base, sizeof(mqtt_base));

  if (server.hasArg("finish_delay")) {
    setFinishDelaySeconds(server.arg("finish_delay").toInt());
  }

  saveConfig();

  server.send(200, "text/html; charset=utf-8", "<meta charset='utf-8'>Сохранено. Перезагрузка...");
  delay(1000);
  ESP.restart();
}

void handleResetWifi() {
  WiFiManager wm;
  wm.resetSettings();
  LittleFS.remove("/config.txt");

  server.send(200, "text/html; charset=utf-8", "<meta charset='utf-8'>Настройки сброшены. Перезагрузка...");
  delay(1000);
  ESP.restart();
}

void setupWeb() {
  server.on("/", handleRoot);
  server.on("/cmd", handleCmd);
  server.on("/settings", handleSettings);
  server.on("/save", HTTP_POST, handleSave);
  server.on("/resetwifi", handleResetWifi);

  httpUpdater.setup(&server, "/update");
  server.begin();
}

void setup() {
  Serial.begin(115200);
  delay(300);

  pinMode(IR_OUT_PIN, OUTPUT);
  digitalWrite(IR_OUT_PIN, LOW);

  pinMode(MOTOR_PIN, INPUT_PULLUP);

  LittleFS.begin();
  loadConfig();

  WiFiManager wm;
  wm.setSaveConfigCallback(saveConfigCallback);
  wm.setConfigPortalTimeout(180);

  WiFiManagerParameter custom_mqtt_host("host", "MQTT host", mqtt_host, 40);
  WiFiManagerParameter custom_mqtt_port("port", "MQTT port", mqtt_port, 6);
  WiFiManagerParameter custom_mqtt_user("user", "MQTT user", mqtt_user, 32);
  WiFiManagerParameter custom_mqtt_pass("pass", "MQTT password", mqtt_pass, 32);
  WiFiManagerParameter custom_mqtt_base("base", "MQTT base topic", mqtt_base, 50);

  wm.addParameter(&custom_mqtt_host);
  wm.addParameter(&custom_mqtt_port);
  wm.addParameter(&custom_mqtt_user);
  wm.addParameter(&custom_mqtt_pass);
  wm.addParameter(&custom_mqtt_base);

  bool res = wm.autoConnect("WindowCleaner-Setup");

  strcpy(mqtt_host, custom_mqtt_host.getValue());
  strcpy(mqtt_port, custom_mqtt_port.getValue());
  strcpy(mqtt_user, custom_mqtt_user.getValue());
  strcpy(mqtt_pass, custom_mqtt_pass.getValue());
  strcpy(mqtt_base, custom_mqtt_base.getValue());

  if (shouldSaveConfig) saveConfig();
  if (!res) ESP.restart();

  topicCmd = String(mqtt_base) + "/cmd";
  topicState = String(mqtt_base) + "/state";
  topicMotor = String(mqtt_base) + "/motor";
  topicAvailability = String(mqtt_base) + "/availability";
  topicFinishDelaySet = String(mqtt_base) + "/finish_delay/set";
  topicFinishDelayState = String(mqtt_base) + "/finish_delay/state";

  mqtt.setBufferSize(1024);
  mqtt.setServer(mqtt_host, atoi(mqtt_port));
  mqtt.setCallback(mqttCallback);

  ArduinoOTA.setHostname("window-cleaner");
  ArduinoOTA.begin();

  setupWeb();
}

void loop() {
  ArduinoOTA.handle();
  server.handleClient();

  if (!mqtt.connected()) reconnectMqtt();
  mqtt.loop();

  updateMotorStatus();
}

Credits

dudeden
1 project • 0 followers

Comments