From bff19e1286b3e931f98f1d9b944c6af11116750e Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Tue, 26 May 2015 20:47:43 +0100 Subject: [PATCH] Use statistics library for average and various monitoring changes. Signed-off-by: Pat Thoyts --- PlateWarmer.ino | 310 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 217 insertions(+), 93 deletions(-) diff --git a/PlateWarmer.ino b/PlateWarmer.ino index a43cb1e..c775a96 100644 --- a/PlateWarmer.ino +++ b/PlateWarmer.ino @@ -11,10 +11,13 @@ */ #include -#include -#include // JeeLib library +#include // Arduino core +#include // JeeLib library +#include // Arduino PID library +#include // Statistics library #include #include +#include #define COMMON_CATHODE // This device is common cathode @@ -74,8 +77,6 @@ volatile int current_digit = 0; // The timer initial counter value. static unsigned int tcnt2 = 0; -static unsigned int led_cnt = 0; - // Flag to make the display flash visibly #define STATE_NORMAL 0 #define STATE_USERPRESS 1 @@ -85,6 +86,47 @@ static byte flash_cntr = 0; static long target_temp = 0; static long last_target = 0; +class CPID +{ +public: + double mKp, mKi, mKd; + double mCp, mCi, mCd; + double mSetPoint, mPrevTime, mPrevErr; + CPID(double Kp, double Ki, double Kd) : mKp(Kp), mKi(Ki), mKd(Kd) { + mSetPoint = mPrevTime = mPrevErr = 0; + mCp = mCi = mCd = 0; + } + void SetPoint(double point) { mSetPoint = point; } + double update(double current_time, double current_value) { + double err = mSetPoint - current_value; + double dt = current_time - mPrevTime; + double de = err - mPrevErr; + mCi += err * dt; + if (mCi < 0.0) mCi = 0.0; + else if (mCi > 5000.0) mCi = 5000.0; + double D = 0; + if (dt > 0) D = de/dt; + mPrevTime = current_time; + mPrevErr = err; + err = (mKp * err) + (mKi * mCi) + (mKd * D); + if (err < 0.0) err = 0.0; + else if (err > 5000.0) err = 5000.0; + return err; + } + double GetKp() const { return mKp; } + double GetKi() const { return mKi; } + double GetKd() const { return mKd; } +}; + + +double Input = 0, Output = 0, SetPoint = 0; +int WindowSize = 5000; +unsigned long windowStartTime; +// original 2, 5, 1 massive overshoot and +2 degrees offset +// 0.072, 14.8, 2.39, DIRECT); +//PID pid(&Input, &Output, &SetPoint, 1.0, 4.0, 10.0, DIRECT); +CPID pid(1.0, 0.0, 0.0); + // Serial print using FLASH strings: Serial_print(PSTR(...)); static void Serial_print(PGM_P s) { @@ -122,9 +164,13 @@ display(int digit, const char *num) return; } - // select our digit and configure - digitalWrite(select[digit], LOW); - set_segments( Digits[num[digit] - 0x30] | ((digit == 1) ? 0x80 : 0) ); + // do not use digit 0 (only showing tenths of a degree) + // The OR'd part selects the decimal point (after digit 2) + if (digit != 0) { + // select our digit and configure + digitalWrite(select[digit], LOW); + set_segments( Digits[num[digit] - 0x30] | ((digit == 2) ? 0x80 : 0) ); + } } // Initialize timer2 as interrupt source @@ -146,7 +192,8 @@ Timer2Init() // Delay is (prescale / cpu_freq * 1e6) // [@16MHz with 1024 prescaler yields 75us tick] // So set tcnt2 = 256 - (6000us / 75us) for 6ms update - tcnt2 = 176; + // was 176 for 6ms, now 190 for 5ms + tcnt2 = 190; TCNT2 = tcnt2; // 18.9e: re-enable the timer overflow interrupt flag TIMSK2 |= (1 << TOIE2); @@ -157,11 +204,6 @@ ISR(TIMER2_OVF_vect) TCNT2 = tcnt2; // reset the timer display(current_digit, Display); current_digit = (current_digit + 1) % 4; - ++led_cnt; - if (led_cnt > 166) { - //PORTB ^= _BV(PB5); // toggle pin 13 - led_cnt = 0; - } } // We store the currently set target temperature in the EEPROM @@ -176,7 +218,7 @@ static long stored_target_read() | (EEPROM.read(1) << 8) | (EEPROM.read(0)); } - Serial_print(PSTR("eeprom read: ")); + Serial.print(F("# eeprom read: ")); Serial.println(t); return t; } @@ -187,12 +229,14 @@ static void stored_target_write(long t) EEPROM.write(1, (t >> 8) & 0xff); EEPROM.write(2, (t >> 16) & 0xff); EEPROM.write(3, (t >> 24) & 0xff); - Serial_print(PSTR("eeprom write: ")); + Serial.print(F("# eeprom write: ")); Serial.println(t); } static void setDisplay(long v) { + // Only show 1 dp (input is in hundreds) + v = (v + 50) / 10; // round to nearest 10th degree Display[3] = 0x30 + (v % 10); Display[2] = 0x30 + ((v/10) % 10); Display[1] = 0x30 + ((v/100) % 10); @@ -203,7 +247,7 @@ void setup() { // disable TWI, Timer1, SPI and USART0 - //PRR &= (1< 11000) target_temp = 11000; setDisplay(target_temp); - Serial_print(PSTR("target: ")); + Serial_print(PSTR("# select target: ")); Serial.println(target_temp); } -MilliTimer timer0, timer1, timer2, timer3; +static void setHeating(bool on) +{ + // setting pin 8 low puts the MOC led _on_ + if (on) { + digitalWrite(MOC_PIN, LOW); + digitalWrite(LED_PIN, HIGH); + } else { + digitalWrite(MOC_PIN, HIGH); + digitalWrite(LED_PIN, LOW); + } +} + +static void Serial_print_value_space(double value) +{ + Serial.print(value); + Serial_print(PSTR(" ")); +} + +MilliTimer timerPID, timerKeypress, timer2, timerPrint; bool keypress_delay = false; +bool need_debounce = true, debounce_left = false, debounce_right = false; void loop() { - long v = 0, t[NUMSAMPLES]; int n; - if (timer1.poll(100)) { - bool left_button = digitalRead(BUTTON_L) != HIGH; - bool right_button = digitalRead(BUTTON_R) != HIGH; - switch (display_state) - { - case STATE_NORMAL: - if (left_button || right_button) { - setState(STATE_USERPRESS); - timer2.set(1000); - } - break; - case STATE_USERPRESS: - if (timer2.poll()) { + wdt_reset(); + + // key press handling + if (timerKeypress.poll(50)) { + bool left = digitalRead(BUTTON_L) != HIGH; + bool right = digitalRead(BUTTON_R) != HIGH; + if (need_debounce) { + debounce_left = left; + debounce_right = right; + need_debounce = false; + } else { + // debounce: button press must be true on each 50ms pass + bool left_button = left && debounce_left; + bool right_button = right && debounce_right; + need_debounce = true; + switch (display_state) + { + case STATE_NORMAL: if (left_button || right_button) { - setState(STATE_FLASHING); + setState(STATE_USERPRESS); + timer2.set(1000); + } + break; + case STATE_USERPRESS: + if (timer2.poll()) { + if (left_button || right_button) { + setState(STATE_FLASHING); + timer2.set(5000); + keypress_delay = true; + setDisplay(target_temp); + last_target = target_temp; + } + else { + setState(STATE_NORMAL); + } + } + break; + case STATE_FLASHING: + // avoid accepting presses for the first second + if (!keypress_delay) { + if (left_button) + incrementTargetTemp(-100); + if (right_button) + incrementTargetTemp(100); + } + if (left_button || right_button) // reset timeout timer2.set(5000); - keypress_delay = true; - setDisplay(target_temp); - last_target = target_temp; + if (keypress_delay) { + if (timer2.remaining() < 4000) + keypress_delay = false; } - else { + if (timer2.poll()) { setState(STATE_NORMAL); + if (target_temp != last_target) { + stored_target_write(target_temp); + } } - } - break; - case STATE_FLASHING: - // avoid accepting presses for the first second - if (!keypress_delay) { - if (left_button) - incrementTargetTemp(-100); - if (right_button) - incrementTargetTemp(100); - } - if (left_button || right_button) // reset timeout - timer2.set(5000); - if (keypress_delay) { - if (timer2.remaining() < 4000) - keypress_delay = false; - } - if (timer2.poll()) { - setState(STATE_NORMAL); - if (target_temp != last_target) { - stored_target_write(target_temp); - } - } - break; + break; + } + } + } + + long current_temp = 0; + Statistic stats; + if (true) { // timerPID.poll(200)) { + //long t[NUMSAMPLES]; + unsigned long now = millis(); + + for (n = 0; n < NUMSAMPLES; ++n) { + //t[n] = getTemperature(); + long v = getTemperature(); + stats.add(v); + delay(10); + } + //for (n = 0 ; n < NUMSAMPLES; ++n) { + // current_temp += t[n]; + //} + //current_temp /= NUMSAMPLES; + current_temp = static_cast(stats.average()); + + // Use PID algorithm to determine the 'on' time. + SetPoint = target_temp; + Input = current_temp; + //pid.Compute(); + pid.SetPoint(target_temp); + Output = pid.update(now, current_temp); + + if (now - windowStartTime > WindowSize) { + windowStartTime += WindowSize; + } + if (Output > now - windowStartTime) { + setHeating(true); + } else { + setHeating(false); } } - if (timer0.poll(1000)) { + if (timerPrint.poll(1000)) { + unsigned long now = millis(); + String s; + char sp = ' '; + s.concat(now); + s.concat(sp); + s.concat(static_cast(SetPoint)); + s.concat(sp); + s.concat(static_cast(Input)); + s.concat(sp); + s.concat(static_cast(Output)); + s.concat(sp); + s.concat(static_cast(stats.pop_stdev())); + s.concat(sp); + uint8_t sum = 0; + for (int n = 0; n < s.length(); ++n) { + sum += static_cast(s[n]); + } + Serial.print(s); + Serial.println(sum, HEX); +#if 0 + Serial.print(now); + Serial_print(PSTR(" ")); + Serial_print_value_space(SetPoint); + Serial_print_value_space(Input); + Serial_print_value_space(Output); + // pin 8 == PORTB pin 0: low for on + Serial.println((PORTB&1) ? "off " : "on "); +#endif if (display_state != STATE_FLASHING) { - Serial.print(millis()); - Serial.print(" "); - for (n = 0; n < NUMSAMPLES; ++n) { - t[n] = getTemperature(); - delay(10); - } - for (n = 0 ; n < NUMSAMPLES; ++n) { - v += t[n]; - } - v /= NUMSAMPLES; - // setting pin 8 low puts the MOC led _on_ - if (v < target_temp) { - digitalWrite(MOC_PIN, LOW); - digitalWrite(LED_PIN, HIGH); - } else { - digitalWrite(MOC_PIN, HIGH); - digitalWrite(LED_PIN, LOW); - } - // pin 8 == PORTB pin 0: low for on - Serial.print((PORTB&1) ? "off " : "on "); - Serial.println(v); - setDisplay(v); + setDisplay(current_temp); } } } -- 2.23.0