Made a much more involved kettle control class.

All the input/output tracking is handled with class members
instead of passing pointers to global variables.
This commit is contained in:
Chris Giacofei 2022-01-21 10:56:41 -05:00
parent c7857c1b03
commit 8b72a16203
5 changed files with 100 additions and 77 deletions

View File

@ -3,28 +3,21 @@
#include <SPI.h> #include <SPI.h>
#include <Ethernet.h> #include <Ethernet.h>
#include <EEPROM.h> #include <EEPROM.h>
#include <PubSubClient.h>
#include <cJSON.h>
// Additoinal Libraries // Additoinal Libraries
#include <cJSON.h>
#include <PubSubClient.h>
#include <LiquidCrystal_I2C.h> #include <LiquidCrystal_I2C.h>
#include <LiquidMenu.h> // LiquidMenu_config.h needs to be modified to use I2C. #include <LiquidMenu.h> // LiquidMenu_config.h needs to be modified to use I2C.
#include <MD_REncoder.h> #include <MD_REncoder.h>
#include <Adafruit_MAX31865.h>
// My Includes // My Includes
#include "config.h" #include "config.h"
#include "globals.h"
#include "button.h" #include "button.h"
#include "slowPWM.h" #include "slowPWM.h"
#include "thermoControl.h" #include "thermoControl.h"
// Global variables.
uint8_t KettleDuty = 0;
uint8_t KettleTemp;
uint8_t KettleSetpoint;
modes KettleMode = OFF;
const int UpdateInterval = 5000;
/* Defined in config.h for now */ /* Defined in config.h for now */
// uint8_t ThreshPWR = 5; // uint8_t ThreshPWR = 5;
// double Hysteresis = 1; // double Hysteresis = 1;
@ -34,24 +27,20 @@ Button Enter;
MD_REncoder rotary = MD_REncoder(I_DT, I_CLK); MD_REncoder rotary = MD_REncoder(I_DT, I_CLK);
LiquidCrystal_I2C lcd(0x27,20,4); LiquidCrystal_I2C lcd(0x27,20,4);
// Internal I/O // Internal I/O objects.
Adafruit_MAX31865 KettleThermo = Adafruit_MAX31865(kettleRTDCS);
slowPWM boilPWM; slowPWM boilPWM;
thermoControl KettleController; thermoControl KettleController;
void MessageReceived(char*, byte*, unsigned int); // Network objects.
EthernetClient net; EthernetClient net;
PubSubClient mqtt_client; PubSubClient mqtt_client;
unsigned long lastRun = 0;
// Return a character array to represent the // Return a character array to represent the
// On/Off state of the kettle. // state of the kettle.
char* ShowKettleState() { char* ShowKettleState() {
if (KettleMode == MANUAL) { if (KettleController.Mode() == MANUAL) {
return (char*)F("Kettle: Manual"); return (char*)F("Kettle: Manual");
} else if (KettleMode == AUTOMATIC) { } else if (KettleController.Mode() == AUTOMATIC) {
return (char*)F("Kettle: Auto"); return (char*)F("Kettle: Auto");
} else { } else {
return (char*)F("Kettle: Off"); return (char*)F("Kettle: Off");
@ -61,18 +50,18 @@ char* ShowKettleState() {
char* ShowKettleSetting() { char* ShowKettleSetting() {
static char LCD_Line[21]; static char LCD_Line[21];
char setting[4]; char setting[4];
if (KettleMode == MANUAL) { if (KettleController.Mode() == MANUAL) {
strcpy(LCD_Line, (char*)F("Kettle Power: ")); strcpy(LCD_Line, (char*)F("Kettle Power: "));
itoa(KettleDuty / 10, setting, 10); itoa(KettleController.Power(), setting, 10);
strcat(LCD_Line, setting); strcat(LCD_Line, setting);
strcat(LCD_Line, "%"); strcat(LCD_Line, (char*)F("%"));
return LCD_Line; return LCD_Line;
} else if (KettleMode == AUTOMATIC) { } else if (KettleController.Mode() == AUTOMATIC) {
strcpy(LCD_Line, (char*)F("Kettle Temp: ")); strcpy(LCD_Line, (char*)F("Kettle Temp: "));
itoa(KettleSetpoint / 10, setting, 10); itoa(KettleController.Setpoint(), setting, 10);
strcat(LCD_Line, setting); strcat(LCD_Line, setting);
strcat(LCD_Line, "F"); strcat(LCD_Line, (char*)F("F"));
return LCD_Line; return LCD_Line;
} else { } else {
return (char*)""; return (char*)"";
@ -87,18 +76,20 @@ void doEncoder()
{ {
uint8_t result = rotary.read(); uint8_t result = rotary.read();
uint8_t inc; uint8_t inc;
uint8_t KettleDuty = (uint8_t)KettleController.Power();
if (result) { if (result) {
uint8_t speed = rotary.speed(); uint8_t speed = rotary.speed();
speed >= 10 ? inc = 50 : inc = 10; speed >= 10 ? inc = 5 : inc = 1;
} }
if (result == DIR_CW && KettleDuty < 1000) { if (result == DIR_CW && KettleDuty < 100) {
KettleDuty = (KettleDuty / inc) * inc + inc; KettleDuty = (KettleDuty / inc) * inc + inc;
} else if (result == DIR_CCW && KettleDuty > 0) { } else if (result == DIR_CCW && KettleDuty > 0) {
KettleDuty = (KettleDuty / inc) * inc - inc; KettleDuty = (KettleDuty / inc) * inc - inc;
} }
KettleController.Power((double)KettleDuty);
SettingChanged = true;
} }
// LCD menu setup. // LCD menu setup.
@ -146,23 +137,16 @@ void setup() {
}; };
void UpdateBoilKettle(){ void UpdateBoilKettle(){
static uint8_t last_KettleDuty = 0;
static uint8_t last_KettleTemp = 0;
if (Enter.pressed()) { if (Enter.pressed()) {
KettleController.CycleMode();
KettleMode = (modes)(KettleMode + 1); SettingChanged = true;
menu.update();
} }
if (last_KettleDuty != KettleDuty) { if (SettingChanged) {
last_KettleDuty = KettleDuty;
menu.update();
}
if (last_KettleTemp != KettleTemp) {
last_KettleTemp = KettleTemp;
menu.update(); menu.update();
SettingChanged = false;
} }
if (KettleController.Mode() != OFF) { if (KettleController.Mode() != OFF) {
@ -174,7 +158,6 @@ void UpdateBoilKettle(){
} }
void loop() { void loop() {
KettleTemp = (uint8_t)(KettleThermo.readRTD() * 10);
UpdateBoilKettle(); UpdateBoilKettle();
unsigned long elapsedTime = (millis() - lastRun); unsigned long elapsedTime = (millis() - lastRun);

View File

@ -1,3 +1,4 @@
void ConnectMQTT() { void ConnectMQTT() {
ConfigData config; ConfigData config;
EEPROM.get(ConfAddress, config); EEPROM.get(ConfAddress, config);

View File

@ -17,7 +17,6 @@ class slowPWM {
lastSwitchTime = 0; lastSwitchTime = 0;
outputState = LOW; outputState = LOW;
pinMode(pin, OUTPUT); pinMode(pin, OUTPUT);
Serial.println("Setup PWM");
} }
byte compute(uint8_t duty) { byte compute(uint8_t duty) {

View File

@ -3,14 +3,25 @@
#include <EEPROM.h> #include <EEPROM.h>
#include "thermoControl.h" #include "thermoControl.h"
void thermoControl::begin(uint8_t* input_temp, uint8_t* setpoint_temp, uint8_t* output_pwm, uint8_t max_threshold, uint8_t hysteresis) {
void thermoControl::begin(int8_t pinRTD) {
RTD = new Adafruit_MAX31865(pinRTD);
outMax = 100;
outMin = 10;
OpMode = OFF;
SampleTime = 100;
lastTime = millis()-SampleTime;
}
void thermoControl::begin(int8_t pinRTD, double temp, double output_pwm, double max_pwr_threshold, double hysteresis) {
output_pwm = output_pwm; output_pwm = output_pwm;
input_temp = input_temp;
setpoint_temp = setpoint_temp; RTD = new Adafruit_MAX31865(pinRTD);
control_temp = temp;
outMax = 1000;
outMin = 100; outMax = 100;
outMin = 10;
OpMode = OFF; OpMode = OFF;
SampleTime = 100; SampleTime = 100;
lastTime = millis()-SampleTime; lastTime = millis()-SampleTime;
@ -20,20 +31,22 @@ bool thermoControl::Compute() {
unsigned long now = millis(); unsigned long now = millis();
unsigned long timeChange = (now - lastTime); unsigned long timeChange = (now - lastTime);
if(timeChange>=SampleTime && OpMode == AUTOMATIC) { double temp = RTD->readRTD();
uint8_t output;
uint8_t error = *setpoint_temp - *input_temp;
if (error >= *max_threshold) { if(timeChange>=SampleTime && OpMode == AUTOMATIC) {
double output;
double error = control_temp - temp;
if (error >= max_pwr_threshold) {
output = outMax; output = outMax;
} else if (error > *hysteresis) { } else if (error > hysteresis) {
output = 100 * error / *max_threshold; output = 100 * error / max_pwr_threshold;
output = max(output, outMin); output = max(output, outMin);
output = min(output, outMax); output = min(output, outMax);
} else { } else {
output = 0; output = 0;
} }
*output_pwm = output; output_pwm = output;
lastTime = now; lastTime = now;
return true; return true;
@ -42,28 +55,48 @@ bool thermoControl::Compute() {
} }
} }
double thermoControl::Power() {
return output_pwm;
}
void thermoControl::Power(double pwr) {
output_pwm = pwr;
}
double thermoControl::Setpoint() {
return control_temp;
}
void thermoControl::Setpoint(double temp) {
control_temp = temp;
}
void thermoControl::SetSampleTime(int NewSampleTime) { void thermoControl::SetSampleTime(int NewSampleTime) {
if (NewSampleTime > 0) { if (NewSampleTime > 0) {
SampleTime = (unsigned long)NewSampleTime; SampleTime = (unsigned long)NewSampleTime;
} }
} }
void thermoControl::SetPowerLimits(uint8_t Max, uint8_t Min) { void thermoControl::SetPowerLimits(double Max, double Min) {
if(Min >= Max) return; if(Min >= Max) return;
outMax = Max; outMax = Max;
outMin = Min; outMin = Min;
} }
void thermoControl::SetMode(modes newMode) { void thermoControl::Mode(modes newMode) {
OpMode = newMode; OpMode = newMode;
} }
modes thermoControl::GetMode() { modes thermoControl::Mode() {
return OpMode; return OpMode;
} }
modes thermoControl::CycleMode() { modes thermoControl::CycleMode() {
OpMode = (modes)(OpMode + 1); if (OpMode + 1 == OVERFLOW) {
OpMode = (modes)(0);
} else {
OpMode = (modes)(OpMode + 1);
}
return OpMode; return OpMode;
} }

View File

@ -1,32 +1,39 @@
#ifndef THERMOCONTROL_h #ifndef THERMOCONTROL_h
#define THERMOCONTROL_h #define THERMOCONTROL_h
enum modes : uint8_t {OFF, AUTOMATIC, MANUAL}; #include <Adafruit_MAX31865.h>
enum modes : uint8_t {OFF, AUTOMATIC, MANUAL, OVERFLOW};
class thermoControl { class thermoControl {
private: private:
double *input_temp; Adafruit_MAX31865* RTD;
double *output_pwm; double output_pwm;
double *setpoint_temp; double control_temp;
double *hysteresis; double hysteresis;
double *max_threshold; double max_pwr_threshold;
double outMax; int outMax;
double outMin; int outMin;
modes OpMode; modes OpMode;
unsigned long SampleTime; unsigned long SampleTime;
unsigned long lastTime; unsigned long lastTime;
public: public:
void begin(uint8_t*, uint8_t*, uint8_t*, uint8_t, uint8_t); void begin(int8_t, double, double, double, double);
void begin(int8_t);
bool Compute(); bool Compute();
double Power();
void Power(double);
double Setpoint();
void Setpoint(double);
void SetSampleTime(int); void SetSampleTime(int);
void SetPowerLimits(uint8_t, uint8_t); void SetPowerLimits(double, double);
void SetHysteresis(uint8_t); void SetHysteresis(double);
void SetThreshPWR(uint8_t); void SetThreshPWR(double);
void SetMode(modes); void Mode(modes);
modes GetMode(); modes Mode();
modes CycleMode(); modes CycleMode();
}; };