Spiria logo.

Connected Washing Machine With A Raspberry Pi

April 11, 2016.

Last year, I was given a Raspberry Pi B+ with a full complement of accessories (prototyping breadboard, wireless network interface, LEDs, push-buttons, etc.). I frankly had no idea what to do with it. I used it for a while as a multimedia centre, but since I already had an Apple TV, it was somewhat redundant. Then, one day, as I forgot the laundry in the washing machine for the umpteenth time, (I don’t hear the buzzer because the washer is in the garage), the penny dropped: I was going to use my Raspberry Pi to solve the problem once and for all.

Research

I did a bit of research on the Internet to get better acquainted with what the Raspberry Pi could do. As I looked at similar projects, I saw that various people had approached the same problem from various angles:

  1. Some had used a vibration-detection system.
  2. Some had used a light-detection system to monitor the machine’s “On” indicator.
  3. Some had plugged right into the machine’s electronics.

There was no way I was going to risk breaking my washing machine by messing with its electronics; option 3 was therefore ruled out. Those who had gone with option 2 had stuck their light-detection system on the front of the machine, which seemed awkward to me. So I was down to option 1, the vibration-detection system.

Vibration Detector

Once again, I had a choice of various possibilities. There are several types of vibration-detection technologies: electromechanical, piezoelectrical, optical…

I randomly chose an electromechanical vibration detector, whose main component is a SW-18010P vibration sensor switch.

This is a very simple device consisting of a metal pin inside a spring. Vibrations cause the spring to touch the pin, closing the circuit. In order to match the signal generated from the device with the required input levels of the Raspberry Pi, I chose to use a little conditioning circuit to do the job.

Since I hadn’t worked on electrical circuits since university, I once again turned to the Internet to learn how to make use of this component with the Raspberry Pi. I found a five-dollar module on Amazon which combined the SW-18010P vibration sensor switch with a LM393 voltage comparator.

This module can be hooked up directly to the Raspberry Pi (3.3 volts) and its signal can also be input directly, which is exactly what I needed.

“Push” Notifications

Since the whole point of this project was to receive a remote notification at the end of the wash cycle, I had to find a relatively simple way for my Raspberry Pi to send notifications to my phone. Since I had already used a push notification system called Pushover for a previous project, and was familiar with it, I decided to use my existing account for this project.

There are many other competing systems, some of which charge monthly fees in exchange for a certain number of push notifications. But the Pushover service is based on a one-time payment for an application (for smartphone/tablet or desktop), in exchange for a certain number of notifications per month. This solution seemed appropriate for my non-commercial needs.

When I originally set up my account on the Pushover site, I received a user key which enabled me to receive notifications on my devices. I then had to create a new application key for each notification-sending application or script that I created. So I just had to create a new application key for this project. Here is a look at my Pushover account page:

Delivering push notifications from Python consist in sending an POST (HTTP) message containing the user key, the application key and the message to be sent.

System Design

Once I received my vibration detector, I had to think of the features I’d need for a complete solution. I wanted:

  1. To be able to tell the Raspberry Pi when a washing cycle started (to keep it from interpreting environmental vibrations as a wash cycle).
  2. To have a visual indicator confirming the proper functioning of my system (since the Raspberry Pi is not connected to a screen).
  3. To receive a notification on my mobile phone at the end of the wash cyle.

Rummaging through my Raspberry Pi components, I found what I needed: a push-button to indicate the start of the wash cycle and a LED to confirm that the Raspberry Pi was actually monitoring machine vibrations. Since the components I received with my Raspberry Pi included a WiFi adaptor, I would have no problem sending my notifications over the Internet.

Familiarizing Myself with the Raspberry Pi

The first order of business was to install an operating system on the Raspberry Pi’s Micro SD card. This was very easy: raspberrypi.org provides everything you need to create a Micro SD card with the NOOBS installer, which allows you to choose which operating system to install on the card. I decided to use the Raspbian operating system, since it is Linux-based and seemed to provide most of what I needed for my project.

Once Raspbian was installed, I checked out all of its features to get an idea of its capabilities. I quickly saw that Python came pre-installed, which seemed to be the ideal platform for my system. A quick search on the Web led me to the RPi.GPIO library, which allowed me to control the various digital inputs and outputs of the Raspberry Pi.

To familiarize myself with the library, I started by trying to control the lighting of the LED. In order to do this, I connected the LED to the Raspberry Pi with a series resistor circuit in order to provide it with the appropriate current. This is what the circuit looked like:

The RPi.GPIO library was very easy to use; I was able to make my prototype work with minimal code:

try:
    # import necessary libraries
    import RPi.GPIO as GPIO
    import time

except RuntimeError:
    print("Error loading RPi.GPIO")

# define exit pin connected to LED
LED = 37

def main():
    try:
        # configure GPIO pins
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(LED, GPIO.OUT)

        while True:
            # after 2 seconds, light LED (providing GND)
            time.sleep(2)
            GPIO.output(LED, False)

            # after 2 seconds, shut off LED (providing 3.3V)
            time.sleep(2)
            GPIO.output(LED, True)
    except:
        print(“Quit on exception”)
    finally:
        GPIO.cleanup()

if __name__ == '__main__' : main()

Bolstered by this success, I was able to move on to the meat of the matter.

Final Circuit

Now that I knew how to control the RPi.GPIO library, I just had to plug my push-button into the circuit to indicate the start of a wash, then the vibration detector to indicate the end of the cycle.

The push-button connection is open by default, and pressing on it makes the connection and closes the circuit. One of the terminals must be connected to a power source (3.3 volts from the RPi), and the other to one of the RPi’s GPIO pins (I chose pin 33 — GPIO 13). This way, pressing on the push-button sends a 3.3-volt signal to the RPi. However, this alone does not guarantee the proper functioning of the system. The problem is that when the system is used as is, with the push-button in default position (that is, with the circuit open), pin 33, which is configured as an input, is no longer connected to anything. It is in a floating state, neither connected to the 3.3v (high level), nor to the GND (low level). There is a physical solution to the problem, i.e. connecting a “pull-down” resistor between pin 33 and the GND. However, the Raspberry Pi already implicitly provides “pull-down” and “pull-up” resistors on each of the GPIO pins. Therefore, you can configure the pins with the software to connect them to the internal RPi resistor, as demonstrated lower down.

As to the vibration detector, its functioning is super-simple. All you have to do is to connect it directly to the Raspberry Pi, then plug its output pin to one of the RPi’s GPIO pins configured as an input pin.

This is what the final circuit looks like:

And here is the code:

try:
    # Import necessary libraries
    import RPi.GPIO as GPIO
    import time
    import http.client, urllib # for push notifications
    import logging             # for debugging
except RuntimeError:
    print("Error loading RPi.GPIO")

DELAYINSECS = 180 # Time in seconds before declaring end of cycle

# Define RPi input/output pins
BUTTON = 33
LED = 37
VIBRATION = 31

# Function to send push notification with pushover.net
def pushdone():
    conn = http.client.HTTPSConnection("api.pushover.net:443")
    conn.request("POST", "/1/messages.json",
        urllib.parse.urlencode({
            "token": "",
            "user":  "",
            "message": "Laundry is done",
        }), { "Content-type": "application/x-www-form-urlencoded" })
    conn.getresponse()

def main():
    try:
        # Configure GPIO pins
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(VIBRATION, GPIO.IN)
        GPIO.setup(LED, GPIO.OUT)

        # "Pull-down" resistor must be added to input 
        # push-button to avoid floating value at 
        # RPi input when button not in closed circuit.
        GPIO.setup(BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

        # Start of sensing of upwards or downwards front on
        # pin connected to vibration detector. 
        # "Bouncetime" argument ignores effect of bouncing caused by
        # sudden state changes.
        GPIO.add_event_detect(VIBRATION, GPIO.BOTH, bouncetime=200)

        # Configure debugging journal file on RPi
        logging.basicConfig(filename='/home/pi/washer.log', 
                            level=logging.INFO, 
                            format='%(asctime)s %(levelname)s:%(message)s')
        logging.info("****************************")
        stop = False
        logging.info("Entering main loop")

        # Main loop, waits for push-button to be 
        # pressed to indicate beginning of cycle, then 
        # periodically checks vibration.
        while not stop:
            logging.info("Main loop iteration")
            GPIO.output(LED, True) # LED off
            GPIO.wait_for_edge(BUTTON, GPIO.RISING) # wait for signal 
                                                    # from push-button
            logging.info(" Started")
            going = True
            GPIO.output(LED, False) # LED on

            # Secondary program circuit, checks every 3 
            # minutes for vibrations during this time. 
            # If no vibration for the last 3 
            # minutes, cycle considered done.
            while going:
                logging.info("  Inner loop iteration")
                time.sleep(DELAYINSECS)
                logging.info("  Just slept %ds", DELAYINSECS)
                
                # Manual override to stop the current cycle; 
                # keep push-button
                # pressed during check.
                if GPIO.input(BUTTON):
                    stop = True
                    going = False

                # End of cycle if no vibration detected.
                if not GPIO.event_detected(VIBRATION):
                    logging.info("  Stopped vibrating")
                    pushdone()
                    going = False
            logging.debug(" End of iteration")
    except:
        logging.warning("Quit on exception")
    finally:
        logging.info("Cleaning up")
        GPIO.remove_event_detect(VIBRATION)
        GPIO.cleanup()

if __name__ == '__main__': main()

Once the code is written in a text document (/home/pi/washer_cycle.py), all you need to do is to add some instructions to the Raspbian start-up script (/etc/rc.local) to launch the script in superuser mode at system launch (the RPi.GPIO library does have a few compatibility issues running in regular user mode, hence the need to execute the command with sudo).

  • At the end of /etc/rc.local :
/home/pi/script_auto_run
  • In /home/pi/script_auto_run :
#!/bin/bash
echo "Autorun washer script"
sudo /usr/bin/python3 /home/pi/washer_cycle.py

Final Test

Time to go live! I installed my system on top of the machine, started a wash and pressed the push-button. The LED lit up, confirming the proper functioning of the script.

About an hour later, I received the following notification on my phone: