The result of this guide will be a local control panel with ESP32, pushbuttons, LED indicators, relays which can control the connected AC appliances by the simple pushbutton press. When connected to Wi-Fi, it will be able to control the thing over the internet with simple HTTP cURL commands. This guide and code can be used for home automation or creating smart power outlet. Single relay version of this project can be found as our one previous guide. This method does not involve having Node-RED or running own server or Raspberry Pi as a hub. It is maintenance-free, low cost, easy thing.
This guide assumes that the reader (i) already configured ESP32 to use with Arduino IDE (ii) can connect ESP32 Arduino with IBM Watson IoT platform with simple codes. (iii) Understands that AC shock can kill a human.
For theoritical purpose, we have discussed managing multiple MQTT topics, URL format for sending commands over HTTP.
---
We will suggest reading our linked articles and guides to understand the thing and setup. Here on our GitHub repo all the required files are kept. This is the required code for ESP32 Arduino, using two relays has been shown in the code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | #include <WiFi.h> #include <PubSubClient.h> #include <Ticker.h> #include <HTTPClient.h> #include <SPI.h> const char* ssid = "change"; const char* password = "change"; //-------- Customise these values ----------- #define ORG "change" #define DEVICE_TYPE "change" #define DEVICE_ID "change" #define TOKEN "change" //-------- Customise the above values -------- // first relay with two status LEDs #define DEVICE_BUTTON 5 #define DEVICE_RELAY 19 #define DEVICE_GREEN_LED 18 #define DEVICE_RED_LED 4 // second relay with two status LEDs #define DEVICE_BUTTON_1 13 #define DEVICE_RELAY_1 21 #define DEVICE_GREEN_LED_1 22 #define DEVICE_RED_LED_1 23 // nothing to modify for adding more relays char server[] = ORG ".messaging.internetofthings.ibmcloud.com"; char authMethod[] = "use-token-auth"; char token[] = TOKEN; char clientId[] = "d:" ORG ":" DEVICE_TYPE ":" DEVICE_ID; // you are sending this URL format to IBM's server #define CMD_STATE_1 "/pin1/" #define CMD_STATE_2 "/pin2/" // use the '+' wildcard so it subscribes to any command with any message format // you are sending this URL format to IBM's server // changing the format will change cURL command // you can add more relays in the same way const char commandTopic1[] = "iot-2/cmd/pin1/fmt/+"; const char commandTopic2[] = "iot-2/cmd/pin2/fmt/+"; Ticker ledBlinker; // IBM setup does not need strcmd for HTTP command void gotMsg(char* topic, byte* payload, unsigned int payloadLength); WiFiClient wifiClient; PubSubClient client(server, 1883, gotMsg, wifiClient); // you can add more relays by appending 2, 3 where I used 1 beside keywords & strings int buttonPressDuration; int buttonPressDuration1; void setup() { Serial.begin(115200); Serial.println(); pinMode(DEVICE_RELAY, OUTPUT); pinMode(DEVICE_GREEN_LED, OUTPUT); pinMode(DEVICE_RED_LED, OUTPUT); pinMode(DEVICE_RELAY_1, OUTPUT); pinMode(DEVICE_GREEN_LED_1, OUTPUT); pinMode(DEVICE_RED_LED_1, OUTPUT); ledBlinker.attach(0.1, ledBlink); // fast blink indicates Wifi connecting wifiConnect(); ledBlinker.attach(0.4, ledBlink); // slower blink indicates MQTT connecting mqttConnect(); ledBlinker.detach(); // we have two status LEDs digitalWrite(DEVICE_GREEN_LED, LOW); // low is led on to show connected digitalWrite(DEVICE_GREEN_LED_1, LOW); // low is led on to show connected // we have two buttons pinMode(DEVICE_BUTTON, INPUT); attachInterrupt(DEVICE_BUTTON, buttonPress, CHANGE); pinMode(DEVICE_BUTTON_1, INPUT); attachInterrupt(DEVICE_BUTTON_1, buttonPress1, CHANGE); } // isolate the relay devices and HTTP commands int lastHeartBeat; int lastHeartBeat1; void loop() { if (buttonPressDuration > 0) { doCommand(digitalRead(DEVICE_RELAY) ? "off" : "on"); buttonPressDuration = 0; } if (buttonPressDuration1 > 0) { doCommand(digitalRead(DEVICE_RELAY_1) ? "off" : "on"); buttonPressDuration1 = 0; } if (!client.loop()) { mqttConnect(); } if (millis()-lastHeartBeat > 10000) { Serial.print("loop: gpio "); Serial.print(DEVICE_RELAY); Serial.print(" current state "); Serial.println(digitalRead(DEVICE_RELAY) ? "On" : "Off"); digitalWrite(DEVICE_GREEN_LED, HIGH); // flicker LED to show its active delay(200); digitalWrite(DEVICE_GREEN_LED, LOW); lastHeartBeat = millis(); } if (millis()-lastHeartBeat1 > 10000) { Serial.print("loop: gpio1 "); Serial.print(DEVICE_RELAY_1); Serial.print(" current state "); Serial.println(digitalRead(DEVICE_RELAY_1) ? "On" : "Off"); digitalWrite(DEVICE_GREEN_LED_1, HIGH); // flicker LED to show its active delay(200); digitalWrite(DEVICE_GREEN_LED_1, LOW); lastHeartBeat1 = millis(); } } void gotMsg(char* topic, byte* payload, unsigned int payloadLength) { Serial.print("gotMsg: invoked for topic: "); Serial.println(topic); // we defined CMD_STATE 1 and 2 before // it is URLs on IBM server // seek on the first URL if (String(topic).indexOf(CMD_STATE_1) > 0) { String cmd = ""; for (int i=0; i<payloadLength; i++) { cmd += (char)payload[i]; } doCommand(cmd); } else { // unexpected error was for one relay setup // uncommenting will evoke false warning message // Serial.print("gotMsg: unexpected topic: "); Serial.println(topic); } // seek on the second URL if (String(topic).indexOf(CMD_STATE_2) > 0) { String cmd1 = ""; for (int i=0; i<payloadLength; i++) { cmd1 += (char)payload[i]; } doCommand1(cmd1); } else { // unexpected error was for one relay setup // uncommenting will evoke false warning message // Serial.print("gotMsg: unexpected topic: "); Serial.println(topic); } } // doCommand is a variable name // it is for the first relay void doCommand(String cmd) { int currentState = digitalRead(DEVICE_RELAY); int newState = (cmd == "on"); digitalWrite(DEVICE_RELAY, newState); Serial.print("Relay switched from "); Serial.print(currentState ? "On" : "Off");Serial.print(" to "); Serial.println(newState ? "On" : "Off"); } // it is for the second relay void doCommand1(String cmd1) { int currentState = digitalRead(DEVICE_RELAY_1); int newState = (cmd1 == "on"); digitalWrite(DEVICE_RELAY_1, newState); Serial.print("Relay switched from "); Serial.print(currentState ? "On" : "Off");Serial.print(" to "); Serial.println(newState ? "On" : "Off"); } // First and second buttons with debouncing unsigned long startPress = 0; unsigned long startPress1 = 0; void buttonPress() { int currentState = digitalRead(DEVICE_BUTTON); if (currentState == 0) { // 0 is pressed, 1 is released startPress = millis(); } else { int diff = millis() - startPress; if (diff > 100) { // debounce buttonPressDuration = diff; } } Serial.print("Button "); Serial.print(currentState ? "released" : "pressed"); Serial.print(" duration="); Serial.println(buttonPressDuration); } void buttonPress1() { int currentState1 = digitalRead(DEVICE_BUTTON_1); if (currentState1 == 0) { // 0 is pressed, 1 is released startPress1 = millis(); } else { int diff = millis() - startPress1; if (diff > 100) { // debounce buttonPressDuration1 = diff; } } Serial.print("Button 2"); Serial.print(currentState1 ? "released" : "pressed"); Serial.print(" duration="); Serial.println(buttonPressDuration1); } void ledBlink() { digitalWrite(DEVICE_GREEN_LED, ! digitalRead(DEVICE_GREEN_LED)); digitalWrite(DEVICE_GREEN_LED_1, ! digitalRead(DEVICE_GREEN_LED_1)); } void wifiConnect() { Serial.print("Connecting to "); Serial.print(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.print("\nWiFi connected, IP address: "); Serial.println(WiFi.localIP()); } void mqttConnect() { if (!!!client.connected()) { Serial.print("Reconnecting MQTT client to "); Serial.println(server); while (!!!client.connect(clientId, authMethod, token)) { Serial.print("."); delay(500); } Serial.println(); } // add more relays here to subscribe subscribeTo(commandTopic1); subscribeTo(commandTopic2); } void subscribeTo(const char* topic) { Serial.print("subscribe to "); Serial.print(topic); if (client.subscribe(topic)) { Serial.println(" OK"); } else { Serial.println(" FAILED"); } } |
The first relay can be switched on by using this command :
1 2 3 4 5 | # # curl -u <use-the-API-Key>:<use-auth-token> -H "Content-Type: text/plain" -v -X POST http://<your org>.messaging.internetofthings.ibmcloud.com:1883/api/v0002/application/types/<yourDeviceType>/devices/<yourDeviceId>/commands/pin1 -d "on" # # |
The first relay can be switched off by using this command :
1 2 3 4 5 | # # curl -u <use-the-API-Key>:<use-auth-token> -H "Content-Type: text/plain" -v -X POST http://<your org>.messaging.internetofthings.ibmcloud.com:1883/api/v0002/application/types/<yourDeviceType>/devices/<yourDeviceId>/commands/pin1 -d "off" # # |
The second relay can be switched on by using this command :
1 2 3 4 5 | # # curl -u <use-the-API-Key>:<use-auth-token> -H "Content-Type: text/plain" -v -X POST http://<your org>.messaging.internetofthings.ibmcloud.com:1883/api/v0002/application/types/<yourDeviceType>/devices/<yourDeviceId>/commands/pin2 -d "on" # # |
The second relay can be switched off by using this command :
1 2 3 4 5 | # # curl -u <use-the-API-Key>:<use-auth-token> -H "Content-Type: text/plain" -v -X POST http://<your org>.messaging.internetofthings.ibmcloud.com:1883/api/v0002/application/types/<yourDeviceType>/devices/<yourDeviceId>/commands/pin2 -d "off" # # |
The above four commands are kept as bash scripts on our GitHub repository. You should edit them to add API key, auth token, org name, device type name, device ID etc, make them executable script by running usual command :
1 2 3 4 5 6 7 | # Example with one # chmod +x Relay_1_ON.sh # # run it sh Relay_1_ON.sh |
That is it. You can use Android application described on our other article to create buttons with those URLs. If you correctly follow our linked URLs and the guides, you’ll get an instant working professional-grade thing.
What is the limitation of this method? First, it will suck a huge bandwidth of free quota of IBM Watson IoT account. Secondly, you can not easily create a toggle button. HTTP is one-directional. MQTT is bi-directional. For security reasons, IBM will not allow to route software/code outside their network and the device (ESP32 in our case). The only way to create a toggle button is to create a Node-RED instance on IBM Cloud’s infrastructure to catch the status of the relays while another web app running on IBM Cloud with a toggle button.
So, this method alone is great for small scale need. You need a server or Raspberry Pi running Home-Assistant to control everything of multiple houses. You can upgrade this project using ESP32’s touch button feature. Home-Assistant can integrate IBM Watson IoT platform. Scaling up is not an issue.
Tagged With 3x-opus relay/relay 208 83 246 115/198 24 163 27 , how to confirm my product device esp32 device while ota