I would like to PWM control speed of a small 12v water pump that circulates water through a solar hot water panel. I want to regulate the flow to achieve a reasonably constant 6-7DegC temp difference between inlet temp and discharge temp. The temp difference sensed with 2 DS18B20 Dallas sensors. I have the sketch below working to just turn pump on and off but would like the PWM speed control option. I am sure it is possible but being a copy and paste programmer I am a bit lost.
Any ideas and suggestions welcome.
Cheers
Dennis
// This Arduino sketch reads DS18B20 "1-Wire" digital
// temperature sensors.
// Tutorial:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-tutorial.html
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
DeviceAddress InletTemp = { 0x28, 0x07, 0x78, 0xA9, 0x04, 0x00, 0x00, 0x6C };
DeviceAddress DischargeTemp = { 0x28, 0x45, 0x20, 0xA9, 0x04, 0x00, 0x00, 0xDC };
int setpoint = 25; // Set the desired Inlet maximum temp in Celcius here
int differential = 5; // Set the differential between source temp and storage temp in Celcius here
int relayPin = 13; // Relay connected to digital pin 13
void setup(void)
{
pinMode (13, OUTPUT);
// start serial port
Serial.begin(9600);
// Start up the library
sensors.begin();
// set the resolution to 10 bit (good enough?)
sensors.setResolution(InletTemp, 10);
sensors.setResolution(DischargeTemp, 10);
}
void printTemperature(DeviceAddress deviceAddress)
{
float tempC = sensors.getTempC(deviceAddress);
if (tempC == -127.00) {
Serial.print("Error getting temperature");
} else {
Serial.print("C: ");
Serial.print(tempC);
//Serial.print(" F: ");
//Serial.print(DallasTemperature::toFahrenheit(tempC));
}
}
void loop(void){
//Serial.print("Getting temperatures...\n\r");
sensors.requestTemperatures();
Serial.print("InletTemp ");
printTemperature(InletTemp);
Serial.print("\n\r");
Serial.print("DischargeTemp ");
printTemperature(DischargeTemp);
Serial.print("\n\r");
if (sensors.getTempC(InletTemp) > setpoint)
{
digitalWrite (relayPin, LOW); //if temp is below x degree turn pin "OFF"
Serial.println("Pump OFF"); //Prints pump status to serial
}
else if ((sensors.getTempC(DischargeTemp)) > (sensors.getTempC(InletTemp) + differential))
{
digitalWrite (relayPin, HIGH); //if temp is above x degrees turn pin "ON"
Serial.println("Pump ON");
}
else
{
digitalWrite (relayPin, LOW); //if temp is below x degree turn pin "OFF"
Serial.println("Pump OFF "); //Prints pump status to serial
}
delay(1000*15);
}
You need to implement a feedback algorithm to determine the pump speed required to achieve your target temperature difference. Essentially, you code would:
Read the temperatures and calculate the current temperature difference.
Compare the actual difference against your target difference to calculate the error
Calculate the pump speed required to reduce the error
Run the pump at the calculated speed
The algorithm would run repeatedly. The most complex part is 'Calculate the pump speed required to reduce the error'. If your system is simple and fairly consistent and stable then you might be able to get away with a simple proportional control, but if you have significant lag and inertia you will probably find that a PID feedback algorithm gives better results. There is a PID library for Arduino but using it is non-trivial so I suggest you give the simple proportional feedback algorithm a try first.
Thanks Peter,
I have been looking at "for" loops and "do while" loops and wonder if one of those would do the job. When the temp difference was sufficient enough for the pump to start I could set it to start at say 25% wait for 10-15 seconds and test again. If temp difference was greater than last test increment ++ by 10% and so on till it reached 100% duty cycle or reached a point where the temp difference is no longer increasing. If the difference goes below the set point a "else" statement could decrement -- by 10% until the difference gets back to set point. I would have two set points. One where the pump won't start till the difference is >= to 5DegC and the other set point would be 6-7DegC or higher perhaps. I guess if I set a range of say 6 to 8 or wider would take care of temp fluctuations.
Could this work?
The feedback algorithm you describe might work - depends on the characteristics of the system you're trying to control. Give it a go and see what happens.
I don't see any need for loops in your code. Remember that the loop() function is called repeatedly so you can just put your recurring logic here. In pseudo-code, the algorithm you describe could be implemented like this:
loop:
measure temperature difference
if temperature difference too high and pump speed less than 100%, increase pump speed a bit
if temperature difference too low and pump speed more than 0%, decrease pump speed a bit
wait to allow the system to respond
G'Day Paul,
At the moment the pump is a small 12v dc bilge pump. The sketch above just turns pump on if discharge temp is equal to or greater than 5DegC warmer than inlet temp. This is sort of ok but if there is not a lot of sun the collector will cool down too quickly with 100% flow, so that is why I am keen to reduce flow by slowing the pump via PWM. If the difference drops below 5DegC the pump turns off. I would like the pump to ramp up if the difference is greater than say 8DegC or slow down if difference is less than 6DegC. That would give a 2DegC differential where the sped could settle. This may need to be wider but I could change that later.
so that is why I am keen to reduce flow by slowing the pump via PWM.
It's fine to want to do something, as long as you recognize that it may not be possible. The starter motor on my bike, for instance, can not be run at any speed I like. Anything less that full current/full voltage means that sometimes the bike won't start. Your motor may, or may not, operate the same way.
You've said nothing, yet, about HOW the Arduino is controlling this motor. The Arduino is NOT simply turning a pin on or off, causing the motor to turn on or off. You need something between the Arduino and the motor - either a motor driver shield or a relay. Knowing which you were using would be useful. A mechanical relay, if that is what you are using, can not be turned on and off, effectively, using PWM.
A solid state relay could, as could a motor driver shield.
At the moment I only have the arduino turning a led on and off. For just starting and stopping the pump I would use a relay, but for speed controlling the pump I would use a FET or if I go to a ac pump I would use a SSR.
If the pump has trouble starting I would do a 100% start then reduce speed quickly. I think the pump will start ok without doing that though.
curnow:
At the moment I only have the arduino turning a led on and off. For just starting and stopping the pump I would use a relay, but for speed controlling the pump I would use a FET or if I go to a ac pump I would use a SSR.
If the pump has trouble starting I would do a 100% start then reduce speed quickly. I think the pump will start ok without doing that though.
If you have a SSR, the SSR can take care of turning it on and off and the relay becomes redundant.
Some motors swill start if switched on at 50% or above, but this can produce a lot of heat in the windings. Almost no motors -- that I know of -- will start reliably at 1%. I'd plan on starting at 100% for at least a few milliseconds.
G'Day Gang,
I have had a play with the code below and added PWM pump speed control.
It compiles ok but could someone have a look through and see if there are any glaring errors.
TIA
Cheers
Dennis
// This Arduino sketch reads DS18B20 "1-Wire" digital
// temperature sensors.
// Tutorial:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-tutorial.html
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
//pinMode(13,OUTPUT);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
DeviceAddress InletTemp = { 0x28, 0x07, 0x78, 0xA9, 0x04, 0x00, 0x00, 0x6C };
DeviceAddress DischargeTemp = { 0x28, 0x45, 0x20, 0xA9, 0x04, 0x00, 0x00, 0xDC };
int setpoint = 25; // Set the desired Inlet maximum temp in Celcius here
int differential = 5; // Set the differential between source temp and storage temp in Celcius here
int motorPin = 9; //Set the PWM Motor pin as an output
void setup(void)
{
pinMode (9, OUTPUT);
// start serial port
Serial.begin(9600);
// Start up the library
sensors.begin();
// set the resolution to 10 bit (good enough?)
sensors.setResolution(InletTemp, 10);
sensors.setResolution(DischargeTemp, 10);
}
void printTemperature(DeviceAddress deviceAddress)
{
float tempC = sensors.getTempC(deviceAddress);
if (tempC == -127.00) {
Serial.print("Error getting temperature");
} else {
Serial.print("C: ");
Serial.print(tempC);
//Serial.print(" F: ");
//Serial.print(DallasTemperature::toFahrenheit(tempC));
}
}
void loop(void){
//Serial.print("Getting temperatures...\n\r");
sensors.requestTemperatures();
Serial.print("InletTemp ");
printTemperature(InletTemp);
Serial.print("\n\r");
Serial.print("DischargeTemp ");
printTemperature(DischargeTemp);
Serial.print("\n\r");
if (sensors.getTempC(InletTemp) >= setpoint)
{
digitalWrite (motorPin, LOW); //if temp is equal or above setpoint turn pin "OFF"
Serial.println("Pump OFF"); //Prints pump status to serial
}
if ((sensors.getTempC(DischargeTemp)) > (sensors.getTempC(InletTemp) + differential))
//If DischargeTemp higher than InletTemp + Differential start pump at25%
{
int i=64; i<=255; //Start Motor at 25%
analogWrite (motorPin, i);
Serial.println(i);
}
if ((sensors.getTempC(DischargeTemp)) - (sensors.getTempC(InletTemp) > 10))
//If DischargeTemp 10 degrees higher than InletTemp increase pump speed by 10
{
int i=64; i+10; i<=255; //Increase Motor Speed from 64
analogWrite (motorPin, i);
Serial.println(i);
delay(1000*15);
}
else if ((sensors.getTempC(DischargeTemp)) - (sensors.getTempC(InletTemp) < 7))
//If DischargeTemp less than 7 degrees higher than InletTemp reduce pump speed by 10
{
int i=64; i-10;
analogWrite (motorPin, i);
Serial.print(i);
delay(1000*15);
}
Hi, I'm watching this post closely, I have similar plans in the future, and arduino is perfect for the job.
Just a quick suggestion, if you find that you are running the motor using PWM for long periods of time at lower speed you made need to fit a ventilation fan to keep the motor cool.
So when you have the system or a prototype running it would be worth checking the motor temperature.
I agree with 100% start and then slow down, most computers that have speed controlled fans do this, DELL servers literally scream when you turn them on, especially if one of its fans is on the blink.
Tom.. 8)
The code not working as planned.
Below is copy from serial.
Setpoint is 25 and differential is 5
As you can see the code still has pump running while differential is not adequate.
InletTemp C: 16.25
DischargeTemp C: 16.50
Pump Decreasing
Sketch follows.
// This Arduino sketch reads DS18B20 "1-Wire" digital
// temperature sensors.
// Tutorial:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-tutorial.html
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
//pinMode(13,OUTPUT);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// Assign the addresses of your 1-Wire temp sensors.
// See the tutorial on how to obtain these addresses:
// http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html
DeviceAddress InletTemp = { 0x28, 0x07, 0x78, 0xA9, 0x04, 0x00, 0x00, 0x6C };
DeviceAddress DischargeTemp = { 0x28, 0x45, 0x20, 0xA9, 0x04, 0x00, 0x00, 0xDC };
int setpoint = 25; // Set the desired Inlet maximum temp in Celcius here
int differential = 5; // Set the differential between source temp and storage temp in Celcius here
int motorPin = 9; //Set the PWM Motor pin as an output
void setup(void)
{
pinMode (9, OUTPUT);
// start serial port
Serial.begin(9600);
// Start up the library
sensors.begin();
// set the resolution to 10 bit (good enough?)
sensors.setResolution(InletTemp, 10);
sensors.setResolution(DischargeTemp, 10);
}
void printTemperature(DeviceAddress deviceAddress)
{
float tempC = sensors.getTempC(deviceAddress);
if (tempC == -127.00) {
Serial.print("Error getting temperature");
} else {
Serial.print("C: ");
Serial.print(tempC);
//Serial.print(" F: ");
//Serial.print(DallasTemperature::toFahrenheit(tempC));
}
}
void loop(void){
//Serial.print("Getting temperatures...\n\r");
sensors.requestTemperatures();
Serial.print("InletTemp ");
printTemperature(InletTemp);
Serial.print("\n\r");
Serial.print("DischargeTemp ");
printTemperature(DischargeTemp);
Serial.print("\n\r");
if (sensors.getTempC(InletTemp) >= setpoint){
digitalWrite (motorPin, LOW); //if temp is equal or above setpoint turn pin "OFF"
Serial.println("Set Temp Achieved"); //Prints pump status to serial
delay (1000*15);
}
else if ((sensors.getTempC(DischargeTemp)) >= (sensors.getTempC(InletTemp) + differential)){
//If DischargeTemp higher than InletTemp + Differential start pump at25%
int i=64; i<=255; //Start Motor at 25%
analogWrite (motorPin, i);
Serial.println("Pump Start");
delay (1000*15);
}
else if ((sensors.getTempC(DischargeTemp)) >= (sensors.getTempC(InletTemp)+ differential + 10)){
//If DischargeTemp 10 degrees higher than InletTemp increase pump speed by 10
int i=0; i+10; i<=255; //Increase Motor Speed from 64
analogWrite (motorPin, i);
Serial.println("Pump Increasing");
delay(1000*15);
}
else if ((sensors.getTempC(DischargeTemp)) <= (sensors.getTempC(InletTemp) + 7)){
//If DischargeTemp less than 7 degrees higher than InletTemp reduce pump speed by 10
int i=64; i-10; i>=64;
analogWrite (motorPin, i);
Serial.println("Pump Decreasing");
delay(1000*15);
}
else
{
analogWrite (motorPin, LOW);
Serial.println (" Pump Off");
}
}
That example code that you copied is crap. This function has a name that implies one thing while the function does something different. This function modifies global variables. there is nothing in its name that suggests it is going to do that. Give this function a name that defines what it REALLY does, and make it NOT modify any global variables. It does not need to.
You need to put every { on a new line
and use Tools + Auto Format.
Code the jerks all
over the page is very
hard to read. Too hard for me to bother.
As you can see, this part of your else if chain is true when the discharge temp is low:
else if ((sensors.getTempC(DischargeTemp)) <= (sensors.getTempC(InletTemp) + 7))
You don't have anything that checks against your difference being less than the differential. You need an additional if clause to do this in the chain before the one above.
Other issues:
int i=64;
i-10;
i>=64;
analogWrite (motorPin, i);
Declaring i in each section means that the motor value is not persisted, so you would never be able to ramp down the speed - it'll be the same (64) each time this section executes. Make the variable global and give it a rather better name. i-10; and i>=64; do nothing. i-=10; will do for the first, for the second take a look at the constrain function.
Thanks wildbill,
I sort of understand what you are saying but unfortunately I don't have the skill to do what you suggest.
So at this point I will give up and just go back to the simple pump on pump off sketch.
Thanks tylernt,
That has got very close to what I am trying to achieve.
The code seems to bypass this bit of the sketch when temps are reducing. I am trying to get the speed to reduce when the difference between the discharge temp and inlet is below 10 but above 6.
The rest of the sketch seems to work although I haven't checked the wave form on the output pin yet.
else if (sensors.getTempCByIndex(0) - sensors.getTempCByIndex(1) < 10 > 6 )
//If temp difference less than 10
//but greater than 6
{
{
i = i - 10; //Decrease motor speed by 10
}
analogWrite (motorPin, i);
Serial.println("Pump Decreasing");
delay(1000*15);
}
This is the complete sketch.
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
int motorPin = 9;
int i=64;
//#define 'DischargeTemp' = (sensors.getTempCByIndex(0));
//#define 'InletTemp' = (sensors.getTempCByIndex(1));
void setup(void)
{
pinMode(motorPin, OUTPUT);
// start serial port
Serial.begin(9600);
Serial.println("Welcome to Dallas Temperature");
// Start up the library
sensors.begin();
}
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
//Serial.print(" Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
//Serial.println("DONE");
Serial.print("DischargeTemp ");
Serial.println(sensors.getTempCByIndex(0)); // Why "byIndex"?
// You can have more than one IC on the same bus.
// 0 refers to the first IC on the wire
Serial.print("InletTemp ");
Serial.println(sensors.getTempCByIndex(1)); // Why "byIndex"?
// You can have more than one IC on the same bus.
// 0 refers to the first IC on the wire
if (sensors.getTempCByIndex(1) > 25)
{
analogWrite (motorPin, 0); // sensor 2 > sensor 1
Serial.println ("Set Temp Achieved");
delay (1000*15);
}
else if (sensors.getTempCByIndex(0) - sensors.getTempCByIndex(1) > 10)
{
if(i < 245)
{
i = i + 10; // ...increase motor speed by 10
}
analogWrite (motorPin, i);
Serial.println("Pump Increasing");
delay(1000*15);
}
else if (sensors.getTempCByIndex(0) - sensors.getTempCByIndex(1) < 10 > 6 )
//If temp difference less than 10
//but greater than 7
{
{
i = i - 10; //Decrease motor speed by 10
}
analogWrite (motorPin, i);
Serial.println("Pump Decreasing");
delay(1000*15);
}
else if (sensors.getTempCByIndex(0) - sensors.getTempCByIndex(1) >= 5)
{
analogWrite (motorPin, i); //Start Motor at 25%
Serial.println("Pump Start");
delay (1000*15);
}
else
{
analogWrite (motorPin, 0);
Serial.println("Pump Off");
delay(1000*15);
}
}
That is not how progress is made. Try something. If it doesn't work, post the code for review again. Really, we don't mind helping people that are trying.
PaulS
I do understand.
But after several days trying to get code to work and hitting a brick wall I was ready to backtrack to a sketch that would have done the job albeit not as well as I would have liked.
As you can see above I am close to getting what I need. I just need to find out how to get the code to check if the difference falls between 2 values.
else if (sensors.getTempCByIndex(0) - sensors.getTempCByIndex(1) < 10 > 6 )
Please explain what you expect this to do.
In reality, it is computing the difference between the two values. The difference is then compared to 10. The result of that comparison is either true or false. The true or false value (1 or 0) is compared to 6. The result of that comparison is always false.