void start_autotune() { power = 127; auto_tune.SetControlType(1); auto_tune.SetNoiseBand(config.noise_band); auto_tune.SetOutputStep(127); auto_tune.SetLookbackSec((int)(config.lookback_min * 60)); Serial.println("Starting Autotune"); tuning = true; }
void setup() { pinMode(MOSFET_PIN, OUTPUT); pinMode(HEAT_ON_LED_PIN, OUTPUT); pid.SetOutputLimits(-PID_OUTPUT_RANGE, PID_OUTPUT_RANGE); pid_autotune.SetOutputStep(PID_OUTPUT_RANGE); pid_autotune.SetControlType(1); // PID control type. EEPROM.begin(EEPROM_TOTAL_BYTES); byte* p = (byte*)(void*)&settings; for (unsigned int i = 0; i < sizeof(settings); i++) { *p++ = EEPROM.read(EEPROM_SETTINGS_LOCATION + i); } update_pid_tunings(); handle_enabled(); }
void update() { if (settings.enabled) { if (autotune_running) { if (pid_autotune.Runtime()) { autotune_running = false; settings.pid.kp = pid_autotune.GetKp(); settings.pid.ki = pid_autotune.GetKi(); settings.pid.kd = pid_autotune.GetKd(); update_pid_tunings(); write_settings(); } } if (! autotune_running) { pid.Compute(); } pid_output_sum += pid_output; pid_output_count++; unsigned long now = millis(); if (now >= window_end_millis) { pid_output_average = pid_output_sum / pid_output_count; if (pid_output_average >= settings.heat_on.threshold) { set_heat(true); } else if (pid_output_average + settings.heat_on.delta <= settings.heat_on.threshold) { set_heat(false); } reset_window(); } } }
void control_loop() { pinMode( control_pin, OUTPUT ); if(tuning && auto_tune.Runtime()) { finish_autotune(); } else if(!tuning) { pid.SetMode(!config.paused); pid.SetTunings(profiles[config.driving].kp, profiles[config.driving].ki, profiles[config.driving].kd); pid.SetSampleTime(1000 * profiles[config.driving].sample_time); // Update the control value once per second current_target_temp = profiles[config.driving].target_temp; pid.Compute(); } if(last_power != power) { last_power = power; analogWrite(control_pin, power); Serial.print("Log\tpower\t"); Serial.println((100.0 * ((float)power / 255.0))); } }
void finish_autotune() { Serial.println("Finished autotune"); Serial.print("Kp: "); Serial.println(auto_tune.GetKp()); Serial.print("Ki: "); Serial.println(auto_tune.GetKi()); Serial.print("Kd: "); Serial.println(auto_tune.GetKd()); profiles[config.driving].kp = auto_tune.GetKp(); profiles[config.driving].ki = auto_tune.GetKi(); profiles[config.driving].kd = auto_tune.GetKd(); save(); tuning = false; }
namespace Thermostat { settings_struct settings; bool heat_on = false; unsigned long window_end_millis; double pid_output; double pid_output_sum; int pid_output_count; double pid_output_average = 0.0; bool autotune_running = false; PID pid( &TemperatureSensor::temperature, &pid_output, &settings.temperature, settings.pid.kp, settings.pid.ki, settings.pid.kd, DIRECT); PID_ATune pid_autotune(&TemperatureSensor::temperature, &pid_output); void setup() { pinMode(MOSFET_PIN, OUTPUT); pinMode(HEAT_ON_LED_PIN, OUTPUT); pid.SetOutputLimits(-PID_OUTPUT_RANGE, PID_OUTPUT_RANGE); pid_autotune.SetOutputStep(PID_OUTPUT_RANGE); pid_autotune.SetControlType(1); // PID control type. EEPROM.begin(EEPROM_TOTAL_BYTES); byte* p = (byte*)(void*)&settings; for (unsigned int i = 0; i < sizeof(settings); i++) { *p++ = EEPROM.read(EEPROM_SETTINGS_LOCATION + i); } update_pid_tunings(); handle_enabled(); } void update() { if (settings.enabled) { if (autotune_running) { if (pid_autotune.Runtime()) { autotune_running = false; settings.pid.kp = pid_autotune.GetKp(); settings.pid.ki = pid_autotune.GetKi(); settings.pid.kd = pid_autotune.GetKd(); update_pid_tunings(); write_settings(); } } if (! autotune_running) { pid.Compute(); } pid_output_sum += pid_output; pid_output_count++; unsigned long now = millis(); if (now >= window_end_millis) { pid_output_average = pid_output_sum / pid_output_count; if (pid_output_average >= settings.heat_on.threshold) { set_heat(true); } else if (pid_output_average + settings.heat_on.delta <= settings.heat_on.threshold) { set_heat(false); } reset_window(); } } } void handle_enabled() { if (settings.enabled) { reset_window(); pid.SetMode(AUTOMATIC); } else { cancel_autotune(); pid.SetMode(MANUAL); set_heat(false); } } void update_pid_tunings() { pid.SetTunings(settings.pid.kp, settings.pid.ki, settings.pid.kd); } void set_heat(bool is_on) { digitalWrite(MOSFET_PIN, is_on); digitalWrite(HEAT_ON_LED_PIN, ! is_on); heat_on = is_on; } void reset_window() { window_end_millis = millis() + settings.pid.window_seconds * 1000; pid_output_sum = 0.0; pid_output_count = 0; } void start_autotune() { pid_autotune.SetLookbackSec(settings.pid_autotune.lookback_minutes * 60); pid_autotune.SetNoiseBand(settings.pid_autotune.noise_band); autotune_running = true; } void cancel_autotune() { if (autotune_running) { pid_autotune.Cancel(); autotune_running = false; } } void apply_settings(settings_struct new_settings) { settings_struct old_settings = settings; settings = new_settings; if (settings.enabled != new_settings.enabled) { handle_enabled(); } if (settings.temperature != new_settings.temperature) { cancel_autotune(); } if (settings.pid.kp != new_settings.pid.kp or settings.pid.ki != new_settings.pid.ki or settings.pid.kd != new_settings.pid.kd) { update_pid_tunings(); } write_settings(); } void write_settings() { const byte* p = (const byte*)(const void*)&settings; for (unsigned int i = 0; i < sizeof(settings); i++) { EEPROM.write(EEPROM_SETTINGS_LOCATION + i, *p++); } EEPROM.commit(); } }
void cancel_autotune() { if (autotune_running) { pid_autotune.Cancel(); autotune_running = false; } }
void start_autotune() { pid_autotune.SetLookbackSec(settings.pid_autotune.lookback_minutes * 60); pid_autotune.SetNoiseBand(settings.pid_autotune.noise_band); autotune_running = true; }
int main(int argc, char **argv) { // Setup setup_config(argc,argv); setup_state(); setup_sigs(); // Objects logger.setup(); server.setup(); sensor.setup(); heater.setup(); // Thread if(pthread_create( &serverThread, NULL, serverPoll, NULL) != 0) logger.fail("Server thread creation failure"); float chosenSetPoint; int sensorResult = 0; clock_t nextLoop; conf.update = 1000/conf.update; timeTick(); nextLoop = state.time + conf.update; logger.info("Starting up Coffeed"); while(state.run) { // Time update timeTick(); if(state.time >= nextLoop) { // Get sensor data sensorResult = sensor.update(); // If active choose set point if(state.active) { if(state.brewmode == TRUE) chosenSetPoint = conf.brewPoint; else chosenSetPoint = conf.steamPoint; // Sensor fault if(!sensorResult) { logger.fail("Sensor read failure"); state.active = FALSE; heater.off(); } // Tuning mode else if(state.tuning != FALSE) { // Begin if(state.tuning == 2) { state.tuning = TRUE; heater.setPower(50); aTune.cancel(); //aTune.setNoiseBand(1); //aTune.setOutputStep(50); //aTune.setLookbackSec(20); //aTune.setControlType(1); logger.info("Autotuning BEGIN %f %f %f",conf.pgain,conf.igain,conf.dgain); } // While (1) we want to tune + (2) update is not done else if(state.tuning == TRUE && aTune.update() == FALSE) { logger.info("tuning"); } // Tuning complete because state.tuning = TRUE but aTune == TRUE else { logger.info("tuning successful am I right?"); // Done conf.pgain = aTune.getKp(); conf.igain = aTune.getKi(); conf.dgain = aTune.getKd(); logger.info("Autotuning FINISH %f %f %f",conf.pgain,conf.igain,conf.dgain); state.tuning = FALSE; } } // Regular control else { // PID result state.pidResult = pid.result(chosenSetPoint, state.tempPoint); // Set Heater Duty heater.setPower( state.pidResult ); // Log logger.debug("PID Result %d temperature %f setP %f brewP %f steamP %f", state.pidResult, state.tempPoint, chosenSetPoint, conf.brewPoint, conf.steamPoint); } } // Inactive system else { // Tuning can't happen in a inactive system if(state.tuning != FALSE) { state.tuning = FALSE; aTune.cancel(); } if(state.brewmode) state.brewmode = TRUE; chosenSetPoint = 0; heater.off(); } // Get Heater Duty // This should be centralized to the heater state.power = heater.getPower(); // Schedule the next loop after the whole operation nextLoop = state.time + conf.update; } else { // Sleep until we need you usleep(1000 * (nextLoop - state.time)); } } logger.info("Shutting down Coffeed"); }