commit 7a49669a92326128519b0505f5f9b756289d0ff2 Author: Burathar Date: Sun Oct 4 13:48:50 2020 +0200 add initial files diff --git a/Steinhart-Hart coefficients Green Thermorisistor.txt b/Steinhart-Hart coefficients Green Thermorisistor.txt new file mode 100644 index 0000000..b921a84 --- /dev/null +++ b/Steinhart-Hart coefficients Green Thermorisistor.txt @@ -0,0 +1,14 @@ +Measurements Green Thermoresistor + +1 graden C: 36500 Ohm +41 graden c: 4930 Ohm +100 graden C: 611 Ohm + +Steinhart-Hart coefficients: + +A 0.0010626977757858514 +B 0.00025567427237838396 +C -8.706543235296982e-8 + +Calculated with: +https://rusefi.com/Steinhart-Hart.html diff --git a/button.cpp b/button.cpp new file mode 100644 index 0000000..76c651f --- /dev/null +++ b/button.cpp @@ -0,0 +1,43 @@ +// Source: https://forum.arduino.cc/index.php?topic=14479.0 + +#include "button.h" + +Button::Button(byte pin) { + this->pin = pin; + init(); +} + +void Button::init() { + pinMode(pin, INPUT); +} + +byte Button::getState() { + byte event = 0; + buttonVal = digitalRead(pin); + // Button pressed down + if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce) + { + event = 1; + downTime = millis(); + ignoreUp = false; + } + // Button released + else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce) + { + if (not ignoreUp) upTime = millis(); + } + + // Test for hold + if (buttonVal == LOW && (millis() - downTime) >= holdTime) { + // Trigger "normal" hold + event = 2; + ignoreUp = true; + // Trigger "long" hold + if ((millis() - downTime) >= longHoldTime) + { + event = 3; + } + } + buttonLast = buttonVal; + return event; +} diff --git a/button.h b/button.h new file mode 100644 index 0000000..21b7d09 --- /dev/null +++ b/button.h @@ -0,0 +1,32 @@ +//https://roboticsbackend.com/arduino-object-oriented-programming-oop/ + +#ifndef MY_BUTTON_H +#define MY_BUTTON_H + +#include + +class Button { + + private: + byte pin; + + // Button timing variables + int debounce = 20; // ms debounce period to prevent flickering when pressing or releasing the button + int holdTime = 500; // ms hold period: how long to wait for press+hold event + int longHoldTime = 1500; // ms long hold period: how long to wait for press+hold event + + // Button variables + boolean buttonVal = HIGH; // value read from button + boolean buttonLast = HIGH; // buffered value of the button's previous state + long downTime = -1; // time the button was pressed down + long upTime = -1; // time the button was released + boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered + + public: + Button(byte pin); + + void init(); + byte getState(); +}; + +#endif diff --git a/queue.cpp b/queue.cpp new file mode 100644 index 0000000..e2ce285 --- /dev/null +++ b/queue.cpp @@ -0,0 +1,49 @@ +#include "queue.h" + +Queue::Queue() { + init(); +} + +void Queue::init(){ + +} + +void Queue::push(byte value) { + list[writeIndex] = value; + writeIndex = (writeIndex + 1) % sizeof(list); +} + +byte Queue::pop() { + byte value = list[readIndex]; + list[readIndex] = 0; + readIndex = (readIndex + 1) % sizeof(list); + return value; +} + +byte * Queue::clone() { + for(int i=0; i maxValue) maxValue = list[i]; + } + return maxValue; +} + +byte Queue::getMin(){ + byte minValue = 255; + for (int i=0;i 0) minValue = list[i]; + } + return minValue; +} diff --git a/queue.h b/queue.h new file mode 100644 index 0000000..66833ca --- /dev/null +++ b/queue.h @@ -0,0 +1,28 @@ +#ifndef MY_QUEUE_H +#define MY_QUEUE_H + +#define TEMP_QUEUE_LENGTH 58 + +#include + +class Queue { + + private: + byte list[TEMP_QUEUE_LENGTH]; + byte queue[TEMP_QUEUE_LENGTH]; + byte readIndex = 0; + byte writeIndex = 0; + + public: + Queue(); + + void init(); + void push(byte value); + byte pop(); + byte * clone(); + byte getMax(); + byte getMin(); + byte lookup(byte index); +}; + +#endif diff --git a/relay.cpp b/relay.cpp new file mode 100644 index 0000000..8496603 --- /dev/null +++ b/relay.cpp @@ -0,0 +1,31 @@ +#include "relay.h" + +Relay::Relay(byte pin) { + // Use 'this->' to make the difference between the + // 'pin' attribute of the class and the + // local variable 'pin' created from the parameter. + this->pin = pin; + init(); +} + +void Relay::init() { + pinMode(pin, OUTPUT); + // Always try to avoid duplicate code. + // Instead of writing digitalWrite(pin, LOW) here, + // call the function off() which already does that + off(); +} + +void Relay::on() { + digitalWrite(pin, LOW); + state = LOW; +} + +void Relay::off() { + digitalWrite(pin, HIGH); + state = HIGH; +} + +bool Relay::getState() { + return !state; +} diff --git a/relay.h b/relay.h new file mode 100644 index 0000000..2e2f3c8 --- /dev/null +++ b/relay.h @@ -0,0 +1,21 @@ +#ifndef MY_RELAY_H +#define MY_RELAY_H + +#include + +class Relay { + + private: + byte pin; + bool state = false; + + public: + Relay(byte pin); + + void init(); + void on(); + void off(); + bool getState(); +}; + +#endif diff --git a/smoker.ino b/smoker.ino new file mode 100644 index 0000000..358c330 --- /dev/null +++ b/smoker.ino @@ -0,0 +1,301 @@ +#include +#include +#include + +#include "button.h" +#include "relay.h" +#include "temperature_sensor.h" +#include "queue.h" + +#define AIR_TEMP_SENSOR_PIN A1 + +#define RELAY_PIN 6 +#define SPEAKER_PIN 7 +#define BUTTON_RIGHT_PIN 8 +#define BUTTON_LEFT_PIN 9 +#define BUTTON_DOWN_PIN 10 +#define BUTTON_UP_PIN 11 + +#define MAX_STATE 4 // Amount of display states + +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels + +#define TEMP_DELTA 4 // Minimum temperature difference before relay is toggled +#define ALARM_TEMP_DELTA 15 // Minimum temperature difference before the alarm wil sound +#define TEMP_PLOT_INTERVAL 60000 // Amount of milliseconds between temperature measuring points in the plot + +byte state = 0; + +int targetTemp = 70; +int alarmState = 0; + +long lastTempQueue = -1000000; + +Button buttonUp(BUTTON_UP_PIN); +Button buttonDown(BUTTON_DOWN_PIN); +Button buttonLeft(BUTTON_LEFT_PIN); +Button buttonRight(BUTTON_RIGHT_PIN); + +Relay relay(RELAY_PIN); + +// Pass pin, reference voltage, and a,b,c coefficients +TemperatureSensor airTempSensor(AIR_TEMP_SENSOR_PIN, 10000, 0.0010626977757858514, 0.00025567427237838396, -8.706543235296982e-8); + +// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); + +Queue tempQueue; + +void setup() { + //Serial.begin(9600); + if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { + //Serial.println(F("SSD1306 allocation failed")); + for(;;); + } + display.setRotation(2); + display.clearDisplay(); + display.setTextColor(WHITE); + airTempSensor.preload(); +} + +void loop() { + updateAirTemp(); + byte bUp = buttonUp.getState(); + byte bDown = buttonDown.getState(); + byte bLeft = buttonLeft.getState(); + byte bRight = buttonRight.getState(); + + if (bRight >= 1){ + if(state >= MAX_STATE) state = 0; + else state += 1; + } + if (bLeft >= 1){ + if(state <= 0) state = MAX_STATE; + else state -= 1; + } + + display.clearDisplay(); + displayMenuBar(); + + switch (state) { + case 0: + targetTempState(bUp, bDown); + break; + case 1: + manualState(bUp, bDown); + break; + case 2: + uptimeState(bUp, bDown); + break; + case 3: + tempPlotState(bUp, bDown); + break; + case 4: + alarmSettingState(bUp, bDown); + break; + } + display.display(); +} + +void updateAirTemp(){ + airTempSensor.update(); + if(millis() - lastTempQueue > TEMP_PLOT_INTERVAL){ + tempQueue.push(airTempSensor.getTemperature()); + lastTempQueue = millis(); + } +} + +void updateRelay(){ + float temperature = airTempSensor.getTemperature(); + if (temperature - targetTemp >= TEMP_DELTA){ //Temp too high + relay.off(); + } else if (targetTemp - temperature >= TEMP_DELTA) { //Temp too low + relay.on(); + } + if(temperature - targetTemp >= ALARM_TEMP_DELTA){ + int second = millis() % 1000; + if(alarmState == 0 and second < 100){ + alarmState = 1; + tone(SPEAKER_PIN, 440, 400); + } + if(alarmState == 1 and second > 500 and second < 600){ + alarmState = 0; + tone(SPEAKER_PIN, 554, 400); + } + } +} + +void targetTempState(byte bUp, byte bDown){ + updateRelay(); + + switch(bUp){ + case 1: + targetTemp +=1; + break; + case 2: + targetTemp +=1; + break; + case 3: + targetTemp +=3; + break; + } + switch(bDown){ + case 1: + targetTemp -=1; + break; + case 2: + targetTemp -=1; + break; + case 3: + targetTemp -=3; + break; + } + + displayCurrentTemp(); + + // Target Temp + display.setTextSize(1); + display.setCursor(0,40); + display.print("Target: "); + display.setTextSize(2); + display.setCursor(0,50); + display.print(targetTemp); + display.print(" "); + display.setTextSize(1); + display.cp437(true); + display.write(167); + display.setTextSize(2); + display.print("C"); + + //display.drawRect(0, 0, display.width(), display.height(), SSD1306_WHITE); +} + +void manualState(byte bUp, byte bDown) { + if (bUp == 1) { + relay.on(); + } + if (bDown == 1) { + relay.off(); + } + + displayCurrentTemp(); + + display.setTextSize(1); + display.setCursor(0,40); + display.print("Manual Mode"); + display.setTextSize(2); + display.setCursor(0,50); + if (relay.getState()){ + display.print("Heater ON"); + } else { + display.print("Heater OFF"); + } +} + +void uptimeState(byte bUp, byte bDown){ + updateRelay(); + unsigned int seconds = millis()/1000; //convect milliseconds to seconds + unsigned int minutes=seconds/60; //convert seconds to minutes + unsigned int hours=minutes/60; //convert minutes to hours + seconds=seconds-(minutes*60); //subtract the coverted seconds to minutes in order to display 59 secs max + minutes=minutes-(hours*60); //subtract the coverted minutes to hours in order to display 59 minutes max + + displayCurrentTemp(); + + display.setTextSize(1); + display.setCursor(0,40); + display.print("Uptime:"); + display.setTextSize(2); + display.setCursor(0,50); + if(hours < 10) display.print(0); + display.print(hours); + display.print(":"); + if(minutes < 10) display.print(0); + display.print(minutes); + display.print(":"); + if(seconds < 10) display.print(0); + display.print(seconds); +} + +void displayMenuBar(){ + byte space = display.width() / (MAX_STATE + 1); + for (byte i = 0; i < MAX_STATE + 1; i++){ + if (i == state) display.fillCircle(space * i + space / 2, 3, 3, WHITE); + else display.drawCircle(space * i + space / 2, 3, 3, WHITE); + } +} + +void tempPlotState(byte bUp, byte bDown){ + updateRelay(); + + byte maxValue = tempQueue.getMax(); + byte minValue = tempQueue.getMin(); + + if(minValue > 99) minValue = 99; + + display.setTextSize(1); + if(maxValue > 99)display.setCursor(110,8); + else display.setCursor(116,8); + display.print(maxValue); + display.setCursor(116,57); + display.print(minValue); + + if(maxValue - minValue < 10){ + minValue -= 1; + maxValue += 1; + } + + for (int i=0; ipin = pin; + this->refResistance = refResistance; + this->a = a; + this->b = b; + this->c = c; + init(); +} + +#define DEBUG_OUTPUT Serial + +void TemperatureSensor::init() { + pinMode(pin, INPUT); +} + +void TemperatureSensor::preload() { + for(int i=0; i< 10; i++){ + update(); + } +} + +void TemperatureSensor::update() { + if(measurementCounter < 10){ //Measure 10 times before calculating temperature from average + measurementAccumulative += analogRead(pin); + measurementCounter++; + } else { + int sensorValue = measurementAccumulative/measurementCounter; + // voltage = sensorValue*5.0/1024.0 + // r1 = (vdd-vout)*r2/vout + double resistance = ((1.0-sensorValue/1024.0)*refResistance/(sensorValue/1024.0)); // this is our probe resistance. + double lt = log(resistance); + temperature = (1 / (a+b*lt+c*lt*lt*lt)) - 273.15; //Calculate temperature in Kelvin according to Steinhart-Hart equation, then convert to Celcius + if(temperature < -35) temperature = sqrt (-1); // If value is unpossibly low, return NaN + measurementAccumulative = 0; + measurementCounter = 0; + } +} + +float TemperatureSensor::getTemperature() { + return temperature; +} diff --git a/temperature_sensor.h b/temperature_sensor.h new file mode 100644 index 0000000..7658e5c --- /dev/null +++ b/temperature_sensor.h @@ -0,0 +1,26 @@ +#ifndef MY_TEMP_SENSOR_H +#define MY_TEMP_SENSOR_H + +#include + +class TemperatureSensor { + + private: + byte pin; + int refResistance; + double a,b,c; + + byte measurementCounter; + int measurementAccumulative; + float temperature; + + public: + TemperatureSensor(byte pin, int refResistance, double a, double b, double c); + + void init(); + void preload(); + void update(); + float getTemperature(); +}; + +#endif