While Damon is away it is a good time to post this. Don't want him to have a heart attack.
Large PV array water heating with up to 9 heating elements for tube floor heating. This
program progressively turns on up to 9 heating elements in multiple steps to maintain
power point. Initial 20 steps are smaller to accommodate lower power light levels. This
switching in and out of elements always matches resistance to the panels power point
voltage. Eight elements are turned either on or off. The ninth element is PWMed from a
capacitor storage bank to allow power to increase in about 2% steps. HYST is adjustable
for smoother element transition. Internally set point is fixed, voltage is adjusted
with external pot. Elements 1-8 only need a slow FET driver. PWM rate is 240Hz requiring
just a slightly faster driver. This is a concept program and has not been tested in operation.
Code is written in a very simplistic form that a total beginner can understand and modify to your situation.
If you can't understand and maintain this code you should avoid this project. It's a demonstrator for beginners.
D12 = Heat Switch, close to common for heat
A0 = PV voltage, about 2.8V from voltage divider
A1 = HYST voltage change needed to transition, connect to 3V3 if pot not used
D3 = Element #9 PWM element must always be present
D2 = Element #1
D4 = Element #2 three elements total is suggested minimum system
D5 = Element #3
D7 = Element #4
D8 = Element #5
D9 = Element #6
D10 = Element #7
D11 = Element #8
/*
PE9 FLOOR HEAT is program for tank heating with 9 heating elements of
the same value resistance. 8 elements are ON/OFF & one is multi step PWM.
For use with large tank systems capable of multiple heating elements.
Panel volts are read on A0 from a pot divider. Internal PV set point fixed.
Uses relay contact temp sensor on D12 input to common for heat. Must use
470 ohm pullup with LED in series to indicate heat mode is on. Hystresis
is adjustable from A1 for step change. Analog power (step) out pin 6 for
meter. Any number of heating elements may be used, must include PWM.
Initial power levels are progressive.
1. Blink LED 13 ONCE periodically to show micro is working
2. Blink LED 13 additional multiple times for heating level
3. Read panel voltage eight times to get average
4. 8 elements ON/OFF, one PWM multi step
5. Progressively higher steps at low power
6. External temp controller, switch contact
7. Send out serial data periodically of all variables
Ver 1.1 02/17/18 semi working baseline program
revised 02/18/18 added ninth element and progressive power levels
The following two characters * and / end a multi line comment
*/
// Declare program variables as integers and set initial values
// ALL THE SEQUENTIAL PANEL READINGS
int PV0 = 0; // SOLAR PANEL raw A/D conversion #1
int PV1 = 0; // SOLAR PANEL raw A/D conversion #2
int PV2 = 0; // SOLAR PANEL raw A/D conversion #3
int PV3 = 0; // SOLAR PANEL raw A/D conversion #4
int PV4 = 0; // SOLAR PANEL raw A/D conversion #5
int PV5 = 0; // SOLAR PANEL raw A/D conversion #6
int PV6 = 0; // SOLAR PANEL raw A/D conversion #7
int PV7 = 0; // SOLAR PANEL raw A/D conversion #8
int ADavg = 0; // Solar panel average A/D value
int SETpoint = 600; // panel power point for heating (about 2.8V)
int HYST = 0 ; // panel power point HYST
int COUNTheater = 0; // WATER HEATER UP/DOWN COUNTER
int PWM3drv = 0; // WATER HEATER PWM ELEMENT driver
int ELEMENT_1 = 0; // #1 WATER HEATER ELEMENT ON/OFF state
int ELEMENT_2 = 0; // #2 WATER HEATER ELEMENT ON/OFF state
int ELEMENT_3 = 0; // #3 WATER HEATER ELEMENT ON/OFF state
int ELEMENT_4 = 0; // #4 WATER HEATER ELEMENT ON/OFF state
int ELEMENT_5 = 0; // #5 WATER HEATER ELEMENT ON/OFF state
int ELEMENT_6 = 0; // #6 WATER HEATER ELEMENT ON/OFF state
int ELEMENT_7 = 0; // #7 WATER HEATER ELEMENT ON/OFF state
int ELEMENT_8 = 0; // #8 WATER HEATER ELEMENT ON/OFF state
int blinktime = 0; // Used to blink LED power level
int PRINTcycle = 0; // used for periodic serial print
void setup() {
// initialize the digital pins as outputs.
// Pin 13 is on board LED and is a visual
// indicator the micro is working
pinMode(13, OUTPUT); // Onboard Monitor LED
pinMode(12, INPUT); // TEMP switch contact 0=heat
pinMode(11, OUTPUT); // ELEMENT #8
pinMode(10, OUTPUT); // ELEMENT #7
pinMode(9, OUTPUT); // ELEMENT #6
pinMode(8, OUTPUT); // ELEMENT #5
pinMode(7, OUTPUT); // ELEMENT #4
pinMode(5, OUTPUT); // ELEMENT #3
pinMode(4, OUTPUT); // ELEMENT #2
pinMode(2, OUTPUT); // ELEMENT #1
pinMode(6, OUTPUT); // 960Hz PWM METER OUT (reference only)
pinMode(3, OUTPUT); // 244Hz PWM ELEMENT #9 (reference only)
digitalWrite (12,1); // set weak pullup on D12 (old version)
Serial.begin (9600); // Set up serial monitor routine, 9600 baud
} // End setup
void loop() { // This is the start of the program loop
// *****************************************************************
// LOWER THE PWM FREQUENCY FOR LESS FET HEATING and SIMPLE DRIVER
// This changes the frequency of PWM pins 11 & 3 ONLY
// Fewer transitions = less heat created
// Pins 11 and 3 for 16MHz clock
// Settinng Divisor Hz
// 0x01 1 31250
// 0x02 8 3906
// 0x03 32 976
// 0x04 64 488
// 0x05 128 244
// 0x06 256 122
// 0x07 1024 30
// TCCR2B = TCCR2B & 0b11111000 | <setting>;
TCCR2B = TCCR2B & 0b11111000 | 0x05; // changes the PWM rate 11 & 3
// PWM pins 9 & 10 These you can change as above
// TCCR1B = TCCR1B & 0b11111000 | <setting>;
//TCCR1B = TCCR1B & 0b11111000 | 0x06 ;
//Pins 5 and 6: Standard
// changing this changes program delay times AVOID
//Setting Divisor Frequency
// 0x03 64 976
//TCCR0B = TCCR0B & 0b11111000 | 0x03 ;
// *********************************************************
// This section reads the analog signals. Range is 0 to 5V DC or 0 to 1023
// counts. A delay before the first read allows capacitor bank voltage to
// stabilize fron new PWM. Eight readings average out noise which can
// be several counts. We don't have much to do and lots of time so might as well.
HYST = analogRead (1); // Anolog In. HYST voltage from pot on A1
HYST = HYST / 50 + 8; // HYST from 8 to 28
// NOTE: Tie A1 to 3V3 if pot not used
delay (200); // Delay before first reading allowing last
// PWM to take effect on capacitor bank
PV0 = analogRead (0); // Anolog In. Read the panel voltage #1
delay (4); // Delay between readings
PV1 = analogRead (0); // Anolog In. Read the panel voltage #2
delay (4); // Delay between readings
PV2 = analogRead (0); // Anolog In. Read the panel voltage #3
delay (4); // Delay between readings
PV3 = analogRead (0); // Anolog In. Read the panel voltage #4
delay (4); // Delay between readings
PV4 = analogRead (0); // Anolog In. Read the panel voltage #5
delay (4); // Delay between readings
PV5 = analogRead (0); // Anolog In. Read the panel voltage #6
delay (4); // Delay between readings
PV6 = analogRead (0); // Anolog In. Read the panel voltage #7
delay (4); // Delay between readings
PV7 = analogRead (0); // Anolog In. Read the panel voltage #8
delay (4);
// ###################################################
// This section gets the average of the eight readings to remove noise which
// can be several counts. Simplified to be unserstood by new programmers.
// Average should closely match raw A/D values.
ADavg = PV0 + PV1 + PV2 + PV3 + PV4 + PV5 + PV6 + PV7; // Add the eight readings
ADavg = ADavg / 8; // get average reading
// ########################################################
// HEATER CONTROL
// This section controls the up/down count for the 9 heaters shown. Minimum of three
// heaters should be connected. It will operate fine with additional heaters up to
// 9. Once written to, the PWM stays at that duty cycle till changed. When PWM count
// reaches 5 (typical)the next heater is turned on and PWM drive goes to zero.
// This allows minimal size capacitor bank.
// Up / DOWN counter of heater
// Increasing duty cycle
if (ADavg > SETpoint) COUNTheater = COUNTheater + 1; // Is panel voltage higher than
// power point, add fine tune UP
// Decreasing duty cycle
if (ADavg < SETpoint - HYST ) COUNTheater = COUNTheater - 1; // panel voltage lower than power
// point, subtract fine tune DOWN
// Big step down for big A/D change (sudden cloud)
if (ADavg > SETpoint - HYST - 25) COUNTheater = COUNTheater - 3; // panel voltage much lower, subtract
// RAPID response for big change DOWN
//*******************************************************************
// COUNTheater = analogRead (7); // TEST TEST TEST manually enter count ******
// COUNTheater = COUNTheater / 18; // TEST TEST TEST with pot, up to 56 *******
//********************************************************************
// Check switched temp input D12 to see if HEAT REQUESTED
// Weak pullup, add external LED & 470 ohm resistor for sufficient contact current
if (digitalRead(12) == 1) COUNTheater = 0; // TEMP SWITCH OPEN, HEAT not requested
// turn off all drive to elements
// place limits on count
if (COUNTheater <= 0) COUNTheater = 0; // Limit Lower boundry of COUNT to zero
if (COUNTheater >= 56) COUNTheater = 56; // Limit upper boundry of COUNT
// 56 indicates not enough load, maxed out
// #################################################################
// FIRST ELEMENT FIRST ELEMENT FIRST ELEMENT FIRST ELEMENT FIRST ELEMENT
if (COUNTheater >= 13) ELEMENT_1 = 1; // turn ELEMENT_1 ON
if (COUNTheater < 13) ELEMENT_1 = 0; // turn ELEMENT_1 OFF
digitalWrite (2, ELEMENT_1); // output state of ELEMENT_1
// ###################################################################
// SECOND ELEMENT SECOND ELEMENT SECOND ELEMENT SECOND ELEMENT SECOND ELEMENT
if (COUNTheater >= 20) ELEMENT_2 = 1; // turn ELEMENT_2 ON
if (COUNTheater < 20) ELEMENT_2 = 0; // turn ELEMENT_2 OFF
digitalWrite (4, ELEMENT_2); // output state of ELEMENT_2
// #################################################################
// THIRD ELEMENT THIRD ELEMENT THIRD ELEMENT THIRD ELEMENT THIRD ELEMENT
if (COUNTheater >= 25) ELEMENT_3 = 1; // turn ELEMENT_3 ON
if (COUNTheater < 25) ELEMENT_3 = 0; // turn ELEMENT_3 OFF
digitalWrite (5, ELEMENT_3); // output state of ELEMENT_3
// #####################################################################
// FOURTH ELEMENT FOURTH ELEMENT FOURTH ELEMENT FOURTH ELEMENT FOURTH ELEMENT
if (COUNTheater >= 30) ELEMENT_4 = 1; // turn ELEMENT_4 ON
if (COUNTheater < 30) ELEMENT_4 = 0; // turn ELEMENT_4 OFF
digitalWrite (7, ELEMENT_4); // output state of ELEMENT_4
// ###############################################################
// FIFTH ELEMENT FIFTH ELEMENT FIFTH ELEMENT FIFTH ELEMENT FIFTH ELEMENT
if (COUNTheater >= 35) ELEMENT_5 = 1; // turn ELEMENT_5 ON
if (COUNTheater < 35) ELEMENT_5 = 0; // turn ELEMENT_5 OFF
digitalWrite (8, ELEMENT_5); // output state of ELEMENT_5
// ################################################################
// SIXTH ELEMENT SIXTH ELEMENT SIXTH ELEMENT SIXTH ELEMENT SIXTH ELEMENT
if (COUNTheater >= 40) ELEMENT_6 = 1; // turn ELEMENT_6 ON
if (COUNTheater < 40) ELEMENT_6 = 0; // turn ELEMENT_6 OFF
digitalWrite (9, ELEMENT_6); // output state of ELEMENT_5
// ######################################################################
// SEVENTH ELEMENT SEVENTH ELEMENT SEVENTH ELEMENT SEVENTH ELEMENT SEVENTH ELEMENT
if (COUNTheater >= 45) ELEMENT_7 = 1; // turn ELEMENT_7 ON
if (COUNTheater < 45) ELEMENT_7 = 0; // turn ELEMENT_7 OFF
digitalWrite (10, ELEMENT_7); // output state of ELEMENT_7
// ####################################################################
// EIGHTH ELEMENT EIGHTH ELEMENT EIGHTH ELEMENT EIGHTH ELEMENT EIGHTH ELEMENT
if (COUNTheater >= 50) ELEMENT_8 = 1; // turn ELEMENT_8 ON
if (COUNTheater < 50) ELEMENT_8 = 0; // turn ELEMENT_8 OFF
digitalWrite (11, ELEMENT_8); // output state of ELEMENT_8
// #################################################################
// PWM LAST ELEMENT PWM LAST ELEMENT PWM LAST ELEMENT PWM LAST ELEMENT
// If elements are not the same resistanve, use the lowest resistance for PWM element
// clunky, but easy to understand. Initialize PWM drive to zero each loop for default
PWM3drv = 0;
// NOW SET PWM VALUE
// LOWEST RANGE smaller progressive steps, avoid short time on first count
if (COUNTheater == 1) PWM3drv = 9; // turn LAST ELEMENT 4% ON
if (COUNTheater == 2) PWM3drv = 15; // turn LAST ELEMENT 6% ON
if (COUNTheater == 3) PWM3drv = 25; // turn LAST ELEMENT 10% ON
if (COUNTheater == 4) PWM3drv = 35; // turn LAST ELEMENT 14% ON
if (COUNTheater == 5) PWM3drv = 50; // turn LAST ELEMENT 20% ON
if (COUNTheater == 6) PWM3drv = 75; // turn LAST ELEMENT 30% ON
if (COUNTheater == 7) PWM3drv = 100; // turn LAST ELEMENT 40% ON
if (COUNTheater ==
PWM3drv = 125; // turn LAST ELEMENT 50% ON
if (COUNTheater == 9) PWM3drv = 150; // turn LAST ELEMENT 58% ON
if (COUNTheater == 10) PWM3drv = 175; // turn LAST ELEMENT 68% ON
if (COUNTheater == 11) PWM3drv = 200; // turn LAST ELEMENT 78% ON
if (COUNTheater == 12) PWM3drv = 225; // turn LAST ELEMENT 88% ON
// HEATER #1 TURNS ON AT 13
// larger progressive steps
if (COUNTheater == 14) PWM3drv = 30; // turn LAST ELEMENT 12% ON
if (COUNTheater == 15) PWM3drv = 65; // turn LAST ELEMENT 25% ON
if (COUNTheater == 16) PWM3drv = 100; // turn LAST ELEMENT 39% ON
if (COUNTheater == 17) PWM3drv = 135; // turn LAST ELEMENT 53% ON
if (COUNTheater == 18) PWM3drv = 170; // turn LAST ELEMENT 67% ON
if (COUNTheater == 19) PWM3drv = 210; // turn LAST ELEMENT 82% ON
// HEATER #2 TURNS ON AT 20
// Begin 5 step at this power level
if (COUNTheater == 21) PWM3drv = 50; // turn LAST ELEMENT 1/5 ON
if (COUNTheater == 22) PWM3drv = 100; // turn LAST ELEMENT 2/5 ON
if (COUNTheater == 23) PWM3drv = 150; // turn LAST ELEMENT 3/5 ON
if (COUNTheater == 24) PWM3drv = 200; // turn LAST ELEMENT 4/5 ON
// HEATER #3 TURNS ON AT 25
if (COUNTheater == 26) PWM3drv = 50; // turn LAST ELEMENT 1/5 ON
if (COUNTheater == 27) PWM3drv = 100; // turn LAST ELEMENT 2/5 ON
if (COUNTheater == 28) PWM3drv = 150; // turn LAST ELEMENT 3/5 ON
if (COUNTheater == 29) PWM3drv = 200; // turn LAST ELEMENT 4/5 ON
// HEATER #4 TURNS ON AT 30
if (COUNTheater == 31) PWM3drv = 50; // turn LAST ELEMENT 1/5 ON
if (COUNTheater == 32) PWM3drv = 100; // turn LAST ELEMENT 2/5 ON
if (COUNTheater == 33) PWM3drv = 150; // turn LAST ELEMENT 3/5 ON
if (COUNTheater == 34) PWM3drv = 200; // turn LAST ELEMENT 4/5 ON
// HEATER #5 TURNS ON AT 35
if (COUNTheater == 36) PWM3drv = 50; // turn LAST ELEMENT 1/5 ON
if (COUNTheater == 37) PWM3drv = 100; // turn LAST ELEMENT 2/5 ON
if (COUNTheater == 38) PWM3drv = 150; // turn LAST ELEMENT 3/5 ON
if (COUNTheater == 39) PWM3drv = 200; // turn LAST ELEMENT 4/5 ON
// HEATER #6 TURNS ON AT 40
if (COUNTheater == 41) PWM3drv = 50; // turn LAST ELEMENT 1/5 ON
if (COUNTheater == 42) PWM3drv = 100; // turn LAST ELEMENT 2/5 ON
if (COUNTheater == 43) PWM3drv = 150; // turn LAST ELEMENT 3/5 ON
if (COUNTheater == 44) PWM3drv = 200; // turn LAST ELEMENT 4/5 ON
// HEATER #7 TURNS ON AT 45
if (COUNTheater == 46) PWM3drv = 50; // turn LAST ELEMENT 1/5 ON
if (COUNTheater == 47) PWM3drv = 100; // turn LAST ELEMENT 2/5 ON
if (COUNTheater == 48) PWM3drv = 150; // turn LAST ELEMENT 3/5 ON
if (COUNTheater == 49) PWM3drv = 200; // turn LAST ELEMENT 4/5 ON
// HEATER #8 TURNS ON AT 50
//HIGHEST POWER RANGE
if (COUNTheater == 51) PWM3drv = 50; // turn LAST ELEMENT 1/5 ON
if (COUNTheater == 52) PWM3drv = 100; // turn LAST ELEMENT 2/5 ON
if (COUNTheater == 53) PWM3drv = 150; // turn LAST ELEMENT 3/5 ON
if (COUNTheater == 54) PWM3drv = 200; // turn LAST ELEMENT 4/5 ON
if (COUNTheater >= 55) PWM3drv = 255; // turn LAST ELEMENT FULLY ON
digitalWrite (3,PWM3drv); // output state of PWM ELEMENT
// *************************************************************************
// Analog Power output signal for external power meter 0-100% (ACTUALLY STEPS) if used
analogWrite(6, COUNTheater * 9 / 2); // analogWrite 0 to 255 to D6 POWER LEVEL METER
// each count is 255/57 or approximately 4.5
// ##################################################################
// This section blinks the on board LED #13
blinktime = blinktime + 1; // increment blink timer each loop
if (blinktime >= 100) blinktime = 0; // 100 loops per blink cycle, reset
// This section blinks LED ONCE to show program is operating
if (blinktime == 1) digitalWrite (13, LOW); // LED OFF INSURE INITIAL INDICATOR OFF
if (blinktime == 3) digitalWrite (13, HIGH); // LED ON ONE BLINK CPU RUNNING
if (blinktime == 15) digitalWrite (13, LOW); // turn LED OFF
// This section blinks LED to show power level, STATUS LED #13
if (blinktime == 20 && COUNTheater >= 10) digitalWrite( 13, HIGH); // LED ON Second BLINK low
if (blinktime == 25) digitalWrite (13, LOW); // turn LED OFF
if (blinktime == 30 && COUNTheater >= 18) digitalWrite (13, HIGH); // LED ON Third BLINK
if (blinktime == 32) digitalWrite (13, LOW); // turn LED OFF
if (blinktime == 34 && COUNTheater >= 25) digitalWrite (13, HIGH); // LED ON Fourth BLINK
if (blinktime == 36) digitalWrite (13, LOW); // turn LED OFF
if (blinktime == 38 && COUNTheater >= 32) digitalWrite (13, HIGH); // LED ON Fifth BLINK
if (blinktime == 40) digitalWrite (13, LOW); // turn LED OFF
if (blinktime == 42 && COUNTheater >= 39) digitalWrite (13, HIGH); // LED ON Sixth BLINK
if (blinktime == 44) digitalWrite (13, LOW); // turn LED OFF
if (blinktime == 46 && COUNTheater >= 46) digitalWrite (13, HIGH); // LED ON Seventh BLINK
if (blinktime == 48) digitalWrite (13, LOW); // turn LED OFF
if (blinktime == 50 && COUNTheater >= 53) digitalWrite (13, HIGH); // LED ON Eighth BLINK
if (blinktime == 52) digitalWrite (13, LOW); // turn LED OFF
// ###############################################################
// This section prints the values to the programmers serial screen. Printing takes time. It
// is done after a number of program loops and the count is reset. This speeds up the loop
// and makes reading data easier
PRINTcycle = PRINTcycle + 1; // increment printer count
if (PRINTcycle >= 10) // if count reached, do the following between brackets
{ // start of stuff to do
PRINTcycle = 0; // reset counter value
// print four spaced A/D readings. This can indicate capacitor bank droop
Serial.print(PV0); // raw PV value #0
Serial.print(" ");
Serial.print(PV2); // raw PV value #2
Serial.print(" ");
Serial.print(PV4); // raw PV value #4
Serial.print(" ");
Serial.print(PV6); // raw PV value #6
Serial.print(" raw ");
Serial.print(ADavg); // averaged A/D value of the EIGHT readings
Serial.print(" avg ");
Serial.print(HYST); // differential count between steps
Serial.print("H ");
Serial.print(COUNTheater); // output overall heater count value
Serial.print(" cnt ");
Serial.print(ELEMENT_1); // following ON/OFF states of each heater
Serial.print(" ");
Serial.print(ELEMENT_2);
Serial.print(" ");
Serial.print(ELEMENT_3);
Serial.print(" ");
Serial.print(ELEMENT_4);
Serial.print(" ");
Serial.print(ELEMENT_5);
Serial.print(" ");
Serial.print(ELEMENT_6);
Serial.print(" ");
Serial.print(ELEMENT_7);
Serial.print(" ");
Serial.print(ELEMENT_8);
Serial.print(" ");
Serial.print(PWM3drv); // output PWM value for LAST heater
Serial.println(" PWM3 V1.1"); // This is like the carrage return if
// you know what a typwriter is
} // end bracket, print routine
// this is end of all printing stuff
// ##############################################################
} // END of the program loop