A Simple MQTT Pub/Sub Node With Arduino UNO and W5100 Ethernetshield
by diy_bloke in Circuits > Arduino
43446 Views, 15 Favorites, 0 Comments
A Simple MQTT Pub/Sub Node With Arduino UNO and W5100 Ethernetshield
UPDATE: new code added
Nowadays many MQTT nodes are based around the ESP8266. However far before the ESP8266 the Arduino UNO already had Ethernet capabilities with an Ethernetshield.
The aim of this instructable is to show how one can use A UNO with W5100 shield or module as an MQTT node and in the software I will show some simple techniques of how to deal with outbound MQTT messages and inbound MQTT messages. If you still have an Old ENC28J60 Ethershield, don't despair, one can use those as well. I will cover that in my next instructable.
If you know what MQTT is, go to the next Step. If not, read on
For the uninitiated a brief explanation of MQTT: MQTT is a protocol to transfer messages between devices that are connected to a LAN. Those messages are formed by a topic and a payload
A device can publish messages to the LAN and it can subscribe to messages on the LAN. Now when I say "publish to the LAN" that is a bit of a eufemism, because in fact a node will be publishing its messages and subscribing to messages from a so called MQTT broker. The broker is merely a device that will receive the published messages, keep track of what device subscribed to what topic and then send the proper message to the right device.
An MQTT broker can be housed on your own LAN and often a RaspberryPi is used for that, or a public broker can be used.
Now obviously, just building one Node is not going to do much good. Who is it going to talk to? So obviously this would fit in a bigger systems with various nodes talking to eachother and the user having a frienly interface such as OpenHab. But in the google playstore various MQTTdashboards are available as app that allow for a simple interface. When developing MQTT software, it is always smart to install mqtt-spy. It is available for several platforms and what it basically does is to show MQTT messages going around on your LAN and to allow you to manually send MQTT messages
The Software
The node I describe has several functions:
It reads from several switches (e.g. door contacts), it reads the value of an LDR, it reads temperature and humidity from a DHT11, it reads a bell signal, and it reads the state of a relay and sends those onto the broker. It does that every 3 seconds
Publishing is done with the command: client.publish(topic,message);
The command is a bit picky with its format. It will happily publish:
client.publish("MyTopic","Hello World");
but it will not do a string variable such as:
String messagestr="Hello World";
client.publish("MyTopic",messagestr);
Often that is not a problem, but in some cases. I have used the sending of the ip number as an example of how to deal with that.
The node subscribes to only one topic and depending on the message in that topic, it performs several functions:
0- it switches a relay off
1-it switches a relay on
2-it publishes the IP address of the node
3-it publishes the state of the relay
On startup of the node, it automatically publishes the IP address as well.
Subscriptions are usually handled in a function called 'Callback' (but up to you to chose the name)
For ease I have only used one topic and commands of 1 character length.
however it is easy to check for longer command messages and I have shown in the software how to do that.
So instead of just sending a "0" or a "1" to the general subscription topic, it is possible to make a topic called "/home/br/sb/relay" and use messages such as "ON" and "OFF" to switch the relay. However, that all costs memory and on a UNO with an Ethernetcard one needs to be a bit careful with memory use (though it still ahs much more compared to using an ENC28j60 Ethernetcard)
A Simple MQTT Pub/Sub Node With Arduino UNO and W5100 Ethernetshield: the Program
/* Arduino UNO with W5100 Ethernetshield or W5100 Ethernet module, used as MQTT client It will connect over Wifi to the MQTT broker and controls a digital output (LED, relay) and gives the Temperature and Humidity, as well as the state of some switches The topics have the format "home/br/sb" for southbound messages and "home/nb" for northbound messages Southbound are messages going to the client, northbound are messages coming from the client As the available memory of a UNO with Ethernetcard is limited, I have kept the topics short Also, the payloads are kept short The Northbound topics are home/br/nb/temp for temperature home/br/nb/humid for humidity home/br/nb/deur for a door switch home/br/nb/l for the lightintensity home/br/nb/pr for the status of a PIR sensor home/br/nb/ip showing the IP number of the client home/br/nb/relay showing the relaystate There is only one southbound topic: home/br/sb The payload here determines the action: 0 -Switch the relay off 1-Switch the relay on 2-Publish the IP number of the client 3 Ask for the relaystate// REMOVED On Startup, the Client publishes the IP number */ #include <Ethernet.h>// Ethernet.h library #include "PubSubClient.h" //PubSubClient.h Library from Knolleary #include "DHT.h" //DHT.h library #define CLIENT_ID "Hal" #define PUBLISH_DELAY 3000 // that is 3 seconds interval #define DHTPIN 3 #define DHTTYPE DHT11 #define ledPin 13 #define relayPin 8 String ip = ""; bool statusKD = HIGH; bool statusBD = HIGH; bool statusGD = HIGH; bool relaystate = LOW; bool pir = LOW; bool startsend = HIGH;// flag for sending at startup int lichtstatus; //contains LDR reading uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x06}; EthernetClient ethClient; PubSubClient mqttClient; DHT dht(DHTPIN, DHTTYPE); long previousMillis; void setup() { pinMode(4, INPUT_PULLUP); pinMode(5, INPUT_PULLUP); pinMode(6, INPUT_PULLUP); pinMode(7, INPUT); pinMode(relayPin, OUTPUT); // setup serial communication Serial.begin(9600); while (!Serial) {}; Serial.println(F("MQTT Arduino Demo")); Serial.println(); // setup ethernet communication using DHCP if (Ethernet.begin(mac) == 0) { //Serial.println(F("Unable to configure Ethernet using DHCP")); for (;;); } Serial.println(F("Ethernet configured via DHCP")); Serial.print("IP address: "); Serial.println(Ethernet.localIP()); Serial.println(); //convert ip Array into String ip = String (Ethernet.localIP()[0]); ip = ip + "."; ip = ip + String (Ethernet.localIP()[1]); ip = ip + "."; ip = ip + String (Ethernet.localIP()[2]); ip = ip + "."; ip = ip + String (Ethernet.localIP()[3]); //Serial.println(ip); // setup mqtt client mqttClient.setClient(ethClient); // mqttClient.setServer("test.mosquitto.org", 1883);//use public broker mqttClient.setServer( "192.168.1.102", 1883); // or local broker //Serial.println(F("MQTT client configured")); mqttClient.setCallback(callback); // setup DHT sensor dht.begin(); Serial.println(F("DHT sensor initialized")); Serial.println(); Serial.println(F("Ready to send data")); previousMillis = millis(); mqttClient.publish("home/br/nb/ip", ip.c_str()); } void loop() { statusBD = digitalRead(4);// FrontdoorSwitch statusGD = digitalRead(5);// Garagedoor Switch statusKD = (digitalRead(6));//LivingRoom Switch lichtstatus = analogRead(A0);//Reads an LDR pir = digitalRead(7);//Reads a PIR sensor relaystate = digitalRead(relayPin);// Reads the state of a relay // it's time to send new data? if (millis() - previousMillis > PUBLISH_DELAY) { sendData(); previousMillis = millis(); } mqttClient.loop(); } void sendData() { char msgBuffer[20]; float h = dht.readHumidity(); float t = dht.readTemperature(); Serial.print("Temperature: "); Serial.print(t); Serial.println("oC"); Serial.print("Humidity: "); Serial.print(h); Serial.println("%"); Serial.print("Relay is: "); Serial.println((relaystate == LOW) ? "OPEN" : "CLOSED"); if (mqttClient.connect(CLIENT_ID)) { mqttClient.publish("home/br/nb/temp", dtostrf(t, 6, 2, msgBuffer)); mqttClient.publish("home/br/nb/humid", dtostrf(h, 6, 2, msgBuffer)); mqttClient.publish("home/br/nb/deur", (statusBD == HIGH) ? "OPEN" : "CLOSED"); mqttClient.publish("home/br/nb/garage", (statusGD == HIGH) ? "OPEN" : "CLOSED"); mqttClient.publish("home/br/nb/bel", (statusKD == HIGH) ? "OPEN" : "CLOSED"); mqttClient.publish("home/br/nb/l", dtostrf(lichtstatus, 4, 0, msgBuffer)); mqttClient.publish("home/br/nb/p", (pir == HIGH) ? "OPEN" : "CLOSED"); mqttClient.publish("home/br/nb/relay", (relaystate == LOW) ? "OPEN" : "CLOSED"); mqttClient.subscribe("home/br/sb"); if (startsend) { mqttClient.publish("home/br/nb/ip", ip.c_str()); startsend = LOW; } } } void callback(char* topic, byte* payload, unsigned int length) { char msgBuffer[20]; // I am only using one ascii character as command, so do not need to take an entire word as payload // However, if you want to send full word commands, uncomment the next line and use for string comparison // payload[length]='\0';// terminate string with 0 //String strPayload = String((char*)payload); // convert to string // Serial.println(strPayload); Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] ");//MQTT_BROKER for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); Serial.println(payload[0]); // Examine only the first character of the message if (payload[0] == 49) // Message "1" in ASCII (turn output ON) { digitalWrite(LED_BUILTIN, HIGH); // digitalWrite(relayPin, HIGH); } else if (payload[0] == 48) // Message "0" in ASCII (turn output OFF) { digitalWrite(relayPin, LOW); // digitalWrite(LED_BUILTIN, LOW); } else if (payload[0] == 50) { mqttClient.publish("home/br/nb/ip", ip.c_str());// publish IP nr } else { Serial.println("Unknown value"); mqttClient.publish("home/br/nb", "Syntax Error"); } }
As copying this program could introduce all sorts of errors and because Instructabels sometimes drops the library declarations, the code is available for download here. I also have attached it to this instructable. Mind you that the to be downloaded code may still have some comments that mean more to me than to you.
UPDATE
Added new code: W5100MQTTV2.1.ino