Time to start coding the software part of this project. If you remember, the requirement seems to be simple: To make the washing machine send a notificacion when the washing cycle ends (when the door unlocks).
To send these notifications, we could choose one of many routes: e-mail, SMS, push notifications, using a custom app also designed by us… To save time, I decided to use pushbullet, a third party push notification service.
Pushbullet allows us among other things, to send small text notifications to all the devices linked to the service. We could then, from the microcontroller, send a notification and receive it in my phone and my smart watch. I have a small IoT server in my home: an orange PI PC with OpenHAB, which I use to control my smart power outlets. What if I connect my washing machine to that server and make it send the notifications for me? That way, I could check the washing machine state and send a command to start the washing machine cycle, all remotely.
Let’s start with the way of communications, which will have the ESP-01 miccrocontroller with my IoT server. In this case, I decided to use MQTT, for its simplicity: You define channels of communication (topics), the receiver will subscribe to those topics and the sender will publish messages to them. The PubSubClient library will help us use MQTT with ESP8266 microcontrollers.
Let’s open Arduino IDE!. First, we will define the WiFi and MQTT server connections. In my case, I defined these parameters in a separate header (.h) file, to keep them separated:
//Connection.h: const char* ssid = "<YourSSID>"; const char* password = "<YourSSIDPassword>"; const char* mqttServer = "<YourMQTTServerIP>"; const int mqttPort = 1883; const char* mqttUser = "<YourMQTTServerUser>"; const char* mqttPassword = "<YourMQTTServerPassword>";
A couple of subroutines will be used to prepare the connection and connect to the server. Also, we will define variables for the different topics: mqttDoorTopic, for the state of the machine’s door locking mechanism, and mqttStartTopic for receiving the washing cycle start command:
#include <WiFiClient.h> #include <ESP8266WiFi.h> #include <PubSubClient.h> #include "Connection.h" const char* mqttClientId = "GF_WashingMachine"; const char* mqttDoorTopic = "Home/GF_Patio/WashingMachine/Door"; const char* mqttStartTopic = "Home/GF_Patio/WashingMachine/Start"; WiFiClient espClient; PubSubClient client(espClient); void setup() { setup_wifi(); client.setServer(mqttServer, mqttPort); client.setCallback(callback); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); } void setup_wifi() { delay(10); // We start by connecting to a WiFi network WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); } } void callback(char* topic, byte* payload, unsigned int length) { } void reconnect() { // Loop until we're reconnected while (!client.connected()) { // Attempt to connect if (client.connect(mqttClientId, mqttUser, mqttPassword)) { // Once connected, resubscribe client.subscribe(mqttStartTopic); } else { // Wait 5 seconds before retrying delay(5000); } } }
Pins GPIO0 and GPIO2 of the ESP-01 are used to choose the boot mode at the moment of booting the microcontroller, which complicates the use of them for some uses. I decided to use the RX and TX pins instead as GPIO, using their FUNCTION_3 pin mode in the setup() subroutine. After that, they can be configured as OUTPUT and INPUT respectively:
//Disable Serial pins in order to use them as GPIO pinMode(1,FUNCTION_3); //TX pinMode(3,FUNCTION_3); //RX pinMode(startCyclePin, OUTPUT); // For washing cycle start pinMode(doorLockPin, INPUT); // For door lock detection
A simple subroutine will return if the machine’s door is locked or not, reading pin 1:
bool isDoorLocked() { //Returns true if pin is high (door is locked); false otherwise return digitalRead(doorLockPin) == 1; }
For starting the washing cycle, a new subrourine will press (bridge the contacts) the button of the washing machine, using a relay with pin 3.
void startWashingCycle() { if(!isDoorLocked()) { //Presses the start button for 0.5 seconds digitalWrite(startCyclePin, HIGH); delay(500); //Releases the start button and waits for the washing cycle to start //(the door will lock after some time) digitalWrite(startCyclePin, LOW); while(!isDoorLocked()) { delay(1000); } } }
In the main loop(), we will check the state of the door’s locking mechanism every 5 seconds, and publish the state to the doorlock topic. If the door was locked and gets unlocked for 10 seconds or more, the state of the doorlock is published as “OPEN”, updating the state of the washing cycle to “OFF”. If hte door was open and gets locked, the state of the doorlock is published as “CLOSED” and the state of the washing cycle as “ON”:
long now = millis(); //Check status every 5 seconds if (now - lastCheck > 5000) { lastCheck = now; if (isDoorLocked()) { if (!wasDoorLocked) { wasDoorLocked = true; client.publish(mqttStartTopic, "ON"); } client.publish(mqttDoorTopic, "CLOSED"); if (doorUnlockedFor > 0) { doorUnlockedFor = 0; } } else { if (wasDoorLocked) { doorUnlockedFor++; } //Door needs to be unlocked for 10 seconds (2 checks). This is //done to avoid misunderstanding power fluctuations as //unlocking signals. if (doorUnlockedFor >= 2) { client.publish(mqttDoorTopic, "OPEN"); client.publish(mqttStartTopic, "OFF"); wasDoorLocked = false; } } }
Last but not least, we check the start washing cycle command in the callback() routine. The value received is converted to string, adding a “\0” at the end, and checking if the value is “ON”. In this case, we will start the washing cycle using another routine:
//Add \0 to payload, to convert it to string later byte* payloadNull = (byte*)malloc(length+1); memcpy(payloadNull, payload, length); payloadNull[length] = '\0'; if(String(topic).equals(String(mqttStartTopic))) { if (String((char*)payloadNull).equals("ON")) { startWashingCycle(); } }
That is enough for the microcontroller. You can find my code in my github, in case you want to take a look 🙂. What is left is the configuration of my OpenHAB server.
In the server, an MQTT broker is needed to handle MQTT messages and the plugin (binding) to add MQTT devices. Also, in this case, we will need PushBullet, to send the notifications.
The MQTT broker is added:
Then the MQTT binding:
And last, the PushBullet connector:
We will need to add the device (thing) that represents the washing machine, their channels with their respective MQTT topics and its items, to make it able to interact with it. Let’s start with the thing:
The channels are then added. For the door lock detection, a channel of type “contact”, with possible values “OPEN” and “CLOSED”. For the start washing cycle command, a channel of type “switch”, with possible values “ON” and “OFF”. It is important to assign the same topics as in our ESP-01, or else it wont work:
The new channels are mapped with two new items, to be able to interact with them:
And with this, we can already control our washing machine:
Now in order to send notifications when the washing cycle has finished, we will need to install the rule engine plugin:
The interaction with the PushBullet plugin cannot be done from the GUI yet, so we will have to do it the old way: accessing via SSH to our server. A new file needs to be created in /etc/openhab2/rules. In my case, I created “lavadora.rules”, with the following code:
rule "lavadora" when Item Lavadora_BloqueoPuerta changed from CLOSED to OPEN then sendPushbulletNote("Lavadora", "Lavado completado") end
With that, when the door lock state changes from CLOSED to OPEN, the nofitication is sent:
The only thing that is left is to connect all these guts to the washing machine 🙂
See you in my next post!