Dryer’s Done: An IoT project to end the tedium of ironing
Ironing. No one likes doing it, yet you’re stuck doing it if you don’t empty the clothes dryer once the cycle has finished. In my household, we have a 27-year-old natural gas clothes dryer that just keeps humming along. Given its age, it doesn’t have any modern features to signal when the drying process is done. When the clothes are sufficiently dry it just turns off, the clothes sit, and those wrinkles appear. In addition, the dryer is in a separate part of the house and even if it had an “end of cycle” signal, we probably wouldn’t hear it. I decided this problem needed a solution that would send a message to my wife or I when the laundry was ready. In addition, I wanted something that would not require me to modify the dryer in any way. If this dryer has been chugging along for over 27 years, I’m not about to open it up and “fix” something that’s not broken.
After considering several options I settled on an idea to detect the vibration the dryer makes while it’s running. Originally I thought of writing a small Android phone app that used the accelerometer in the phone to detect vibration. The phone could then make some sounds or send a message when the dryer stopped vibrating. Eventually, I decided to try to build a solution using a microcontroller board that we’ve not yet written about much in this blog.
Our past IoT blog articles have mostly focused on the popular Arduino or Raspberry Pi boards. For this project, I wanted something that was Wifi enabled and relatively low-cost. The WiFi-enabled version of the Arduino, known as the Arduino Yun, costs about $65. The ESP8266 evaluation board that I used for this project cost around $7 on eBay including the 3 AA battery holder needed to power the project. To this, I had to add a vibration sensor which cost about $3 on eBay. About 1.5 years ago the ESP8266 board became available and originally most people used these as a sort of WiFi “modem” to add networking capability to Arduino projects. In an earlier blog post, Kevin Kazmierczak showed how to use an ESP8266 in this manner to connect an Arduino Uno to the internet. For this project, I decided to forgo the Arduino entirely as the ESP8266 is quite a capable device on its own. It’s very similar in capability to an Arduino though it lacks the number of digital GPIO pins found on an Arduino and has only 1 analog input. There are actually several variants of the ESP8266 chip and some expose more I/O pins than others. The evaluation board I’m using for this project is based on the ESP-12 variant. For this project, my needs were very basic as I needed only 1 digital input, so pretty much any variant would have worked. Several months after the ESP8266 was introduced a project was started to allow the device to be programmed using the same IDE and sketch programming ability that is offered for the Arduino. This makes working with the ESP8266 easy for someone already familiar with the Arduino.
The ESP8266 is offered in many different forms. I settled on using an ESP8266 test board which comes complete with the ESP8266 mounted on a board with headers, a series of LEDs, a light sensor, a voltage regulator, and a battery pack already attached that holds 3 AA batteries.
Because of the voltage regulator, you can wire this board directly to a 5-volt power source like the AC adapter typically used to charge a smartphone. If you would like to learn more about the board itself you can read this excellent review and if you purchase a board, you’ll want to look at these sample applications as well. The review and the sample apps got me started on understanding how I could use the ESP8266 as a web server to control devices connected to it, or as a web client that could be used to report the status of things connected to it.
Here’s a list of components needed to make this project:
The overall idea is to have the ESP8266 monitor the state of the vibration sensor and periodically report its status to a server. The server will store the state of the device and provide a simple browser-based UI to show the current vibration state of the device and the last date/time it reported a status. The server will also support push notifications so that users can subscribe/unsubscribe to notifications from the device.
For this project, I settled on the following behavior
- When the device is powered on it immediately attempts to connect to the WiFi in my house.
- Once a connection is established the device performs an HTTP POST to a REST API running on a web server to record the initial state of the object as not vibrating. In this case, I chose to write the web server in Node.js and hosted it on OpenShift, but you can choose another provider to host the application. In addition, the tri-color LED on the ESP8266 eval board turns blue to signify that the device has connected to Wifi and is in standby waiting for vibrations to occur.
- The ESP8266 then begins to poll a vibration sensor to see if vibration has been detected. Once a vibration has been detected it performs another HTTP POST to the web server to inform it that the vibration has started. The LED on the test board is then changed to GREEN to indicate that vibrations are currently present and the dryer is currently running. Approximately every 20 seconds the ESP8266 performs another POST to update the vibration status. This lets me monitor the dryer status remotely and determine the last time the ESP8266 reported in some status.
- If no vibration is detected from the sensor a “timeout” will occur while reading the sensor state. I wait up to 20 seconds worth of these timeouts before considering the dryer to be “idle” at which time another HTTP POST is made to notify the subscribed users that the dryer cycle has completed.
- Every 20 seconds thereafter the POSTs are repeated to send subsequent notifications that the dryer cycle is done.
- When you arrive at the dryer to remove the clothes you simply cut power to the ESP8266 and the notifications stop. Powering up the device again will start the process over again to monitor the next load of laundry.
For the notifications, I considered several approaches including sending text messages through something like Twilio. However, I wanted those messages to come through immediately and there can sometimes be a short delay for text messages to arrive; I certainly don’t want those wrinkles to set while awaiting a text! As an alternative, I considered using push notifications. In the past, that meant creating an app that would have to be installed on our phones or other mobile devices. I didn’t want the overhead of having to install an app on each device to monitor the sensor state. I was aware that some time ago Google had added push notification support to the Chrome browser and decided this would be a great time to try push notifications on the browser for this app. Now we’re able to receive notifications from the dryer right from the Chrome browser on Android, Windows, Mac OSX, and on Chromebooks. One area that Chrome push notifications don’t yet support is iOS, so if you want to receive push notifications on iOS this is currently not an option. An alternative to using Chrome push notifications might be to use a service like PushBullet which offers support for iOS, Android, and several browsers.
This is a very simple project to wire if you are using the ESP8266 evaluation board. The ESP8266 is connected to the vibration sensor with 3 wires. I elected to use GPIO pin 14 on the ESP8266 which is wired to the OUT pin on the vibration sensor. VCC on the ESP8266 is connected to VCC on the vibration sensor and the GND pins are connected together as well.
That’s all you need to do hardware wise. Now we have to write some code to monitor the OUT pin of the vibration sensor. When that pin drops to LOW, a vibration has been sensed. It’s important to note that this pin drops low for a very brief amount of time (microseconds) and the duration that it is low indicates the amount of vibration sensed. There is a small LED on the vibration sensor board (indicated by D1 in the diagram above) that will flicker when a vibration is sensed. The LED will glow more brightly if the vibration is stronger. Note also that resistor R6 (the blue box with a brass screw on top) serves as a variable resistor and may need to be adjusted to detect smaller vibration levels.
Programming the ESP8266
The code to monitor the vibration sensor is essentially a polling loop. To detect when a vibration is present I read the state of the GPIO pin via the following code
The pulseIn function examines the level of the specified digital pin and waits for the amount of time specified by TIMEOUT for pin to go into the LOW state and returns the number of microseconds the pin is in that state. I found an issue with the ESP8266 implementation of pulseIn that would cause the board to crash and restart after 3 consecutive TIMEOUT intervals. After some experimentation, I found that setting TIMEOUT to a value of 750000 ( .75 sec) would not cause this issue. The ESP8266 implementation of pulseIn() seems to work a bit different than the Arduino documentation for pulseIn. If the timeout occurs, the value of the timeout, in this case, 750000, is returned rather than a zero value.
Performing some experimentation with the vibration sensor connected to the clothes dryer showed that the vibration would not always be detected during the timeout interval. Despite its age, the clothes dryer still doesn’t vibrate that much and the sensor isn’t great at picking up very small vibrations. However, I found that generally within a few seconds I could reliably sense some sort of vibration as my readVibeSensor() function would return values < 750000. I decided that the dryer would be presumed “idle” if approximately 15 or more seconds passed by with the readVibeSensor() continually returning the timeout value. When the number of timeouts (the number of times we get the .75 sec timeout) reaches a threshold I trigger the notification that the machine has gone idle.
Each time through the loop I measure how much time has elapsed since the device reported its status to the server. Presently, I have it reporting status every 20 seconds. In addition, the updateVibeLed(status) updates the tri-color LED on the ESP8266 evaluation board to show if the device is currently detecting vibrations (GREEN) or if the machine has gone idle (RED).
The REST API
To build this portion I used the Node.js Express framework. I needed some way to persist the state of the devices and record which users had subscribed to push notifications from the device. I wanted something very lightweight that could run as part of my Node application so I would not have another external dependency. To do this I used LowDB which is the database engine behind JSON-Server that I featured in an earlier blog post about quickly building APIs. LowDB worked out well for this simple app as the number of devices I plan to monitor and the amount of traffic to the server are low enough that LowDB can easily keep up with the requests. Just be sure you are hosting your application on a server where the file system will maintain the changes that LowDB writes out. Services like Heroku use an ephemeral filesystem which will reset whenever the app shuts down. For this application I wound up trying OpenShift which offers Node.js support, and a file system that truly persists changes.
The code for the REST API can be found here. I created a “device” model to act as the data access layer for the project. Inside this file, you can find all the data access methods to interact with LowDB. The API for LowDB is pretty easy to use. Here’s an example of fetching the status of a given device by ID
In the routes directory, you can find a file called api.js that supports the routes for the REST API. There are methods in this API to get and update the status (/device/:deviceId/status) of each device. In addition, there are methods to “register” a browser to receive notifications (/device/:deviceId/register) and remove a browser from the registration list. There’s also a notify endpoint (/device/:deviceId/notify) that allows the ESP8266 to notify all browsers that have registered to receive notifications from it. This code first retrieves the device in question via LowDB to obtain the list of registration IDs and then performs an HTTPS POST to the Google Cloud Messaging API with a list of IDs that should be notified of the change in state.
Originally I planned to write a native Android app to receive the push notifications from my service. However, there are 2 issues with this
- I have to install yet another app on my phone just to get push notifications. There are already far too many apps on my device and I wanted it to be as frictionless as possible to register a device to receive notifications.
- The push notifications only work on Android mobile devices. I wanted the notifications to appear in the Chrome browser on Mac, Windows, and Chromebooks as well.
Enter Chrome push notifications. The push notification support on Chrome relies on the Google Cloud Messaging (GCM) infrastructure; the same messaging platform that performs push notifications on native Android apps. To make this work each browser that the user wants to receive messages on has to “subscribe” to the device in order to receive a notification. The code I wrote to support notifications is based on Google’s own push notifications lab. This code runs in the client browser and in my code example you’ll find main.js and sw.js in /public/js. Note that service workers must be hosted either from localhost or over SSL. This was another reason for hosting the project on OpenShift as they support SSL certificates for the site.
The long sequence of characters after the /send/ in the URL is the registration ID. This is the number we need to reference in sending push notifications to GCM for this particular browser. The registration id is parsed out of the URL and passed to a method called addRegistration() which then uses the browser’s fetch API to POST the registration ID to our REST API. The registration method in the REST API then adds this value to an array of registration ids associated with the device and stores that in LowDB. Unsubscribing involves detecting if the browser has already subscribed. In the case of an existing subscription, there will already be a subscription object on the page which we can pass to removeRegistration() to perform an HTTP DELETE against the REST API to remove the registration ID from the array of devices so that it is no longer targeted for push notifications for that device.
Sending the notifications themselves is quite simple. The Node.js app has a route at /device/:deviceId/notify that will look up the device in LowDB and obtain the array of registration ids from that device. This is then used to make an HTTP POST against https://android.googleapis.com/gcm/send with an array of registration IDs that should be notified.
When the push notifications arrive on the browser the service worker receives a “push” event. Inside this event is where we construct the notification that is displayed to the user. This is done via a showNotification() method on the subscription. One limitation with Chrome push notifications is that you cannot pass data along with the notification. For this example, that was not really needed. However, if you wanted to customize the text or icon that appears in the notification you would have to store that association server side ahead of time based on the registration_id. Then the service worker could make a request to your API with that registration_id and obtain the data needed prior to calling showNotification. This is something Google hopes to improve in the future.
There are a few considerations you should be aware of when working with Chrome push notifications
- They must operate over SSL or localhost and the browser’s same origin rule applies. This might be an issue if you just want to run your server locally like on a Raspberry Pi or other device. It’s one of the reasons I decided to host the services on a cloud-based server.
- You may find it necessary to unregister/update a service worker in order for it to pick up code changes. In Chrome devtools the Resources panel has a service workers section that lets you debug, update, and even manually trigger a push event right in the browser. This can be helpful when wanting to test the code you have in place to respond to ‘push’ events.
When I started this project I assumed the most difficult part would be getting the ESP8266 to sense the vibration of the dryer. In the end, I probably spent more time working on the Node.js portion of this solution and resolving issues with Chrome push notifications than I did on the ESP8266 portion. The ESP8266 turned out to be easy to work with and I look forward to using it for other projects. The project turned out to be a nice way for me to experiment and learn not only the ESP8266 but also Chrome push notifications. Most importantly, it just might serve to get me out of doing some ironing as well!
If you are interested in the source code for the Node.js and ESP8266 portions of this project they can be found in my GitHub repository.