IoT Alarm System with pushbutton for door, webpage for viewing status, and keypad for disabling alarm.

Dependencies:   PinDetect mbed wave_player FATFileSystem

Fork of ESP8266-WEB-Mbed-LPC1768-Controller by jim hamblen

main.cpp

Committer:
gholden3
Date:
2016-03-17
Revision:
5:28e4c7315a20
Parent:
4:40dd020463ea

File content as of revision 5:28e4c7315a20:

// ECE410 Lab 4 Alarm System Holden&Wasserman
/*
The following describes an Mbed IoT Alarm system. A push button is pressed to signify 
an “open door”.  The user accesses a web page that displays the statuses of the Mbed Alarm System. 
The webpage displays the status of the door, as well as the time, and the background indicates the
alarm state. Once the door is opened, the user has a time limit to turn off the alarm or it will sound. 
 Once the correct code is keyed in, the alarm will shut off. This status is also displayed on the webpage. 
*/
//Webserver code based off: ESP8266-WEB-Mbed-LPC1768-Controller

//Further work: get rtos working to play a sound on the speaker for the alarm. Add more detailed states that
//allow the user to key in the code before the alarm sounds. Allow user to turn off alarm from webpage.

#include "mbed.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#define twilight_WAVFILE        "/sd/wavfiles/twilight_zone_x.wav"
#include "PinDetect.h"
#include <mbed.h>
#include <mpr121.h>
#include <string>

// Standard Mbed LED definitions
DigitalOut  led1(LED1);      
DigitalOut  led2(LED2);   
DigitalOut  led3(LED3);     
DigitalOut  led4(LED4);

//wifi chip
Serial pc(USBTX, USBRX);
Serial esp(p28, p27); // tx, rx
DigitalOut  reset(p25);

//timer for alarm countdown
Ticker alarmTimer;
float numSecondsRemaining = 30;

// interrupt pin for keypad
InterruptIn interrupt(p26);
// Setup the i2c bus on pins 9 and 10 keypad
I2C i2c(p9, p10);
// Setup the Mpr121: touch sensor
// constructor(i2c object, i2c address of the mpr121)
Mpr121 mpr121(&i2c, Mpr121::ADD_VSS);

//door is triggered by a pushbutton
// no external PullUp resistor needed
// Pushbutton from P12 to GND.
// A pb falling edge (hit) generates an interrupt and activates the interrupt routine
PinDetect doorPB(p12);
//door status variable
enum doorStatus { OPEN, CLOSED };
doorStatus doorStatus = CLOSED;

//alarm status variable
enum alarmStatus { SOUNDING, OFF };
alarmStatus alarm1Status = OFF;

//we need to get rtos working for this :( 
PwmOut speaker(p21);

//timeout timer for getreply()
Timer t1;
//timeout timer for getcheck()
Timer t2;

//struct used for RTC
struct tm t;

int bufflen, DataRX, count, getcount, replycount, servreq, timeout;
int bufl, ipdLen, linkID, weberror, webcounter;
char pwd[] = "1234";
char enterpwd[100] = "";
char secondsRemaining[10];
char webcount[8];
char lasthit[30];
char timebuf[30];
char type[16];
char type1[16];
char channel[2];
char cmdbuff[32];
char replybuff[1024];
char webdata[1024]; // This may need to be bigger depending on WEB browser used
char webbuff[4096];     // Currently using 1986 characters, Increase this if more web page data added

void SendCMD(),getreply(),ReadWebData(),startserver(),sendpage(),SendWEB(),sendcheck();
void gettime(),setRTC();

void timerExpired() //called every second once door is opened
{
    numSecondsRemaining--;
    if (numSecondsRemaining == 0) {   //your time to disable the alarm is up. sound alarm!
        alarmTimer.detach();
        alarm1Status = SOUNDING;
    }
    sprintf(secondsRemaining, "%2.0f",numSecondsRemaining);
}

void pb_hit_callback (void) //door
{
    pc.printf("pushbutton hit!\r\n");
    pc.printf("door is %s \r\n", doorStatus);
    if (doorStatus == OPEN)
        doorStatus = CLOSED;
    else if (doorStatus == CLOSED) {
        doorStatus = OPEN;
        //start counting every second
        alarmTimer.attach(&timerExpired, 1.0); 
    }
    wait(.1);
}


// manual set RTC values
int minute      =00;    // 0-59
int hour        =12;    // 2-23
int dayofmonth  =26;    // 1-31
int month       =8;     // 1-12
int year        =15;    // last 2 digits

int port        =80;  // set server port
int SERVtimeout =5;    // set server timeout in seconds in case link breaks.

// Serial Interrupt read ESP data
void callback()
{
    while (esp.readable()) {
        webbuff[count] = esp.getc();
        count++;
    }
    if(strlen(webbuff)>bufflen) {
        DataRX=1;
    }
}

// Key hit/release interrupt routine
void fallInterrupt()
{
    int key_code=0;
    int i=0;
    int value=mpr121.read(0x00);
    value +=mpr121.read(0x01)<<8;
    // puts key number out to LEDs for demo
    for (i=0; i<12; i++) {
        if (((value>>i)&0x01)==1) key_code=i+1;
    }
    if (key_code == 0)
    return;
    else key_code--;
    led4=key_code & 0x01;
    led3=(key_code>>1) & 0x01;
    led2=(key_code>>2) & 0x01;
    led1=(key_code>>3) & 0x01;
    char result[10];
    //convert float to cstring
    sprintf(result,"%d",key_code);
    pc.printf("result: %s\r\n", result);
    //concatenate to the end
    strcat(enterpwd,result);
    pc.printf("enterpwd: %s\r\n",enterpwd);
    //only compares the first four values entered. future work: make a clear/reset option
    if((enterpwd[0]==pwd[0]) && (enterpwd[1]==pwd[1]) && (enterpwd[2]==pwd[2]) && (enterpwd[3]==pwd[3])){
        alarm1Status=OFF;
        alarmTimer.detach();
        pc.printf("correct!\r\n");
    }
}


int main()
{
    //attach interrupt for keypad
    interrupt.fall(&fallInterrupt);
    interrupt.mode(PullUp);

    doorPB.mode(PullUp);
    // Delay for initial pullup to take effect
    wait(.01);
    // Attach the address of the interrupt handler routine for pushbutton
    doorPB.attach_deasserted(&pb_hit_callback);
    doorPB.setSampleFrequency();
    reset=0;
    pc.baud(115200);
    pc.printf("\f\n\r------------ ESP8266 Hardware Reset --------------\n\r");
    wait(0.5);
    reset=1;
    timeout=6000;
    getcount=500;
    getreply();
    // ESP8266 baudrate. Maximum on KLxx' is 115200, 230400 works on K20 and K22F
    esp.baud(115200);  
    if (time(NULL) < 1420070400) {
        setRTC();
    }
    startserver();
    while(1) {
        if(DataRX==1) {
            ReadWebData();
            if (servreq == 1 && weberror == 0) {
                sendpage();
            }
            esp.attach(&callback);
            pc.printf(" IPD Data:\r\n\n Link ID = %d,\r\n IPD Header Length = %d \r\n IPD Type = %s\r\n", linkID, ipdLen, type);
            pc.printf("\n\n  HTTP Packet: \n\n%s\n", webdata);
            pc.printf("  Web Characters sent : %d\n\n", bufl);
            pc.printf("  -------------------------------------\n\n");
            strcpy(lasthit, timebuf);
            servreq=0;
        }
    }
}


// Static WEB page
void sendpage()
{
    gettime();
    // WEB page data
    strcpy(webbuff, "<!DOCTYPE html>");
    strcat(webbuff, "<html><head><title>ESP8266 Mbed LPC1768</title></head>");
    if(alarm1Status == SOUNDING) {
        strcat(webbuff, "<body style=\"background-color:#FF0000;\" >");
    } else if (alarm1Status==OFF) {
        strcat(webbuff, "<body style=\" background-color:#32CD32;\" >");
    }
    strcat(webbuff, "<div style=\"text-align:center; background-color:#F4F4F4; color:#00AEDB;\"><h1>Mbed Alarm System</h1>");
    strcat(webbuff, "Hit Count - ");
    strcat(webbuff, webcount);
    strcat(webbuff, "<br>Last Hit - ");
    strcat(webbuff, lasthit);
    strcat(webbuff, "</div><br /><hr>");
    strcat(webbuff, "<h3>Mbed RTC Time -&nbsp&nbsp");
    strcat(webbuff, timebuf);
    strcat(webbuff, "</h3>\r\n");
    strcat(webbuff, "<p><form method=\"POST\"><strong> Door Status:&nbsp&nbsp<input type=\"text\" size=6 value=\"");
    if(doorStatus == CLOSED)
        strcat(webbuff, "CLOSED");
    else if(doorStatus == OPEN)
        strcat(webbuff, "OPEN");
    strcat(webbuff, "\"> </sup> <form method=\"POST\"> <strong> &nbsp&nbspSeconds Remaining:&nbsp&nbsp<input type=\"text\" size=4 value=\"");
    strcat(webbuff, secondsRemaining);
    strcat(webbuff, "\"> </sup>");
    strcat(webbuff, "</strong><p><input type=\"submit\" value=\"send-refresh\" style=\"background: #3498db;");
    strcat(webbuff, "background-image:-webkit-linear-gradient(top, #3498db, #2980b9);");
    strcat(webbuff, "background-image:linear-gradient(to bottom, #3498db, #2980b9);");
    strcat(webbuff, "-webkit-border-radius:12;border-radius: 12px;font-family: Arial;color:#ffffff;font-size:20px;padding:");
    strcat(webbuff, "10px 20px 10px 20px; border:solid #103c57 3px;text-decoration: none;");
    strcat(webbuff, "background: #3cb0fd;");
    strcat(webbuff, "background-image:-webkit-linear-gradient(top,#3cb0fd,#1a5f8a);");
    strcat(webbuff, "background-image:linear-gradient(to bottom,#3cb0fd,#1a5f8a);");
    strcat(webbuff, "text-decoration:none;\"></form></span>");
    strcat(webbuff, "<p/><h2>How to use:</h2><ul>");
    strcat(webbuff, "<li>Background color reflects alarm state. Green is good red is bad. </li>");
    strcat(webbuff, "<li>You can refresh the page with the browser button or send/refresh.</li>");
    strcat(webbuff, "<li>Seconds remaining tells you how much time before alarm sounds</li>");
    strcat(webbuff, "</ul>");
    strcat(webbuff, "</body></html>");
// end of WEB page data
    bufl = strlen(webbuff); // get total page buffer length
    sprintf(cmdbuff,"AT+CIPSEND=%d,%d\r\n", linkID, bufl); // send IPD link channel and buffer character length.
    timeout=200;
    getcount=7;
    SendCMD();
    getreply();
    SendWEB();  // send web page
    memset(webbuff, '\0', sizeof(webbuff));
    sendcheck();
}

//  wait for ESP "SEND OK" reply, then close IP to load web page
void sendcheck()
{
    weberror=1;
    timeout=500;
    getcount=24;
    t2.reset();
    t2.start();
    while(weberror==1 && t2.read() <5) {
        getreply();
        if (strstr(replybuff, "SEND OK") != NULL) {
            weberror=0;   // wait for valid SEND OK
        }
    }
    if(weberror==1) { // restart connection
        strcpy(cmdbuff, "AT+CIPMUX=1\r\n");
        timeout=500;
        getcount=10;
        SendCMD();
        getreply();
        pc.printf(replybuff);
        sprintf(cmdbuff,"AT+CIPSERVER=1,%d\r\n", port);
        timeout=500;
        getcount=10;
        SendCMD();
        getreply();
        pc.printf(replybuff);
    } else {
        sprintf(cmdbuff, "AT+CIPCLOSE=%s\r\n",channel); // close current connection
        SendCMD();
        getreply();
        pc.printf(replybuff);
    }
    t2.reset();
}

// Reads and processes GET and POST web data
void ReadWebData()
{
    wait_ms(200);
    esp.attach(NULL);
    count=0;
    DataRX=0;
    weberror=0;
    memset(webdata, '\0', sizeof(webdata));
    int x = strcspn (webbuff,"+");
    if(x) {
        strcpy(webdata, webbuff + x);
        weberror=0;
        int numMatched = sscanf(webdata,"+IPD,%d,%d:%s", &linkID, &ipdLen, type);
        sprintf(channel, "%d",linkID);
        if (strstr(webdata, "GET") != NULL) {
            servreq=1;
        }
        if (strstr(webdata, "POST") != NULL) {
            servreq=1;
        }
        webcounter++;
        sprintf(webcount, "%d",webcounter);
    } else {
        memset(webbuff, '\0', sizeof(webbuff));
        esp.attach(&callback);
        weberror=1;
    }
}
// Starts and restarts webserver if errors detected.
void startserver()
{
    gettime();
    pc.printf("\n\n RTC time   %s\r\n\n",timebuf);
    pc.printf("++++++++++ Resetting ESP ++++++++++\r\n");
    strcpy(cmdbuff,"AT+RST\r\n");
    timeout=8000;
    getcount=1000;
    SendCMD();
    getreply();
    pc.printf(replybuff);
    pc.printf("%d",count);
    if (strstr(replybuff, "OK") != NULL) {
        pc.printf("\n++++++++++ Starting Server ++++++++++\r\n");
        strcpy(cmdbuff, "AT+CIPMUX=1\r\n");  // set multiple connections.
        timeout=500;
        getcount=20;
        SendCMD();
        getreply();
        pc.printf(replybuff);
        sprintf(cmdbuff,"AT+CIPSERVER=1,%d\r\n", port);
        timeout=500;
        getcount=20;
        SendCMD();
        getreply();
        pc.printf(replybuff);
        wait(1);
        sprintf(cmdbuff,"AT+CIPSTO=%d\r\n",SERVtimeout);
        timeout=500;
        getcount=50;
        SendCMD();
        getreply();
        pc.printf(replybuff);
        wait(5);
        pc.printf("\n Getting Server IP \r\n");
        strcpy(cmdbuff, "AT+CIFSR\r\n");
        timeout=2500;
        getcount=200;
        while(weberror==0) {
            SendCMD();
            getreply();
            if (strstr(replybuff, "0.0.0.0") == NULL) {
                weberror=1;   // wait for valid IP
            }
        }
        pc.printf("\n Enter WEB address (IP) found below in your browser \r\n\n");
        pc.printf("\n The MAC address is also shown below,if it is needed \r\n\n");
        replybuff[strlen(replybuff)-1] = '\0';
        //char* IP = replybuff + 5;
        sprintf(webdata,"%s", replybuff);
        pc.printf(webdata);
        //  led2=1;
        bufflen=200;
        count=0;
        pc.printf("\n\n++++++++++ Ready ++++++++++\r\n\n");
        esp.attach(&callback);
    } else {
        pc.printf("\n++++++++++ ESP8266 error, check power/connections ++++++++++\r\n");
        while(1) {}
    }
    t2.reset();
    t2.start();
}

// ESP Command data send
void SendCMD()
{
    esp.printf("%s", cmdbuff);
}

// Large WEB buffer data send
void SendWEB()
{
    int i=0;
    if(esp.writeable()) {
        while(webbuff[i]!='\0') {
            esp.putc(webbuff[i]);
            i++;
        }
    }
}

// Get Command and ESP status replies
void getreply()
{
    memset(replybuff, '\0', sizeof(replybuff));
    t1.reset();
    t1.start();
    replycount=0;
    while(t1.read_ms()< timeout && replycount < getcount) {
        if(esp.readable()) {
            replybuff[replycount] = esp.getc();
            replycount++;
        }
    }
    t1.stop();
}

// Get RTC time
void gettime()
{
    time_t seconds = time(NULL);
    strftime(timebuf,50,"%H:%M:%S %a %d %b %y", localtime(&seconds));
}

void setRTC()
{
    t.tm_sec = (0);             // 0-59
    t.tm_min = (minute);        // 0-59
    t.tm_hour = (hour);         // 0-23
    t.tm_mday = (dayofmonth);   // 1-31
    t.tm_mon = (month-1);       // 0-11  "0" = Jan, -1 added for Mbed RCT clock format
    t.tm_year = ((year)+100);   // year since 1900,  current DCF year + 100 + 1900 = correct year
    set_time(mktime(&t));       // set RTC clock
}