Things used in this project

Hardware components:
Photon new
Particle Photon
×1
Adafruit industries ada161 image 75px
Photo resistor
×2
Mfr 25frf52 10k sml
Resistor 10k ohm
×2
Hand tools and fabrication machines:
09507 01
Soldering iron (generic)

Schematics

Laundry Monitor Schematic
Use a breadboard or solder it up.

Code

Laundry MonitorC/C++
Create a new Photon project and use this code for the .ino file
/*
 * Project Laundry Monitor
 * Description: Washer / Dryer monitor and notifications
 * Author: Martin Smaha
 * Date: 3/9/2017
 */
#include <string.h>

 TCPServer server = TCPServer(8080);//web page port
 TCPClient client;
 char LANBuf[1026],pBuf[128];
 int LED = D7;
 int washerAnalogPin = A0;
 int dryerAnalogPin = A1;

 bool isWasherRunning;
 bool isDryerRunning;
 unsigned long WasherRunTime_sec;
 unsigned long DryerRunTime_sec;
 unsigned long msecLast;
 int washerValue;
 int dryerValue;

 void WebHeader(void);
 void WebServer(void);
 void FormatRuntime(unsigned long xSeconds);

// setup() runs once, when the device is first turned on.
void setup() {
  // start listening for clients
  server.begin();
  // small blue LED on board
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  // variables
  LANBuf[0] = 0;
  msecLast = millis();
  washerValue = 0;
  dryerValue = 0;
}

// loop() runs over and over again, as quickly as it can execute.
void loop() {
  WebServer();
  unsigned long msec = millis();
  if (msec > msecLast) {
    unsigned long diff = msec - msecLast;
    if (diff >= 1000) {
      ReadAnalogs();//only read them once per second
      diff -= 1000;
      msecLast = msec - diff;
    }
  } else {
    msecLast = msec;// this throws away 1 millisecond every 49 days due to wrapping
  }
}

// Web Header
void WebHeader(void) {
  client.print("HTTP/1.1 200 OK\r\n\r\n<html><head><title>Laundry</title><style>div {border: 2px solid #a1a1a1;padding: 10px 40px;width: 200px;border-radius: 25px;}button {width: 100px;height: 40px;background: blue;color: white;}button:hover {color: black;}</style>");
  client.print("<META HTTP-EQUIV='Pragma' CONTENT='no-cache'>");
  client.print("<META HTTP-EQUIV='Expires' CONTENT='-1'>");
  client.print("<meta content='text/html; charset=ISO-8859-1' http-equiv='Content-Type'>");
  client.print("<meta name='viewport' content='width=device-width, initial-scale=1'></head>");
}

// Web server
void WebServer(void) {
  if (client.connected()) {
    int isRequest = -1;
    if (client.available()) { //get first data segment
      memset(LANBuf,0,sizeof(LANBuf));//clear old data
      client.read((uint8_t*)LANBuf,sizeof(LANBuf) - 2);//leave the buffer zero terminated
      //determine which page browser has requested
      if (strstr(LANBuf,"GET") != NULL) {
        isRequest = 0;
      }
      if (strstr(LANBuf,"status.html") != NULL) {
        isRequest = 1;
      }
    }
    //send response
    if (isRequest == 0) { //standard http error message for page not found
      client.print("HTTP/1.1 404 \r\n\r\n");
      client.stop();
    }
    if (isRequest == 1) { //status.html
      WebHeader();
      client.print("<body bgcolor=#ffffff width=250px><div><h1>Laundry</h1><table>");
      // status LEDs
      if (isWasherRunning) {
        client.print("<tr><td><span style=background:#00ff00;> &nbsp &nbsp </span></td><td><font>Washer</font></td>");
        FormatRuntime(WasherRunTime_sec);
        client.print(pBuf);
      } else {
        client.print("<tr><td><span style=background:#cccccc;> &nbsp &nbsp </span></td><td><font>Washer</font></td><td>OFF</td>");
      }
      client.print("</tr><tr><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td></tr>");
      if (isDryerRunning) {
        client.print("<tr><td><span style=background:#00ff00;> &nbsp &nbsp </span></td><td><font>Dryer</font></td>");
        FormatRuntime(DryerRunTime_sec);
        client.print(pBuf);
      } else {
        client.print("<tr><td><span style=background:#cccccc;> &nbsp &nbsp </span></td><td><font>Dryer</font></td><td>OFF</td>");
      }
      client.print("</tr></table>");
      //diagnostic section. Comment out this section once analog values have been adjusted
      sprintf(pBuf,"<hr><p>Washer: %d<br>Dryer: %d</p>",washerValue,dryerValue);
      client.print(pBuf);
      //end diagnostic section
      client.print("</div></body></html>");
      client.stop();
    }
  } else {
    // if no client is yet connected, check for a new connection
    client = server.available();
  }
}
void ReadAnalogs(void) {
  washerValue = analogRead(washerAnalogPin);  // read the analogPin
  bool isON = (washerValue < 1024);
  if (isWasherRunning) {
    if (isON) {
      WasherRunTime_sec++;
    } else {
      isWasherRunning = false;
      if (WasherRunTime_sec >= 60) { //prevents notifications when cycling thru timer
        Particle.publish("LAUNDRY","Washer Done",60,PRIVATE);
      }
    }
  } else {
    if (isON) {
      isWasherRunning = true;
      WasherRunTime_sec = 0;
    }
  }
  dryerValue = analogRead(dryerAnalogPin);  // read the analogPin
  isON = (dryerValue < 1024);
  if (isDryerRunning) {
    if (isON) {
      DryerRunTime_sec++;
    } else {
      isDryerRunning = false;
      if (DryerRunTime_sec >= 60) { //prevents notifications when cycling thru timer
        Particle.publish("LAUNDRY","Dryer Done",60,PRIVATE);
      }
    }
  } else {
    if (isON) {
      isDryerRunning = true;
      DryerRunTime_sec = 0;
    }
  }
  if (isWasherRunning || isDryerRunning) { //used of initial testing
    digitalWrite(LED, HIGH);
  } else {
    digitalWrite(LED, LOW);
  }
}

void FormatRuntime(unsigned long xSeconds) {
  unsigned int hours;
  unsigned int minutes;
  unsigned int seconds;
  hours = xSeconds / 3600;
  xSeconds -= hours * 3600;
  minutes = xSeconds / 60;
  xSeconds -= minutes * 60;
  seconds = xSeconds;
  sprintf(pBuf,"<td><font>ON for %dh %dm %ds</font></td>",hours,minutes,seconds);
}

Credits

183193 102389716507771 4133659 n rhhfeodpxv
Martin Smaha

Designing hardware and software since 1975.

Contact

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

Control Adafruit NeoPixels using Python and JQWidgets
Easy
  • 933
  • 8

Full instructions

In this tutorial, we'll see how to control an Adafruit NeoPixel ring via mobile using Python and JQWidgets.

What's My I2C Address?
Easy
  • 244
  • 4

The story of how to make a clock through using a LCD 1602 or LCD 2004 over I2C.

I Keep My Cucumber Growing Conditions Moist - Part 3
Easy
  • 268
  • 3

Full instructions

This is the next step with an Particle Photon and softAP integration: cucumbers go online.

MyHumidity Controlled by BME280 and Photon
Easy
  • 521
  • 6

Full instructions

Checks if humidity threshold is reached and informs the user by using Blynk/IFTTT-services with softAP integration.

Study Buddy
Easy
  • 2,850
  • 141

Full instructions

Know when an assignment is coming up!

Study Buddy

Team Alexa and Shannon

MyLCD20x4 clock with value-added information
Easy
  • 41
  • 0

Full instructions

Use a Particle Photon, a BME280 sensor and a cheap LCD20x4 to start your own clock-project.

Add projectSign up / Login