The Safe Kitchen

Inspiration

Hardware for a Safer Life

Over 162,000 Home fires every year in the U.S. involving cooking equipment - National Fire Prevention Assoc. (NFPA)

Ranges account for greater than 60% of the fires, and 86% of the deaths!

Unattended equipment is the leading cause

What it does

OvenOn:

Embedded system to detect and alert users of appliances left on!

Web Application for data logging and user messaging

How we built it

Embedded System - Photon Particle, Sparkfun clamp-on current sensor (transformer)

Web Application

Ruby on Rails on private server
Mandrill e-mail services

Challenges we ran into

Nonexistant/Poor pinout documentation for some components

Not developing with a real oven/stovetop during the Hackathon

Apple MacBook Pro Graphics Card failures during Web App Development

Accomplishments that we're proud of

Building a strong/talented team --Jim Beck --C Cantello --Jim Cifarelli --Eric Robrigado

What we learned

Photon programming

Resources offered by the Center of Gravity

What's next for The Safe Kitchen

Next Generation Project The Smart Spoon Rest -- added sensors, advanced detection/alerting algorithms

Built With

  • photon-particle
Share this project:

Updates

posted an update

Photon Code by Eric Robrigado /* range_sense.ino 30-Jan, 2016

*/

include

const char server[] = "162.243.58.60"; // destination server

//const int POST_RATE = 100000; // Time between posts, in ms. unsigned long POST_RATE = 10000, lastPost = 0; // global variable to keep track of last post time unsigned int new_val, old_val = 0, max_val; unsigned char led0_state = 0, led7_state = 0; float f_val, a_voltage;

void setup() { // serial debug Serial.begin(9600);

pinMode(A1, INPUT);
// sample rate
pinMode(D0, OUTPUT);
// setup interrupt
attachInterrupt(D0, sampleCurrentSense, RISING);
// set interrupt rate
tone(D0, 120, 0);
// upload rate
pinMode(D7, OUTPUT);

}

void loop() {

if ((lastPost + POST_RATE) > millis())
{
    //max_val = 1000;
    //f_val = float max_val;
    a_voltage = 3.3 * max_val / 4096.0;
    postToSafeKitchen(a_voltage);
    lastPost = millis();

    led7_state = !led7_state;
    digitalWrite(D7, led7_state);

}

//delay(2);

}

void sampleCurrentSense() { // sample the current sensor new_val = analogRead(A1); max_val = max(new_val, old_val); old_val = new_val;

Serial.println("new_A1");
Serial.println(new_val);
// chk sample speed via oscilloscope
// change the digital out state
led0_state = !led0_state;
digitalWrite(D0, led0_state);

}

int postToSafeKitchen(float voltage_val) {

TCPClient client;
char response[512];
int i = 0;
int retVal = 0;
char get_data[256];
unsigned int a_time;

if (client.connect(server, 80))
{
    // Post message to indicate connect success
    Serial.println("Posting!"); 

    //a_time = millis();
    //sprintf(get_data, "GET /devices/2c0047000747343232363230/data_points/push?data_type=current&value=%u", a_time);
    sprintf(get_data, "GET /devices/2c0047000747343232363230/data_points/push?data_type=current&value=%.2f", voltage_val);
    Serial.println(get_data);
    client.println(get_data);
    delay(1000);
    // Now we'll do some simple checking to see what (if any) response
    // the server gives us.
    while (client.available())
    {
        char c = client.read();
        Serial.print(c);    // Print the response for debugging help.
        if (i < 512)
            response[i++] = c; // Add character to response string
    }

    // Search the response string for "200 OK", if that's found the post
    // succeeded.
    if (strstr(response, "200 OK"))
    {
        Serial.println("Post success!");
        retVal = 1;
    }
    else if (strstr(response, "400 Bad Request"))
    {   // "400 Bad Request" means the Phant POST was formatted incorrectly.
        // This most commonly ocurrs because a field is either missing,
        // duplicated, or misspelled.
        Serial.println("Bad request");
        retVal = -1;
    }
    else
    {
        // Otherwise we got a response we weren't looking for.
        retVal = -2;
        Serial.println("retVal = -2");
    }
}
else
{   // If the connection failed, print a message:
    Serial.println("connection failed");
    retVal = -3;
}
client.stop();  // Close the connection to server.
return retVal;  // Return error (or success) code.

}

Log in or sign up for Devpost to join the conversation.

posted an update

Pseudo-code for Version 2 by Jim Beck // driver to test functions

include

int main() {

float segment_time, averaged_temp;

printf(“Hello World start\n”);

reset_readings();

time_temp_arr[0][0] = 5.0; time_temp_arr[1][0] = 72.0; time_temp_arr[0][1] = 605.0; time_temp_arr[1][1] = 78.0; time_temp_arr[0][2] = 630.0; time_temp_arr[1][2] = 84.0; num_readings = 3;

printf("%4d, %4.2f\n",heating_phase(&segment_time), segment_time); printf("%4d, %4.2f\n",cooling_phase(&segment_time), segment_time); printf("%4d, %4.2f\n",steady_state(&averaged_temp), averaged_temp); printf("%4d\n",throw_alert());

printf(“Hello World end\n”);

return 0;

} // end of test driver

//ambient_temp (Farenheit), used mostly to aid low-level heating situation float ambient_temp = 72.0;

// default time (in seconds), used to establish an "ignore readings" period to provide // a "settling" time, expressed in seconds; hence, first 10 minutes of readings to be ignored. global static initial_settling_time = 600.0;

// array holding time, temp reading pairs, assumed to be in seconds, degrees Farenheit. parameter MAX_READINGS; global static float time_temp_arr[2,MAX_READINGS]; global static int num_readings;

// this to be invoked at device startup, as well as when errant event has been determined that // might taint readings, such as device location being changed, wiping of stovetop, opening of a // house window, fan being turned on, etc. // With these invocations, sanctity of previous data is considered to be of lower importance // than the reporting of a weakly-determined condition. function reset_readings() { num_readings = 0; }

function add_reading() { if (num_readings >= MAX_READINGS) // should instead remove several of the earliest readings via left-shifting array values to create space return FALSE; else { // time_temp_arr[0][num_readings] = get_time_reading(); // time_temp_arr[1][num_readings] = get_temp_reading(); num_readings++; } }

// device considered in a heating phase if there exists a "sufficiently" steep positive slope or % of positive slopes between readings. logical function heating_phase( float segment_time; ) { int count, pos_slope_count, first_reading, last_reading; float temp_diff; // define the "percentage" of needed positively-sloped intervals in order to consider the range of values to be part of an overall period of "heating." // Also aids allowing an amount of possibly spurious readings to be ignored. float pos_slope_percentage = 0.80; // define what is considered to be an acceptable threshold slope to accept a range of temperature // readings to be in a "heating" phase // empirical collection showed approx. 0.6 degree Farenheit rise per 60 seconds in an Energy Star // range float heating_slope = 0.01;

count = 0;
pos_slope_count = 0;

for (i = 0; i< num_readings-1; i++) {
    if (time_temp_arr[0][i+1] > initial_settling_time) {
        count++;
        temp_diff = time_temp_arr[1][i+1] - time_temp_arr[1][i];
        if (temp_diff > 0.0) {
            pos_slope_count++;
            if (pos_slope_count == 1) first_reading = i;
            last_reading = i+1;
        }
    }
}  // end of i for loop

if (pos_slope_count == 0) return FALSE;

segment_time = time_temp_arr[0][last_reading] - time_temp_arr[0][first_reading];
overall_slope = (time_temp_arr[1][last_reading] - time_temp_arr[1][first_reading])          / segment_time;

if (float(pos_slope_count)/float(count) > pos_slope_percentage .AND.
 overall_slope > heating_slope) {
    return TRUE;
else
    return FALSE;
}

} // end of heating_phase function

// similar to heating_phase logic, rate of cooling expected to be much less that rate of heating // though, as well as negatively-sloped logical function cooling_phase() { int count, neg_slope_count, first_reading, last_reading; float temp_diff; // define the "percentage" of needed negatively-sloped intervals in order to consider the range of values to be part of an overall period of "cooling." // Also aids allowing an amount of possibly spurious readings to be ignored. float neg_slope_percentage = 0.80; // define what is considered to be an acceptable threshold slope to accept a range of temperature // readings to be in a "cooling" phase // empirical collection showed approx. 0.1 degree Farenheit drop per 60 seconds in an Energy // Star range float cooling_slope = -0.0016;

count = 0;
neg_slope_count = 0;

for (i = 0; i< num_readings-1; i++) {
    if (time_temp_arr[0][i+1] > initial_settling_time) {
        count++;
        temp_diff = time_temp_arr[1][i+1] - time_temp_arr[1][i];
        if (temp_diff < 0.0) {
            neg_slope_count++;
            if (neg_slope_count == 1) first_reading = i;
            last_reading = i+1;
        }
    }
}  // end of i for loop

if (neg_slope_count == 0) return FALSE;

overall_slope = (time_temp_arr[1][last_reading] - time_temp_arr[1][first_reading]) /
     (time_temp_arr[0][last_reading]-time_temp_arr[0][first_reading]);

if (float(neg_slope_count)/float(count) > neg_slope_percentage .AND.
 overall_slope < cooling_slope) {
    return TRUE;
else
    return FALSE;
}

} // end of cooling_phase fuinction

// is rate of temperature change very small, either as a gain or loss? // power could truly be OFF, or in a slow cooling mode, OR, power could be ON, with there being // a low-level amount of heating logical function steady_state( float averaged_temp; ) {

averaged_temp = 81.0;
return TRUE;

} // end of steady_state function

// system needs to be in heating phase for a specified time OR // the steady_state temp needs to be "sufficiently" above room ambient temperature // (use of above_ambient_temp setting, here at % degrees Farenheit). logical function throw_alert(){ // max_heating_interval is length of the heating_phase, expressed in seconds, below which is // not considered to be a reportable condition, set here to 30 minutes. float max_heating_interval = 1800.0; float above_ambient_temp = 5.0;

if ( (heating_phase(segment_time) .AND. segment_time > max_heating_interval) .OR.   (steady_state(averaged_temp) .AND.
    averaged_temp-ambient_temp > above_ambient_temp) )
    return TRUE;
else
    return FALSE;

} // end of throw_alert function

Log in or sign up for Devpost to join the conversation.