Project 1 Self-powered Health & Security Monitoring System USB device

Dependencies:   C12832_lcd LM75B MMA7660 USBDevice mbed

main.cpp

Committer:
wane
Date:
2014-03-28
Revision:
0:88dc49222b35
Child:
1:930838234048

File content as of revision 0:88dc49222b35:

// Project: Self-powered USB Health and Security Monitoring Device (HSMD)

/* 241: Added temp alarm
   251: Added motion alarm, sensitivity level
                 Validated alarm status handling
   252: Added lock, passcode control, mode (T-Testing; O-Operational)                 
   261: Added non-blocking serial entry

TO-DO: 
   
   implement USB connection detection
   try record sound
   try gps
   add menu to LCD
   
*/

#include "mbed.h"
#include "C12832_lcd.h"
#include "LM75B.h"
#include "MMA7660.h"
#include "USBKeyboard.h"

// Configure device as USBKeyboard 
USBKeyboard dev;

#define UP 0x1
#define DOWN 0x2
#define LEFT 0x4
#define RIGHT 0x8
#define ON true
#define OFF false

// System configuration parameters
#define YEAR 2014
#define MONTH 3
#define DATE 26
#define HOUR 17
#define MIN 35
#define DEF_LOWER_TEMP 72               // Lower bound of monitoring temperature
#define DEF_UPPER_TEMP 89               // Upper bound of monitoring temperature
#define LOW_SEN 0.7                     // Low sensitivity
#define MID_SEN 0.5                     // Medium sensitivity
#define HIGH_SEN 0.3                    // High sensitivity
#define BUZZ_POWER 0.5                  // Buzz power level
#define BUZZ_SPERIOD 0.2                // Security alarm buzz period
#define BUZZ_TPERIOD 1                // Temperature alarm buzz period

// System I/O
C12832_LCD lcd;
PwmOut alarm(p21);
BusIn joy(p15,p12,p13,p16);
InterruptIn IRQJoyUp(p15);
InterruptIn IRQJoyDown(p12);
InterruptIn IRQJoyLeft(p13);
InterruptIn IRQJoyRight(p16);
//DigitalIn fire(p14);
InterruptIn IRQFire(p14);

Serial pc(USBTX, USBRX); // tx, rx
LM75B Ctmp(p28,p27);
AnalogIn pot1(p19);
AnalogIn pot2(p20);
MMA7660 MMA(p28, p27);
DigitalOut connectionLed(LED1);
DigitalOut lockLed(LED2);
//PwmOut Xaxis_p(LED2);
//BusOut leds(LED3,LED4);
//DigitalOut ledTAlarm(LED3);
//DigitalOut ledSAlarm(LED4);
PwmOut ledSAlarm(LED4);
PwmOut ledTAlarm(LED3);

// Gobal parameters
float setUpperTemp;          // Upper bound of temperature monitoring range
float setLowerTemp;          // Lower bound of temperature monitoring range
bool sAlarmOn;               // State of alarm
int statusAlarm;             // Status indicating whether health or/and security alarm is triggered
bool sHealthFeatureActive;          // State indicating temperature monitoring is activated
bool sSecurityFeatureActive;        // State indicating motion monitoring is activated
bool sLockActive;            // State indicating lock is active
char sState;                 // security setting state
char gState;                 // Global state
int buzzPattern; 
float buzzPeriod;
Ticker timer1;
Ticker timer3;
Ticker timer4;
Timer timer2;
Ticker ticAlarm;
bool first; 
int count;
bool activated;             // for debounce fire
time_t yet;
time_t now;
int alarmType;              // 0 - Health; 1 - Security
char senLevel;              // Sensitivity level of motion alarm
int mode;                   // Operation mode: 
                            // 0 = Test mode (for testing features)
                            // 1 = Lock mode (lock and unlock device)
int passcode;               // 4-digit passcode for lock and unlock               
bool sPCOK;                 // state indicating a valid passcode is existing

bool updateCurTempDisplay;
bool updateJoyDisplay;
bool tempOverride;
float lowerTempBase;        // For simulated lower bound temperature
float upperTempBase;        // For simulated upper bound temperature
float temp;

bool entryReady;            // For tracking if user input is comlete
unsigned char entryState;   // State tracking input entry
char inBuf[128];            // Input buffer 
char pos;                   // Gobal buffer index


float timeElapsed;
int tol;                        // Tolerance for hysteresis effect
time_t seconds;

float potBase1; 
float potBase2; 

#ifdef C
#define deg 'C'
#else 
#define deg 'F'
#endif 

typedef struct {
    bool active;
    float factor;
    unsigned char sym[3];
} SENTYPE;

const SENTYPE senParm[4] = { 
    { false, LOW_SEN, "  " },
    { true,  LOW_SEN, "S1" },                 // Low sensitivity
    { true,  MID_SEN, "S2" },                 // Mid sensitivity
    { true,  HIGH_SEN, "S3" },                 // High sensitivity
};

const char modeSym[2] = { 'T', 'O' }; 

/*
enum FUNCTION_KEY {
    KEY_SCROLL_LOCK = 141,    // Scroll lock 
    KEY_CAPS_LOCK,      // caps lock 
    KEY_NUM_LOCK,       // num lock 
};
*/

// Function prototype
void ISRFirePressed();
void ISRJoyUp();
void ISRJoyDown();
void ISRJoyLeft();
void ISRJoyRight();
void debounce();
void buzzOn(int pattern, float period);
void buzzOff();
void buzzTest();
void peepEntry();
void calibratePot();
bool EnterTemp();
bool SetPasscode();
bool UnlockPasscode();
void USBConnCheck();
void SetTime(int year, int month, int date, int hour, int min, int sec);
void TempMonitor();
void ReadTemp();
void MotionMonitor();
void DisplayLCD();
void AlarmCheck();
void FSM(void);


// ISR for fire button
void ISRFirePressed()
{
    now = time(NULL);
    if (!activated && now > yet) {
        
        tempOverride = !tempOverride;
        //EnterTemp();
        if (tempOverride)
            gState = 3;
        yet = now + 1;
        activated = true;
    }
        
}


/*    
    now = time(NULL);
    if (!activated && now > yet) {
        sAlarmOn = !sAlarmOn;
        yet = now + 1;
        activated = true;
        //first = true;
        count++;
        //printf("sAlarmOn: %d\n\r", sAlarmOn);
    }
*/


// ISR for joystick up direction
void ISRJoyUp()
{
    now = time(NULL);
    if (!activated && now > yet) {
        //if (mode == 1 && sPCOK == false)
        //    EnterPasscode();
        senLevel = (senLevel + 1) % 4;
        sSecurityFeatureActive = senParm[senLevel].active;
        updateJoyDisplay = true;
        yet = now + 1;
        activated = true;
    }
}

// ISR for joystick down direction
void ISRJoyDown()
{
    now = time(NULL);
    if (!activated && now > yet) {
        mode = (mode + 1) % 2;
        updateJoyDisplay = true;
        yet = now + 1;
        activated = true;
    }
}

// ISR for joystick left direction
void ISRJoyLeft()
{
    now = time(NULL);
    if (!activated && now > yet) {
        sHealthFeatureActive = !sHealthFeatureActive;
        updateJoyDisplay = true;
        yet = now + 1;
        activated = true;
    }
}

// ISR for joystick right direction
void ISRJoyRight()
{
    now = time(NULL);
    if (!activated && now > yet) {
        if (mode == 1)
        {
            if (!sLockActive)
                gState = 1; 
            else
                gState = 2; 
        }
        yet = now + 1;
        activated = true;
    }
}

// Debouncer function  
void debounce()
{
    if (activated)
        activated = false;
}
 
// Turn on buzz in different patterns
void buzzOn(int pattern, float period) 
{
    switch (pattern) {
        case 0:     // Temperature 
            alarm = BUZZ_POWER;
            wait(0.2);
            alarm = 0;
            wait(period);
            /*
            for(float p=0; p<1.0; p += 0.1) {
                alarm = p;
                wait(period);
            }
            */
            break;
        case 1:     // Security
            alarm = BUZZ_POWER;
            wait(0.2);
            alarm = 0;
            wait(period);
            break;

        }
    /*
        led = led + 0.01;
        wait(0.2);
        if(led == 1.0) {
            led = 0;
        }
        */
    
}

// Turn on led in different patterns
void ledOn(int type, float period) 
{
    switch (type) {
        case 0:
            ledTAlarm = 1;
            wait(0.1);
            ledTAlarm = 0;
            wait(0.05);
            break;
        case 1:
            for(int p=0; p<10; p += 1) {
                ledSAlarm = 1;
                wait(0.02);
                ledSAlarm = 0;
                wait(0.02);
            }
            /*
            ledSAlarm = 1;
            wait(0.1);
            ledSAlarm = 0;
            wait(0.02);
            */
            break;

        }
            /*
            for(float p=0; p<1.0; p += 0.1) {
                leds = statusAlarm;
                wait(period);
            }
            */

    /*
        led = led + 0.01;
        wait(0.2);
        if(led == 1.0) {
            led = 0;
        }
        */
    
}

// Turn off alarm 
void buzzOff()
{
    alarm = 0;
}

void buzzTest()
{
    sAlarmOn = OFF; 

    timer1.attach(&debounce, 1);
    //IRQFire.rise(&ISRFirePressed);
    //IRQFire.fall(&ISRFireRelease);
    //IRQJoyUp.rise(&ISRJoyUp);
    //IRQJoyDown.rise(&ISRJoyDown);    
    
    alarm.period(0.020);          // servo requires a 20ms period
    
    printf("Test starts...\n\r");
    
    while (1)
    {
        if (sAlarmOn) {
            buzzOn(buzzPattern, buzzPeriod);
                printf("%s: pattern: %d period: %.1f count: %d\n\r", sAlarmOn? "ON": "OFF", buzzPattern, buzzPeriod, count);
        } else {
            buzzOff();
                buzzPattern = (buzzPattern + 1) % 2;
                count = 0;
        }
        wait(0.02);
    }    
}   
        

// Set date and time
// Input: Year, month, date, hour, minute, second
void SetTime(int year, int month, int date, int hour, int min, int sec)
{
    struct tm Clock;
    Clock.tm_year = year - 1900;
    Clock.tm_mon  = month - 1;
    Clock.tm_mday = date;
    Clock.tm_hour = hour;
    Clock.tm_min  = min;
    Clock.tm_sec  = sec;
    time_t epoch = mktime(&Clock);
    if (epoch == (time_t) -1) {
        error("Error: Invalid clock setting! Please try again.\n");
    }
    set_time(epoch);
}

// Calibrate analog pot to allow tuning each pot for controlling upper and lower bound control temperature
void calibratePot()
{
    potBase1 = pot1.read();
    potBase2 = pot2.read();
    //tempBase = temp;
    lowerTempBase = setLowerTemp;
    upperTempBase = setUpperTemp;

    
    updateJoyDisplay = true;

    wait(0.5);

}         

// Non-blockingly innput a simulated temperature
// Input: None
bool EnterTemp()
{
    int i;
    bool done = false; 
    
    switch (entryState)
    {
        case 0:
            break;
        case 1: 
            pc.printf("\n\rEnter a simulated temperature: ");
            entryReady = false;
            entryState = 2;
            break;
        case 2: 
            peepEntry();
            if (!entryReady)
                break;
            else
                entryState = 3;
        case 3:
            sscanf(inBuf, "%d", &i);
            if (i < 0 || i >= 100) {
                printf("\n\rEnter a temperature between 0F and 100F: ");            
                entryReady = false;
                entryState = 2;
                break;
            } else {
                temp = i;
                printf("Current temperature is now %dF.\n\r", i);
                updateCurTempDisplay = true;
                entryState = 1;
                done = true;
                calibratePot();
                break;
            }

            
    }
    return done; 

}

// Peep if serial data is received and read if so
void peepEntry()
{
    int i;
    char c;
    i = pc.readable();

    if (i > 0) {
        //pc.printf("%d: ", i);
        while (i > 0) {
            c = pc.getc();

            if (c == '\r') {
                pc.printf("\n\r");
                inBuf[pos] = '\0';
                //pc.printf("Return: %s\n\r", inBuf);
                pos = 0;
                entryReady = true;

            } else {
                //pc.printf("%c\n\r", c);
                pc.putc(c);
                inBuf[pos] = c;
                pos++;
            }
            i--;
        }

    }
}

// Non-blockingly set a passcode
// Input: None
bool SetPasscode()
{
    int i;
    bool done = false; 
    
    switch (entryState)
    {
        case 0:
            break;
        case 1: 
            if (statusAlarm & 0x80000000)
            {
                if (!sPCOK)
                    entryState = 2;
                else
                    entryState = 5;
            }
            else
            {
                pc.printf("No USB connection detected. Check USB device being connected.\n\r");
            }
            break;
                    
        case 2:
            pc.printf("\n\rEnter a 4-digit passcode: ");
            entryReady = false;
            entryState = 3;
            break;
        case 3:
            peepEntry();
            if (!entryReady)
                break;
            else
                entryState = 4;
        case 4:
            sscanf(inBuf, "%d", &i);
            if (i < 0000 || i > 9999) {
                pc.printf("\n\rEnter a passcode between 0000 and 9999: ");
                entryReady = false;
                entryState = 3;
                break;
            } else {
                passcode = i;
                pc.printf("\n\rPasscode is set (%d).\n\r", passcode);
                sPCOK = true;
                entryState = 5;
            }
        case 5:
            sLockActive = true;
            lockLed = 1;
            updateJoyDisplay = true;
            pc.printf("USB is now locked. Enter passcode to unlock.\n\r");
            entryState = 1;
            done = true;
            break;
    }
    return done; 
}

// Non-blockingly enter data to unlock passcode
bool UnlockPasscode()
{
    int i = 0;
    bool done = false;
    
    switch (entryState)
    {
        case 0:
            break;
        case 1: 
            pc.printf("\n\rTo unlock, enter a 4-digit passcode: ");
            entryReady = false;
            entryState = 2;
            break;
        case 2:
            peepEntry();
            if (!entryReady)
                break;
            else
                entryState = 3;    
        case 3:
            sscanf(inBuf, "%d", &i);
            if (i < 0000 || i > 9999) {
                pc.printf("\n\rEnter a passcode between 0000 and 9999: ");
                entryReady = false;
                entryState = 3;
            } else {
                if (passcode == i) {
                    pc.printf("\n\r====> Unlock successful. :-) \n\r");
                    sLockActive = false;
                    statusAlarm &= 0xFFFFFFFD;
                    lockLed = 0;
                    updateJoyDisplay = true;
                } else {
                    pc.printf("\n\r====> Incorrect passcode!\n\r");
                }
                done = true;
            entryState = 1;
                
            }
            break;
        
    }
    return done;    
}

// Check if USB is connected   
// To read lock status: bit0=NUM_LOCK bit1=CAP_LOCK bit2=SCROLL_LOCK
void USBConnCheck()
{
    char status = 0;
    char result; 
    
    //if (!(statusAlarm & 0x80000000))
    //{
    //    dev.keyCode(KEY_NUM_LOCK);
    
    if (dev.keyCode(KEY_CAPS_LOCK))
            printf("\rSend KEY_CAPS_LOCK OK\n\r");
    
    status = dev.lockStatus();
    //printf("1: lockStatus = 0x%x statusAlarm = 0x%x\n\r", status, statusAlarm);
    if (!(statusAlarm & 0x80000000) && (status & 0x2))
    {
        statusAlarm |= 0x80000000; 
        result = true;
    }
    else if ((statusAlarm & 0x80000000) && !(status & 0x2))
    {
        statusAlarm &= 0x7FFFFFFF;
        result = false;
    }
    //printf("2: lockStatus = 0x%x statusAlarm = 0x%x\n\r", status, statusAlarm);
    //return result;
    
}

// Turn on and off the alarm comparing the current temperature with and the set temperature
// Input: None
void TempMonitor()
{
    // Record current state of relay switch
    int tempAlarm = statusAlarm & 0x1;
    if (((temp < (setLowerTemp - tol)) || (temp > (setUpperTemp + tol))) && sHealthFeatureActive) 
    {
        statusAlarm |= 0x1; 
        alarmType = 0;
    }
    else if (((temp >= setLowerTemp) && (temp <= setUpperTemp)) || !sHealthFeatureActive)
    {
        statusAlarm &= 0xFFFFFFFE;
    }
    // When alarm is turned on or off
    if (tempAlarm != (statusAlarm & 0x1))
    {
        seconds = time(NULL);
        char buffer[32];  
        strftime(buffer, 32, "%T", localtime(&seconds));
        
        if (statusAlarm & 0x1)
        {
            if (timer2.read())
            {
                timer2.stop();
                pc.printf("Temperature Alarm off for %.2f sec.\n\r", timer2.read());
            }
            timer2.reset();
            pc.printf("Temperature Alarm on.... %s\n\r", buffer);
            timer2.start();
        }
        else
        {
            timer2.stop();
            timeElapsed = timer2.read();
            pc.printf("Temperature Alarm off... %s (Duration: %.2f sec)\n\r", buffer, timeElapsed);
            timer2.reset();
            timer2.start();
        }
        updateJoyDisplay = true;
    }
}



// Read temperature sensor
// Input: None
void ReadTemp()
{
    //temp = Ctmp.read();
    temp = Ctmp.read()*1.8+32;
    updateCurTempDisplay = true;
}

// Monitor accelerometer to detect motion and turn on and off the alarm after comparing the motion data with
// preconfigurated motion factor.
void MotionMonitor()
{
    //Xaxis_p = MMA.x() || -MMA.x();
    
    float xpos, ypos, zpos;
    float senNum = senParm[senLevel].factor;
    xpos = MMA.x();
    ypos = MMA.y();
    zpos = MMA.z();
    if ((xpos > senNum || xpos < -(senNum) || ypos > senNum || ypos < -(senNum))  && sSecurityFeatureActive) 
    {
        statusAlarm |= 0x2;
        pc.printf("Security Alarm ON 0x%x(senNum: %f Xpos: %6.3f Ypos: %6.3f Zpos: %6.3f)\n\r", statusAlarm, senNum, xpos, ypos, zpos);
        //updateJoyDisplay = true;
    } 
    else
    {
        if ((statusAlarm & 0x2) && (mode == 0 || !sLockActive))
        {
            statusAlarm &= 0xFFFFFFFD;
            pc.printf("Security Alarm OFF 0x%x(Xpos: %6.3f Ypos: %6.3f Zpos: %6.3f)\n\r", statusAlarm, xpos, ypos, zpos);
        }
        //else
        //    pc.printf("Security Alarm %s (Xpos: %6.3f Ypos: %6.3f Zpos: %6.3f)\n\r", (statusAlarm & 0x2)? "sON": "sOFF", xpos, ypos, zpos);
        //updateJoyDisplay = true;
    }
}

// Display date, time, heater state, relay state, current and set temperature at LCD
// Input: None
void DisplayLCD()
{
    seconds = time(NULL);
    
    char buffer[32];  
    strftime(buffer, 32, "%F %T", localtime(&seconds));
        
    lcd.locate(0,3);
    lcd.printf("%s\n",buffer);
    if (updateCurTempDisplay || updateJoyDisplay)
    {
        lcd.printf("Now: %.1f%c L:%3.0f%c U: %3.1f%c\n", temp, deg, setLowerTemp, deg, setUpperTemp, deg);
        updateCurTempDisplay = false;
    }

            
    if (updateJoyDisplay)
    {
        lcd.printf("Mode: %c %1s%2s  Status: %s%s\n", modeSym[mode], sHealthFeatureActive? "H": " ", senParm[senLevel].sym, sLockActive? "L":" ", sAlarmOn? "A": " ");
        updateJoyDisplay = false;
    }
        
}

// Check if alarm is to be triggered by either temp or security events
// Do buzz for temperature and security alarm events accordingly
void AlarmCheck()
{
    bool prevState = sAlarmOn;
    if (statusAlarm & 0x1 || statusAlarm & 0x2)
        sAlarmOn = ON;
    else         
        sAlarmOn = OFF;
        
    if (statusAlarm & 0x80000000)
        connectionLed = 1;
    else
        connectionLed = 0;        
        
    if (prevState != sAlarmOn)
        updateJoyDisplay = true;        
    
    //leds = statusAlarm;
    if ((statusAlarm & 0x2) && (statusAlarm & 0x1))
    {
        buzzOn(1, BUZZ_SPERIOD);
        ledOn(1, BUZZ_SPERIOD);
        ledOn(0, BUZZ_TPERIOD);
    } 
    // Security alarm
    else if (statusAlarm & 0x2)
    {
        buzzOn(1, BUZZ_SPERIOD);
        ledOn(1, BUZZ_SPERIOD);
    }
    // Temperature alarm
    else if (statusAlarm & 0x1)
    {
        buzzOn(0, BUZZ_TPERIOD);
        ledOn(0, BUZZ_TPERIOD);
    }
}

    
// Initialize settings
void Init(void)
{
    SetTime(YEAR, MONTH, DATE, HOUR, MIN, 0);
    
    IRQFire.rise(&ISRFirePressed);
    IRQJoyUp.rise(&ISRJoyUp);
    IRQJoyDown.rise(&ISRJoyDown);
    IRQJoyLeft.rise(&ISRJoyLeft);
    IRQJoyRight.rise(&ISRJoyRight);    
    timer1.attach(&DisplayLCD, 1);
    timer3.attach(&debounce, 1);
    //timer4.attach(&USBConnCheck, 2);
    timer4.attach(&AlarmCheck, 1);
    //timer2.attach(&ReadTemp, 5);
    sHealthFeatureActive = OFF;
    sSecurityFeatureActive = OFF;
    sAlarmOn = OFF;
    statusAlarm = 0;
    sState = 0;
    setLowerTemp = DEF_LOWER_TEMP;
    setUpperTemp = DEF_UPPER_TEMP;
    tempOverride = false;
    senLevel = 0;
    buzzPattern = 0;
    buzzPeriod = 0.2;
    mode = 0;
    timeElapsed = 0;
    entryState = 1; 
    lcd.cls();
    lcd.locate(0,3);
    updateJoyDisplay = true;

    calibratePot();            
    pc.printf("\n\rHealth & Security Monitoring starts...\n\r");
    
}

// Main loop in executing major temperature, motion monitoring tasks and alarm check task
void FSM(void)    
{
    while(1) 
    {
        //USBConnCheck();
        
        // Override temperature read for testing. Use pot to control temperature
        if (!tempOverride)
            ReadTemp();
        
        setLowerTemp = lowerTempBase + (pot1.read()-potBase1) * 12;
        setUpperTemp = upperTempBase + (pot2.read()-potBase2) * 12;
        updateCurTempDisplay = true;
 
        switch (gState)
        {
            case 0: 
                    break;
            case 1: 
                    if (SetPasscode())
                        gState = 0;
                    break;
            case 2: 
                    if (UnlockPasscode())
                        gState = 0;
                    break;
            case 3: 
                    if (EnterTemp())
                        gState = 0;
                    break;                    
        }
 
        MotionMonitor();        
        TempMonitor();
        //AlarmCheck();
        
        /* Live check
        lockLed = 1;
        wait (0.1);
        lockLed = 0;
        wait(0.1);
        */
    }

}

int main()
{
    //buzzTest();
    Init();
    FSM();    
}