IoT Laundry Notification
2018-08-10 | By ASHLEY
License: None 3D Printing Arduino
General Description:
A lot of people have a washer and dryer that are not in close proximity to their basic living space. This can be an inconvenience as they are unable to hear when one cycle is finished to go start another, causing laundry to take more than twice the necessary time to complete. Wouldn’t it be great if your washer and dryer notified you when their cycles were finished? Well dive into this article to read about our solution to this problem, and learn how to recreate it for your home.
Development Board and Accelerometer:
This project is using the Wi-Fi development board MKR1000 from Arduino. This is an open-source board that is programmed using the free Arduino Software IDE. Included on the board is a Li-Po charging circuit to allow for a wireless connection or battery charging when an external power source is connected. When selecting a battery for this, ensure it has a 700mAh capacity or larger. Please note that powering via the battery will disable the power status LED on the development board to save energy. Take a look at the datasheet for basic stats and parameters.
The MMA8451 Accelerometer Evaluation Board from Adafruit is a user friendly option for people that don’t have much experience with accelerometers. There is example code in the Adafruit library to make this fairly simple to integrate into the design.
Assembly:
This step is fairly straightforward. The accelerometer for the washer needs a jumper wire soldered from ground to pin A (the address pin). This allows for individual addressing of the two sensors to differentiate them in the code.
Connecting the accelerometers to the MK1000 WiFi board requires power, ground, and two additional connections; the SCL (serial clock) and SDA (serial data) from each accelerometer board to the MKR1000 board. SCL and SDA from the two accelerometers will need to be tied together, then run to the MKR1000.
Once these connections are made, power using a 3.7V Li-Po battery or a wall wart. To save on battery life, a switch was added. One discovery that was made, is that because the board automatically switches between battery and USB power, if the battery is plugged in before the USB, the computer will not recognize the board to program.
The enclosures were custom drawn in Autodesk Fusion 360. See the end of the article for the public links to our STL files. HM221-ND and 377-1756-ND can also be used if no 3D printer is available.
The module mounts to the side of the washer and dryer via magnets located on the bottom of the enclosure. This will allow for them to be placed anywhere on the machines.
Code:
To start coding, the correct libraries need to be called. Libraries utilized in this code are SPI.h, WiFi101.h, BlynkSimpleWiFiShield101.h, Adafruit_MMA8451.h, and Adafruit_Sensor.h. The Adafruit_MMA8451 library needs to be downloaded from the Adafruit website and imported. See Adafruit’s website for instructions on how to do this. Then differentiate between the two sensors with Adafruit_MMA8451 mma and mma2.
After the above steps are done, connect to WiFi with the ssid and password. Download the Blynk library. Go to the Blynk app to configure. It will need the network name and password, along with the authentication code that is generated by Blynk. The authentication code will need to be placed in the code as well.
Now into the void setup. Create the array locations and initialize them to zero. Then start the serial monitor which is where the array information will be displayed. To communicate with the Blynk service, “Blynk.begin(auth, ssid, pass);” needs to be added to the setup. Then add all of the info that will be displayed in the serial monitor for each accelerometer. In this code, the sensor value is to be read every second and displayed in the serial monitor. The void loop simply contains two lines of code initiating blynk and timer.
The readSensor function will have the bulk of the information in it. First, set the tolerance for the accelerometers. Here 0.25 is used for a washer and dryer seated on a cement floor. This may need to be changed for each individual setup. The lower the number, the higher the sensitivity. Basically the accelerometer values will be read and stored every second for both the washer and dryer, then displayed in the serial monitor. The code will add the amount of seconds that motion was detected for each individually within the last 60 seconds. If that sum surpasses 20 for either machine, a push notification will be sent to your phone stating the cycle has started. This works in the opposite direction as well, where if it has detected motion constantly, then if the sum drops below 20 seconds, a notification will be sent that the cycle has ended for either machine, respectively.
Full code with comments and explinations of each line is located at the end of the article. There are links for the libraries that need to be downloaded within the code as well.
Conclusion:
This project is perfect for people living in dorms or apartments, or even those that have their washer and dryer in the basement and out of audible range. Never sit and wonder if the cycle is finished, let a load sit unbalanced for a long period of time, or forget about the laundry again. I hope this technology sparks an idea for other areas in your home or life that this technology would be useful.
Fusion 360 STL files:
/*
Digi-Key Electronics
Kyle Meier
2018
Laundry Push Notification Project - MKR1000 and Adafruit MMA8451 accelerometer eval board
This project uses the Blynk Service - www.blynk.cc
Please follow the instructions here to get setup with Blynk: www.blynk.cc/getting-started/
http://docs.blynk.cc/#getting-started-getting-started-with-the-blynk-app
A few libraries are required:
WiFi101.h // From Arduino library manager- see here for more details: https://www.arduino.cc/en/Reference/WiFi101
BlynkSimpleWiFiShield101.h // https://github.com/blynkkk/blynk-library/releases/latest
Adafruit_MMA8451.h // https://github.com/adafruit/Adafruit_MMA8451_Library/archive/master.zip
Adafruit_Sensor.h // https://github.com/adafruit/Adafruit_Sensor/archive/master.zip
Set ssid and pass to the wireless network name and password you'll be using.
Set Auth value to the authentication code provided from Blynk service after registration.
To adjust sensitivity, change "tolerance" variable value. Lower values increase sensitivity.
Hex Addresses for i2c sensors:
Sensor 1 ("A" pin left open/on board pullup) - Put this accelerometer in the enclosure you want to place on the washer
0x1D
Sensor 2 ("A" pin soldered to ground) - Put this accelerometer in the enclosure you want to place on the dryer
0x1C
Parts of this code are from Adafruit, Blynk, and Arduino example codes. Credit to the authors.
*/
#include <SPI.h>
// *************************** DOWNLOAD THESE LIBRARIES:
#include <WiFi101.h> // From Arduino library manager- see here for more details: https://www.arduino.cc/en/Reference/WiFi101
#include <BlynkSimpleWiFiShield101.h> // https://github.com/blynkkk/blynk-library/releases/latest
#include <Adafruit_MMA8451.h> // https://github.com/adafruit/Adafruit_MMA8451_Library/archive/master.zip
#include <Adafruit_Sensor.h> // https://github.com/adafruit/Adafruit_Sensor/archive/master.zip
Adafruit_MMA8451 mma = Adafruit_MMA8451();
Adafruit_MMA8451 mma2 = Adafruit_MMA8451();
// You should get an Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "AUTHENTICATION CODE HERE";
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "NETWORK NAME HERE";
char pass[] = "PASSWORD HERE";
BlynkTimer timer;
// ************************ Global Variable Declaration ************************
bool last60Wash[60]; // This array will store the last 60 sensor readings as a 1 or 0, movement or no movement respectively. This way we can determine if on average there was or was not movement in the last 60 readings (Seconds).
float lastXWash = 0; // Initialize global variables to track movement/acceleration in X and Y axis
float lastYWash = 0;
int arrayLocation = 0; // This variable keeps track of the position within the array we are writing to.
int lastArraySumWash; // This variable is used to track differences between current reading and previous reading of array
bool last60Dry[60];
float lastXDry = 0;
float lastYDry = 0;
int lastArraySumDry;
// *****************************************************************************
void setup()
{
for (int i = 0; i < 60; i++) { // For loop to clear all array locations in array last60Wash & Dry are set to 0 before start
last60Wash[i] = 0;
last60Dry[i] = 0;
}
SerialUSB.begin(9600); // Begin Serial communication with computer for debugging
Blynk.begin(auth, ssid, pass); // Begin communication with Blynk service
// Blynk.notify("Device started"); // Uncomment (remove "//" at the beginning of this line) to receive a notification from Blynk when the device is powered on
SerialUSB.println("Adafruit MMA8451 test!");
if (! mma.begin(0x1C)) { // Washer sensor- hex address of 0x1C. This sensor will have the "A" pin soldered to GND and be placed in the small enclosure.
Serial.println("Couldnt start first sensor");
while (1);
}
if (! mma2.begin(0x1D)) { // Dryer sensor- hex address of 0x1D. This sensor will have the "A" pin open/disconnected.
Serial.println("Couldnt start second sensor");
while (1);
}
SerialUSB.println("MMA8451 found!");
mma.setRange(MMA8451_RANGE_2_G);
mma2.setRange(MMA8451_RANGE_2_G);
timer.setInterval(1000L, readSensor); // A timer to read sensor values every 1 second. It will call the readSensor function
}
void loop()
{
Blynk.run();
timer.run();
}
void readSensor()
{
sensors_event_t event; // Read sensor values
sensors_event_t event2;
mma.getEvent(&event);
mma2.getEvent(&event2);
float xDiffWash = (event.acceleration.x - lastXWash); // Read x and y acceleration values- xDiffWash and yDiffWash determine difference between previous value and current value
float yDiffWash = (event.acceleration.y - lastYWash);
xDiffWash = abs(xDiffWash); // Because the acceleration values can be positive or negative, make the values absolute for easier indication
yDiffWash = abs(yDiffWash);
float xDiffDry = (event2.acceleration.x - lastXDry); // Read x and y acceleration values- xDiffDry and yDiffDry determine difference between previous value and current value
float yDiffDry = (event2.acceleration.y - lastYDry);
xDiffDry = abs(xDiffDry); // Because the acceleration values can be positive or negative, make the values absolute for easier indication
yDiffDry = abs(yDiffDry);
SerialUSB.print("xDiffWash: "); SerialUSB.print(xDiffWash); SerialUSB.print("\t"); // Serial print acceleration variables for testing
SerialUSB.print("yDiffWash: "); SerialUSB.print(yDiffWash); SerialUSB.print("\t");
SerialUSB.println();
SerialUSB.print("xDiffDry: "); SerialUSB.print(xDiffDry); SerialUSB.print("\t"); // Serial print acceleration variables for testing
SerialUSB.print("yDiffDry: "); SerialUSB.print(yDiffDry); SerialUSB.print("\t");
SerialUSB.println();
arrayLocation++; // Increment arrayLocation variable. This variable increments the array location we are recording to in each loop.
if (arrayLocation == 60) arrayLocation = 0; // If variable has reached 60, reset to zero to cycle through the array.
float tolerance = 0.25;
/********************************************************************************************************************************************************************
This tolerance value is the acceleration tolerance for movement. It is the sensitivity, adjust as needed. I found 0.25 worked well for both my washer and dryer.
My laundry room has a concrete floor, however, and will not move like machines mounted on a wood floor which may be sensitive to nearby foot traffic.
Lower value = higher sensitivity
********************************************************************************************************************************************************************/
if ((xDiffWash > tolerance) || (yDiffWash > tolerance)) // If the acceleration differences are larger than the tolerance value
{
if (xDiffWash > tolerance) SerialUSB.print ("X "); // Serial print which axes exceeded the tolerance value
if (yDiffWash > tolerance) SerialUSB.print ("Y ");
SerialUSB.println("OVER");
last60Wash[arrayLocation] = 1; // Movement occured, store a "1" in the current array location
}
else last60Wash[arrayLocation] = 0; // Movement did not occur, store a "0" in the current array location
if ((xDiffDry > tolerance) || (yDiffDry > tolerance)) // If the acceleration differences are larger than the tolerance value
{
if (xDiffDry > tolerance) SerialUSB.print ("X "); // Serial print which axes exceeded the tolerance value
if (yDiffDry > tolerance) SerialUSB.print ("Y ");
SerialUSB.println("OVER");
last60Dry[arrayLocation] = 1; // Movement occured, store a "1" in the current array location
}
else last60Dry[arrayLocation] = 0; // Movement did not occur, store a "0" in the current array location
for (int i = 0; i < 60; i++) { // Display the full array via Serial monitor
SerialUSB.print(last60Wash[i]);
SerialUSB.print(", ");
if (i == 59) SerialUSB.println();
}
for (int i = 0; i < 60; i++) { // Display the full array via Serial monitor
SerialUSB.print(last60Dry[i]);
SerialUSB.print(", ");
if (i == 59) SerialUSB.println();
}
int arraySumWash = 0; // This variable stores a sum of the values within the array
int arraySumDry = 0;
for (int i = 0; i < 60; i++) { // For loop to add all the values of the array
arraySumWash += last60Wash[i];
arraySumDry += last60Dry[i];
}
SerialUSB.print("arraySumWash: ");
SerialUSB.print(arraySumWash);
SerialUSB.print(" lastArraySumWash: ");
SerialUSB.println(lastArraySumWash);
SerialUSB.print("arraySumDry: ");
SerialUSB.print(arraySumDry);
SerialUSB.print(" lastArraySumDry: ");
SerialUSB.println(lastArraySumDry);
if ((arraySumWash == 20) && (lastArraySumWash == 19)) { // If arraySumWash is increasing in value and increased from 19 to 20, send push notification that the washer has been started
Blynk.notify("Washer Started");
SerialUSB.println("Washer Started");
}
else if ((arraySumWash == 19) && (lastArraySumWash == 20)) { // If arraySumWash is decreasing in value and decreased from 20 to 19, send push notification that the washer has finished
Blynk.notify("Washer Finished");
SerialUSB.println("Washer Finished");
}
if ((arraySumDry == 20) && (lastArraySumDry == 19)) {
Blynk.notify("Dryer Started");
SerialUSB.println("Dryer Started");
}
else if ((arraySumDry == 19) && (lastArraySumDry == 20)) {
Blynk.notify("Dryer Finished");
SerialUSB.println("Dryer Finished");
}
SerialUSB.println();
lastXWash = event.acceleration.x; // Store current acceleration values in lastXWash and lastYWash variables for calculations at beginning of the void loop
lastYWash = event.acceleration.y;
lastArraySumWash = arraySumWash; // Store the arraySumWash value in variable lastArraySumWash to compare in the next reading
lastXDry = event2.acceleration.x;
lastYDry = event2.acceleration.y;
lastArraySumDry = arraySumDry;
}
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum