/*
We need your help to stop forest fires and bake tasty cookies!
See `requirements.md` for how to help.
Then check `notes.md`.
*/
#include "api.h"
#define VREF_MIN 4500 // minimum expected vref voltage in millivolts
#define VREF_MAX 5500 // maximum expected vref voltage in millivolts
#define TEMP_MIN -10.0 // minimum expected sensor temperature
#define TEMP_MAX 300.0 // maximum expected sensor temperature
#define SENSOR_MIN_PERCENT 10.0 // minimum expected vsig/vref ratio
#define SENSOR_MAX_PERCENT 90.0 // maximum expected vsig/vref ratio
#define HUNDRED_PERCENT 100 // pre-defined macro for calculating percentages
#define DOOR_OPEN_THRESHOLD 4500 // analog voltage threshold for the door sensor
#define DOOR_CLOSED_THRESHOLD 500 // analog voltage threshold for the door sensor
#define PERCENT_TEMP_SLOPE (TEMP_MAX - TEMP_MIN) / (SENSOR_MAX_PERCENT - SENSOR_MIN_PERCENT)
#define IGNITER_PERIOD 5000 // 5 seconds in simulation time
#define CONSOLE_REFRESH_PERIOD 1000 // time between each console temp update in milliseconds
typedef enum {
DOOR_CLOSED,
DOOR_OPEN
} DoorState;
typedef enum {
HIGHER_OR_EQUAL_TO_180,
LOWER_THAN_180
} OvenTemp;
typedef enum {
TIMER_OFF,
TIMER_STARTED,
TIMER_LAPSED
} IgniterTimerState;
// State Variables Initialization (safer-ish initial values)
DoorState door_state = DOOR_OPEN; // initialize to DOOR_OPEN
OvenTemp oven_temp = HIGHER_OR_EQUAL_TO_180; // initialize to HIGHER_OR_EQUAL_TO_180
IgniterTimerState igniter_timer_state = TIMER_OFF; // initialize to TIMER_OFF
void setup() {
Serial.begin(115200);
setup_api();
Serial.println("Elf oven 2000 starting up.");
serial_printf("Days without fire incident: %i\n\n", 0);
unit_test();
}
void loop() {
// Update the state machine variables
update_input_state();
// Run the state machine
state_machine_run();
}
void state_machine_run() {
// Timer Variables
unsigned long static timer_start; // ignition start time
unsigned long static timer_current;
if (door_state == DOOR_CLOSED) {
if (oven_temp == LOWER_THAN_180) {
switch (igniter_timer_state) {
case TIMER_OFF: // Start of the oven
timer_start = millis(); // timer start time
igniter_timer_state = TIMER_STARTED; // state transition
set_output(GAS_VALVE, true);
set_output(IGNITER, true);
break;
case TIMER_STARTED:
timer_current = millis(); // current time
if (timer_current - timer_start >= IGNITER_PERIOD) {
igniter_timer_state = TIMER_LAPSED; // state transition
set_output(IGNITER, false);
}
break;
case TIMER_LAPSED:
// ADD Error checking here
set_output(IGNITER, false);
break;
}
}
else { // Oven temp > 180, turn of the gas, igniter
set_output(GAS_VALVE, false);
set_output(IGNITER, false);
// set timer status
igniter_timer_state = TIMER_OFF; // state transition
}
}
else { // Oven door is open, turn off the oven
set_output(GAS_VALVE, false);
set_output(IGNITER, false);
// set timer status
igniter_timer_state = TIMER_OFF; // state transition
}
}
// Update the state machine variables
void update_input_state() {
float oven_tempreture; // variable to store the oven temperature
bool is_oven_closed; // 1 if oven is closed, 0 if oven is open
// get the tempreture and door status
oven_tempreture = get_temperature();
is_oven_closed = get_door_status();
// Update the state machine variables
if (oven_tempreture >= 180) {
oven_temp = HIGHER_OR_EQUAL_TO_180;
}
else {
oven_temp = LOWER_THAN_180;
}
// Update the state machine variables
if (is_oven_closed == true) {
door_state = DOOR_CLOSED;
}
else {
door_state = DOOR_OPEN;
}
}
/**
\brief Get Oven Temperature
\details Reads the oven temperature sensor data and converts it to degrees C.
\param [out] temp_in_c Oven temperature in degrees C.
*/
float get_temperature(void) {
uint16_t temp_sensor_voltage;
uint16_t temp_sensor_reference;
uint32_t temp_percent;
uint32_t temp_in_c;
// Read sensor measurements
temp_sensor_voltage = read_voltage(TEMPERATURE_SENSOR);
temp_sensor_reference = read_voltage(TEMPERATURE_SENSOR_REFERENCE);
// ERROR Checking
if ((temp_sensor_reference < VREF_MIN) || (temp_sensor_reference > VREF_MAX)) {
serial_printf("ERROR: Temperature sensor refrence malfunction detected \n");
Serial.flush();
}
temp_percent = ((float)temp_sensor_voltage / (float)temp_sensor_reference) * (float)HUNDRED_PERCENT;
// ERROR Checking
if ((temp_percent < SENSOR_MIN_PERCENT) || (temp_percent > SENSOR_MAX_PERCENT)) {
serial_printf("ERROR: Temperature sensor malfunction detected \n");
Serial.flush();
}
temp_in_c = temp_interpolate(temp_percent);
console_temp_update(temp_sensor_reference, temp_sensor_voltage, temp_in_c);
return temp_in_c;
}
/**
\brief Calculate Oven Temperature By Interpolation
\details Calculates the oven temperature by interpolation.
temp_percent values expected between SENSOR_MIN_PERCENT and SENSOR_MAX_PERCENT.
Values outside thisrange would trigger an exception and the application would exit.
\param [in] temp_percent Vsig/Vref ratio/percentage of the temperature sensor.
\param [out] temp_in_c Oven temperature in degrees C.
*/
float temp_interpolate(float temp_percent) {
float temp_in_c;
// ERROR Checking
if ((temp_percent < SENSOR_MIN_PERCENT) || (temp_percent > SENSOR_MAX_PERCENT)) {
serial_printf("ERROR: Interpolating outside of range\n");
serial_printf("Application Quitting\n");
Serial.flush();
}
temp_in_c = (PERCENT_TEMP_SLOPE * (temp_percent - SENSOR_MIN_PERCENT)) + TEMP_MIN;
return temp_in_c;
}
/**
\brief Read Door Sensor
\details Reads the door status sensor data and returns a bool to describe if the oven door is open.
\param [out] door_status True if the oven door is closed, false if open.
*/
bool get_door_status(void) {
uint16_t door_sensor_voltage;
bool door_status;
// !!!!!DEBOUNCE THIS MEASUREMENT!!!!!
door_sensor_voltage = read_voltage(DOOR_SENSOR);
door_status = is_door_closed(door_sensor_voltage);
return door_status;
}
/**
\brief Determine Oven Door State
\details Determines if the oven door is open or closed from ADC measurements.
Returns true if oven door is closed, i.e., door_sensor_voltage
is less than DOOR_CLOSED_THRESHOLD, and false if the oven door is
open, i.e., door_sensor_voltage is more than DOOR_OPEN_THRESHOLD.
\param [in] door_sensor_voltage Sampled voltage of oven door sensor.
\param [out] bool true if closed, false if open.
*/
bool is_door_closed(uint16_t door_sensor_voltage) {
if (door_sensor_voltage < DOOR_CLOSED_THRESHOLD) { // DOOR IS CLOSED
return true;
}
else if (door_sensor_voltage > DOOR_OPEN_THRESHOLD) { // DOOR IS OPEN
return false;
}
/* if the value is between 500mv and 4500mv ,there is high noise coupled
in the door sensor wires, report as an error in the logs. For now */
serial_printf("ERROR: Door sensor malfunction detected \n");
return false; // Return false as a fail-safe
}
/**
\brief Print Oven Temperature
\details Reports the oven temperature to the user periodically every CONSOLE REFRESH PERIOD.
Only prints to the console if it has been called and CONSOLE REFRESH PERIOD has
passed since the last function call.
\param [in] temp_sensor_reference Temperature sensor reference voltage in mv.
\param [in] temp_sensor_voltage Temperature sensor signal voltage in mv.
\param [in] temp_in_c Oven temperature in degrees C.
*/
void console_temp_update(uint16_t temp_sensor_reference,
uint16_t temp_sensor_voltage,
float temp_in_c) {
unsigned long static last_print; // The time the last update was sent to the console
unsigned long static messege_count = 0; // The messege ID of the current message
if (millis() - last_print >= CONSOLE_REFRESH_PERIOD) {
serial_printf("\n");
serial_printf("Message ID: %i\n", messege_count);
serial_printf("Oven tempreture vref: %i mv\n", temp_sensor_reference);
serial_printf("Oven tempreture signal : %i mv\n", temp_sensor_voltage);
serial_printf("Oven tempreture: ");
Serial.print(temp_in_c, 1);
serial_printf(" C\n");
last_print = millis();
++messege_count;
}
}
void unit_test(void) {
Serial.println("Software Unit Tests Started");
// ADD MORE UNIT TESTS
serial_printf("Interpolation Test 1: %s\n", (temp_interpolate(50.0) == 145.0) ? "PASS" : "FAIL");
serial_printf("Door Status Test 1: %s\n", (is_door_closed(50) == true) ? "PASS" : "FAIL");
serial_printf("Door Status Test 2: %s\n", (is_door_closed(4900) == false) ? "PASS" : "FAIL");
//serial_printf("Door Status Test 3: %s\n", (is_door_closed(2500) == false) ? "PASS" : "FAIL");
}