Author Topic: THE BUTTON  (Read 2653 times)

0 Members and 1 Guest are viewing this topic.

Offline Jeremy Briddle

  • Administrator
  • Sr. Member
  • *****
  • Posts: 321
    • View Profile
    • My online portfolio
THE BUTTON
« on: November 13, 2012, 03:58:29 pm »
I have put the finishing touches on our open alert system. We're calling it "THE BUTTON". Version 1.0 is now up and working at Xerocraft. I will be posting the source code and schematics soon. I already have some modifications to make for version 1.1:

  • Double-click button to close.
  • Triple-click button to open.
« Last Edit: November 13, 2012, 07:53:53 pm by Jeremy Briddle »

Offline Jeremy Briddle

  • Administrator
  • Sr. Member
  • *****
  • Posts: 321
    • View Profile
    • My online portfolio
Re: THE BUTTON
« Reply #1 on: November 13, 2012, 07:51:01 pm »
Source code for THE BUTTON v1.0

Code: [Select]
// THE BUTTON 1.0 by Jeremy Briddle

#include <SPI.h> // needed in Arduino 0019 or later
#include <Ethernet.h>
#include <Twitter.h>
#include <EthernetUdp.h>
#include <LiquidCrystal.h>
//#include <stdlib.h>

// The includion of EthernetDNS is not needed in Arduino IDE 1.0 or later.
// Please uncomment below in Arduino IDE 0022 or earlier.
//#include <EthernetDNS.h>

#define LED_red 6    // the pin for the red LED
#define LED_grn 7    // the pin for the green LED
#define BUTTON 8     // input pin of the pushbutton
#define LCD_bklt 1  // the pin for the LCD backlight

int btn_val = 0;     // stores the state of the input pin

int shop_status = 0;  // 0 = We're currently CLOSED
                      // 1 = We're currently OPEN
                     
int status = 0;   

int gotTime = 0;

LiquidCrystal lcd(10, 9, 5, 4, 3, 2);

// Ethernet Shield Settings
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// If you don't specify the IP address, DHCP is used(only in Arduino 1.0 or later).
// byte ip[] = { 192, 168, 2, 250 };

unsigned int localPort = 8888;      // local port to listen for UDP packets

IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server

// IPAddress timeServer(0, 0, 0, 0);  // Test case for no timestamp

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
 EthernetUDP Udp;

// Your Token to Tweet (get it from http://arduino-tweet.appspot.com/)
Twitter twitter("[REDACTED]"); // @Xerocraft

  char tweet[141];  // The tweet to send
  char timestamp[8];

// Messages to post
  char msgOpen[] = "Xerocraft is open";
  char msgClosed[] = "Xerocraft is closed";

// ************************************************************

void setup()
{
  pinMode(LED_red, OUTPUT);    // tell Arduino LED_red is an output
  pinMode(LED_grn, OUTPUT);    // tell Arduino LED_grn is an output
  pinMode(LCD_bklt, OUTPUT);
  pinMode(BUTTON, INPUT);      // tell Arduino BUTTON is an input
 
//  Serial.begin(9600);
  lcd.clear();
  lcd.begin(20, 4);
  lcd.clear();
  lcd.print("Standby.");                                  // LCD: FIRST ROW (0,0)
 
  if (Ethernet.begin(mac) == 0) {
    delay(100);
    lcd.clear();
    delay(100);
    lcd.print("Failed DHCP config.");
    // no point in carrying on, so do nothing forevermore:
    for(;;) {                                              // Change this
      lcd.setCursor(0,2);
      lcd.print("RESET and try again.");                // LCD: SECOND ROW (0,2)
      delay(60000);
    } 
  }
 
  Udp.begin(localPort);
 
  lcd.clear();
  lcd.print("Ready.");                                    // LCD: FIRST ROW (0,0)
}

// ************************************************************

void loop()
{
  // read input value and store it fresh
  btn_val = digitalRead(BUTTON);
 
  // check if button has been pressed
  if ((btn_val == HIGH)) {
//    Serial.println("Button has been pressed!");
    // we were closed but now we're OPENING
    if (shop_status == 0) {             
      lcd.clear();
      digitalWrite(LCD_bklt, HIGH);
      lcd.print("Xerocraft is OPENING");                     // LCD: FIRST ROW (0,0)
//      Serial.println("We're closed. Time to OPEN!");
//      Serial.println("");
       strcpy(tweet, msgOpen);
    // else shop_status == 1, which means we were open but now we're CLOSING   
    } else {                           
      lcd.clear();
      digitalWrite(LCD_bklt, HIGH);
      lcd.print("Xerocraft is CLOSING");                  // LCD: FIRST ROW (0,0)
//       Serial.println("We're open. Time to CLOSE!");
//       Serial.println("");   
      strcpy(tweet, msgClosed);
    } 
       if (getTime()) {
         gotTime = 1;
       // == 1, then the timestamp was created successfully         
         strcat(tweet, " as of ");
         strcat(tweet, timestamp); 
       } else { 
       // ELSE returned 0, send tweet w/o timestamp
         gotTime = 0;
         lcd.setCursor(0,2);       
         lcd.print("Tweeting w/o timestp");                  // LCD: SECOND ROW (0,2)
//         Serial.println("Sending tweet w/o timestamp.");           // *WARNING*: Duplicate tweets will be rejected by Twitter!
       }
     
       strcat(tweet, ".");
       // == 1 then tweet sent successfully
       if(sendTweet(tweet)) {         
         shop_status = 1 - shop_status;
         if(gotTime) {
           lcd.setCursor(0,1);          // WAS (0,2)
           lcd.print("Tweet sent!");                   
           delay(1000);
         } else {
           lcd.setCursor(0,2);             
           lcd.print("Tweet sent!");                        // LCD: SECOND ROW (0,2) (or THIRD ROW (0,1) if tweet sent w/o timestamp)
           delay(1000);
         } 
//         lcd.print(tweet);
//         Serial.print("Tweet sent: ");
//         Serial.println(tweet);
//         Serial.println("");
         if (shop_status) {     
         // == 1, then we're now OPEN
         // Turn green LED on
         // Turn red LED off
           digitalWrite(LED_grn, HIGH);   
           digitalWrite(LED_red, LOW);   
           lcd.clear();
           if (gotTime) {
             lcd.print("OPEN as of ");                    // LCD: FIRST ROW (0,0)
             lcd.print(timestamp);
           } else {
           lcd.print("Xerocraft is OPEN");                 
           }
         } else {     
         // == 0, then we're now CLOSED
         // Turn green LED off           
         // Turn red LED on
           digitalWrite(LED_grn, LOW);   
           digitalWrite(LED_red, HIGH);   
           lcd.clear();
           if (gotTime) {
             lcd.print("CLOSED as of ");                  // LCD: FIRST ROW (0,0)
             lcd.print(timestamp);
           } else {
           lcd.print("Xerocraft is CLOSED");                 
           }
         }
       } else {
         lcd.setCursor(0,2);
         lcd.print("Tweet not sent.");                       // LCD: SECOND ROW
         lcd.setCursor(0,1);
         lcd.print("Try again in 1 min.");                   // LCD: THIRD ROW
//         Serial.println("FAILURE! Tweet not sent. Try again in 1 min.");
         // Flash red LED, return to previous LED state
         digitalWrite(LED_red, LOW);     
         digitalWrite(LED_grn, LOW);     
         for (int x = 0; x < 10; x++) {
           digitalWrite(LED_red, HIGH);
           delay(200);
           digitalWrite(LED_red, LOW);
           delay(200);
         }
         if (shop_status) {
         // == 1, then we're now OPEN           
           digitalWrite(LED_grn, HIGH);   
           digitalWrite(LED_red, LOW);
           lcd.setCursor(0,0);
           lcd.print("Xerocraft is OPEN");             // LCD: FIRST ROW (0,0)
         } else {
         // == 0, then we're now CLOSED 
           digitalWrite(LED_grn, LOW);   
           digitalWrite(LED_red, HIGH);
           lcd.setCursor(0,0);
           lcd.print("Xerocraft is CLOSED");                 // LCD: FIRST ROW (0,0)
         }
      }
    lcd.setCursor(0,2);
    lcd.print("Please wait 1 min."); 
    // one minute required by website 
    delay(5000);
    digitalWrite(LCD_bklt, LOW);
    delay(55000);
    lcd.setCursor(0,2);   
    lcd.print("Ready               ");         //THIRD???      // LCD: SECOND ROW (0,2). DON'T OVERWRITE STATUS ON FIRST ROW!
   
    for(int k = 0; k < 3; k++) {
      digitalWrite(LCD_bklt, HIGH);
      delay(300);
      digitalWrite(LCD_bklt, LOW);
      delay(300);
    } 

   
    // one-minute countdown timer
/*    for(int i = 60000; i > 0; i = i - 10000) {       
        Serial.print("Please wait ");
        Serial.print(i/10000);
        Serial.println("sec");
        delay(10000);
    } */
   
/*    for(int i = 60; i > 0; i--) {
     Serial.print(i);
     Serial.print(" ");
     delay(1000);
    }
    Serial.println("");
    Serial.println("Ready.");
    Serial.println(""); */
  } 
}

// ************************************************************

boolean getTime() {
 
  int hour = 0;
  int timezone = -7;   // AZ timezone is UTC-7.
 
  sendNTPpacket(timeServer); // send an NTP packet to a time server

  // wait to see if a reply is available
  delay(1000); 
  // We've received a packet, read the data from it
  if ( Udp.parsePacket() ) { 
    // read the packet into the buffer
    Udp.read(packetBuffer,NTP_PACKET_SIZE); 
    Udp.flush();
    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;             
    const unsigned long seventyYears = 2208988800UL;     
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;                               
    // print the hour (86400 equals secs per day).
    int hour24 = ((epoch % 86400L) / 3600) + timezone; 
   
    if (hour24 < 0)
      hour24 += 24;

    if (hour24 == 0)
        hour = 12;
    else if (hour24 <= 12)
        hour = hour24;
    else 
        hour = hour24 - 12;

    int minute = (epoch % 3600) / 60;
   
    // build the timestamp
    char hourStr [3];
    char minStr [3];
   
    itoa(hour, hourStr, 10);
    itoa(minute, minStr, 10);
   
    strcpy(timestamp, hourStr);   
   
    if (minute < 10)
      strcat(timestamp, ":0");
    else
      strcat(timestamp, ":");
     
    strcat(timestamp, minStr);
 
    if (hour24 < 12)
      strcat(timestamp, "am");
    else
      strcat(timestamp, "pm"); 
  } else {
//      Serial.println("Could not retrieve timestamp.");
      return 0;
  }
//  Serial.print("Current time: ");
//  Serial.println(timestamp);
  return 1;
}

// ************************************************************

boolean sendTweet(char* msg) {
//  Serial.println("Connecting ...");
  if (twitter.post(msg)) {
    // Specify &Serial to output received response to Serial.
    // If no output is required, you can just omit the argument, e.g.
    // status = twitter.wait();
    status = twitter.wait(&Serial);
//    Serial.println("");
    if (status == 200) {
//      Serial.println("Tweet POSTED!");
      return 1;
    } else {
      lcd.clear();
      lcd.print("Failure! Error ");                        // LCD: FIRST ROW (0,0)
      lcd.print(status);
//      Serial.print("Tweet FAILED! Error code: ");
//      Serial.println(status);
      return 0;
    }
  } else {
    lcd.clear();
    lcd.print("Connection failed.");                       // LCD: FIRST ROW (0,0)
//    Serial.println("Connection failed.");
    return 0;
  }
}

// ************************************************************

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
  Udp.begin(localPort);
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);

  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)

  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:    
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);   
  Udp.endPacket();
}

Offline Jeremy Briddle

  • Administrator
  • Sr. Member
  • *****
  • Posts: 321
    • View Profile
    • My online portfolio
Re: THE BUTTON
« Reply #2 on: November 14, 2012, 06:50:28 pm »
Schematic for THE BUTTON:


Offline Jeremy Briddle

  • Administrator
  • Sr. Member
  • *****
  • Posts: 321
    • View Profile
    • My online portfolio
Re: THE BUTTON
« Reply #3 on: November 17, 2012, 08:12:05 pm »











Offline Jeremy Briddle

  • Administrator
  • Sr. Member
  • *****
  • Posts: 321
    • View Profile
    • My online portfolio
Re: THE BUTTON
« Reply #4 on: November 21, 2012, 03:09:06 pm »
  • The website I used as an intermediary between the Arduino and Twitter's API is arduino-tweet.appspot.com.
  • I connected our Twitter profile to our Facebook page in Twitter's settings so that our tweets also show up as Facebook status updates.
  • Using IFTTT.com, I set every open and closed tweet to prompt an update of our Twitter profile picture to one that says "open" or "closed" at the bottom. Unfortunately, Twitter forced IFTTT to remove tweets as a trigger but since Twitter still offers an RSS feed (until March 2013) I was able to use RSS as a trigger instead.
  • To help keep track of when we're open and closed each month I also set IFTTT to post every open/closed tweet to our Google Calendar.
  • I could also set IFTTT to update our Facebook picture to one that says open or closed but I haven't done that yet.
  • To keep our Facebook page and Twitter feed clean, I delete open/closed tweets that are over 24 hours old.