Aes encryption code

Dependencies:   Crypto USBDevice mbed

Fork of larada by MZJ

main.cpp

Committer:
stupid
Date:
2016-03-02
Revision:
2:35e620097381
Parent:
1:ccb26dd9845a
Child:
3:cff2f80f0a42

File content as of revision 2:35e620097381:

#include "mbed.h"
#include "USBSerial.h"
#include "IAP.h"

// #define SERIAL //comment for USB operation, uncomment for serial
#define CALIBRATE 0
#define FUNCTION_CHECK 0

// debounce duration for ON/OFF switch
#define ON_OFF_DEBOUNCE 3000

// longest user command we will accept
#define MAX_COMMAND_LEN 64

// where to start reading stored values from the EEPROM
#define BASE_ADDRESS 1

// temperature control stuff //
// fixed values
#define CONTROL_INTERRUPT 1000 // every 1ms
#define MAX_DUTY 100 // This is both the number of times the heater_control interrupt will run per control period and the max amount of interrupts the heater triac can be on.

// adjustable values
#define DEFAULT_FILTER 0.05 // first order digital filter parameter, should be [0.0 - 1]. less means filter more, a value of 1 should turn it off
//#define INITIAL_DUTY 25 // 25% duty generally results in a few degrees under 57C output for the ambient temperatures I've been testing at
#define DEFAULT_INITIAL_DUTY 45 
#define DEFAULT_NOMINAL_DUTY 28 // 11% duty is a good set point for Beta init (testing at 20C ambient)
//#define SETPOINT 435 // this results in roughly 57C at the tines on the Alphas (really 53)
#define DEFAULT_SETPOINT 58 //seems like there is a ~4C temperature drop between the sensor and the outside of the tips 
#define DEFAULT_TOLERANCE 2 // if we are within this many ADC counts of the setpoint don't make any changes to the duty cycle
//#define DUTY_TOO_LARGE 40 // If the duty gets larger than this we have problems (Alphas)
#define DEFAULT_DUTY_TOO_LARGE 70 // If the duty gets larger than this we have problems (Betas) -> 65 duty should result in roughly 62C tip temps
//#define DUTY_TOO_LARGE 85 // For testing
#define DEFAULT_TEMP_LIMIT_LOWER 12 // If the temperature measures too low during operation we are either doing a bad job or have a faulty temperature sensor
//#define DEFAULT_TEMP_LIMIT_LOWER 0 // For testing
//#define TEMP_LIMIT_UPPER 485 // No burning allowed (Alphas)
#define DEFAULT_TEMP_LIMIT_UPPER 65 // No burning allowed (Betas)

float filter = DEFAULT_FILTER;
uint8_t initial_duty = DEFAULT_INITIAL_DUTY;
uint8_t nominal_duty = DEFAULT_NOMINAL_DUTY;
uint16_t setpoint = DEFAULT_SETPOINT;
uint8_t tolerance = DEFAULT_TOLERANCE;
uint8_t duty_too_large = DEFAULT_DUTY_TOO_LARGE;
uint16_t temp_limit_lower = DEFAULT_TEMP_LIMIT_LOWER;
uint16_t temp_limit_upper = DEFAULT_TEMP_LIMIT_UPPER;

enum State {
    IDLE,
    WAIT_FOR_TIP,
    INITIAL_RAMP,
    ACTIVE,
    DONE,
    ERROR,
};

enum Heater_State {
    ON,
    OFF,
};

// not used yet
enum Error {
    NONE,
    TEMP_TOO_LOW,
    TEMP_TOO_HIGH,
    DUTY_TOO_LARGE,
};

// Heater control pins
DigitalOut fan(P0_22);
DigitalOut heater_pin(P1_27);
AnalogIn temp_sense(P0_16);

// LED pins
DigitalInOut empty_led(P0_10);
DigitalInOut fuel_gage_1(P0_9);
DigitalInOut fuel_gage_2(P0_8);
DigitalInOut fuel_gage_3(P1_21);
DigitalInOut fuel_gage_4(P1_31);
DigitalOut tip_light(P1_14);

// Other pins
DigitalIn tip_sensor(P0_14);
DigitalIn on_off(P1_28);

#define ON_TIME_S 2700L // The device is active for 45 minutes
//#define ON_TIME_S 150L
#define RED_LED_ON_TIME_S 300L // The red LED is active for the last 5 minutes
//#define RED_LED_ON_TIME_S 30L
#define TIP_FLASH_INTERVAL_S 25L // How often the tip will flash
//Serial pc(0x1f00, 0x2012, 0x0001, false);

#ifdef SERIAL
Serial pc(P0_19, P0_18); // tx, rx
#else
USBSerial pc(0x1f00, 0x2012, 0x0001);
#endif

Ticker control_interrupt;
Ticker check_limits_interrupt;
Ticker temperature_interrupt;
Ticker tick_interrupt;

volatile Heater_State heater = OFF;
volatile State state = IDLE;
volatile Error error_state = NONE;

volatile uint8_t duty = DEFAULT_INITIAL_DUTY;
volatile float temperature = 0.0;
unsigned long start_time = 0L;
unsigned long current_time = 0L;
bool connected = false;
volatile uint32_t millis = 0;

void loop(void);
void calibrate(bool first);
void interpret(char parameter, int value);
void spin_lights(bool first);

//INTERRUPT - increment this every ms since the normal mbed timer returns an int and we need an unsigned type
void tick(void) {millis++;}

uint32_t get_time(void){
    uint32_t copy = 0;
    __disable_irq();
    copy =  millis;
    __enable_irq();
    return copy;
}

uint8_t get_duty(void){
  __disable_irq();
  uint8_t duty_copy = duty;
  __enable_irq();
  return duty_copy;
}

void set_duty(uint8_t new_duty){
  __disable_irq();
  duty = new_duty;
  __enable_irq();
}

float get_temperature(void){
  __disable_irq();
  float temperature_copy = temperature;
  __enable_irq();
  return temperature_copy;
}

//pull the settings saved to flash into memory
void read_settings(void){
    uint8_t address = BASE_ADDRESS;
    uint16_t filter_temp = 0;
    
    IAP iap;  

    iap.read_eeprom((char*)address, (char*)&filter_temp, sizeof(filter_temp));
    address += sizeof(filter_temp);
    filter = (float)filter_temp/100;
    iap.read_eeprom((char*)address, (char*)&initial_duty, sizeof(initial_duty));
    address += sizeof(initial_duty);
    iap.read_eeprom((char*)address, (char*)&setpoint, sizeof(setpoint));
    address += sizeof(setpoint);
    iap.read_eeprom((char*)address, (char*)&tolerance, sizeof(tolerance));
    address += sizeof(tolerance);
    iap.read_eeprom((char*)address, (char*)&duty_too_large, sizeof(duty_too_large));
    address += sizeof(duty_too_large);
    iap.read_eeprom((char*)address, (char*)&temp_limit_lower, sizeof(temp_limit_lower));
    address += sizeof(temp_limit_lower);
    iap.read_eeprom((char*)address, (char*)&temp_limit_upper, sizeof(temp_limit_upper));
    address += sizeof(temp_limit_upper);
    iap.read_eeprom((char*)address, (char*)&nominal_duty, sizeof(nominal_duty));
    address += sizeof(nominal_duty);
}

//write settings to persistent memory
void write_settings(void){
 IAP iap;
 uint8_t address = 1; //BASE_ADDRESS;
 uint16_t filter_temp = 0;
    
 filter_temp = (int)(filter*100);
 
 iap.write_eeprom((char*)&filter_temp, (char*)address, sizeof(filter_temp));
 address += sizeof(filter_temp);
 iap.write_eeprom((char*)&initial_duty, (char*)address, sizeof(initial_duty));
 address += sizeof(initial_duty);
 iap.write_eeprom((char*)&setpoint, (char*)address, sizeof(setpoint));
 address += sizeof(setpoint);
 iap.write_eeprom((char*)&tolerance, (char*)address, sizeof(tolerance));
 address += sizeof(tolerance);
 iap.write_eeprom((char*)&duty_too_large, (char*)address, sizeof(duty_too_large));
 address += sizeof(duty_too_large);
 iap.write_eeprom((char*)&temp_limit_lower, (char*)address, sizeof(temp_limit_lower));
 address += sizeof(temp_limit_lower);
 iap.write_eeprom((char*)&temp_limit_upper, (char*)address, sizeof(temp_limit_upper));
 address += sizeof(temp_limit_upper);
 iap.write_eeprom((char*)&nominal_duty, (char*)address, sizeof(nominal_duty));
 address += sizeof(nominal_duty);
}

//parse commands. commands take the form of a character followed by a number, delimited by "\r|\n|;"
void getInput(void){
  static int i = 0;
  static char parameter = '_';
  static char buffer[MAX_COMMAND_LEN + 1];
  int value = 0;
  //char *endp = NULL;
  
  if(!connected) return;
  
  // listen for commands coming in
#ifdef SERIAL
  while (pc.readable()){
      char ch = pc.getc();
#else
  while (pc.available()){
      char ch = pc._getc();
#endif
    if((ch == '\r' || ch == ';' || ch == '\n') && parameter != '_'){
      if(i > 1){
        buffer[i-1] = 0;
        value = atoi(buffer);
      }
      pc.printf("%c, %d", parameter, value);
      //Serial.println("not _");
      interpret(parameter, value);
      parameter = '_';
      buffer[0] = 0;
      i=0;
      break;
    }
    else{
      if(i==0) parameter = ch;
      else buffer[i-1] = ch;
      i++;
    }
    
    if(ch == '_' || ch == '\r' || ch == ';' || ch == '\n'){
      parameter = '_';
      buffer[0] = 0;
      i=0;
    }
  }
}

//print usage info
void usage(void){
    if(!connected)return;
    pc.printf("\r\nCommands are a character followed by a number.\r\n");
    wait_ms(1);
    pc.printf("Available commands are:\r\n");
    wait_ms(1);
    pc.printf("'c': duty cap [1-100]\r\n");
    wait_ms(1);
    pc.printf("'d': set the initial duty used on startup [1-100]\r\n");
    wait_ms(1);
    pc.printf("'f': set the smoothing filter for the temperature sensor.");
    wait_ms(1);
    pc.printf(" smaller is more smoothing. [1-100]\r\n");
    wait_ms(1);
    pc.printf("'l': lower temperature limit. arbitrary units. [1-1000]\r\n");
    wait_ms(1);
    pc.printf("'r': reset all parameters to default values.\r\n");
    wait_ms(1);
    pc.printf("'n': set the nominal duty used on control cycle start [1-100]\r\n");
    wait_ms(1);
    pc.printf("'s': setpoint. same units as upper and lower temperature");
    wait_ms(1);
    pc.printf(" limit [1-1000]\r\n");
    wait_ms(1);
    pc.printf("'t': tolerance. no control action within this band. same ");
    wait_ms(1);
    pc.printf("units as upper and lower temperature limit [1-1000]\r\n");
    wait_ms(1);
    pc.printf("'u': upper temperature limit. arbitrary units. [1-1000]\r\n");
    wait_ms(1);
    pc.printf("'w': write the current control parameters to permanent memory\r\n");
    wait_ms(1);
    pc.printf("\r\n\r\n");
    wait_ms(1);
    
    pc.printf("Values can be modified by entering a ");
    pc.printf("command followed by a number.");
    wait_ms(1);
    pc.printf(" For example, entering 'c50' <enter> would limit the duty");
    wait_ms(1);
    pc.printf(" to 50%\r\n\r\n");
    wait_ms(1);

    pc.printf("Status of individual variables can be queried by entering ");
    wait_ms(1);
    pc.printf("that command name (no number afterwards), or typing any ");
    wait_ms(1);
    pc.printf("unrecognised command (which prints this message).\r\n\r\n");
    wait_ms(1);
}

void print_active_settings(void){
    if(!connected)return;
    pc.printf("Duty capped at: %u%\r\n", duty_too_large);
    wait_ms(1);
    pc.printf("Initial duty is: %u\r\n", initial_duty);
    wait_ms(1);
    pc.printf("Nominal duty is: %u\r\n", nominal_duty);
    wait_ms(1);
    pc.printf("Temperature filter is: %d\r\n", (int)(filter * 100));
    wait_ms(1);
    pc.printf("Lower temperature limit is: %u\r\n", temp_limit_lower);
    wait_ms(1);
    pc.printf("Upper temperature limit is: %u\r\n", temp_limit_upper);
    wait_ms(1);
    pc.printf("Setpoint is: %u\r\n", setpoint);
    wait_ms(1);
    pc.printf("Tolerance is %u\r\n\r\n", tolerance);
    wait_ms(1);
    //pc.printf("Spot treatment time is %d\r\n\r\n", TIP_FLASH_INTERVAL_S);
    //wait_ms(1);
}

//interpret a user command
void interpret(char parameter, int value){
  switch(parameter){
  case 'c':
    if(value != 0) duty_too_large = value;
    pc.printf("Duty cap is %u\r\n", duty_too_large);
    break;
  case 'd':
    if(value != 0) initial_duty = value;
    pc.printf("Initial duty is %u\r\n", initial_duty);
    break;
  case 'f':
    if(value != 0) filter = ((float)value) / 100.0;
    pc.printf("Filter is %d\r\n", (int)(filter * 100));
    break;
  case 'r':
    filter = DEFAULT_FILTER;
    initial_duty = DEFAULT_INITIAL_DUTY;
    nominal_duty = DEFAULT_NOMINAL_DUTY;
    setpoint = DEFAULT_SETPOINT;
    tolerance = DEFAULT_TOLERANCE;
    duty_too_large = DEFAULT_DUTY_TOO_LARGE;
    temp_limit_lower = DEFAULT_TEMP_LIMIT_LOWER;
    temp_limit_upper = DEFAULT_TEMP_LIMIT_UPPER;
    write_settings();
    pc.printf("All parameters reset to default values:\r\n");
    print_active_settings();
    break; 
  case 'l':
    if(value != 0) temp_limit_lower = value;
    pc.printf("Lower temperature limit is %u\r\n", temp_limit_lower);
    break;
  case 'n':
    if(value != 0) nominal_duty = value;
    pc.printf("Nominal duty is %u\r\n", nominal_duty);
    break;
  case 's':
    if(value != 0) setpoint = value;
    pc.printf("Setpoint is %u\r\n", setpoint);
    break;
  case 't':
    if(value != 0) tolerance = value;
    pc.printf("Tolerance is %u\r\n", tolerance);
    break;
  case 'u':
    if(value != 0) temp_limit_upper = value;
    pc.printf("Upper temperature limit is %u\r\n", temp_limit_upper);
    break;
  case 'w':
    write_settings();
    pc.printf("Wrote the current control parameters to memory.\r\n");
    break;


  default:
    usage();    
    print_active_settings();
    break;
  }
}

//put everything into a low power/off state. control loop will be unaffected by this command, so that will also need to be disabled before the heater will stay off
void all_off(){
  heater_pin = 0;
  heater = OFF; //need to treat this as volatile
  fan = 0;
  empty_led.input(); //
  fuel_gage_1.input(); // = 1;
  fuel_gage_2.input(); // = 1;
  fuel_gage_3.input(); // = 1;
  fuel_gage_4.input(); // = 1;
  tip_light = 0;
}

// run this to manually check the hardware works
// probably best to disable the heater on the first try
void functional_check(void){
  all_off();
  if(connected)pc.printf("Tip light\r\n");
  tip_light = 1;
  wait_ms(1000);
  all_off();
  if(connected)pc.printf("Empty\r\n");
  empty_led.output();
  empty_led = 0;
  wait_ms(1000);
  all_off();
  if(connected)pc.printf("Fuel Gage 1\r\n");
  fuel_gage_1.output();
  fuel_gage_1 = 0;
  wait_ms(1000);
  all_off();
  if(connected)pc.printf("Fuel Gage 2\r\n");
  fuel_gage_2.output();
  fuel_gage_2 = 0;
  wait_ms(1000);
  all_off();
  if(connected)pc.printf("Fuel Gage 3\r\n");
  fuel_gage_3.output();
  fuel_gage_3 = 0;
  wait_ms(1000);
  all_off();
  if(connected)pc.printf("Fuel Gage 4\r\n");
  fuel_gage_4.output();
  fuel_gage_4 = 0;
  wait_ms(1000);
  all_off();
  if(connected)pc.printf("Fan\r\n");
  fan = 1;
  wait_ms(5000);
  if(connected)pc.printf("Heater\r\n");
  heater_pin = 1;
  
  while(1){
    if(connected)pc.printf("Temp: %u\r\n", temp_sense.read_u16()>>6);
    wait_ms(50);
    tip_light = 1;
    wait_ms(50);
    tip_light = 0;
  }
}

// INTERRUPT - called every 4ms, samples the ADC and filters the value with a low pass first order digital filter
void get_temp(void){
  // not bothering with units, this means we need to keep the update constant at once every 4ms or we will need to set the filter again
  //temperature = filter * (temp_sense.read_u16()>>6) + (1 - filter) * temperature;
  // temperature is now in degrees C (value is at sensor, not at tips)
  temperature = filter * (51.282 * (temp_sense.read()*3.3 - 0.4)) + (1 - filter) * temperature;
}

// INTERRUPT - called every millisecond to handle maintaining the on/off state of the triac
void heater_control(void){
  static uint8_t count = 0;
  
  // count up once per interrupt.
  // safety: check to make sure we haven't exceeded MAX_DUTY safety limit
  // after duty is reached for this control period, turn the heat off 
  count++;
  
  if(heater == ON && fan.read()){
    if(count > MAX_DUTY){
      count = 0;
      heater_pin = 1;
    }
    if(count > duty)heater_pin = 0;
  }
  else heater_pin = 0;
}

// bail, something is wrong
void abort(bool error){
  uint16_t period = 1000;
  if(error) period = 250;
  // turn everything off, leave the fan on for a few seconds and flash the red LED
  control_interrupt.detach();
  all_off();
  empty_led.input();
  fan = 1;
  wait_ms(3000);
  fan = 0;
  while(1){
    empty_led.output();
    empty_led = 0;
    heater_pin = 0;
    wait_ms(period);
    heater_pin = 0;
    empty_led.input();
    heater_pin = 0;
    wait_ms(period);
  }
}

// INTERRUPT - monitor stuff that might indicate an error condition
void check_limits(void){
// need to move printing to the main loop
  if(duty >= duty_too_large){
    if(connected){
        pc.printf("Error!!! Duty cycle has become unreasonably ");
        pc.printf("large, Aborting.\r\n");
    }
    abort(true);
  }
   
  if(get_temperature() > temp_limit_upper){
    if(connected)pc.printf("Error!!! Temperature is too high, Aborting.\r\n");
    abort(true);
  }
  
  if((state == ACTIVE || state == INITIAL_RAMP) && (get_temperature() < temp_limit_lower)){
    if(connected){
        pc.printf("%f\r\n", get_temperature());
        pc.printf("Error!!! Abnormally low temperature detected. ");
        pc.printf("Please check the sensor, Aborting.\r\n");
    }
    abort(true);
  }
}

// values pulled from the MCP9701T-E/LT datasheet
// 19.5mV/deg-C, 400mV @ 0C
float adc_to_temp(float adc_value){
 //return 0.195 * adc_value - 
 return 0.196 * adc_value - 31.322;
}

void init(void){
#ifdef SERIAL
  pc.baud(115200);
  connected = true;
#else
  pc.connect();
  connected = pc.vbusDetected();
#endif

  if(connected)pc.printf("hello\r\n");
  
  tick_interrupt.attach(&tick, 0.001);
  control_interrupt.attach(&heater_control, 0.001);
  temperature_interrupt.attach(&get_temp, 0.004);
  if(!CALIBRATE)check_limits_interrupt.attach(&check_limits, 0.5);

  read_settings();
  set_duty(initial_duty);
  all_off();
}

void check_on_off(void){
static uint32_t count = 0;

    // debounce
    if(on_off){
      count++;
      if(count > ON_OFF_DEBOUNCE){
        all_off();
        state = IDLE;
      }
    }
    else count = 0; 
}

void do_idle(bool first){
    if(!on_off) state = WAIT_FOR_TIP;
}

// tip neeeds to be present before we can start the cycle
// later we should also abort the cycle if the tip is removed
void do_wait_for_tip(bool first){
  unsigned long time = get_time();
  unsigned long time_increment = time % 1800;
  
  if(first){
    if(connected)pc.printf("Looking for tip\r\n");  
  }  
    if(!tip_sensor){ //tip present is low == present
      tip_light = 1;
      fuel_gage_1.output();
      fuel_gage_1 = 0;
      fuel_gage_2.output();
      fuel_gage_2 = 0;
      fuel_gage_3.output();
      fuel_gage_3 = 0;
      fuel_gage_4.output();
      fuel_gage_4 = 0;
      empty_led.input();
      state = INITIAL_RAMP;
      if(connected)pc.printf("Found the tip\r\n");
      return;
    }
        
    if(time_increment < 300){
        fuel_gage_1.input(); // = 1;
        fuel_gage_2.input(); // = 1;
        fuel_gage_3.input(); // = 1;
        fuel_gage_4.input(); // = 1;
    }
    if(time_increment > 600){
      fuel_gage_1.output();
      fuel_gage_1 = 0;
    }
    if(time_increment > 900){
      fuel_gage_2.output();
      fuel_gage_2 = 0;
    }
    if(time_increment > 1200){
      fuel_gage_3.output();
      fuel_gage_3 = 0;
    }
    if(time_increment > 1500){
      fuel_gage_4.output();
      fuel_gage_4 = 0;
    }
}

//This should quickly take us up from ambient to the setpoint.
void do_initial_ramp(bool first){
  // set duty to initial_duty and wait to reach the setpoint to break out
  static uint32_t start_time = 0;
  static uint32_t last_print = get_time();
  
  if(first){
    print_active_settings();
    start_time = get_time();
    fan = 1;
    set_duty(initial_duty);
    heater = ON;
    if(connected)pc.printf("Initial ramp up. Duty will be held constant until setpoint is reached.\r\n");
  }
  
  if(get_time() - start_time > 60000){
     if(connected)pc.printf("Took too long to reach setpoint, aborting.\r\n");
     abort(true);
  }
  
  if(get_time() - last_print > 5000){
    if(connected)pc.printf("Duty: %u, Temp: %.2f, Time: %.2fs\r\n", get_duty(), get_temperature(), (float)get_time()/1000);
    last_print = get_time();
  } 
  
  if(get_temperature() > setpoint - tolerance){
    //now we are roughly up to temperature
    set_duty(nominal_duty);
    state = ACTIVE; 
    return;
  }
}

void do_treatment_cycle(bool first){
  static uint32_t start_time = 0;
  static uint32_t control_timer = 0;
  uint8_t duty_copy;
  float temperature_copy;
  
  if(first){
    start_time = get_time();
    control_timer = start_time;
  }
  
  uint32_t current_time = get_time() - start_time;
  
  // check if we're done
  if(current_time >= ON_TIME_S * 1000L){
    if(connected)pc.printf("Done!\r\n");
    //abort(false);
    set_duty(0);
    fan = 0;
    state = DONE;
  }
  
  //if(!tip->in_place())abort(false);
   
  if(current_time - control_timer > 5000){ // run the control loop every 5 seconds
    control_timer = current_time;
    duty_copy = get_duty();
    temperature_copy = get_temperature();
    if(temperature_copy > setpoint + tolerance) duty_copy--;
    if(temperature_copy < setpoint - tolerance) duty_copy++;
    set_duty(duty_copy);
    
    if(connected)pc.printf("Duty: %u, Temp: %.2f, Time: %.2fs\r\n", get_duty(), get_temperature(), ((float)get_time() - (float)start_time)/1000);
  }
}

void do_done(bool first){
  static uint32_t start_time = 0;
  
  if(first){
    start_time = get_time();
    //control_interrupt.detach();
    all_off();
    if(connected)pc.printf("Done!\r\n");
  }
  
  heater_pin = 0;
  uint32_t current_time = get_time() - start_time;

  if(current_time % 2000 > 1000){
    empty_led.input();
  }
  else{
    empty_led.output();
    empty_led = 0;  
  }
}

void print_state(void){
    if(!connected)return;
    printf("State:\t");
    switch(state){
        case IDLE:
          printf("IDLE\r\n");
          break;
        case WAIT_FOR_TIP:
          printf("WAIT_FOR_TIP\r\n");
          break;
        case INITIAL_RAMP:
          printf("INITIAL_RAMP\r\n");
          break;
        case ACTIVE:
          printf("ACTIVE\r\n");
          break;
        case DONE:
          printf("DONE\r\n");
          break;
        case ERROR:
          printf("ERROR\r\n");
          break;
        default: break;
    }
}

int main(){
  static State last_state = IDLE;
  init();
  if(FUNCTION_CHECK) functional_check();
  if(CALIBRATE){
      calibrate(true);
      while(1)calibrate(false);
  }
  
  while(1){
    getInput();
    
    check_on_off();
    
    bool state_change = false;
    if(state != last_state){
        state_change = true;
        last_state = state;
        print_state();
    }
      
   switch(state){
      case IDLE:
        do_idle(state_change);
        break;
      case WAIT_FOR_TIP:
        do_wait_for_tip(state_change);
        break;
      case INITIAL_RAMP:
        do_initial_ramp(state_change);
        spin_lights(state_change);
        break;
      case ACTIVE:
        do_treatment_cycle(state_change);
        spin_lights(false);
        break;
      case DONE:
        do_done(state_change);
        break;
      case ERROR:
        abort(true);
        break;
      default: break;
    }   
  }
}

void spin_lights(bool first){
  static uint32_t start_time = 0;
  
  if(first) start_time = get_time();
  uint32_t current_time = get_time() - start_time;
  
  // tip should be solid for TIP_FLASH_INTERVAL_S seconds, then flash for 3 seconds
  uint32_t tip_time = current_time % (TIP_FLASH_INTERVAL_S * 1000 + 3000);
  if(tip_time < (TIP_FLASH_INTERVAL_S * 1000)) tip_light = 1;
  else tip_light = (tip_time % 100 < 50)?1:0;
  
  // handle fuel gage LEDs
  // this should be more directly linked to the treatment stop condition
  uint32_t step = (ON_TIME_S - RED_LED_ON_TIME_S)/5;
  step *= 1000;
  
  if(current_time > step) fuel_gage_4.input();
  else{
    fuel_gage_4.output();
    fuel_gage_4 = 0;
  }
  if(current_time > 2*step) fuel_gage_3.input();
  else{
    fuel_gage_3.output();
    fuel_gage_3 = 0;
  }
  if(current_time > 3*step) fuel_gage_2.input();
  else{
    fuel_gage_2.output();
    fuel_gage_2 = 0;
  }
  if(current_time > 4*step) fuel_gage_1.input();
  else{
    fuel_gage_1.output();
    fuel_gage_1 = 0;
  }
  if(current_time > 5*step){
      empty_led.output();
      empty_led = 0;
  }
  else empty_led.input();
}

void calibrate(bool first){
  static uint32_t start_time = 0;
  static uint32_t control_timer = 0;
  uint8_t duty_copy;
  
  if(first){
    start_time = get_time();
    control_timer = start_time;
    fan = 1;
    set_duty(5);
    heater = ON;
  }
  
  uint32_t current_time = get_time() - start_time;
  
  if(current_time - control_timer > 15000){ // increase the duty by 5% every 15 seconds
    if(connected)pc.printf("Duty: %u, Temp: %f, Time: %.2fs\r\n", get_duty(), get_temperature(), ((float)get_time() - (float)start_time)/1000);
    control_timer = current_time;
    duty_copy = get_duty();
    duty_copy += 5;
    // check if we're done
    if(duty > 75 && connected){
      set_duty(1);
      pc.printf("Done!\r\n");
      abort(false);
    }
    set_duty(duty_copy);
  }    
}

/*
void calibrate(void){
  // this is really not necessary because all we care about is the temperature we need to hold the sensor at,
  // which we are assuming directly correlates to output air temp.
  // in reality it will probably be affected by ambient temperature,
  // humidity, air flow rate, elevation (air density), and other factors 
  static uint16_t count = 0;
  static uint8_t duty_copy = 5; //inital duty
  float temperature_copy;
  static int tester_count = 0;

  current_time = get_time();
  count++;

  check_limits();

  if(count > 200){ // run the control loop every second
    count = 0;
    tester_count++;
    if(tester_count % 30 == 0){ //30 seconds each duty cycle
      duty_copy += 5;
      if(duty > 85)abort(false);
      if(connected)pc.printf("Duty: %u\r\nTemps:\r\n", duty_copy);
    }
    __disable_irq();
    duty = duty_copy;
    temperature_copy = temperature;
    __enable_irq();
  
    if(connected && (tester_count % 30) > 24)pc.printf("\t%f\t%u\t%f\r\n", temperature_copy, temp_sense.read_u16(), temp_sense.read());
  }
}*/