danvanf
Published © GPL3+

Laundry IFTTT Alert

Make laundry machines let you know when they are done, non-invasive automatic start with IFTTT call at end of cycle.

IntermediateFull instructions provided6,123
Laundry IFTTT Alert

Things used in this project

Story

Read more

Custom parts and enclosures

Completed Build

The completed device installed in a case

Schematics

Wire connections

Wii nunchuck supplies both motion detection and hookup for buttons.

Code

Laundry Machine to IFTTT

Arduino
Monitors laundry machines through discarded (or new) Wii Nunchucks. Automatically starts, monitors the cycle then fires an IFTTT event when complete.
#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetClient.h>
#include <EthernetServer.h>
#include <EthernetUdp.h>
#include <Wire.h>
#include <ArduinoNunchuk.h>
/*
  Washing Machine Control
   Copyright 2016 Dan Van Fleet http://danvanfleet.com
 
   Project URL: None Yet
   Monitor machine for movement to automatically start, monitor cycle, send IFTTT event at end of cycle.
   Uses Wii Nunchuck for motion and buttons as manual start and cancel buttons.
*/
const bool isWasherOrDryer = false ; // true for washer, false for Dryer
const bool isTestMode = false;  // true won't fire IFTTT integration
const int isFastMode = 1; // used for testing divides timers by entered value.  0 or 1 runs normal time.
 
const String washerIftttEventName = "WasherDone";
const String dryerIftttEventName = "DryerDone";
const String iftttKey = "PutYourIFtttKeyHere";
const char server[] = "maker.ifttt.com";
 
byte dryerMac[] = { 0xDA, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE };
byte washerMac[] = { 0xDA, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
const IPAddress dryerIP(192, 168, 16, 177);  // Set the static IP address to use if the DHCP fails to assign
const IPAddress washerIP(192, 168, 16, 178);  // Set the static IP address to use if the DHCP fails to assign
 
/*
 
  These settings are for a Front Loading Kenmore Elite washer and dryer sitting on a drawer on concrete. 
  The dryer is rather a consistent vibration, and significantly larger than the washer other than spin cycle.
  The washer stops for a fairly long period of time and has a high RunWaitTime in comparison.
  DryerAutostartMovement time should be longer than Washer Spin cycle.
  Washer AutoStart could be set fairly high to startup during the wash spin cycle rather than close to initial startup.
*/
 
const int washerRunSensitivity = 4; // movement sensitivity, lower more sensitive 1 - 255
const float washerRunWaitTime = 4.1;  // minutes set to maximum no movement time during a run, at the end the event is fired.
 
const int washerAutoStartSensitivity = 3;  //movement sensitivity for automatic start, can be set big to get caught by first spin cycle
const float washerAutoStartMovementTime = 60; // Seconds movement required before auto start
const int washerAutoStartThreshold = 250; // skips # of not breaking sensitivity allowed during autostart
 
const int dryerRunSensitivity = 6; // movement sensitivity, lower more sensitive 1 - 255
const float dryerRunWaitTime = 0.5;  // minutes set to maximum no movement time during a run, at the end the event is fired.
 
const int dryerAutoStartSensitivity = 4;  //movement sensitivity for automatic start
const float dryerAutoStartMovementTime = 480; // Seconds movement required before auto start, needs to be longer than washer's fast spin cycle
const int dryerAutoStartThreshold = 85; // skips # of loops reading lower than sensitivity allowed during autostart
 
const int pinRun = 4;  // Pin Run LED is connected to Green.  Illumiate 50% during run, 100% during ifttt send.
const int pinAutoStartCheck = 7; // Pin AutoStart in progress LED Blue.  Illuminates at 50% of AutoStartMovementTime. Flashes at initial power up, after post.
const int pinEndCycleCheck = 6;  // Pin End Cycle check LED Red. Illuminates at 10% of RunWaitTime.
const int pinAutoStarted = 5;  //Pin cycle was autostarted LED Yellow. Illuminates when autostart detected.
 
const int timerAdjustment = 5000; // loops per minute
const int BAUDRATE = 19200;
// End Setup items
 
ArduinoNunchuk nunchuk = ArduinoNunchuk();
bool isRunning = false;  // program contol
float lastAccelX = 0;  // stores last positions for delta determinations.
float lastAccelY = 0;  // ''
float lastAccelZ = 0;  // ''
int counter = 0;  // stores counter position for run loop counter
float startMoveCounter = 0;  // stores counter position for auto start cycle loops
int lastChangeCount = 0;  // stores counter position of last seen change
// these come from washer or dryer settings.
int runSensitivity = 0; // stores minimum movement value to continuing running.
float runWaitTime = 0; // stores maximum no movement time in seconds before run ends
int autoStartSensitivity = 0; // stores minimum value to consider starting
float autoStartMovementTime = 0; // stores seconds of start movement before auto start
int autoStartThreshold = 0; // stores maximum no movement loops to ignore during auto start check.
String iftttEventName = ""; // stores ifttt event name to run
byte mac[]  = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // placeholder for mac address
IPAddress ip(192, 168, 16, 175);  // place holder for IP address //end washer or dryer settings
int logX = 0; // Used for Display
int logY = 0;
int logZ = 0;
int countWrap = 0;
int displayWrap = 0; // end Display
EthernetClient client; // used to talk
 
void setup()
{
   pinMode(pinRun, OUTPUT);
  pinMode(pinAutoStartCheck, OUTPUT);
  pinMode(pinEndCycleCheck, OUTPUT);
  pinMode(pinAutoStarted, OUTPUT);
  digitalWrite(pinRun, LOW);
  digitalWrite(pinAutoStartCheck, HIGH);
  digitalWrite(pinEndCycleCheck, LOW);
  digitalWrite(pinAutoStarted, LOW);
  Serial.begin(BAUDRATE);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  nunchuk.init();
   
  if (isWasherOrDryer) // toggle in variables
  {
    runSensitivity = washerRunSensitivity;
    runWaitTime = washerRunWaitTime;
    autoStartSensitivity = washerAutoStartSensitivity;
    autoStartMovementTime = washerAutoStartMovementTime;
    autoStartThreshold = washerAutoStartThreshold;
    iftttEventName = washerIftttEventName;
    memcpy(mac,washerMac,  6);
    memcpy(ip, washerIP, 4);
  }
  else
  {
    runSensitivity = dryerRunSensitivity;
    runWaitTime = dryerRunWaitTime;
    autoStartSensitivity = dryerAutoStartSensitivity;
    autoStartMovementTime = dryerAutoStartMovementTime;
    autoStartThreshold = dryerAutoStartThreshold;
    iftttEventName = dryerIftttEventName;
    memcpy(mac, dryerMac, 6);
    memcpy(ip, dryerIP, 4);    
  }
  if (isFastMode > 1) {
    runWaitTime = runWaitTime / isFastMode;
    autoStartMovementTime = autoStartMovementTime / isFastMode;
  }
}
void loop()
{
  // if there are incoming bytes available
  // from the ifttt server, read and print them:
  if (client.available()) {
    digitalWrite(pinAutoStartCheck, LOW);
    char c = client.read();
    Serial.print(c);
  }
  else {
    nunchuk.update();
    CheckButtons();
    if (isRunning == true)
    {
      digitalWrite(pinAutoStartCheck, LOW);
      digitalWrite(pinRun, HIGH);
      if (counter < (runWaitTime * timerAdjustment)) { RunMovementInspector(); digitalWrite(pinRun, LOW); // keeps LED dimmer so that when ifttt call is being setup and made it gets brighter. 
  {
    // Movement detected
    counter = 0;
    digitalWrite(pinEndCycleCheck, LOW);
  }
  else
  {
    //Not moving, should we get out?
    counter++;
    if (counter > runWaitTime * timerAdjustment / 10)
    { // Light the considering stop light at
      digitalWrite(pinEndCycleCheck, HIGH);
    }
  }  
  PrintLog(counter, changeX, changeY, changeZ);
  
}
 
int GetChangeX(){
  float accel = nunchuk.accelX;
  float change = lastAccelX - accel;
  lastAccelX = accel;
  return abs(change);
}
int GetChangeY(){
  float accel = nunchuk.accelY;
  float change = lastAccelY - accel;
  lastAccelY = accel;
  return abs(change);
}
int GetChangeZ(){
  float accel = nunchuk.accelZ;
  float change = lastAccelZ - accel;
  lastAccelZ = accel;
  return abs(change);
}
void StartupCheck()
{
  float changeX, changeY, changeZ;
  changeX = GetChangeX();
  changeY = GetChangeY();
  changeZ = GetChangeZ();
  if ( changeX > autoStartSensitivity ||  abs(changeY) > autoStartSensitivity || abs(changeZ) > autoStartSensitivity )
  { // Something  moved more than it should have, count it.
    startMoveCounter++;
    lastChangeCount = startMoveCounter;
    StartupLog(startMoveCounter, changeX, changeY, changeZ);  
    if (startMoveCounter > (autoStartMovementTime * timerAdjustment / 120)) {
      digitalWrite(pinAutoStartCheck, HIGH); // if we hit half way point light the led. 
    }
  }
  else
  {
    if (startMoveCounter - lastChangeCount > autoStartThreshold) {
      //too many counts since change detected, reset counters
      startMoveCounter = 0;
      lastChangeCount = 0;
      digitalWrite(pinAutoStartCheck, LOW);
      Serial.println("Reset Counters");
    } else
    {
      startMoveCounter++;
      Serial.println(startMoveCounter, 0);  // we didn't move, print a line
    }
  }
  if (startMoveCounter > (autoStartMovementTime * timerAdjustment / 60 ))  //autoStartMovementTime is in Seconds.
  {
    isRunning = true;
    digitalWrite(pinRun, HIGH);
    digitalWrite(pinAutoStarted, HIGH);
    startMoveCounter = 0;
  }
}
void SendAlert()
{
  // start the Ethernet connection:
  Serial.println("starting");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // try to congifure using IP address instead of DHCP:
    Ethernet.begin(mac, ip);
  }
  // give the Ethernet shield a second to initialize:
  delay(1000);
 
  Serial.println("connecting...");
  if (client.connect(server, 80)) {
    Serial.println("connected");
    //Make a HTTP request:
    client.println("GET /trigger/" + iftttEventName + "/with/key/" + iftttKey + " HTTP/1.1");
    client.println("Host: maker.ifttt.com");
    client.println("Connection: close");
    client.println();
  }
  else //client.connect
  {
    // if you didn't get a connection to the server:
    Serial.println("connection failed");
  }
}
void PrintLog(int counter, int changeX, int changeY, int changeZ)
{
  int summary = 99;  // 83 is closer to a second 100 is easier to read
  displayWrap++;
  // Display largest change over summary movements
  if (countWrap < summary) {
    if (logX < changeX) {
      logX = changeX;
    }
    if (logY < changeY) {
      logY += changeY;
    }
    if (logZ < changeZ) { logZ += changeZ; } countWrap++; } else { Serial.print(counter); Serial.print("-"); Serial.print(logX); Serial.print(" "); Serial.print(logY); Serial.print(" "); if (displayWrap > (summary * 10)) {
      Serial.println(logZ);
      displayWrap = 0;
    }
    else
    {
      Serial.print(logZ);
      Serial.print("   ");
    }
    countWrap = 0;
    logX = 0;
    logY = 0;
    logZ = 0;
  }
}
void StartupLog(int startMoveCounter, int x,int y,int z){
  //we are moving, wrap every 15.
    displayWrap++;
    Serial.print(startMoveCounter);      
    //Serial.print("-");
    //Serial.print(x);
    //Serial.print(" ");
    //Serial.print(y);
    //Serial.print(" ");
    //Serial.print(z);
    //Serial.print(" ");
    if (displayWrap > 15) {
      Serial.println(" ");      
      displayWrap = 0;
    }
    else {
      Serial.print(" ");
    }
}

Credits

danvanf

danvanf

0 projects • 0 followers

Comments