One regular reader of this website asked us how we can control multiple relays with single Arduino ESP32? This article will not give any direct answer. This is a theoretical article discussing how multiple channels on MQTT can be created so that we can separate matters like temperature and humidity from a DHT11 sensor. Normally we connect DHT11 with IBM Watson IoT in a raw manner. In our that linked guide, it is IBM Watson IoT platform which can separate data and create dashboard visualization. IBM Watson IoT platform and IBM Node-RED are almost bare bone – you can easily run the same setup on a Raspberry Pi or own server. IBM Watson IoT has minimum vendor lock-in. That is said. The Watson IoT Platform has specific requirements on what MQTT topics devices and gateways can use. The Watson IoT Platform is not intended for use as a generic MQTT broker.
You can create too difficult things with Cayenne IoT with few clicks but you will get vendor lock-in. It is just a few lines of codes with Cayenne IoT :
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 | #define CAYENNE_PRINT Serial #include <CayenneMQTTESP32.h> char ssid[] = "YourNetworkSSID"; char wifiPassword[] = "networkpassword"; char username[] = "MQTT_username"; //your MQTT username char password[] = "MQTT_password"; //your MQTT password char clientID[] = "CLIENT_ID"; //your clientID const int Relay4 = 14; const int Relay3 = 12; const int Relay2 = 13; const int Relay1 = 15; void setup() { pinMode(Relay4, OUTPUT); pinMode(Relay3, OUTPUT); pinMode(Relay2, OUTPUT); pinMode(Relay1, OUTPUT); digitalWrite(Relay1, HIGH); digitalWrite(Relay2, HIGH); digitalWrite(Relay3, HIGH); digitalWrite(Relay4, HIGH); pinMode(LED_BUILTIN, OUTPUT); Serial.begin(9600); Cayenne.begin(username, password, clientID, ssid, wifiPassword); Serial.println("Ready.."); digitalWrite(LED_BUILTIN, LOW); } unsigned long previousmillis = 0; int setInterval = 5000; void loop() { Cayenne.loop(); unsigned long currentmillis = millis(); if (currentmillis - previousmillis > setInterval) { digitalWrite(LED_BUILTIN, LOW); Cayenne.virtualWrite(6, WiFi.RSSI()); digitalWrite(LED_BUILTIN, HIGH); previousmillis = currentmillis; } } CAYENNE_IN(0) // action for channel 0 { CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString()); getValue.setError("Error message"); if(getValue.asInt()) { digitalWrite(LED_BUILTIN, HIGH); delay(1000); digitalWrite(LED_BUILTIN, LOW); delay(1000); digitalWrite(LED_BUILTIN, HIGH); delay(1000); digitalWrite(LED_BUILTIN, LOW); delay(1000); } } CAYENNE_IN(1) { CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString()); getValue.setError("Error message"); if(getValue.asInt()){ Serial.println("SwitchON 1"); digitalWrite(Relay1,0); //Device will be SwitchON } else { Serial.println("SwitchOFF 1"); digitalWrite(Relay1,1); //Device will be SwitchOFF } } CAYENNE_IN(2) { CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString()); getValue.setError("Error message"); if(getValue.asInt()){ Serial.println("SwitchON 2"); digitalWrite(Relay2,0); //Device will be SwitchON } else { Serial.println("SwitchOFF 2"); digitalWrite(Relay2,1); //Device will be SwitchOFF } } CAYENNE_IN(3) { CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString()); getValue.setError("Error message"); if(getValue.asInt()){ Serial.println("SwitchON 3"); digitalWrite(Relay3,0); //Device will be SwitchON } else { Serial.println("SwitchOFF 3"); digitalWrite(Relay3,1); //Device will be SwitchOFF } } CAYENNE_IN(4) { CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString()); getValue.setError("Error message"); if(getValue.asInt()){ Serial.println("SwitchON 4"); digitalWrite(Relay4,0); //Device will be SwitchON } else { Serial.println("SwitchOFF 4"); digitalWrite(Relay4,1); //Device will be SwitchOFF } } |
But, you can see that you never learn how the CAYENNE_IN
working. The code is enough to get the work done but it escapes the required steps of learning “how I can make it working”.
---
The reader liked our IBM IoT smart switch guide and ESP32 glass touch buttons.
The answer to the actual question as theory is not that easy. For that reason, I am preparing the readers stage by stage, like how to control a fan over IoT. It is not easy to create a smart switch panel compared to a single smart switch. There are other technical matters, apart from just the code. AC connection is dangerous.
Before we learn to control multiple relays with single Arduino ESP32, we need to learn how to create multiple MQTT channels & fetch data. Code for generic version BME280 sensor module will go like this :
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 | #include <WiFi.h> #include <PubSubClient.h> #include <Wire.h> #include <Adafruit_BME280.h> #include <Adafruit_Sensor.h> // Replace the next variables with your SSID/Password combination const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // Add your MQTT Broker IP address, example: //const char* mqtt_server = "192.168.1.144"; const char* mqtt_server = "YOUR_MQTT_BROKER_IP_ADDRESS"; WiFiClient espClient; PubSubClient client(espClient); long lastMsg = 0; char msg[50]; int value = 0; //uncomment the following lines if you're using SPI /*#include <SPI.h> #define BME_SCK 18 #define BME_MISO 19 #define BME_MOSI 23 #define BME_CS 5*/ Adafruit_BME280 bme; // I2C //Adafruit_BME280 bme(BME_CS); // hardware SPI //Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI float temperature = 0; float humidity = 0; // LED Pin const int ledPin = 4; void setup() { Serial.begin(115200); // default settings // (you can also pass in a Wire library object like &Wire2) //status = bme.begin(); if (!bme.begin(0x76)) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1); } setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); pinMode(ledPin, OUTPUT); } void setup_wifi() { delay(10); // We start by connecting to a WiFi network Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void callback(char* topic, byte* message, unsigned int length) { Serial.print("Message arrived on topic: "); Serial.print(topic); Serial.print(". Message: "); String messageTemp; for (int i = 0; i < length; i++) { Serial.print((char)message[i]); messageTemp += (char)message[i]; } Serial.println(); // Feel free to add more if statements to control more GPIOs with MQTT // If a message is received on the topic esp32/output, you check if the message is either "on" or "off". // Changes the output state according to the message if (String(topic) == "esp32/output") { Serial.print("Changing output to "); if(messageTemp == "on"){ Serial.println("on"); digitalWrite(ledPin, HIGH); } else if(messageTemp == "off"){ Serial.println("off"); digitalWrite(ledPin, LOW); } } } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect("ESP8266Client")) { Serial.println("connected"); // Subscribe client.subscribe("esp32/output"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void loop() { if (!client.connected()) { reconnect(); } client.loop(); long now = millis(); if (now - lastMsg > 5000) { lastMsg = now; // Temperature in Celsius temperature = bme.readTemperature(); // Uncomment the next line to set temperature in Fahrenheit // (and comment the previous temperature line) //temperature = 1.8 * bme.readTemperature() + 32; // Temperature in Fahrenheit // Convert the value to a char array char tempString[8]; dtostrf(temperature, 1, 2, tempString); Serial.print("Temperature: "); Serial.println(tempString); client.publish("esp32/temperature", tempString); humidity = bme.readHumidity(); // Convert the value to a char array char humString[8]; dtostrf(humidity, 1, 2, humString); Serial.print("Humidity: "); Serial.println(humString); client.publish("esp32/humidity", humString); } } |
You can see that we have two topics :
1 2 3 4 5 | … client.publish("esp32/temperature", tempString); … client.publish("esp32/humidity", humString); … |
Home Assistance is a bigger hammer and there are a lot of tools/platforms such as :
1 | https://www.home-assistant.io/ |
I can show you another code to give an idea how to handle multiple relays :
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 | #include <PubSubClient.h> #include <WiFi.h> // #include <ArduinoOTA.h> void callback(char* topic, byte* payload, unsigned int length); #define MQTT_SERVER "xxx.xxx.xxx.xxx" //you MQTT IP Address const char* ssid = "YOUESSID"; const char* password = "WIFI_PASSWORD"; const int switchPin1 = D1; const int switchPin2 = D2; const int switchPin3 = D3; const int switchPin4 = D5; char const* switchTopic1 = "/house/switch1/"; char const* switchTopic2 = "/house/switch2/"; char const* switchTopic3 = "/house/switch3/"; char const* switchTopic4 = "/house/switch4/"; WiFiClient wifiClient; PubSubClient client(MQTT_SERVER, 1883, callback, wifiClient); void setup() { //initialize the switch as an output and set to LOW (off) pinMode(switchPin1, OUTPUT); // Relay Switch 1 digitalWrite(switchPin1, LOW); pinMode(switchPin2, OUTPUT); // Relay Switch 2 digitalWrite(switchPin2, LOW); pinMode(switchPin3, OUTPUT); // Relay Switch 3 digitalWrite(switchPin3, LOW); pinMode(switchPin4, OUTPUT); // Relay Switch 4 digitalWrite(switchPin4, LOW); ArduinoOTA.setHostname("My Arduino"); ArduinoOTA.begin(); //start the serial line for debugging Serial.begin(115200); delay(100); //start wifi subsystem WiFi.begin(ssid, password); //attempt to connect to the WIFI network and then connect to the MQTT server reconnect(); //wait a bit before starting the main loop delay(2000); } void loop(){ //reconnect if connection is lost if (!client.connected() && WiFi.status() == 3) {reconnect();} //maintain MQTT connection client.loop(); //MUST delay to allow ESP8266 WIFI functions to run delay(10); ArduinoOTA.handle(); } void callback(char* topic, byte* payload, unsigned int length) { //convert topic to string to make it easier to work with String topicStr = topic; //EJ: Note: the "topic" value gets overwritten every time it receives confirmation (callback) message from MQTT //Print out some debugging info Serial.println("Callback update."); Serial.print("Topic: "); Serial.println(topicStr); if (topicStr == "/house/switch1/") { //turn the switch on if the payload is '1' and publish to the MQTT server a confirmation message if(payload[0] == '1'){ digitalWrite(switchPin1, HIGH); client.publish("/house/switchConfirm1/", "1"); } //turn the switch off if the payload is '0' and publish to the MQTT server a confirmation message else if (payload[0] == '0'){ digitalWrite(switchPin1, LOW); client.publish("/house/switchConfirm1/", "0"); } } // EJ: copy and paste this whole else-if block, should you need to control more switches else if (topicStr == "/house/switch2/") { //turn the switch on if the payload is '1' and publish to the MQTT server a confirmation message if(payload[0] == '1'){ digitalWrite(switchPin2, HIGH); client.publish("/house/switchConfirm2/", "1"); } //turn the switch off if the payload is '0' and publish to the MQTT server a confirmation message else if (payload[0] == '0'){ digitalWrite(switchPin2, LOW); client.publish("/house/switchConfirm2/", "0"); } } else if (topicStr == "/house/switch3/") { //turn the switch on if the payload is '1' and publish to the MQTT server a confirmation message if(payload[0] == '1'){ digitalWrite(switchPin3, HIGH); client.publish("/house/switchConfirm3/", "1"); } //turn the switch off if the payload is '0' and publish to the MQTT server a confirmation message else if (payload[0] == '0'){ digitalWrite(switchPin3, LOW); client.publish("/house/switchConfirm3/", "0"); } } else if (topicStr == "/house/switch4/") { //turn the switch on if the payload is '1' and publish to the MQTT server a confirmation message if(payload[0] == '1'){ digitalWrite(switchPin4, HIGH); client.publish("/house/switchConfirm4/", "1"); } //turn the switch off if the payload is '0' and publish to the MQTT server a confirmation message else if (payload[0] == '0'){ digitalWrite(switchPin4, LOW); client.publish("/house/switchConfirm4/", "0"); } } } void reconnect() { //attempt to connect to the wifi if connection is lost if(WiFi.status() != WL_CONNECTED){ //debug printing Serial.print("Connecting to "); Serial.println(ssid); //loop while we wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } //print out some more debug once connected Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } //make sure we are connected to WIFI before attemping to reconnect to MQTT if(WiFi.status() == WL_CONNECTED){ // Loop until we're reconnected to the MQTT server while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Generate client name based on MAC address and last 8 bits of microsecond counter String clientName; clientName += "esp8266-"; uint8_t mac[6]; WiFi.macAddress(mac); clientName += macToStr(mac); //if connected, subscribe to the topic(s) we want to be notified about //EJ: Delete "mqtt_username", and "mqtt_password" here if you are not using any if (client.connect((char*) clientName.c_str(),"mqtt_username", "mqtt_password")) { //EJ: Update accordingly with your MQTT account Serial.print("\tMQTT Connected"); client.subscribe(switchTopic1); client.subscribe(switchTopic2); client.subscribe(switchTopic3); client.subscribe(switchTopic4); //EJ: Do not forget to replicate the above line if you will have more than the above number of relay switches } //otherwise print failed for debugging else{Serial.println("\tFailed."); abort();} } } } //generate unique name from MAC addr String macToStr(const uint8_t* mac){ String result; for (int i = 0; i < 6; ++i) { result += String(mac[i], 16); if (i < 5){ result += ':'; } } return result; } |
Now, look at our code for previous guide :
1 2 3 4 5 6 | … #define CMD_STATE "/gpio/" // use the '+' wildcard so it subscribes to any command with any message format const char commandTopic[] = "iot-2/cmd/+/fmt/+"; … |
Look at the “Subscribing to device commands” on official documentation from IBM.
We are sending the cURL request like this :
1 2 | curl -u <yourApiKey>:<yourApiPassword> -H "Content-Type: text/plain" -v -X POST http://<yourOrg>.messaging.internetofthings.ibmcloud.com:1883/api/v0002/ application/types/<yourDeviceType>/devices/<yourDeviceId>/commands/gpio -d "on" |
The last part of the command is important to notice :
1 | <yourDeviceType>/devices/<yourDeviceId>/commands/gpio |
The command is issued against the code :
1 2 3 | ... #define CMD_STATE "/gpio/" ... |
and the line o command topic is this :
1 2 3 4 5 | … const char commandTopic[] = "iot-2/cmd/+/fmt/+"; … |
Just with cURL, you can control the whole house. We are communicating with the IoT agent ESP32 directly. We do not need to develop complex Android application. There are few guides on the internet like this one to do that with Sonoff device. IBM compiled Mosquitto in a way to make our work just easy for the server-side.
Tagged With blynk esp32 switch multiple relays , esp32 mqtt relay control , mutiple relay control using adrunio , esp32 single chnnel with multiple gpios , esp32 mysql error:109 , esp32 mqtt relay , esp32 cayenne 4 channel relay , esp 32 outputs to switch 4 x relays , controlling multiple devices esp32 , arduino multiple relays control