Go check the garage!

Made by Chelsie Corbett

Found in DIoT 2018 1- Home Hack

Our garage door stays a few feet open about half the time. My poor dad usually runs outside in the cold before bed to make sure it is shut. I'd like him to receive a text if the garage door is open for longer than 60 minutes so that he will know to go close it.

0

Problem Statement: Our garage door stays a few feet open about half the time. My poor dad usually runs outside in the cold before bed to peek at it and make sure it is shut. I'd like him to receive a text if the garage door is open for longer than 60 minutes so that he will know to go close it. I chose this project because I am an absolute beginner, and it seemed like something I could achieve. I have several projects that I’d like to create someday that I know could be done using reed switches and magnets, so I thought I’d try my hand at using them in a project.   

0

Goal: Garage awareness. Receive a text when the garage door has been open for longer than an hour. By obtaining the information on the garage status via a mobile form factor, my dad will be able to know if the garage isn’t closed from any location. This will save him having to go outside.   

0

Parts:

1) Reed Switch  (A reed switch makes a closed connection and completes a circuit when a magnet is near.  It consists of metal reeds in a hermetically sealed glass container. )

2) Magnet

2) Particle and materials in kit

3) Smartphone with IFTTT account

4) Adhesives to mount the photon and the magnet (I went with tape)

0

Setting up the board: 

0
  //When you randomly find a sautering iron in your garage while doing an IoT project about your garage.   
0

I’m a little nervous that this is not the proper way to sauter a reed switch. I tried looking online to understand how to connect it, but there was very little data about this part of the process. However, the circuit is complete and the reed switch appears to function in this state.   

0

Coding:  

The code was sourced from a particle hackster project that can be found here:

GarageMonitorApp - Monitoring of multiple garage doors Open/Close status to Text/SMS message

Copyright (C) 2015, Jay Moskowitz

https://particle.hackster.io/team-wireless-marvels-inc/garage-door-status-alert-to-sms-text-bc52f0?ref=channel&ref_id=286_popular___&offset=69


0

Processing Steps of Application:

https://particle.hackster.io/team-wireless-marvels-inc/garage-door-status-alert-to-sms-text-bc52f0?ref=channel&ref_id=286_popular___&offset=69  

0 - Process loop starting. Read switches. One time initialization. Read time of day clock.

1 - Start 5 minute timer if any door just opened.

2 - Stop clock if a door closed before a timeout.

3 - Check if a door open too long if not already alerted to an open door

4 - Send alert if necessary and one not already sent. Comments refer to sending a Tweet which is how the Arduino version worked. But the Photon version publishes an event and using IFTTT to monitor events, is much more flexible than just sending a tweet. In fact we don't send a tweet in this version, we just send an SMS. But using IFTTT one can do all sorts of things.

5 - If in alert state and all doors close, send a message to indicate everything is OK again.

6 - If in alert state for 15 minutes, send a reminder that at least one door is still open. Send the status of each door.

7 - Update the time of day every hour to insure our internal clock does not drift from real time.

8 - Update the internal time of day based on how much time has passed since the last time the processing loop executed.

9 - Blink the Blue LED at a rate based on the alert state. The on/off cycle of the LED transcends multiple processing loops. Timers are set to insure the LED blinks at the correct rate.

10 - Check if any door is open or if all doors are closed.

11 - In debug mode print a time stamp every 10 seconds. Also display how many processing loops have been executed and how many milliseconds had passed since the app was started or since the last wrap around of the clock (49+ days).

12 - Delay for 10 milliseconds before returning to step 0.

13 - Show that we are about to return to the Particle firmware for it to do its processing (such as keeping a connection to the Particle cloud alive) before it returns control to the app at step 0.


0
// This #include statement was automatically added by the Particle IDE.
#include "DebugPrint.h"

//    GarageMonitorApp - Monitoring of multiple garage doors Open/Close status to Text/SMS message
  //  Copyright (C) 2015, Jay Moskowitz

// Revisison History
// Rev V4 - Insert Daylight Savings Time code as the Photon library
//          does not account for it. Adjust the GMT offset accordingly.
// Rev V3 - Correct mask in Timer wrap around logic - 10/21/2015
//          - 0xC00 0000 should have been 0xC000 0000
//              - caused LED timer to stop operating when mills()
//                  reached 0x8000 0000
//          - Update set_next_timeout() to insure when close to wrap around
//              time that the new timer is not set to 0 as this value is
//              sometimes used by the logic to indicate if a timer is
//              active or not.
//              - This caused the sync of the date/time from the cloud to
//                  stop and left the Date of alarms from being updated.
//          - Update step 8 in loop() which maintains the current time of day
//              - At clock wrap time, the function went into a loop and
//                  because of the _xC00 0000 error above, resulted in the
//                  clock updates scheduled for a very very long time later
//                  when it came out of the loop. This error and the last one
//                  resulted in the Date/Time on text messages being locked
//                  to a specific Date/Time and not being updated.
//          - All these errors were related to clock wrap around issues.
//
// Rev V2 - Included GNU license and released on Hacketer.io - 9/9/2015
// Rev V1 - Initial release on production Photon and in Operation since 6/1/2015

// This version of the code utilized two functions to deal with clock wrap around.
// These are set_next_timeout() and check_for_timeout(). Using these functions,
// one only needs to maintain a single unsigned long variable to represent the
// timeout time. The code makes sure that near clock wrap around time, it sets the
// next timeout so it occurs after the clock wraps back to 0 to insure that a timeout
// is not accidentally missed which could hang up a function. And alternative method
// of dealing with the clock wrap around entails with having the timer as a struct
// instead of a single unsigned long. The struct would contain the starting time when
// the timeout was requested and the interval of time you wish to wait. Then the
// check_for_timeout function becomes:
//          if (start_time - millis()) < interval) return TRUE; // Timed out
//          else return FALSE;          // Didn't time out
// This requires carrying two values instead of the one being used. It works
// propertly because of unsigned arithmetic which results in this function waiting
// the proper timeout time.
//
//  Monitoring of 3 garage doors via reed switches

//  LED Blink Patterns
//    2 sec on / 2 sec off      -  Idle loop
//    1 sec on / 1 sec off      -  Door is open - not in alarm state
//    4 times per second        -  Door is open - in alarm state - Tweet has been sent
//

// Adapted from an Arduino app and ported to the Photon

#define debug     1    // 1 for debug mode - progress diagnostics - with or without faster (debug) timers
#define debugTimers 0  // use debug timers in debug mode (else use normal timers)
#define debug_step 0   // ! to track each step of processing


#define DoorLft   0
//CHELSIE  We only will use DoorLft for this project since there is only one garage door
#define DoorMid   1
#define DoorRgt   2
#define OPEN      LOW
#define CLOSED    HIGH
#define debounce  (unsigned long)50L    // 50ms debounce timeout
#define LED       D7                    // Show status on blue Photon LED

#define CR '\r'
#define LF '\n'

#define AD_RANGE 4096       // 0 - 3.3v --> 0 to 4095
#define debugReminder 5L    // Debug time in minutes
#define normalReminder 15L  // Normal interval between reminders
#define debugClkUpdate 5L   // update time of day clock from cloud every 5 minutes
#define normalClkUpdate 60L // Once per hour

// Analog read of door status on channels
// Lft =  A0
// Mid =  A1
// Rgt =  A2

#define uint8 byte
#define strcat_P strcat


//---------------------------------------------------------------------------

// Flash stored messages

const char f_left[] PROGMEM = "Lft-";
const char f_open[] PROGMEM = "OPEN ";
const char f_closed[] PROGMEM = "CLOSED ";
const char f_mid[] PROGMEM = "Mid-";
const char f_rgt[] PROGMEM = "Rgt-";
const char f_at[] PROGMEM = " on ";
const char f_sending[] PROGMEM = "\nSending text: ";
const char f_cr[] PROGMEM = "\n";
const char f_startup_msg[] PROGMEM = "\n************ Begin garage door switch test ************";
const char f_gpl1[] PROGMEM = "\nGarageMonitorApp - Version 3, Copyright(c) 2015, Jay Moskowitz";
const char f_gpl2[] PROGMEM = "\nThis program comes with ABSOLUTELY NO WARRANTY";
const char f_gpl3[] PROGMEM = "\nFor details refer to http://www.gnu.org/licenses/ ";
const char f_sl[] PROGMEM = "\nStartup loop()";
const char f_dosc[] PROGMEM = "\nDoor opened - start clock ";
const char f_dcbasc[] PROGMEM = "\nDoor closed before alert - stop clock ";
const char f_mtasdtd[] PROGMEM = "\nMove to alert state due to door ";
const char f_odot[] PROGMEM = "\n--> One or more doors open text";
const char f_adct[] PROGMEM = "\n--> All doors closed text";
const char f_debug_clock[] PROGMEM = "----------> Clock is at ";
const char f_getTime[] PROGMEM = "\n-->Get Date/Time";
const char f_reminder[] PROGMEM = "\nSend Reminder Text";
const char f_step[] PROGMEM = "\nstep: ";

char *dayNames[]={"xxx","Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
char *monthNames[]={"xxx","Jan","Feb","Mar","apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec"};

unsigned long proc_time, ledTimer, resubmit_tweet_timer, reqTimeUpdate;
boolean startup, ledState;
unsigned long open_clock[3], timeout, debounceTime[3], clk_time;
boolean Door[3], last[3];
boolean alert_state, time_to_alert, clock_running[3];
unsigned long secondM, minuteM, hourM, dayM;
byte resubmit_tweet_count, resubmit_getTime_count;
byte nhr, nmin, nsec;
byte fptr, find_state, clk_state;
byte cur_hour, cur_minute, cur_second;
boolean tweet_in_progress, tweet_lost;
boolean getTimeInProgress, getTimeFormatError, clk_set, clk_error;
long mainCount = 0;

char dayNumber = 0;
char monthNumber = 0;
char weekdayNumber = 0;

const char fmt1[] PROGMEM = "%d-%d:%d:%d.%ld";
const char fmt2[] PROGMEM = "%s %s %d, %02d:%02d:%02d";
const char fmt3[] PROGMEM = " mainCount=%lx, millis()=%lx";

#define TWEET_LEN 141
  char tweet[TWEET_LEN];

void formatDoorStatusAndConditionallySend(byte tweet_required){
  unsigned long currtime, tweet_stamp;
  unsigned int seconds, minutes, hours, days;
  unsigned long fraction;

  tweet_stamp = proc_time;

  memcpy_P(tweet, f_left, strlen_P(f_left)+1);                   // "Lft: "
  if (Door[DoorLft] == OPEN) strcat_P (&tweet[strlen(tweet)],f_open);  // "OPEN "
  else strcat_P (tweet,f_closed);                                // "CLOSED "

  strcat_P (tweet,f_mid);                                        // "Mid: "
  if (Door[DoorMid] == OPEN) strcat_P (tweet,f_open);                  // "OPEN "
  else strcat_P (tweet,f_closed);                                // "CLOSED "

  strcat_P (tweet,f_rgt);                                        // "Rgt: "
  if (Door[DoorRgt] == OPEN) strcat_P (tweet,f_open);                  // "OPEN "
  else strcat_P (tweet,f_closed);                                // "CLOSED "

  strcat_P (tweet,f_at+1);                                         // "at " (skip leading space in ' at ')

  currtime = tweet_stamp;
  days = currtime / dayM;
  currtime -= (days * dayM);
  hours = currtime / hourM;
  currtime -= (hours * hourM);
  minutes = currtime / minuteM;
  currtime -= (minutes * minuteM);
  seconds = currtime / secondM;
  currtime -= (seconds * secondM);
  fraction = currtime;

  char fmt[40];
  if (!clk_set) strcpy_P(fmt,fmt1);    // get format string from ROM
  else          strcpy_P(fmt,fmt2);
  if (!clk_set) sprintf(&tweet[strlen(tweet)],fmt,days,hours,minutes,seconds,fraction);
  else sprintf(&tweet[strlen(tweet)],fmt,dayNames[weekdayNumber],monthNames[monthNumber],dayNumber,cur_hour,cur_minute,cur_second);

  if (debug) {
    if (tweet_required) DebugPrintFO(f_sending);     // Serial.print ("\nSending text: ");
    else DebugPrintFO(f_cr);                         // Serial.print ("\n");
    Serial.print (tweet);
  }
}

void send_tweet(bool startup){
   formatDoorStatusAndConditionallySend(TRUE);

   if (startup){
       sprintf(&tweet[strlen(tweet)],"<br>App reset");
       if(debug) Serial.println("\r\nApp reset - restarted\r\n");
   }
   else if(debug) Serial.println((char *)"");


   // Send TextMsg event
   Particle.publish("textMsg",tweet,0x7fff,PUBLIC);          // Publish text message - IFTT will send SMS
}

// Read reed switches and debounce input
void readSwitch(){
  int i;
  boolean swd;

  for (i=DoorLft; i<=DoorRgt; i++){
    swd = digitalRead(D0+i);   // Set HIGH (circuit closed) or LOW (circuit open)

    if (swd != last[i]){
      last[i] = swd;              // save current switch value (high or low)
      set_next_timeout(&debounceTime[i], proc_time, debounce);  // time before reading considered stable
    }

    if (check_for_timeout(debounceTime[i],proc_time)){  // timer has timed out
      Door[i] = swd;             // switch has been debounced. Same setting for some period of time
    }
  }
}

// Parse/Analyze data returned from remote time server
void getTime(){

  unsigned long read_time = Time.now();

  if(Time.year()<=1970) return; // This means the RTC has not yet started

  bool daylightSavings = IsDST(Time.day(), Time.month(), Time.weekday());
  Time.zone(daylightSavings? -4 : -5);

  cur_hour = Time.hour(read_time);
  cur_minute = Time.minute(read_time);
  cur_second = Time.second(read_time);
  dayNumber = Time.day();       // 1 - 31
  monthNumber = Time.month();   // 1 -12
  weekdayNumber = Time.weekday();     // 1 = Sunday

  if (debug) {
      DebugPrintFO(f_getTime);
      Serial.println((char *)"");
  }
  clk_set = TRUE;            // once clock is set at least once, the system will maintain the time
  // Set clk_time to count the number of seconds from now and adjust the current time accordingly
  // The set_next_timeout() function will consider the clock as getting ready to wrap around
  // during the last one minute before wrap around time every 49.7102696 days
  // (49 days, 17 hours, 2 minutes, 47.295 seconds)
  set_next_timeout(&clk_time, millis(),1000L);       // Update local time in 1 second
}

// check to see if timeout has occurred
bool check_for_timeout(unsigned long timeout_time, unsigned long cur_time){
  if ( ( (timeout_time & 0xC0000000) == 0) &&    // timeout time a very low value
         (cur_time & 0x80000000) )              // current time is a very large value
    return FALSE;                               // No timeout yet

  if (timeout_time <= cur_time) return TRUE;     // Timeout has occurred
  else return FALSE;                            // No timeout yet
}


void set_next_timeout(unsigned long *timer, unsigned long baseclock,  unsigned long increment){
  *timer = baseclock + increment;      // timeout for next event
  if (!*timer) *timer = 1;          // Add 1ms to timeout to avoid timeout at 0

  // if the new timeout is within 1 minute of wrapping around, add another minute to insure it
  // wraps around as it is easy to miss the timeout check near the clock wrap around point
  if (*timer > ((unsigned long)0xffffffff - (unsigned long)minuteM)) *timer += minuteM;

  return;
}

void show_step(byte step){
  if (debug_step){
    DebugPrintFO(f_step);
    Serial.println(step);
  }
}

// One time setups
void setup(){
  int i;

  Serial.begin(9600);

  pinMode(LED, OUTPUT);
  pinMode(D0,INPUT_PULLDOWN);   // Normally in LOW state if nothing connected
  pinMode(D1,INPUT_PULLDOWN);
  pinMode(D2,INPUT_PULLDOWN);

  for (i=DoorLft; i<=DoorRgt; i++) {
    Door[i] = CLOSED;            // initial door status before first reading of switched = closed
    last[i] = CLOSED;            // For switch debounce logic
    open_clock[i] = 0;
    clock_running[i] = FALSE;
  }


  if (debug) {
    // print noticeable startup message
    delay(5000);
    DebugPrintF(f_gpl1);	// Display GNU General Public License information
    DebugPrintF(f_gpl2);
    DebugPrintF(f_gpl3);
    for (i=0; i<3; i++) DebugPrintF(f_startup_msg);  // Serial.print("\nBegin garage door switch test\n");
  }

  startup = TRUE;

  alert_state = FALSE;
  time_to_alert = FALSE;

  secondM = 1000L;
  minuteM = 60L * secondM;
  hourM = 60L * minuteM;
  dayM = 24L * hourM;
  if (debug & debugTimers) {
    timeout = 30 * secondM;
  }
  else timeout = 5 * minuteM; // how long to wait before sending out an alarm

  reqTimeUpdate = millis() + (30 * secondM);  // when to update clock - 30 secsafter startup

  Time.zone(-4);      // Set to standard Eastern USA time - will adjust for DST later

  ledState = HIGH;
  clk_set = FALSE;
/*
  // Set up to use either of two WiFi networks - Commented out until fix to Particle firmare
  // as WiFi.setCredentials does not set proper credentials if the network is not accessible
  // when the function is called. Ends up getting the Photon hung in not being able to reach
  // the network. It tries to connect to the Particle Cloud before the app starts and therefore
  // the app never runs.

  Serial.print("WiFi credentials ");
  if (WiFi.clearCredentials()) Serial.println("cleared");
  else Serial.println("not cleared - error!");

  WiFi.setCredentials("SSID_of_network_A","Password-A",WPA2);	// None, WEP or WPA2
  WiFi.setCredentials("SSID_of_network_B","Password-B",WPA2);

  Serial.print("WiFi credentials ");
  if (WiFi.hasCredentials()) Serial.println("now set");
  else Serial.println("not set - error!");

  Serial.println("Set up to connect to either: Network_A or Network_B");
*/

  Serial.print("\nConnected to: ");
  Serial.print(WiFi.SSID());
  Serial.print(", Power: ");
  Serial.print(WiFi.RSSI());
  Serial.print(", IP: ");
  Serial.print(WiFi.localIP());
  Serial.print(", Gateway: ");
  Serial.println(WiFi.gatewayIP());
}

// Processing loop
void loop(){
  int i, j;
  static boolean anyDoorOpen = FALSE;
  static unsigned long print_time, last_clk_time;
  static unsigned long reminderTimer;

  show_step(0);

  proc_time = millis();

  readSwitch();            // Read and debounce all switches and save current debounced switch status when ready

  if (startup) {
    // One time startup code
    digitalWrite(LED, ledState);    // Turn On LED on board to show we are running
    ledTimer = proc_time + 1000;          // 1 second blink timer

    getTime();                      // Start time of day clock
    if (debug) {
        DebugPrintF (f_sl);        // Serial.print("\nStartup loop()\n");
        Serial.println((char *)"");
    }
    for (i = 0; i < 50; i++){
      delay(10);
      proc_time = millis();
      readSwitch();                       // Read door switches for first half second
    }
    send_tweet(startup);
    startup = FALSE;
  }

  if(!clk_set) getTime();       // If Real Time Clock did not start, try again

  show_step(1);

  for (i = DoorLft; i <= DoorRgt; i++) {
    // Start 60 minute clock (timeout period) for each door that has just opened
    //Chelsie modified this because theres only one garage door
    if ( (alert_state == FALSE) && (clock_running[i] == FALSE) && (Door[i] == OPEN) ){
      set_next_timeout(&open_clock[i], proc_time, timeout);  // Start 60 minute clock
      clock_running[i] = TRUE;
      if (debug) {
        DebugPrintFO(f_dosc);        // Serial.print ("\nDoor opened - start clock ");
        Serial.print (i);
        DebugPrintFO(f_at);          // " at "
        Serial.println (proc_time);
      }
    }

    show_step(2);

    // Reset clock associated with any door that closes within the timeout period
    if ( (alert_state == FALSE) && (clock_running[i]) && (Door[i] == CLOSED) ){
      open_clock[i] = 0;        // Stop clock
      clock_running[i] = FALSE;
      if (debug) {
        DebugPrintFO(f_dcbasc);   // Serial.print("\nDoor closed before alert - stop clock ");
        Serial.println(i);
      }
    }
    show_step(3);

    // Check if it's time to alert
    // Have not already sent an alert, a door is open, check if open beyond timeout period
    if ( (alert_state == FALSE) && clock_running[i] && check_for_timeout(open_clock[i],proc_time) ) {
        time_to_alert = TRUE;      // at least one door open too long
        if (debug){
          DebugPrintFO(f_mtasdtd);  // Serial.print("\nMove to alert state due to door ");
          Serial.print(i);
          DebugPrintFO(f_at);      // Serial.print(" at ");
          Serial.println(millis());
        }
    }
  }

  show_step(4);

  // Send Tweet if time to alert then move into alert state
  if ( (alert_state == FALSE) && time_to_alert) {
    if (debug) {
        DebugPrintF(f_odot);  // Serial.print("\n--> One door open tweet\n");
        Serial.println((char *)"");
    }
    alert_state = TRUE;              // indicate we've alerted to at least one door open
    time_to_alert = FALSE;
    send_tweet(FALSE);
    // Once in alert state, we do not start any more door open timers until all doors are closed
    // and a new alert is sent to indicate this

    // Set ReminderTimer to send status Tweets every hour if at least one door remains open
    // Debug - Reminder every 5 minutes, Normal - Reminder every hour
    if (debug & debugTimers) set_next_timeout(&reminderTimer, proc_time, (unsigned long)(debugReminder * minuteM));
    else set_next_timeout(&reminderTimer, proc_time, (unsigned long)(normalReminder * minuteM)); // Remind every 30 minutes

  }

  show_step(5);

  // Turn off alert state and send and all clear alert when all doors closed
  if ( (alert_state == TRUE) &&
       ( (Door[DoorLft] == CLOSED) && (Door[DoorMid] == CLOSED) && (Door[DoorRgt] == CLOSED) ) )
    {
      if (debug) DebugPrintF(f_adct);  // Serial.print("\n--> All doors closed tweet\n");

      alert_state = FALSE;

      send_tweet(FALSE);                // Send all clear tweet
      for (j = 0; j < 3; j++) {
        open_clock[j] = 0;        // clear door open timers
        clock_running[j] = FALSE;
      }
      reminderTimer = 0;  // No more reminders necessary
    }

  show_step(6);

  if (reminderTimer && check_for_timeout(reminderTimer,proc_time)){
    // Reminder has timed out. Time to send status again
    // First set another reminder period
    if (debug & debugTimers) {
      set_next_timeout(&reminderTimer, proc_time, (unsigned long)(debugReminder * minuteM));
      DebugPrintF(f_reminder);
      Serial.println((char *)"");
    }
    else set_next_timeout(&reminderTimer, proc_time, (unsigned long)(normalReminder * minuteM));
    send_tweet(FALSE);
  }

  show_step(7);

  // Update the time of day once each hour
  if (reqTimeUpdate && check_for_timeout(reqTimeUpdate,proc_time)){ // time to update clock from Internet
    getTime();
    if (debug & debugTimers)
         set_next_timeout(&reqTimeUpdate, proc_time, (unsigned long)(debugClkUpdate * minuteM));  // request time again in 5 minute
    else set_next_timeout(&reqTimeUpdate, proc_time, (unsigned long)(normalClkUpdate * minuteM));  // update clock once per hour
  }

  show_step(8);

  // Maintain current time of day once it is read off the Internet
  // proc_time could be incorrect because the application hung up for several minutes
  // resetting the WiFi hardware. But the following loop will account for all
  // the time that has passed and correctly update the time of day clock.
  unsigned long last_timeout;
  if (clk_set && check_for_timeout(clk_time,proc_time)){  // 1 or more seconds have passed since last clock update
    while (clk_time <= proc_time){
      last_clk_time = clk_time;
      cur_second += 1;
      if (cur_second >= 60) {cur_second = 0; cur_minute += 1;}
      if (cur_minute >= 60) {cur_minute = 0; cur_hour += 1;}
      if (cur_hour >= 24) {cur_minute = 0; cur_hour = 0;}
      set_next_timeout(&clk_time, clk_time, (unsigned long)1000L);          // add the 1 second just accounted for - loop until clock is adjusted
      // Check if last clk_time was a large value but the new clk_time is small
      if ((last_clk_time & 0x80000000) && (clk_time && 0xC0000000 == 0)){
         // clock is about to wrap around. Stop updating the time of
         // day until the wrap around which will happen within 1 minute
         // The clock will not be update again until the millis() wrap around
         // It will then incorrectly update the HH:MM:SS because it will have
         // missed about 1 minute of updates. But the next call to get_time()
         // to read the clock from the cloud, will correct the time of day.
         // The clock will be off by 1 minute for 1 hour or less (until the
         // time of day update).
         break;
      }
    }
  }
  show_step(9);

  // ---- LED Light Patterns will reflect the current state of processing

  // Blink Blue LED in various patterns to indicate status
  // 4 sec on / 4 sec off - idle mode
  // 1 sec on / 1 sec off - door is open
  // 4x/ sec - door is open and alert tweet sent - waiting for it to close to send another alert
  if (check_for_timeout(ledTimer,proc_time)){
    ledState = (ledState == HIGH)? LOW : HIGH;    // change state of the Yellow LED
    digitalWrite(LED,ledState);
    // Determine when next change of state is to occur
    if ((Door[DoorLft] == OPEN) || (Door[DoorMid] == OPEN) || (Door[DoorRgt] == OPEN)){
      // At least one door is open
      if (alert_state) set_next_timeout(&ledTimer, millis(), (unsigned long)125L);      // Blink 4x a second in alert state waiting for door to close
      else set_next_timeout(&ledTimer, millis(), (unsigned long)1000L);  // Blink once per second while door open for short time period
    }
    else {
      anyDoorOpen = FALSE;           // All doors were just closed
      set_next_timeout(&ledTimer, millis(), (unsigned long)4000L);    // Blink slowly to show program is running
    }
  }
  show_step(10);

  if ((anyDoorOpen == FALSE) && ((Door[DoorLft] == OPEN) || (Door[DoorMid] == OPEN) || (Door[DoorRgt] == OPEN))) {
    anyDoorOpen = TRUE;
    ledTimer = 0;
    ledState = LOW;          // immediately display open door state
  }
  if ((anyDoorOpen == TRUE) && ((Door[DoorLft] == CLOSED) && (Door[DoorMid] == CLOSED) && (Door[DoorRgt] == CLOSED))) {
    anyDoorOpen = FALSE;
    ledTimer = 0;
    ledState = LOW;          // immediately display open door state
  }
  mainCount++;

  show_step(11);

  if (debug){
    // insure proc_time is up to date. Could be wrong if we got hung up in the TCP
    // stack trying to reset the WiFi hardware. That could hang for minutes.
    proc_time = millis();
    if (check_for_timeout(print_time,proc_time)){
      set_next_timeout(&print_time, proc_time, (unsigned long)10000L);
      DebugPrintFO(f_debug_clock);   // Clock is at ##:##:##
      if (clk_set){
        if(cur_hour <= 9) Serial.print("0");
        Serial.print(int(cur_hour));
        Serial.print(":");
        if(cur_minute <= 9) Serial.print("0");
        Serial.print(int(cur_minute));
        Serial.print(":");
        if(cur_second <=9) Serial.print("0");
        Serial.print(int(cur_second));

        char bfr[90], fmt[68];

        strcpy_P(fmt,fmt3);
        sprintf(bfr,fmt,mainCount,millis());
        Serial.println(bfr);
      }
      else Serial.println((char *)"");
    }
  }

  show_step(12);
  // Wait 10ms before next loop
  delay(10);

  show_step(13);
}

// Check for Daylight Savings Time
bool IsDST(int dayOfMonth, int month, int dayOfWeek)
{
  if (month < 3 || month > 11)
  {
    return false;
  }
  if (month > 3 && month < 11)
  {
    return true;
  }
  int previousSunday = dayOfMonth - (dayOfWeek - 1);
  if (month == 3)
  {
    return previousSunday >= 8;
  }
  return previousSunday <= 0;
}
Click to Expand
0

Setting up IFTTT:  This program is excellent and I highly recommend it. I’ve used the service for years and was thrilled to see that I could make an applet using the photon. The flexibility is amazing and it is exciting to be able to make quick changes with little technical overhead. For this process, I set up an event [Particle.publish(textMsg)] which triggered the IFTTT applet to send a message when ran.

0
Setting up the Trigger in IFTTT
Iot ifttt trigger
0

Setting up the Garage and Magnet: 

0
0

SUCCESS!

0

Challenge:  

1) I didn’t realize that I wouldn’t get wifi in my garage, which created an issue. Luckily, we have a router extender that I was able to move into the backyard. This allowed me to prototype my idea. This challenged how I consider mobility for connected devices; the connection is only as good as their network.

2) My garage door was magnetic!  This created a problem with my reed switch.  Due to this issue, I never was able to create a working prototype.  I had to hold a magnet near my reed switch in vitro to complete my proof of concept. 

0
  • Reflection: This project was much more difficult than I’d anticipated.  I learned the value of breaking functionality and steps into small parts. Not only does this make the process far less overwhelming, it allows for much easier and methodical troubleshooting. I did not get to the point that I would have liked with this project- I was able to fire the trigger by playing around with the magnet and my garage, but I don’t have a homehack that could work reliably under normal conditions.   There were many steps of the process where I felt quite unsure of how to proceed. I have a lot more confidence now that I have tried this project, but I realize how much I need to learn to be able to prototype a reliable device.   I learned to stop asking, "is this right?" and start asking, "is this functional"?  I also am an absolute beginner with code and have so much to learn to be proficient.  I could not have done this project without relying on solid code libraries and the help of other students. 

x
Share this Project

Courses

49713 Designing for the Internet of Things

· 25 members

A hands-on introductory course exploring the Internet of Things and connected product experiences.


Focused on
Tools
About

Our garage door stays a few feet open about half the time. My poor dad usually runs outside in the cold before bed to make sure it is shut. I'd like him to receive a text if the garage door is open for longer than 60 minutes so that he will know to go close it.

Created

January 25th, 2018