I am trying to design a controller that will look at a sensor and then compare the sensor with a set point, when the set point is above the sensor reading the PWM output will be active and call for heating, the further away the temperature reading is from set point the higher the output will be, I can only seem to get the output to be fully on or fully off, if I put 225 into the set point on the program code I get full 6vdc, if I put 113 into my program code I get around 3vdc, however I don't know how to write the code that will compare the sensor temperature (which has been converted to degrees C) and the set point and give me the modulating output. In the past I have used the controller to switch an output on and off when the set point is reached by I am really struggling with this.
This is called a "PID" and you'll see that there's standard C++/Arduino code for this. Google a bit. May save you some time.
What's the hardware setup and code that generates this result? Please post:
- A schematic of your setup
- A clear photograph that shows how everything is connected
- The code you're running (in 'code tags')
Almost all thermostat-controlled heating & cooling systems work by switching on & off, and it usually works better. The temperature can't change instantly anyway, and if you try to set the "perfect" PWM value you can get oscillations ("hunting" for the right temperature).
If there is no thermostat/control, you might see "high", "medium", and "low" settings and then it's up to you to make adjustments when it's too hot or too cold.
...If you want to heat-up slowly, program it in small steps rather than lowering the power with PWM.
How are you reading that? PWM is NOT DC and most digital meters don't work for reading it. (The inertia of an analog meter WILL average-out the reading.)
There's a really good writeup on designing a controller to do just that.
The associated Arduino Library is:
and GitHub - br3ttb/Arduino-PID-Library
and the excellent writeup is:
http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/
Note that system control is strongly dependent on the dynamics of the system you are trying to control. If you are not careful and match the system to the dynamics, the systems will be impossible to tune/control or destroy your control system.
It would help considerably if you posted your sketch and a schematic of your project
Here is an example sketch which uses a proportional band around a set point. It uses a "slow pwm" with a duty cycle and period using a millis() timer. The duty cycle is controlled by the difference of the reading to setpoint.
It might give you some ideas of a way to go which is not full PID, but is not simply on/off with a hysteresis band.
//all values *10 to work with one decimal point
//measured temperatures from sensor in tenths of degree
float tempSensorReading;
int workingTemperature = tempSensorReading *10;
int setPoint = 30.0 * 10;
int difference;
byte dutyCycle;
// four degree proportional band 100% to 0% duty cycle linear transition
//use even number for symmetry around setPoint
byte proportionalBand = 4 * 10;
void setup()
{}
void loop()
{
tempSensorReading = 30.0;//some temperature from sensor in tenths of degree
workingTemperature = 10 * tempSensorReading;
difference = setPoint - workingTemperature;
difference = constrain(difference, -proportionalBand/2, proportionalBand/2);
dutyCycle = map(difference, -proportionalBand/2, proportionalBand/2, 0, 100);
//call slowPWM function with linear proportional dutyCycle
slowPWM(dutyCycle, 5000); // %duty cycle, period milliseconds
}
void slowPWM(byte dutyCycle, unsigned long period)
{
const byte outputPin = 13;// LED pin for visualization
pinMode(outputPin, OUTPUT);
static byte outputState = LOW;
static unsigned long lastSwitchTime = 0;
unsigned long onTime = (dutyCycle * period) / 100;
unsigned long offTime = period - onTime;
unsigned long currentTime = millis();
if (outputState == HIGH && (currentTime - lastSwitchTime >= onTime))
{
lastSwitchTime = currentTime;
outputState = LOW;
}
if (outputState == LOW && (currentTime - lastSwitchTime >= offTime))
{
lastSwitchTime = currentTime;
outputState = HIGH;
}
digitalWrite(outputPin, outputState);
}
Thank you for your response I am very new to this, I have deleted what I had tried to do and have gone back to an old version which was just an on off control, this worked fine for a simple control, however I am trying to control a thyristor heater that requires a 0-10v signal.
```
#define THERMISTOR_PIN A2
#define SET_POINT_PIN A3
#define DIGITAL_OUTPUT_PIN 9
#define DIR1 7
#define DIR2 4
#define DIR3 6
#define DIR4 5
#define HEAT 8
#define AIR 12
#define ENA 10
#define HEATEN 3
#include
#include
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 20, 4);
void setup() {
lcd.begin();
lcd.backlight();
delay(250);
lcd.noBacklight();
delay(1000);
lcd.backlight();
delay(1000);
pinMode(DIR1,OUTPUT);
pinMode(DIR2,OUTPUT);
pinMode(DIR3,OUTPUT);
pinMode(DIR4,OUTPUT);
pinMode(HEAT,INPUT_PULLUP);
pinMode(AIR,INPUT_PULLUP);
pinMode(ENA,INPUT_PULLUP);
pinMode(HEATEN,INPUT_PULLUP);
}
void loop() {
temperatureControlSystem();
FanFault();
HeatFault();
HeatRun();
FanRun();
}
void FanFault() {
if (digitalRead(AIR) == LOW)
{ digitalWrite(DIR4, HIGH);
lcd.setCursor(0, 1);
lcd.print(" ");
}else{
(digitalRead(AIR) == HIGH);
{ digitalWrite(DIR4, LOW );
lcd.setCursor(0, 1);
lcd.print("AIR FAIL ");
}
}
}
void HeatFault() {
if (digitalRead(HEAT) == HIGH)
{ digitalWrite(DIR3, LOW);
lcd.setCursor(9, 1);
lcd.print("OVER HEAT");
}else{
(digitalRead(HEAT) == LOW);
{ digitalWrite(DIR3, HIGH );
lcd.setCursor(9, 1);
lcd.print(" ");
}
}
}
void HeatRun() {
if (digitalRead(HEATEN) == LOW)
{ digitalWrite(DIR1, HIGH);
lcd.setCursor(9, 0);
lcd.print("HEATER RUN");
}else{
(digitalRead(HEATEN) == HIGH);
{ digitalWrite(DIR1, LOW );
lcd.setCursor(9, 0);
lcd.print("HEATER OFF");
}
}
}
void FanRun() {
if (digitalRead(ENA) == LOW)
{ digitalWrite(DIR2, HIGH);
lcd.setCursor(0, 0);
lcd.print("FAN RUN");
}else{
(digitalRead(ENA) == HIGH);
{ digitalWrite(DIR2, LOW );
lcd.setCursor(0, 0);
lcd.print("FAN OFF");
}
}
}
void temperatureControlSystem() {
int thermistorReading = analogRead(THERMISTOR_PIN);
int setPointReading = analogRead(SET_POINT_PIN);
float temperature = calculateTemperature(thermistorReading);
float setPoint = calculateSetPoint(setPointReading);
double tempK = log(10000.0 * ((1024.0 / thermistorReading - 1)));
tempK = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * tempK * tempK )) * tempK ); // Temp Kelvin
float tempC = tempK - 273.15; // Convert Kelvin to Celcius
float tempF = (tempC * 9.0) / 5.0 + 32.0; // Convert Celcius to Fahrenheit
float setPointdisplay = (setPoint);
lcd.setCursor(0, 0);
lcd.print("Temp: C");
// Display Temperature in F
//lcd.print("Temp F ");
lcd.setCursor(6, 0);
// Display Temperature in C
lcd.print(tempC, 1);
// Display Temperature in F
//lcd.print(tempF);
delay(500);
// Display setpoint in C
lcd.setCursor(12, 0);
lcd.print("SP: C");
// Display Temperature in F
//lcd.print("Set F ");
lcd.setCursor(15, 0);
// Display Temperature in C
lcd.print(setPointdisplay, 0);
// Display Temperature in F
//lcd.print(tempF);
delay(500);
if (tempC < setPoint) {
digitalWrite(DIGITAL_OUTPUT_PIN, HIGH);
} else {
digitalWrite(DIGITAL_OUTPUT_PIN, LOW);
}
}
float calculateTemperature(int reading) {
float resistance = 10000.0 / ((1023.0 / reading) - 1.0);
float temperature = log(resistance / 10000.0) / 3950.0;
temperature = temperature + (1.0 / (25.0 + 273.15));
temperature = 1.0 / temperature;
temperature = temperature - 273.15;
return temperature;
}
float calculateSetPoint(int reading) {
float setPoint = map(reading, 0, 1023, 0, 50);
return setPoint;
}
```
Your sketch is incomplete
It also seems that you tried to use code tags and failed
In my experience the easiest way to tidy up the code and add the code tags is as follows
Start by tidying up your code by using Tools/Auto Format in the IDE to make it easier to read. Then use Edit/Copy for Forum and paste what was copied in a new reply. Code tags will have been added to the code to make it easy to read in the forum thus making it easier to provide help.
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.