//#define USE_FASTPID // Required for PID control
//#define USE_DS18B20
//#define READ_VOLTAGE // Enable voltage mode
#define USE_PTC_THERMISTOR
#define USE_NTC_THERMISTOR
//-------------Debugging messages
//#define DEBUG_PID
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <math.h>
#ifdef USE_FASTPID
#include <FastPID.h>
#endif
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Thermistor setup (for temp modes)
const int thermistorPin = A0;
const float seriesResistor = 10000.0;
const float nominalResistance = 1000.0;
const float nominalTemperature = 25.0;
const float bCoefficient = 3950.0; // NTC
const float tempCoefficient = 0.007; // PTC
// Voltage feedback pin
#define PIN_VOLTAGE_FEEDBACK A1 // Measures output voltage after MOSFET
// Button & output
#define BUTTON_UP 2
#define BUTTON_DOWN 3
#define PWM_OUTPUT 5 // Drives MOSFET
// Button debounce
unsigned long lastDebounceTimeUp = 0;
unsigned long lastDebounceTimeDown = 0;
const unsigned long debounceDelay = 50;
bool lastUpState = HIGH;
bool lastDownState = HIGH;
unsigned long lastButtonPressTime = 0;
const unsigned int displayDuration = 3000;
bool showSetTemp = false;
// Temps or Voltage
float currentTemp = 0; // Temp or voltage feedback
float setTemp = 0.0; // Temp or voltage setpoint (default 0)
int pidOutput = 0;
#ifdef USE_DS18B20
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
#define ONE_WIRE_BUS 4
#endif
#ifdef USE_FASTPID
const float Kp = 2.0; // Tune these for voltage control
const float Ki = 0.5;
const float Kd = 5.0;
FastPID pid(Kp, Ki, Kd, 100, false); // 100 Hz, no clamping
#endif
void setup() {
Serial.begin(9600);
#ifdef USE_DS18B20
sensors.begin();
#endif
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(PWM_OUTPUT, OUTPUT);
#ifdef READ_VOLTAGE
pinMode(PIN_VOLTAGE_FEEDBACK, INPUT);
#endif
}
void loop() {
handleButtons();
#ifdef READ_VOLTAGE
currentTemp = reading_voltage(); // Feedback from A1
#else
#ifdef USE_PTC_THERMISTOR
currentTemp = readPTCThermistor();
#else
#ifdef USE_NTC_THERMISTOR
currentTemp = readThermistor();
#endif
#endif
#endif
#ifdef USE_FASTPID
pidOutput = pid.step(setTemp, currentTemp); // Adjust PWM to match setpoint
#else
pidOutput = (currentTemp < setTemp) ? 255 : 0;
#endif
analogWrite(PWM_OUTPUT, pidOutput); // Drive MOSFET
updateDisplay();
#ifdef USE_DS18B20
sensors.requestTemperatures();
Serial.print("DS Temp: ");
Serial.print(sensors.getTempCByIndex(0));
Serial.print(" C | ");
#endif
#ifdef DEBUG_PID
Serial.print("Feedback: ");
Serial.print(currentTemp);
#endif
#ifdef READ_VOLTAGE
Serial.print(" V | Setpoint: ");
//#else
// Serial.print(" C | Setpoint: ");
#endif
#ifdef DEBUG_PID
Serial.print(setTemp);
Serial.print(" | PWM: ");
Serial.println(pidOutput);
#endif
delay(200);
}
#ifdef READ_VOLTAGE
float reading_voltage() {
int val = analogRead(PIN_VOLTAGE_FEEDBACK);
float voltage = 5.0 * val / 1023.0; // 0-5V (adjust to 3.3V if needed)
return voltage;
}
#endif
#ifdef USE_PTC_THERMISTOR
float readPTCThermistor() {
int adcValue = analogRead(thermistorPin);
float resistance = seriesResistor * (1023.0 / adcValue - 1.0);
float temperature = nominalTemperature + (resistance - nominalResistance) / (nominalResistance * tempCoefficient);
return temperature;
}
#endif
#ifdef USE_NTC_THERMISTOR
float readThermistor() {
int adcValue = analogRead(thermistorPin);
float resistance = seriesResistor * (1023.0 / adcValue - 1.0);
float steinhart = resistance / nominalResistance;
steinhart = log(steinhart);
steinhart /= bCoefficient;
steinhart += 1.0 / (nominalTemperature + 273.15);
steinhart = 1.0 / steinhart;
return steinhart - 273.15;
}
#endif
void updateDisplay() {
display.clearDisplay();
int currentTempInt = (int)round(currentTemp);
int setTempInt = (int)round(setTemp);
if (showSetTemp && (millis() - lastButtonPressTime < displayDuration)) {
updateDisplayInfo(setTempInt, currentTempInt);
} else {
updateDisplayInfo(currentTempInt, setTempInt);
showSetTemp = false;
#ifdef USE_DS18B20
display.setTextSize(1);
display.setCursor(0, 56);
display.print("DS:");
int dsTempInt = (int)round(sensors.getTempCByIndex(0));
display.print(dsTempInt);
#endif
}
display.display();
}
void updateDisplayInfo(int largeTemp, int smallTemp) {
bool upState = digitalRead(BUTTON_UP);
bool downState = digitalRead(BUTTON_DOWN);
display.setTextSize(2);
display.setCursor(0, 0);
#ifdef READ_VOLTAGE
display.print("SetV:");
#else
display.print("SetT:");
#endif
display.print(smallTemp);
display.setCursor(100, 0);
display.print(upState);
display.print(downState);
display.setTextSize(6);
if (largeTemp < 10) {
display.setCursor(45, 18);
display.print(largeTemp);
}
else if (largeTemp < 100) {
display.setCursor(25, 18);
display.print(largeTemp);
}
else {
display.setCursor(2, 18);
display.print(largeTemp);
}
}
/*
void updateDisplayButtonsStet() {
display.clearDisplay();
bool upState = digitalRead(BUTTON_UP);
bool downState = digitalRead(BUTTON_DOWN);
if (upState != HIGH) {
// turn LED on:
display.setTextSize(6);
display.setCursor(45, 18);
display.print("UP");
}
if (downState != HIGH) {
// turn LED on:
display.setTextSize(6);
display.setCursor(45, 18);
display.print("Downn");
}
display.display();
delay(200);
}
*/
void handleButtons() {
bool upState = digitalRead(BUTTON_UP);
bool downState = digitalRead(BUTTON_DOWN);
String space = " ";
// Detect state changes
if (upState != lastUpState) {
lastDebounceTimeUp = millis();
Serial.print("UP pressed - lastDebounceTimeDown ");
Serial.println(lastDebounceTimeDown);
}
if (downState != lastDownState) {
lastDebounceTimeDown = millis();
Serial.print("DOWN pressed - lastDebounceTimeDown ");
Serial.println(lastDebounceTimeDown);
}
// Check UP button press
//if ((millis() - lastDebounceTimeUp) > debounceDelay && upState == LOW && lastUpState == HIGH) {
if ((millis() - lastDebounceTimeUp) > debounceDelay && upState == LOW && lastUpState == HIGH) {
//if ((millis() - lastDebounceTimeDown) > debounceDelay && downState == LOW && lastDownState == HIGH)
Serial.print("DOWN pressed - Set Temp decreased to: ");
Serial.println(setTemp);
//if (upState != lastUpState) {
setTemp += 1;
if (setTemp > 150) setTemp = 150;
lastButtonPressTime = millis();
showSetTemp = true;
Serial.print("UP pressed - Set Temp increased to: ");
Serial.println(setTemp);
}
else {
Serial.println("UP NOT pressed");
/*Serial.print("Full condition result: ");
bool condition = ((millis() - lastDebounceTimeUp) > debounceDelay && upState == LOW && lastUpState == HIGH);
bool condition2 = (millis() - lastDebounceTimeUp) > debounceDelay;
Serial.println(condition);*/
/*Serial.println(condition2);
Serial.print("millis()-lastDebounceTimeUp >");
Serial.print(condition2);
Serial.print(" and ");
Serial.print("upState");
Serial.print(upState);
Serial.print("== LOW");
Serial.print("and");
Serial.print("lastUpState");
Serial.print(lastUpState);
Serial.println("HIGH");*/
/*Serial.println(" >debounceDelay && debounceDelay == LOW ");
Serial.print(debounceDelay);
Serial.print(space);
Serial.print(debounceDelay);
//Serial.print(lastDebounceTimeUp);
Serial.println(space ==0);
Serial.print(debounceDelay);
/Serial.print(" debounceDelay");
Serial.print(space);
Serial.print(upState);
Serial.print(space);
Serial.println(lastUpState);*/
}
// Check DOWN button press
if ((millis() - lastDebounceTimeDown) > debounceDelay && downState == LOW && lastDownState == HIGH) {
Serial.print("DOWN pressed - Set Temp decreased to: ");
Serial.println(setTemp);
//if (downState != lastDownState) {
setTemp -= 1;
if (setTemp < 0) setTemp = 0;
lastButtonPressTime = millis();
showSetTemp = true;
Serial.print("DOWN pressed - Set Temp decreased to: ");
Serial.println(setTemp);
}
//else Serial.println("DOWN NOT pressed");
// Save the current state for next cycle
lastUpState = upState;
lastDownState = downState;
}