Ok so my version of this (first time ESP8266 & PlatformIO so a little bit pleased
). In all cases where I refer to Node-RED substitute your control system of choice. Differences:
- Fixed IP (more easily found on network!) comment out the lines if you want a DHCP IP
- I have used a separate topic for each relay rather than parse the payload.
- Payload of 1 or 0 switches the relay
- Relay subscribes to an MQTT heartbeat (in this case created by Node-RED) and sends a response back. This partially solves the two issues above (but see below).
- The topics etc are defined as constants so easily changed
- Initialise all relays to LOW
What I have not worked out how to do, and actually have no idea how to in C++, is use the heartbeat message to set a timer off, that if not reset, would set all relays to safe.
I’m not currently sending back a status message for the relays as doing this just after the relay is set is rather pointless (other than showing it was set). What is actually required is the state of the relay to be stored in a variable as it is set. This then could be returned (instead of just a 1) as a status message for Node-RED to act on (for instance compare returned state with required state).
If you get in the position where the relay loses connection to Node-RED, all the relays would then be set to safe, the status message that is sent when the connection is restored, would indicate all relays are off which may not be what the flow expects so can issue an appropriate command. Equally, if the relay has lost power, it will come up and report this in the status so again Node-RED knows to send the appropriate commands.
I know what I want to do but currently have reached the limit of my C++. If anyone feels like helping a poor newbie C++ programmer…
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
//enironment setup
const char* ssid = "xxx";
const char* password = "xxx";
const char* mqtt_server = "192.168.0.1";
//MQTT Topics
const char* MQTTdeviceIDStr = "HeatingControl";
const char* HeartBeat = "nodered/heartbeat";
const char* LinkHeartBeat = "HeatingControlStatus/Status";
const char* Relay1Cmd = "HeatingControl/Cmd/Relay1";
const char* Relay2Cmd = "HeatingControl/Cmd/Relay2";
const char* Relay3Cmd = "HeatingControl/Cmd/Relay3";
const char* Relay4Cmd = "HeatingControl/Cmd/Relay4";
// these 3 lines for a fixed IP-address
IPAddress ip(192, 168, 7, 153);
IPAddress gateway(192, 168, 7, 25);
IPAddress subnet(255, 255, 255, 0);
WiFiClient espClient;
PubSubClient client(espClient);
void setup_wifi(){
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.hostname(MQTTdeviceIDStr); // DHCP Hostname (useful for finding device for static lease)
WiFi.config(ip, gateway, subnet); // (DNS not required)
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* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
int payloadi = (int)payload[0] - 48; //need to subtract 48 as chr 0 = int 48!
// This looks to see which topic sent the message and outputs to the appropriate pin.
if ((strcmp(topic, Relay1Cmd) == 0)) {
digitalWrite(12, payloadi);
} else if ((strcmp(topic, Relay2Cmd) == 0)) {
digitalWrite(13, payloadi);
} else if ((strcmp(topic, Relay3Cmd) == 0)) {
digitalWrite(14, payloadi);
} else if ((strcmp(topic, Relay4Cmd) == 0)) {
digitalWrite(16, payloadi);
} else if ((strcmp(topic, HeartBeat) == 0)) {
client.publish(LinkHeartBeat, "1");
} else {
Serial.print("Bad Message");
}
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(MQTTdeviceIDStr)) {
Serial.println(" MQTT connected");
// Once connected, publish an announcement...
client.publish(LinkHeartBeat, "1");
// ... and resubscribe
client.subscribe(HeartBeat);
client.subscribe(Relay1Cmd);
client.subscribe(Relay2Cmd);
client.subscribe(Relay3Cmd);
client.subscribe(Relay4Cmd);
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
pinMode(12,OUTPUT), pinMode(13,OUTPUT),pinMode(14,OUTPUT),pinMode(16,OUTPUT);
// ensure all relays are off to LOW - assumed safe
digitalWrite(12, LOW);
digitalWrite(13, LOW);
digitalWrite(14, LOW);
digitalWrite(16, LOW);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
reconnect();
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}