Jump to content

Solar Thermal Hot Water System


Jen-in-Wellies

Featured Posts

23 minutes ago, David Mack said:

Jen wrote:

" When the sun is bright enough, the pump is turned on. There is also temperature measurement of the top of the calorifier. This disables the pump if the calorifier risks overheating."

 

So in bright sun when the calorifier is fully up to temperature, the pump is switched off. Doesn't that lead to even higher water temperatures in the collector? Boiling?

Yes. It is open vented, so the steam has somewhere to go. Propylene Glycol antifreeze mixed in raises the boiling point some.

Jen

Link to comment
Share on other sites

On 18/10/2018 at 20:37, smileypete said:

Don't know but it looks similar to the Navitron Fino one, however that one is only about about 1m2 which is half the size of the Bimble one:

 

https://www.navitron.org.uk/fino-flat-plate-solar-hot-water-panel

I did see that one when looking around for ours, but decided it was not big enough on it's own so would have needed 2. Would ideally like one that's more robust than our current panel, but very happy with our setup other than that. 

On 18/10/2018 at 20:37, smileypete said:

Don't know but it looks similar to the Navitron Fino one, however that one is only about about 1m2 which is half the size of the Bimble one:

 

https://www.navitron.org.uk/fino-flat-plate-solar-hot-water-panel

I did see that one when looking around for ours, but decided it was not big enough on it's own so would have needed 2. Would ideally like one that's more robust than our current panel, but very happy with our setup other than that. 

Link to comment
Share on other sites

  • 7 months later...

Update Part One

An update on the solar hot water controller detailed in this thread. There have been two major changes. One is that the sensor that detects how sunny it is outside has been replaced. The other is that the unit now also measures how much water is left in the main water tank for the boat, so I know when it needs filling up. This obviously isn't related to solar thermal control, but I used the Arduino and display LED's to look after this sensor too as they were available and conveniently close to the water flow meter. I'll detail the water tank meter in another reply.

One of the reasons for replacing the old controller was that the light dependent resistor had drifted over the years. The new controller had two LDR's. One, inside the cabin is used to dim the LED's in line with the ambient light level inside, turning them off entirely at night. This still seems to be working fine. The other, mounted on the roof, measures external light level to decide when to switch the pump on to the solar collector. This had drifted very badly over the winter. So much so that when solar hot water became practical again in the spring, it was incapable of giving a sensible resistance value to the controller. I ran the controller in manual mode, switching on the pump in the morning and off at night. Fortunately I'd included this option in to the design as one position on a three position toggle switch on the panel. The controller would still shut down the pump in the event of the calorifier reaching its set maximum temperature.

 

The search was on for an alternative to the LDR. Two types of sensor presented themselves as being nice and small. Photodiodes and phototransistors. Phototransistors can flow enough current to directly work with Arduino input pins, so the search was narrowed to them. Eventually, the BP103 was selected. They are packaged in a metal can with through hole leads. The chip is protected by a clear epoxy overcoat. They have a a wide angle of view, 110 degrees and work well in the visible light region, with a peak response in the very near infra-red. Ideal for this application.

 

The phototransistor needed mounting in something to protect it and the the leads connected to it. I based this around the sort of PVC cable gland I'd used for the previous LDR. The end of the cable gland was filled with epoxy glue. The next day, after the glue had set hard, it was drilled through 4mm, to allow the leads to pass through, but the transistor can to sit on top, exposed to the sunlight. Working outside, the two connection leads were soldered to the collector (+ve) and emitter (-ve) pins and covered with heat shrink sleaving. The base pin is not used and was cut short. More epoxy was applied to fill the 4mm hole and the can pushed back to glue it to the top of the gland's epoxy fill. Finally, the grommit and gland nut were tightened at the cable entry. Two goes were needed to get a working sensor.

 

Once installed, the light levels measured by the Arduino analog input pin were measured via the usb serial connection to a PC. The resistor needed to get a sensible 0 to 5V measurable signal was adjusted till eventually a 1kohm value was decided on. The pump switch on threshold in the code was adjusted to suit.

 

The new sensor has only been in for a few days so far, so early. Will see how it does and update. The response of the phototransistor is much sharper than the old LDR, but I've got it so that it does switch off the pump when a rain shower comes along. Plenty of those to test it with the last few days!

 

Jen

IMG_20190605_141544.jpg.898da5e435049f280d3a927599d84c21.jpg

Filling the cable gland end with araldite. The frog masking tape prevents it running out the bottom before it has a chance to set! Slow set araldite, 'cause that's what I had in the cupboard...

 

IMG_20190606_085414.jpg.0bcbd2635d42684030a4ffbc9c578b07.jpg

Frog tape removed and the end drilled through, ready to take the leads of the phototransistor.

 

IMG_20190606_144341.jpg.e3b66e3f58b5698c281a837d0bf7813e.jpg

The new sunlight sensor with a BP103 epoxied in to the end of the cable gland.

 

 

Edited by Jen-in-Wellies
Link to comment
Share on other sites

Update Part Deux. Water Tank Meter

The second part of my update. Not strictly solar thermal related, it just uses the Arduino and LED display system.
I've added a water flow meter just upstream of the Jabsco main cold water pump. It provides a stream of pulses to the Arduino when water flows through it. By counting these pulses the Arduino can work out both flow rate and the total amount of water that has passed. It is the second of these that is of interest here. If the tank capacity is known and the system reset when the water tank is full, then the tank level can be displayed and the boater warned when it is going to run dry. Not a necessity by any means. I've had this boat for ten years and not felt the lack, but useful and easy to set up.

 

The flow meter used is a YF-S201. I got mine via ebay at a cheaper price than the link. It is well matched to the Jabsco Parmax water pump, being well within the flow rate and pressure limits. These sensors suffer from inaccuracy at very low flow rates, so I placed it just upstream of the pump. Water only flows when the pump runs and that only happens when the pressure drops low enough in the accumulator and expansion vessel in the hot and cold water systems. This means that the flow rate through the sensor is mostly reasonably constant, or zero. I didn't bother calibrating mine any further, as I'm only interested in % fullness in the tank.

 

The flow meter is connected to one of the Arduino interrupt pins. Either pin 2, or 3 on an Uno. Using interrupts will mean that the Arduino will stop what else it is doing to count pulses from the sender whenever pulses are detected, so it won't miss any while doing something else. I used a suitable library to simplify the coding. Every so often, as the program loops, it updates the total amount of water that has passed through the sensor. This is used to calculate the percent left.

 

I found the total volume of the tank by filling it up, then emptying the contents in to the cut, while running a test code on a spare Arduino connected to the flow meter. It took 40 minutes or so of continuous pump operation. I kept an eye and ear on the pump, but it coped with this continuous running. The arduino reckoned that one tank full is 400 litres, which is a reasonable figure. I derated this by 5% to 380l as the full value in the Arduino sketch.  When the Arduino is power cycled, the calculation of tank level defaults back to 100%, so when I fill the water tank I need to remember to switch the solar controller off and on again.

 

The solar controller has six LED's on the front cover. One is a power on indicator. One flashes when the pump runs. The other four are used to indicate calorifer temperature from 30 to 60C. These four LED's, plus the power on button are also used to indicate tank level. When the tank is relatively full, then four LED's show the level in 20% increments. When the tank is full (>99%), then all are lit. When the level drops below 20%, then the power on LED goes out and the four LED's show 4% increments, so finer grained. The controller displays cauliflower temperature for six seconds, then tank level for three seconds, with a half second gap between each, going round continuously.

 

Tank Level        
<20%   >20%     LED        Calorifier
fine      course    Colour    Temperature
-------------------------------------------------
16%      80%       Red           60C
12%      60%       Orange       50C
8%        40%       Yellow         40C
4%        20%        Blue           30C
Off         On         Green       Power On

 

It seems complicated at first, but it is easy to read at a distance, tens of feet away, as long as you are not colour blind. An LED, or LCD, or other number, or alphanumeric display would work and be as easy, or easier to set up.

 

Jen

IMG_20190612_125909.jpg.16a8c54d6f8d80307e4925d7cd9ea6bc.jpg

The new flow meter, spliced in between the gauze filter and the water pump. Lots of ptfe tape needed to get all this drip free.

 

IMG_20190612_130652.jpg.b0dd3f981de2b1086a021661684b3009.jpg

A spare, out of focus Arduino connected up and measuring how many pulses from the flow meter corresponds with emptying the water tank.

 

Edited by Jen-in-Wellies
Link to comment
Share on other sites

Part Three. The Arduino Sketch

I've included the program for the Arduino. This has been improved quite a bit from the one I published last year. Getting rid of horrible delay() functions amongst other things. Also has some code to allow trials and help with debugging. It now has the water meter measurement code as well. Hopefully useful for someone wanting to do something similar, or anyone familiar with Arduino's. Ignore otherwise!

 

Jen

/*A solar controller to replace an Aton controller with an Arduino Uno.
   Uses a phototransistor to measure sunlight and turn on the pump via a relay when the sunlight reaches a threshold.
   The threshold depends on calorifier temperature, so low sunshine levels won't cool down a hot calorifier.
   Delay turning pump on and off to compensate for passing clouds.
   Pump can be overridden by a switch, either on all the time, off all the time, or auto (DPDTCO).
   LM35 IC used for temperature measurement at calorifier.
   Uses LM35 library https://www.arduinolibraries.info/libraries/lm35 to give direct Celcius readings.
   Four LED temperature indicators.
   An LDR is used to adjust LED indicators to suit ambient cabin illumination with PWM digital pins.
   Overtemperature protection, shuts off pump.
   Undertemperature frost protection shuts off pump.
   Written by Jen-in-Wellies. October 2018.
   Modified May/June 2019:
     Added pumptempWait to prevent relay chatter when upper/lower tank temp limit reached.LM35 signal hss a lot
     of noise on it with the twisted pair connection to the Arduino currently used, so indicated temps can swing +/-1C or so.
     Made the code a bit tighter. Got rid of persistOn variable to carry over pump state. 
     Only uses calorifier temperature once the averaging is giving representative results (tankAveraged).
     Added abilitiy to use test input data, rather than actual sensor readings for light level, cabin light level and calorifier temperature.
     Added a YF-S201B flow meter to the water pump inlet and water tank contents indication on the LED's
     Changed sunshine sensor hardware from LDR to phototransistor. Altered light thresholds to suit.
 */

#include <LM35.h> //LM35 library.
#include <FlowMeter.h>  // https://github.com/sekdiy/FlowMeter

//Sensor properties for YF-S201B flow meter. 30.0f is max flow rate l/min. 7.5f is Hz/l/min pulse rate, from data sheet. 
//Other values are for calibration over range of flow rates (currently disabled, all 1).
FlowSensorProperties YF_S201B = {30.0f, 7.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

//Pin definitions
const int sunPin = 1; //Analogue Input pin from the LDR on the roof.
LM35 tankPin(0); //Analogue Input pin from the LM35 on the cauliflower.
const int ambientPin = 5; //Analogue Input pin from LDR on controller front panel.
const int ledGreen = 3; //LED power on indicator., Also indicates <=20% water in tank.
const int ledBlue = 9; //LED pin for 30C Blue. Warm enough for a bath. Also indicates 4%, or 20% water tank level.
const int ledYellow = 10; //LED pin for 40C yellow. Mixer valve threshold. Also indicates 8%, or 40% water in tank.
const int ledAmber = 11; //LED pin for 50C Orange. Water still hot the next day. Also indicates 12%, or 60% water tank level.
const int ledRed = 6; //LED pin for 60C Red. Very hot! High freq PWM pin. Also indicates 16%, or 80% water tank level.
const int ledPump = 12; //LED pump indicator. Green flashing.
const int relayPump = 4; //Pump output. To relay for pump and hours meter.
const int overrideSwitch = 7; //Overrides pump control. On all the time up to maximum temperature.
const int autorideSwitch = 8; //Selected for normal automatic pump operation. DPDTCO. Centre off disables pump.
FlowMeter Meter1 = FlowMeter(2, YF_S201B); // connect a YF-S201B flow meter to interrupt 0, pin 2.

// define an 'interrupt service handler' (ISR) for reading the flow meter.
void Meter1ISR() {
    // let the flow meter count the pulses
    Meter1.count();
}

//Variables
const int tankMax = 70; //Max calorifier temperature. Pump shuts off.
const int tankFrost = 5; //value for tankTemp frost protection shut off.
const int tankNumReadings = 100; //Number of tank temperature measurements to take to smooth input.
int tankReadings[tankNumReadings]; //Reading of tank temp analogue input
int tankReadIndex = 0; // the index of the current tam temp reading
int tankTotal = 0; // The running tank temp total. 
float tankTemp = 0.0; //Average read temperature from calorifier.
int tankAveraged = 0; // Set to 1 when tankReadIndex = tankNumReadings first reached and tankTemp equals cauliflower temperature.
int sunShine = 0; //Analogue read value from LDR. 0 to 1023.
int sunOn = 0; //LDR sunShine reading at which pump is enabled in auto.
const int sunOnLow = 700; //value between 0 and 1023 for sunOn above which the pump can be enabled in auto. was 290
const int sunOnMed = 800; //Vary the sunOn levels depending on tankTemp, so low levels don't lead to the tankTemp dropping. was 350
const int sunOnHigh = 900; //
const int tankTempLow = 40; //tankTemp below which sunOnLow is used. was 40
const int tankTempMed = 50; //tankTemp at which sunOnMed is used. was 50
const int tank30 = 30; //value for tankTemp to light blue LED. 
const int tank35 = 35; //value for tankTemp to light yellow and blue LED.
const int tank40 = 40; //value for tankTemp to light yellow LED.
const int tank45 = 45; //value for tankTemp to light yellow and amber LED.
const int tank50 = 50; //value for tankTemp to light amber LED.
const int tank55 = 55; //value for tankTemp to light amber and red LED
const int tank60 = 60; //value for tankTemp to light red LED.
int ambientCabin = 0; //Value between 0 and 1023 for cabin light level.
const float cabinDay = 1000.0; //Value between 0 and 1023 for a brightly lit cabin. Float to ensure PWM calc works properly.
const float cabinNight = 50.0; //Value betwixt 0 and 1023 for a cabin in the dark. Float to ensure PWM calc works properly.
int ledPWM = 0; // value for LED light level.
const long pumponWait = 2 * 60 * 1000L; //Wait in ms before turning pump on. Cloud protection!
const long pumpoffWait = 2 * 60 * 1000L; //Wait in ms before turning pump off. Cloud protection!
const long pumptempWait = 5 * 1000L; //Wait in ms before allowing pump to perhaps turn on again if tankMax, ot tankFrost is breached.
int pumpOn; //Pump 1 on, or 0 off.
int overRide = 0; //Override switch read result.
int autoRide = 0; //AutoSwitch read result.
long turnOn1 = 0L; //timers for auto pump turn on - pumponWait.
long turnOn2 = 0L;
long turnOff1 = 0L; //timers for auto pump turn off - pumpoffWait
long turnOff2 = 0L;
long timerWait1 = 0L; //timers to replace horrible delay() This one to output deBug data at intervals.
long timerWait2 = 0L; //This timer used for intervals to total volume of water measured through the flow meter.
long timerLED = 0L; //Timer to control if LED's are displaying water level, or cauliflower temperature.
long loopLED = 0L; //Time during the LED display loop.
const long waterLED = 3000L; // Time that water level is displayed on LED's.
const long cauliflowerLED = 6000L; // Time that calorifier temperature is displayed on LED's.
const long nowtLED = 500L; //Time that LED's are blank when switching between water level and cauliflower temperature.
const float ledGreenEqualise = 0.77; //Equalise 47mcd green LED PWM to adjust all to the same brightness.
const float ledBlueEqualise = 0.36; //Equalise 100mcd blue LED PWM. The brightest LED.
const float ledYellowEqualise = 1.0; //Equalise 36mcd yellow LED PWM. The dimmest LED.
const float ledAmberEqualise = 0.72; //Equalise 50mcd amber LED PWM
const float ledRedEqualise = 0.55; //Equalise 65mcd red LED PWM

//Tank Water Level variables.
const int tankFull = 380; //Tank full level in litres (minus 5% from actual full).
float tankUsed = 0; //Current cumulative water used in litres.
float tankPercent = 100; //Calculated tank level. Assume 100% on power on.
const int tankIntegrate = 5000; //Time in ms between calculation of tank cumulative water draw off.

// Debug
const int deBug = 1; //Get and print debug results over serial. 1 for enable, !1 (0) for disable.
const long loopWait = 3 * 1000L; //Wait to slow the program loop down during deBug.

// Testing. Set testData to 1 to use these, rather than measured inputs.
const int testData = 0; //Use test analogue inputs rather than real ones. 1 for enable. !1 (0) for disable.
const int ambientTest = 800; //test ambient cabin light level.
const int sunTest = 320; //test sunshine level.
const int tankTest = 71; //test cauliflower temperature

void setup() {
  // put your setup code here, to run once:

  //Define the output digital pins.
  pinMode(ledGreen, OUTPUT);
  pinMode(ledBlue, OUTPUT);
  pinMode(ledYellow, OUTPUT);
  pinMode(ledAmber, OUTPUT);
  pinMode(ledRed, OUTPUT);
  pinMode(relayPump, OUTPUT);
  pinMode(ledPump, OUTPUT);

  //Define input digital pins.
  pinMode(overrideSwitch, INPUT_PULLUP);
  pinMode(autorideSwitch, INPUT_PULLUP);
 
  //Set LED's and pump off to start.
  analogWrite(ledGreen, 0);
  analogWrite(ledBlue, 0);
  analogWrite(ledYellow, 0);
  analogWrite(ledAmber, 0);
  analogWrite(ledRed, 0);
  digitalWrite(relayPump, LOW);
  digitalWrite(ledPump, LOW);

  //test LED's
  analogWrite(ledGreen, 255 * ledGreenEqualise);
  delay(500);
  analogWrite(ledBlue, 255 * ledBlueEqualise);
  delay(500);
  analogWrite(ledYellow, 255 * ledYellowEqualise);
  delay(500);
  analogWrite(ledAmber, 255 * ledAmberEqualise);
  delay(500);
  analogWrite(ledRed, 255 * ledRedEqualise);
  delay(500);
  digitalWrite(ledPump, HIGH);
  delay(2000);
  analogWrite(ledGreen, 0);
  analogWrite(ledBlue, 0);
  analogWrite(ledYellow, 0);
  analogWrite(ledAmber, 0);
  analogWrite(ledRed, 0);
  digitalWrite(ledPump, LOW);

  //initialise timers
  turnOn1 = millis();
  turnOff1 = millis();
  timerWait1 = millis();
  timerWait2 = millis();
  timerLED = millis();
  
  //Set all tank temp readings to zero.
  for (int thisReading = 0; thisReading < tankNumReadings; thisReading++) {
    tankReadings[thisReading] = 0;
  }

  //Turn off pumpOn initially as default. Only turn on if conditions are met.
  pumpOn = 0;

  // enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin
  attachInterrupt(INT0, Meter1ISR, RISING);

  // sometimes initializing the gear generates some pulses that we should ignore
  Meter1.reset();

  // start serial port at 9600 bps: Turn on by setting deBug to 1.
  if (deBug == 1) {
    Serial.begin(9600);
  }
}

void loop() {
  // put your main code here, to run repeatedly

  // Every so often, measure the total volume through the flow meter. Update tankPercent full.
  
  if ((millis() - timerWait2) >= tankIntegrate) {
    Meter1.tick(tankIntegrate);
    tankUsed = (Meter1.getTotalVolume());
    tankPercent = 100 * (tankFull - tankUsed) / tankFull;
    //Idiot proof against PLC reset, or power cycle with a partially full tank tankPercent can't be negative.
    if (tankPercent < 0){
      tankPercent = 0;
    }
    timerWait2 = millis();
  }
  
  //Measure the cauliflower temperature. Take rolling average of several readings, one per loop.
  tankTotal = tankTotal - tankReadings[tankReadIndex]; //subtract the last reading of that tankReadIndex.
  tankReadings[tankReadIndex] = tankPin.cel(); //take temperature from LM35 sensor for new tankReadIndex.
  int tankRead = tankReadings[tankReadIndex]; //debug thing to see instant tank temperature.
  tankTotal = tankTotal + tankReadings[tankReadIndex]; //add temperature to total.
  tankReadIndex = tankReadIndex + 1; //Go to next position in array.
  if (tankReadIndex >= tankNumReadings) {
    tankReadIndex = 0; //Wrap around to the beginning when reached number of readings.
    tankAveraged = 1; //Does this once the first time the loop reaches tankNumReadings. Tank temp now smoothed and usable.
  }
  if(testData != 1){
    tankTemp = tankTotal / tankNumReadings; //Calculate average temp
  }
  else{
    (tankTemp = tankTest); //When in test mode use test data.
  }

  //Measure the cabin light level so LED brightness can be adjusted to suit.
  if(testData != 1){
    ambientCabin = analogRead(ambientPin);
  }
  else{
    (ambientCabin = ambientTest); //yse test data in test mode.
  }  

  //LED's at full brightness in bright sunlight.
  if (ambientCabin >= cabinDay) {
    ledPWM = 255;
  }
  //LED's off when cabin is dark.
  if (ambientCabin <= cabinNight) {
    ledPWM = 0;
  }
  //Fade the LED's depending on cabin ambient. Linear, maybe needs to be proportional to 1/log(ambientCabin)
  if ((ambientCabin < cabinDay) && (ambientCabin > cabinNight)) {
    ledPWM = 255 * (ambientCabin - cabinNight) / (cabinDay - cabinNight);
  }

  //Do stuff requiring accuate cauliflower temperature only once the averaging calculation has cycled round once from reset.
  if (tankAveraged == 1) {

    //Set up loop to decide if LED's are displaying water tank level, calorifier temperature, or nothing.
    loopLED = millis() - timerLED;
    if (loopLED >= (nowtLED + waterLED + nowtLED + cauliflowerLED)){
      loopLED = 0;
      timerLED = millis();
    }

    //Light no LED's during gaps between displaying calorifer temp and water level.

    if(((loopLED >= 0) && (loopLED < nowtLED)) || ((loopLED >= (nowtLED + waterLED)) && (loopLED < (nowtLED + waterLED + nowtLED)))){
      analogWrite(ledBlue, 0);
      analogWrite(ledYellow, 0);
      analogWrite(ledAmber, 0);
      analogWrite(ledRed, 0);
      analogWrite(ledGreen, ledPWM * ledGreenEqualise);      
    }

        //Light up the appropriate LED's for water level.
    if((loopLED >= nowtLED) && (loopLED < (nowtLED + waterLED))){
      if (tankPercent >= 99){
        analogWrite(ledBlue, ledPWM * ledBlueEqualise); //Light all LED's
        analogWrite(ledYellow, ledPWM * ledYellowEqualise);
        analogWrite(ledAmber, ledPWM * ledAmberEqualise);
        analogWrite(ledRed, ledPWM * ledRedEqualise);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankPercent >= 80) && (tankPercent < 99)){
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, ledPWM * ledRedEqualise);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankPercent >= 60) && (tankPercent < 80)){
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, ledPWM * ledAmberEqualise);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankPercent >= 40) && (tankPercent < 60)){
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, ledPWM * ledYellowEqualise);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankPercent >= 20) && (tankPercent < 40)){
        analogWrite(ledBlue, ledPWM * ledBlueEqualise);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankPercent >= 16) && (tankPercent < 20)){
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, ledPWM * ledRedEqualise);
        analogWrite(ledGreen, 0);
      }
      if ((tankPercent >= 12) && (tankPercent < 16)){
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, ledPWM * ledAmberEqualise);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, 0);
      }
      if ((tankPercent >= 8) && (tankPercent < 12)){
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, ledPWM * ledYellowEqualise);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, 0);
      }
      if ((tankPercent >= 4) && (tankPercent < 8)){
        analogWrite(ledBlue, ledPWM * ledBlueEqualise);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, 0);
      }
    }
  
    //Light up the appropriate calorifier temperature indicator LED's.
    if((loopLED >= (nowtLED + waterLED + nowtLED)) && (loopLED < (nowtLED + waterLED + nowtLED + cauliflowerLED))){
      if (tankTemp < tank30) {
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankTemp >= tank30) && (tankTemp < tank35)) {
        analogWrite(ledBlue, ledPWM * ledBlueEqualise);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankTemp >= tank35) && (tankTemp < tank40)) {
        analogWrite(ledBlue, ledPWM * ledBlueEqualise);
        analogWrite(ledYellow, ledPWM * ledYellowEqualise);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankTemp >= tank40) && (tankTemp < tank45)) {
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, ledPWM * ledYellowEqualise);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankTemp >= tank45) && (tankTemp < tank50)) {
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, ledPWM * ledYellowEqualise);
        analogWrite(ledAmber, ledPWM * ledAmberEqualise);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankTemp >= tank50) && (tankTemp < tank55)) {
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, ledPWM * ledAmberEqualise);
        analogWrite(ledRed, 0);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankTemp >= tank55) && (tankTemp < tank60)) {
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, ledPWM * ledAmberEqualise);
        analogWrite(ledRed, ledPWM * ledRedEqualise);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
      if ((tankTemp >= tank60) && (tankTemp < tankMax)) {
        analogWrite(ledBlue, 0);
        analogWrite(ledYellow, 0);
        analogWrite(ledAmber, 0);
        analogWrite(ledRed, ledPWM * ledRedEqualise);
        analogWrite(ledGreen, ledPWM * ledGreenEqualise);
      }
    }


  
    //Measure the sunshine.
    if(testData != 1){
      sunShine = analogRead(sunPin);
    }
    else{
      sunShine = sunTest;
    }
  
    //Check if DPDTCO switch is in auto position
    if (digitalRead(autorideSwitch) == LOW) {
      autoRide = 1;
    }
    else {
      autoRide = 0;
    }
  
    //Check if DPDTCO Switch is in override position
    if (digitalRead(overrideSwitch) == LOW)  {
      overRide = 1;
    }
    else {
      overRide = 0;
    }
  
    //Turn on pump if DPDTCO in override position. 
    if ((overRide == 1) && (pumpOn == 0)) {
      pumpOn = 1;
    }
    //Turn off pump if DPDTCO switch is in centre off position. 
    //Will pass through this position when going from override to autoride, so will turn pump off.  
    if ((overRide == 0) && (autoRide == 0) && (pumpOn == 1)) {
      pumpOn = 0;
      }
    
    //Set SunOn, depending on tankTemp.
    if (tankTemp <= tankTempLow) {
      sunOn = sunOnLow;
    }
    if ((tankTemp > tankTempLow) && (tankTemp <= tankTempMed)) {
      sunOn = sunOnMed;
    }
    if (tankTemp > tankTempMed) {
      sunOn = sunOnHigh;
    }
  
    //Turn on Pump if sunlight is bright enough for sufficient continuous time and in auto mode.
    if ((pumpOn == 0) && (sunShine >= sunOn) && (autoRide == 1)) {
      turnOn2 = millis();
      if ((turnOn2 - turnOn1) >= pumponWait) {
        pumpOn = 1;
      }
    }
    //resets timer to current time if sun not bright enough, or goes behind a cloud before delay time is reached.
    else {
      turnOn1 = millis();
    }
  
    //Turn off Pump in auto mode if sunlight is too dull for sufficient time.
    if ((pumpOn == 1) && (sunShine < sunOn) && (autoRide == 1)) {
      turnOff2 = millis();
      if ((turnOff2 - turnOff1) >= pumpoffWait) {
        pumpOn = 0;
      }
    }
    else(turnOff1 = millis()); 
  
    //Turn off pump if measured tank temperature is less than frost protect threshold, or goes over upper limit even momentarily. 
    if ((tankTemp <= tankFrost) || (tankTemp >= tankMax)) {    
      pumpOn = 0; //turn off pump      
      analogWrite(ledBlue, ledPWM * ledBlueEqualise); //light all temperature LED's
      analogWrite(ledYellow, ledPWM * ledYellowEqualise);
      analogWrite(ledAmber, ledPWM * ledAmberEqualise);
      analogWrite(ledRed, ledPWM * ledRedEqualise);       
    } 
    
    //Turn pump and indicator LED on, or off.
    if (pumpOn == 1) {
      digitalWrite(relayPump, HIGH);
      digitalWrite(ledPump, HIGH);
    }
    else {
      digitalWrite(relayPump, LOW);
      digitalWrite(ledPump, LOW);
    }

    //Wait to slow relay chatter if tank temp is on threshold of max, or min limits.
    if ((tankTemp <= tankFrost) || (tankTemp >= tankMax)) {
    tankAveraged = 0; //Measure the average temperature again.
    }    
  }

  //Housekeeping. Preventing timer buffer overflows if the system is left powered up too long.
  if ((turnOn1 > 21470000000) || (turnOn1 < -21470000000)) {
    turnOn1 = 0;
  }
  if ((turnOn2 > 21470000000) || (turnOn2 < -21470000000)) {
    turnOn2 = 0;
  }
  if ((turnOff1 > 21470000000) || (turnOff1 < -21470000000)) {
    turnOff1 = 0;
  }
  if ((turnOff2 > 21470000000) || (turnOff2 < -21470000000)) {
    turnOff2 = 0;
  }

  //Debugging. Turn on by setting deBug to 1.
  if ((deBug == 1) && ((millis() - timerWait1) >= loopWait)) {
    Serial.println("DEBUG");
    Serial.print("time stamp ");
    Serial.println(millis());
    Serial.print("tankTemp ");
    Serial.println(tankTemp);
    Serial.print("sunShine ");
    Serial.println(sunShine);
    Serial.print("sunOn ");
    Serial.println(sunOn);
    Serial.print("overRide ");
    Serial.println(overRide);
    Serial.print("autoRide ");
    Serial.println(autoRide);
    Serial.print("pumpOn ");
    Serial.println(pumpOn);
    Serial.print("turnOn1 ");
    Serial.print(turnOn1);
    Serial.print(" turnOn2 ");
    Serial.print(turnOn2);
    Serial.print(" delta ");
    Serial.println(turnOn2 - turnOn1);
    Serial.print("turnOff1 ");
    Serial.print(turnOff1);
    Serial.print(" turnOff2 ");
    Serial.print(turnOff2);
    Serial.print(" delta ");
    Serial.println(turnOff2 - turnOff1);
    Serial.print("ambientCabin ");
    Serial.println(ambientCabin);
    Serial.print ("ledPWM ");
    Serial.println(ledPWM);
    Serial.print("tankUsed ");
    Serial.println(tankUsed);
    Serial.print("tankPercent ");
    Serial.println(tankPercent);
    timerWait1 = millis(); 
  }  
}

 

Link to comment
Share on other sites

Out of interest for the thermal solar sensor why not use a thermistor bonded to the back of a small black ally plate which would give you a resistance value related to the 'heat output' of the sun rather than the 'brightness'?  Just a guess, but in cold weather on a bright day you may not want the pump running.

 

As an aside, my water tank level sensor is a small aquarium air pump feeding a dip tube to the bottom of the tank, the depth of water in the tank being the pressure in the dip tube.  I use a small mechanical gauge, but am thinking about using a simple electronic sensor to measure the pressure.  If you were to use this method you could simply trigger the pump say once per hour, read the pressure and then have a map to convert depth to litres.  This would also avoid getting problems between refilling and emptying getting out of sync.

Edited by Chewbacka
Link to comment
Share on other sites

Oh Jen you do like to play dont you? In my old boat I used a small solar panel to operate the small solar pump when the sun shined, This boat has the water heated by the immersion heater from the solar which is simple unless I forget?. I do have a gauge to fit on this boat, but aroundtuit has got in the way!! On my old boat it was a clear tube with a red ball in it worked great and was very cheap......................

Link to comment
Share on other sites

10 hours ago, peterboat said:

Oh Jen you do like to play dont you? In my old boat I used a small solar panel to operate the small solar pump when the sun shined, This boat has the water heated by the immersion heater from the solar which is simple unless I forget?. I do have a gauge to fit on this boat, but aroundtuit has got in the way!! On my old boat it was a clear tube with a red ball in it worked great and was very cheap......................

Overcomplicated could be my middle name! This boat did start out with a clear tube water level meter. Then I built a cupboard around it and forgot to check the water level and ran out a lot. Then the tube went all green and yuck inside, despite being in the dark most of the time. That was when I took it out!

Link to comment
Share on other sites

11 hours ago, Chewbacka said:

Out of interest for the thermal solar sensor why not use a thermistor bonded to the back of a small black ally plate which would give you a resistance value related to the 'heat output' of the sun rather than the 'brightness'?  Just a guess, but in cold weather on a bright day you may not want the pump running.

 

As an aside, my water tank level sensor is a small aquarium air pump feeding a dip tube to the bottom of the tank, the depth of water in the tank being the pressure in the dip tube.  I use a small mechanical gauge, but am thinking about using a simple electronic sensor to measure the pressure.  If you were to use this method you could simply trigger the pump say once per hour, read the pressure and then have a map to convert depth to litres.  This would also avoid getting problems between refilling and emptying getting out of sync.

The thermistor on a plate concept could work well and I might try it if the phototransistor dies. Thanks for the idea. Most thermal solar installations use a temperature sensor inside the collector, measuring the plate temperature. On mine, the wire for the sensor comes through the roof quite a distance from the collector and I'd like to avoid either making another hole in the roof, or having thin cable running across the roof, so I'm sort of limited to measuring away from actual conditions inside the collector. I found that the old light dependent resistor was good at determining when there was enough heat to be worth extracting. A bright sunny day in winter wouldn't trigger it, for example. Human eyes are actually pretty rubbish at determining absolute brightness. Our contracting pupils and our getting used to current conditions tend to even things out, so we perceive relatively little difference between a sunny day in winter and summer, or even an overcast, and sunny day next to each other. There is dark adaptation at night as well. People on CWDF are often surprised at just how rubbish their solar photovoltaic panels are in winter compared with summer. They expect them to be a bit worse, not orders of magnitude worse as that is not how it seems to them from the brightness of the sky that they see. It is only rapid change, a sunny day, going to a thunderstorm that we really notice. An instrument shows massive changes that we perceive as small ones. Our eyes just alter the f-stop! ?

 

Jen

Edited by Jen-in-Wellies
Link to comment
Share on other sites

Interesting way of reading water volume.  I've got a MSC meter which works on a little pressure sensor that measures the pressure from the depth of water, with a digital readout.  Could you not use a simple pressure sensor like that to read the depth of water?

Link to comment
Share on other sites

On 16/06/2019 at 15:31, Jen-in-Wellies said:

Eventually, the BP103 was selected

Bit different to scraping the paint off an OC71...  Anything you could do to save money as a struggling teenager. 

 

And an eighth the cost of an OCR71, 50-odd  years later!

Link to comment
Share on other sites

43 minutes ago, WotEver said:

Bit different to scraping the paint off an OC71...  Anything you could do to save money as a struggling teenager. 

 

And an eighth the cost of an OCR71, 50-odd  years later!

That comment takes me back - I used to work in the Mullard Southampton factory where transistors (BC107 family from memory) were made, including the opto devices which were the same device, but had a window in the can.

 

Link to comment
Share on other sites

1 hour ago, Chewbacka said:

That comment takes me back - I used to work in the Mullard Southampton factory where transistors (BC107 family from memory) were made, including the opto devices which were the same device, but had a window in the can.

 

Yes but Mullard cottoned on to folk like myself scraping the black paint off OC71s (blame Practical Electronics) and started filling them with blue goop instead of the clear stuff. 

Link to comment
Share on other sites

Regarding water tank level metering using Arduino with a pressure sensor fitted into a tee in the tank outlet before the pump at very near the bottom of the tank. I think I need a 1 psi (= 2.3 feet of water depth) transducer at 5 volts, but I am struggling to find one in that range. I would look to Aralditing it into the tee. 

 

Less than a tenner + Arduino + tee = <£20. Cheaper than the MSC version. 

 

Can anyone help?

 

TIA

Link to comment
Share on other sites

10 minutes ago, Sapphal said:

Regarding water tank level metering using Arduino with a pressure sensor fitted into a tee in the tank outlet before the pump at very near the bottom of the tank. I think I need a 1 psi (= 2.3 feet of water depth) transducer at 5 volts, but I am struggling to find one in that range. I would look to Aralditing it into the tee. 

 

Less than a tenner + Arduino + tee = <£20. Cheaper than the MSC version. 

 

Can anyone help?

 

TIA

 

My goodness this still seems to be massively over-complicating it. A sight gauge is simple and rarely goes wrong.

 

I do it a different way with a ten quid hose pipe volume counter fitted into the water pump feed. I know my tank holds 330L so by resetting it to zero when I filled up, I know how much I've used and therefore how much is left.

 

 

Link to comment
Share on other sites

15 minutes ago, Sapphal said:

Regarding water tank level metering using Arduino with a pressure sensor fitted into a tee in the tank outlet before the pump at very near the bottom of the tank. I think I need a 1 psi (= 2.3 feet of water depth) transducer at 5 volts, but I am struggling to find one in that range. I would look to Aralditing it into the tee. 

 

Less than a tenner + Arduino + tee = <£20. Cheaper than the MSC version. 

 

Can anyone help?

 

TIA

How about a digital transducer @ 3.5V?

 

https://uk.farnell.com/honeywell/mprls0001pg0000sa/pressure-sensor-digital-1psi-3/dp/2811408

Link to comment
Share on other sites

Thanks for the link. I will order one and give it a go. 

8 hours ago, Mike the Boilerman said:

 

My goodness this still seems to be massively over-complicating it. A sight gauge is simple and rarely goes wrong.

 

I do it a different way with a ten quid hose pipe volume counter fitted into the water pump feed. I know my tank holds 330L so by resetting it to zero when I filled up, I know how much I've used and therefore how much is left.

 

 

I know it’s a bit overkill, but it’s one of those boy’s toys things innit. 

Link to comment
Share on other sites

10 hours ago, Sapphal said:

I would look to Aralditing it into the tee. 

 

10 hours ago, WotEver said:

Interesting sensor. Teeny tiny! I'd look to use an o-ring to seal it, rather than araldite. 2.5mm ID by 1.5mm thick say, with the long port version of the sensor @WotEver links to. Probably more reliable long term.

 

On my water gauge, I am now storing the current tank water used in non-volatile EEPROM on the Arduino when the water pump stops. This means that it can retrieve the current level if the arduino is power cycled, or reset. I've added a button to press when the tank is filled that tells it to reset the usage to zero. Needed to take some care with how I did this as EEPROM only has a limited number of write cycles, but it should last several decades!

 

Jen

Edited by Jen-in-Wellies
Link to comment
Share on other sites

  • 1 month later...

Another update. The controller has now been working with the new sunshine sensor and the water flow meter for a few months over the summer. I've tweaked the control program a few times. In particular, how it handles the calorifier reaching the maximum temperature and shutting down the pump, which I've set to 70C. It has managed to do this on a couple of occasions, even during this poor summer.

The sketch has been split in to sections to help anyone who wants to borrow bits for their own version. There are sections for the water flow meter, the solar controller, temperature monitoring and display of the information. If someone liked the water flow meter, but wanted to use, say a digital display, rather than my LED's, then it should be a simpler matter to extract the bits they need.

This is likely the final version. I don't think it needs much, if any more work and I'm happy with the way it works, so below is the final version of the sketch.

 

Jen

 

/*A solar controller to replace an Aton controller with an Arduino Uno.
   Uses a phototransistor to measure sunlight and turn on the pump via a relay when the sunlight reaches a threshold.
   The threshold depends on calorifier temperature, so low sunshine levels won't cool down a hot calorifier.
   Delay turning pump on and off to compensate for passing clouds.
   Pump can be overridden by a switch, either on all the time, off all the time, or auto (DPDTCO).
   LM35 IC used for temperature measurement at calorifier.
   Uses LM35 library to give direct Celcius readings.
   Four LED temperature and water meter indicators.
   An LDR is used to adjust LED indicators to suit ambient cabin illumination with PWM digital pins.
   Overtemperature protection, shuts off pump, even if in override, or using test data.
   Undertemperature frost protection shuts off pump, even in override.
   Written by Jen-in-Wellies. October 2018.d
   Modified May/June 2019:
     Added pump shut off time to prevent relay chatter when upper/lower tank temp limit reached.LM35 signal has a lot
     of noise on it with the twisted pair connection to the o currently used, so indicated temps can swing +/-1C or so.
     Made the code a bit tighter. Got rid of persistOn variable to carry over pump state. Used a 3D array for LED display values
     Only uses calorifier temperature once the averaging is giving representative results (tankAveraged).
     Added abilitiy to use test input data, rather than actual sensor readings for light level, cabin light level and calorifier temperature.
     Added a YF-S201B flow meter to the water pump inlet and water tank contents indication on the LED's. Uses Sekdiy's library.
     Amount of water used stored in EEPROM after pump stops running to keeo accurate level indication over reboots.
     Zero button added to zero water used after filling the tank. 
     Changed sunshine sensor hardware from LDR to phototransistor. Altered light thresholds to suit.
 */

//Solar Controller. Library. 
#include <LM35.h> //LM35 library https://www.arduinolibraries.info/libraries/lm3

//Water Meter. Libraries. 
#include <FlowMeter.h>  // https://github.com/sekdiy/FlowMeter
#include <EEPROM.h> //Standard library so EEPROM can be used to store water level over Arduino resets.
  //Sensor properties for YF-S201B flow meter. 30.0f is max flow rate l/min. 7.5f is Hz/l/min pulse rate, from data sheet. 
  //Other values are for calibration over range of flow rates (currently disabled, all 1).
FlowSensorProperties YF_S201B = {30.0f, 7.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

//Solar Controller. Pin definitions. Imput
#define sunPin 1 //Analogue Input pin from the LDR on the roof.
LM35 tankPin(0); //Analogue Input pin from the LM35 on the cauliflower.
#define ambientPin 5 //Analogue Input pin from LDR on controller front panel.
#define overrideSwitch 7 //Overrides pump control. On all the time up to maximum temperature.
#define autorideSwitch 8 //Selected for normal automatic pump operation. DPDTCO. Centre off disables pump.

//Water Meter. Pin definitions. Input.
FlowMeter Meter1 = FlowMeter(2, YF_S201B); // connect a YF-S201B flow meter to interrupt 0, pin 2.
#define tankReset A3 //Reset water meter
  // define an 'interrupt service handler' (ISR) for reading the flow meter.
void Meter1ISR() {
  // let the flow meter count the pulses
  Meter1.count();
}
//Display. Pin definitions. Display and Solar Pump Output
byte ledPin[] = {3,9,10,11,6}; //LED indicator pins. Green, Blue, Yellow, Amber, Red.
#define ledPump 12 //LED pump indicator. Green flashing.
#define relayPump 4 //Pump output. To relay for pump and hours meter.

//Solar Controller. Variables.
const byte tankMax = 70; //Max calorifier temperature. Pump shuts off.
const byte tankFrost = 5; //value for tankTemp frost protection shut off.
const byte tankNumReadings = 20; //Number of tank temperature measurements to take to smooth input.
byte tankReadings[tankNumReadings]; //Reading of tank temp analogue input
byte tankReadIndex = 0; // the index of the current tam temp reading
unsigned int tankTotal = 0; // The running tank temp total. 
float tankTemp = 0.0; //Average read temperature from calorifier.
unsigned int sunShine = 0; //Analogue read value from LDR. 0 to 1023.
unsigned int sunOn = 0; //LDR sunShine reading at which pump is enabled in auto.
const unsigned int sunOnLow = 700; //value between 0 and 1023 for sunOn above which the pump can be enabled in auto. was 290
const unsigned int sunOnMed = 800; //Vary the sunOn levels depending on tankTemp, so low levels don't lead to the tankTemp dropping. was 350
const unsigned int sunOnHigh = 900; //
const byte tankTempLow = 40; //tankTemp below which sunOnLow is used. was 40
const byte tankTempMed = 50; //tankTemp at which sunOnMed is used. was 50
const byte tank30 = 30; //value for tankTemp to light blue LED. 
const byte tank35 = 35; //value for tankTemp to light yellow and blue LED.
const byte tank40 = 40; //value for tankTemp to light yellow LED.
const byte tank45 = 45; //value for tankTemp to light yellow and amber LED.
const byte tank50 = 50; //value for tankTemp to light amber LED.
const byte tank55 = 55; //value for tankTemp to light amber and red LED
const byte tank60 = 60; //value for tankTemp to light red LED.
const long pumponWait = 2 * 60 * 1000L; //Wait in ms before turning pump on. Cloud protection!
const long pumpoffWait = 2 * 60 * 1000L; //Wait in ms before turning pump off. Cloud protection!
const long outsideTime = 30 * 60 * 1000L; //Time to stop pump during under/over temp excursion. 
byte pumpOn; //Pump 1 on, or 0 off.
byte overRide = 0; //Override switch read result.
byte autoRide = 0; //AutoSwitch read result.
long turnOn1 = 0L; //timers for auto pump turn on - pumponWait.
long turnOn2 = 0L;
long turnOff1 = 0L; //timers for auto pump turn off - pumpoffWait
long turnOff2 = 0L;
unsigned long timerWait1 = 0L; //timers to replace horrible delay() This one to output deBug data at intervals.
long timerWait2 = -(outsideTime); //Timer to stop relay chatter when over/under temperature. Set negative the time limit so pump can start on power up.
unsigned long analogueWait = 0L; //Timer for putting short wait between analogue reads to get round Uno ADC limitations and resulting poor accuracy when more than one analogue pin is being read per cycle.
                                                           
//Water Meter. Variables.
const signed int tankFull = 380; //Tank full level in litres (minus 5% from actual full).
float tankUsed; //Current cumulative water used in litres.
float tankPercent; //Calculated tank level. Assume 100% on power on.
byte levelAddress = 0; //EEPROM address to store water level.
float tankStored; //Variable for EEPROM stored tank level.
float currentVolume = 0; //Current volume passed by meter.
byte pumpRun = 0; //Has the water pump run? 1 = yes.

//Display. Variables.
int ambientCabin = 0; //Value between 0 and 1023 for cabin light level.
const float cabinDay = 1000.0; //Value between 0 and 1023 for a brightly lit cabin. Float to ensure PWM calc works properly.
const float cabinNight = 50.0; //Value betwixt 0 and 1023 for a cabin in the dark. Float to ensure PWM calc works properly.
unsigned int ledPWM = 0; // value for LED light level. 
byte lightLED[3][10][5] = { //Three dimensional array for LED's to light, depending on tank Percent, or calorifier temperature, or interstitial.
  {
    //green, blue, yellow, amber, red ledPin[] LED state 1 = on, 0 = off.
    {1,0,0,0,0}, //Interstitial display green power LED only. Only this row (0) used.
    {1,0,0,0,0}, //rows 1 to 9 not used, but there so it will compile. 
    {1,0,0,0,0},
    {1,0,0,0,0},
    {1,0,0,0,0},
    {1,0,0,0,0},
    {1,0,0,0,0},
    {1,0,0,0,0},
    {1,0,0,0,0},
    {1,0,0,0,0},
  },
  {
    //green, blue, yellow, amber, red ledPin[] LED state 1 = on, 0 = off.
    {1,1,1,1,1}, //tankPercent >= 99
    {1,0,0,0,1}, //80 <= tankPercent < 99
    {1,0,0,1,0}, //60 <= tankPercent < 80
    {1,0,1,0,0}, //40 <= tankPercent < 60
    {1,1,0,0,0}, //20 <= tankPercent < 40
    {0,0,0,0,1}, //16 <= tankPercent < 20
    {0,0,0,1,0}, //12 <= tankPercent < 16
    {0,0,1,0,0}, //8 <= tankPercent < 12
    {0,1,0,0,0}, //4 <= tankPercent < 8
    {0,0,0,0,0}, // tankPercent < 4
  },
  {
    //green, blue, yellow, amber, red ledPin[] LED state 1 = on, 0 = off.
    {1,1,1,1,1}, //Calorifier temperature <= tankFrost
    {1,0,0,0,0}, //tankFrost <= Calorifier temperature < tank30 
    {1,1,0,0,0}, //tank30 <= Calorifier temperature < tank35
    {1,1,1,0,0}, //tank35 <= Calorifier temperature < tank40
    {1,0,1,0,0}, //tank40 <= Calorifier temperature < tank45
    {1,0,1,1,0}, //tank45 <= Calorifier temperature < tank50
    {1,0,0,1,0}, //tank50 <= Calorifier temperature < tank55
    {1,0,0,1,1}, //tank55 <= Calorifier temperature < tank60
    {1,0,0,0,1}, //tank60 <= Calorifier temperature < tankMax
    {1,1,1,1,1}, //tankMax >= Calorifier temperature.
  }
};
byte displayWhat = 0; //Variable for calling up either interstitial, water meter, or calorifier temperature display.
byte displayRange = 0; //Variable for calling up the correct lightLED row.
unsigned long timerLED = 0L; //Timer to control if LED's are displaying water meter, or cauliflower temperature.
unsigned long loopLED = 0L; //Time during the LED display loop.
const unsigned long waterLED = 3000L; // Time that water meter is displayed on LED's.
const unsigned long cauliflowerLED = 6000L; // Time that calorifier temperature is displayed on LED's.
const unsigned long nowtLED = 500L; //Time that LED's are blank when switching between water meter and cauliflower temperature.
const float ledEqualise[] = {0.77, 0.36, 1.0, 0.72, 0.55}; //Brightness equalisation values for green, blue, yellow, amber, red LED's. 

//Debug. Variables.
const byte deBug = 1; //Get and print debug results over serial. 1 for enable, !1 (0) for disable.
const signed long loopWait = 3 * 1000L; //Wait to slow the program loop down during deBug.

//Testing. Variables. Use test data, instead of real sensor inputs.
const byte testData = 0; //Use test analogue inputs rather than real ones. 1 for enable. !1 (0) for disable.
const signed int ambientTest = 800; //test ambient cabin light level.
const signed int sunTest = 920; //test sunshine level.
const signed int tankTest = 41; //test cauliflower temperature. Won't be used if measured calorifer temperature is > tankMax.


void setup() {
  // put your setup code here, to run once:

  //Display. Define the pump output and LED display digital pins.
  for (byte i = 0; i < 5; i = i + 1){
    pinMode(ledPin[i], OUTPUT);
  }
  pinMode(relayPump, OUTPUT);
  pinMode(ledPump, OUTPUT);

  //Solar Controller. Define input digital pins.
  pinMode(overrideSwitch, INPUT_PULLUP);
  pinMode(autorideSwitch, INPUT_PULLUP);
  
  //Water Meter. Define input digital pins.
  pinMode(tankReset, INPUT_PULLUP);
 
  //Display. Set LED's and pump off to start.
  for (byte i = 0; i < 5; i = i + 1){
    analogWrite(ledPin[i], 0);
  }
  digitalWrite(relayPump, LOW);
  digitalWrite(ledPump, LOW);

  //Display. Test LED's Power up sequence to show the PLC is initialising.
  for (byte i = 0; i < 5; i = i + 1){
    analogWrite(ledPin[i], 255*ledEqualise[i]);
    delay(500);
  }
  digitalWrite(ledPump, HIGH);
  delay(2000);
  for (byte i = 0; i < 5; i = i + 1){
    analogWrite(ledPin[i], 0);
  }
  digitalWrite(ledPump, LOW);

  //Solar Controller. Initialise timers
  turnOn1 = millis();
  turnOff1 = millis();
  timerWait1 = millis();
  //Display. Initialise timer.
  timerLED = millis();
    
  //Solar Controller. Set all tank temp readings to zero.
  for (int thisReading = 0; thisReading < tankNumReadings; thisReading++) {
    tankReadings[thisReading] = 0;
  }

  //Solar Controller. Turn off pumpOn initially as default. Only turn on if conditions are met.
  pumpOn = 0;

  //Water Meter. Enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin
  attachInterrupt(INT0, Meter1ISR, RISING);
    // sometimes initializing the gear generates some pulses that we should ignore
  Meter1.reset();

  //Debug. Start serial port at 9600 bps: Turn on by setting deBug to 1.
  if (deBug == 1) {
    Serial.begin(9600);
  }
  //Water Meter. 
    //Set EEPROM stored tank used Normally not used. Useful to set up a new Arduino. Zero (0.00), or carry over value from old Uno.
  //EEPROM.put(levelAddress, 48.61);
    //ReadEEPROM for stored tank used and set initial tankUsed and tankPercent to these values.
  EEPROM.get(levelAddress, tankStored);
  tankUsed = tankStored;
  tankPercent = 100 * (tankFull - tankUsed) / tankFull; //Recalculate percentage to be displayed immediately.
}


void loop() {
  // put your main code here, to run repeatedly
  
  //Water Meter. Check if water tank level reset button is pressed. If so, then set tankUsed and EEPROM stored value to zero. 
  //Do once only per press by checking if tankUsed has been reset, to prevent multiple EEPROM writes and wearing out. 
  if ((digitalRead(tankReset) == LOW) && (tankUsed != 0.0)){
    EEPROM.put(levelAddress, 0.0);
    tankUsed = 0.0;
    tankStored = 0.0;
    tankPercent = 100 * (tankFull - tankUsed) / tankFull; //Recalculate percentage to be displayed immediately.
    Meter1.reset(); //Reset water meter.
  }
  
  //Solar Controller. Measure the cauliflower temperature. Take rolling average of several readings, one per loop.
  tankTotal = tankTotal - tankReadings[tankReadIndex]; //subtract the last reading of that tankReadIndex.
  tankReadings[tankReadIndex] = tankPin.cel(); //take temperature from LM35 sensor for new tankReadIndex.
  int tankRead = tankReadings[tankReadIndex]; //debug thing to see instant tank temperature.
  tankTotal = tankTotal + tankReadings[tankReadIndex]; //add temperature to total.
  tankReadIndex = tankReadIndex + 1; //Go to next position in array.
  if (tankReadIndex >= tankNumReadings) {
    tankReadIndex = 0; //Wrap around to the beginning when reached number of readings.
  }

  //Solar Controller. Check calorifier isn't over temperature before using test data for calorifier temperature. Safety! 
    //Use real temperature if over temperature, or if testData !=1.
  if ((testData == 1) && (tankTotal/tankNumReadings < tankMax)){
    tankTemp = tankTest;
  }
  else{
    tankTemp = tankTotal / tankNumReadings; //Calculate average temp
  }
  
  //Display. Measure the cabin light level so LED brightness can be adjusted to suit.
  if(testData != 1){
    analogRead(ambientPin); //Read analog pin once, wait 10ms, then read again to get stable result.
    analogueWait = millis();
    while((millis() - analogueWait) <= 10);
    ambientCabin = analogRead(ambientPin);
  }
  else{
    (ambientCabin = ambientTest); //use test data in test mode.
  }  

  //Display. LED's at full brightness in bright sunlight.
  if (ambientCabin >= cabinDay) {
    ledPWM = 255;
  }
  //Display. LED's off when cabin is dark.
  if (ambientCabin <= cabinNight) {
    ledPWM = 0;
  }
  //Display. Fade the LED's depending on cabin ambient. Linear.
  if ((ambientCabin < cabinDay) && (ambientCabin > cabinNight)) {
    ledPWM = 255 * (ambientCabin - cabinNight) / (cabinDay - cabinNight);
  }

  //Display. Set up loop of time to decide if LED's are displaying water tank level, calorifier temperature, or just power status (nowt).
  loopLED = millis() - timerLED;
  if (loopLED >= (nowtLED + waterLED + nowtLED + cauliflowerLED)){    
    //Check water usage over loopLED cycle and update tankUsed and tankPercent. //When pump stops, update EEPROM with new tankUsed.
    Meter1.tick(loopLED);
    currentVolume = Meter1.getCurrentVolume();
    if(currentVolume > 0){
      pumpRun = 1; //If pump is running, set pumpRun to 1.
      tankUsed = tankUsed + currentVolume; //Add amount measured since last loop
      tankPercent = 100 * (tankFull - tankUsed) / tankFull;
      if(tankPercent < 0) tankPercent = 0.00; //No negative tankPercent.
    }
    if ((currentVolume == 0) && (pumpRun == 1)){
      pumpRun = 0; //If pump has stopped, set pumpRun to zero and write new tankUsed to EEPROM.
      EEPROM.put(levelAddress, tankUsed); 
      tankStored = tankUsed;        
    }
    loopLED = 0; //reset loop.
    timerLED = millis();  
  }

  //Display. Decide when and which LED's to light depending.
  //Display nowt, between displaying water meter and solar controller information.
  if(((loopLED >= 0) && (loopLED < nowtLED)) || ((loopLED >= (nowtLED + waterLED)) && (loopLED < (nowtLED + waterLED + nowtLED)))){
    displayWhat = 0;
    displayRange = 0;
  }

  //Display. Water Meter. Decide when and which LED's to light for the tank water level.
  if((loopLED >= nowtLED) && (loopLED < (nowtLED + waterLED))){
    displayWhat = 1;
    if (tankPercent >= 99) displayRange = 0;
    if ((tankPercent >= 80) && (tankPercent < 99)) displayRange = 1;
    if ((tankPercent >= 60) && (tankPercent < 80)) displayRange = 2;
    if ((tankPercent >= 40) && (tankPercent < 60)) displayRange = 3;
    if ((tankPercent >= 20) && (tankPercent < 40)) displayRange = 4;
    if ((tankPercent >= 16) && (tankPercent < 20)) displayRange = 5;
    if ((tankPercent >= 12) && (tankPercent < 16)) displayRange = 6;
    if ((tankPercent >= 8) && (tankPercent < 12)) displayRange = 7;
    if ((tankPercent >= 4) && (tankPercent < 8)) displayRange = 8;
    if (tankPercent < 4) displayRange = 9;
  }

  //Display. Solar Controller. Decide when and which LED's to light for the cauliflower temperature.
  if((loopLED >= (nowtLED + waterLED + nowtLED)) && (loopLED < (nowtLED + waterLED + nowtLED + cauliflowerLED))){
    displayWhat = 2;
    if(tankTemp <= tankFrost) displayRange = 0;
    if (tankTemp < tank30) displayRange = 1;
    if ((tankTemp >= tank30) && (tankTemp < tank35)) displayRange = 2;
    if ((tankTemp >= tank35) && (tankTemp < tank40)) displayRange = 3;
    if ((tankTemp >= tank40) && (tankTemp < tank45)) displayRange = 4;
    if ((tankTemp >= tank45) && (tankTemp < tank50)) displayRange = 5;
    if ((tankTemp >= tank50) && (tankTemp < tank55)) displayRange = 6;
    if ((tankTemp >= tank55) && (tankTemp < tank60)) displayRange = 7;
    if ((tankTemp >= tank60) && (tankTemp < tankMax)) displayRange = 8;
    if (tankTemp >= tankMax) displayRange = 9;
  }

  //Display. Light approproate LED's.
  //Go through each LED in turn and light it depending on the tempRange determined above.
  //Brightness depends on each LED's ledEqualise[] value and the overall ledPWM set by ambient cabin brightness.
  for (byte i = 0; i < 5; i = i + 1){
    analogWrite(ledPin[i], (lightLED[displayWhat][displayRange][i] * ledEqualise[i] * ledPWM));
  }

  //Solar Controller. Measure the sunshine, or use test data..
  if(testData != 1){
    analogRead(sunPin); //Read analog pin once, wait 10ms, then read again to get stable result.
    analogueWait = millis();
    while((millis() - analogueWait) <= 10);
    sunShine = analogRead(sunPin);
  }
  else{
    sunShine = sunTest;
  }

  //Solar Controller. Check if DPDTCO switch is in auto position
  if (digitalRead(autorideSwitch) == LOW) {
    autoRide = 1;
  }
  else {
    autoRide = 0;
  }

  //Solar Controller. Check if DPDTCO Switch is in override position
  if (digitalRead(overrideSwitch) == LOW)  {
    overRide = 1;
  }
  else {
    overRide = 0;
  }

  //Solar Controller. Turn on pump if DPDTCO in override position. 
  if ((overRide == 1) && (pumpOn == 0)) {
    pumpOn = 1;
  }
  //Solar Controller. Turn off pump if DPDTCO switch is in centre off position. 
  //Will pass through this position when going from override to autoride, so will turn pump off.  
  if ((overRide == 0) && (autoRide == 0) && (pumpOn == 1)) {
    pumpOn = 0;
    }
  
  //Solar Controller. Set SunOn, depending on tankTemp.
  if (tankTemp <= tankTempLow) {
    sunOn = sunOnLow;
  }
  if ((tankTemp > tankTempLow) && (tankTemp <= tankTempMed)) {
    sunOn = sunOnMed;
  }
  if (tankTemp > tankTempMed) {
    sunOn = sunOnHigh;
  }

  //Solar Controller. Turn on Pump if sunlight is bright enough for sufficient continuous time and in auto mode.
  if ((pumpOn == 0) && (sunShine >= sunOn) && (autoRide == 1)) {
    turnOn2 = millis();
    if ((turnOn2 - turnOn1) >= pumponWait) {
      pumpOn = 1;
    }
  }
    //resets timer to current time if sun not bright enough, or goes behind a cloud before delay time is reached.
  else {
    turnOn1 = millis();
  }

  //Solar Controller. Turn off Pump in auto mode if sunlight is too dull for sufficient time.
  if ((pumpOn == 1) && (sunShine < sunOn) && (autoRide == 1)) {
    turnOff2 = millis();
    if ((turnOff2 - turnOff1) >= pumpoffWait) {
      pumpOn = 0;
    }
  }
  else(turnOff1 = millis()); 

  //Solar Controller. Turn off pump if measured tank temperature is less than frost protect threshold, 
  //or goes over upper limit even momentarily. Delay of 10 seconds on power up to get stable temperature reading.
  if (((tankTemp <= tankFrost) || (tankTemp >= tankMax)) && (millis() > (10 * 1000))) {
    timerWait2 = millis();
  }
  //Solar Controller. Disable pump for a time after tank stops being over/under temperature.
  if ((millis() - timerWait2) < outsideTime){
    pumpOn = 0; //turn off pump      
  } 
  
  //Display and Pump. Turn pump and indicator LED on, or off.
  if (pumpOn == 1) {
    digitalWrite(relayPump, HIGH);
    digitalWrite(ledPump, HIGH);
  }
  else {
    digitalWrite(relayPump, LOW);
    digitalWrite(ledPump, LOW);
  }

  //Solar Controller. Housekeeping. Preventing timer overflows if the system is left powered up too long. 2^31ms, or 24.86 days.
  //Could lead to weird errors, so reset to zero when approaching +/-2^31.
  if ((turnOn1 > 21470000000) || (turnOn1 < -21470000000)) {
    turnOn1 = 0;
  }
  if ((turnOn2 > 21470000000) || (turnOn2 < -21470000000)) {
    turnOn2 = 0;
  }
  if ((turnOff1 > 21470000000) || (turnOff1 < -21470000000)) {
    turnOff1 = 0;
  }
  if ((turnOff2 > 21470000000) || (turnOff2 < -21470000000)) {
    turnOff2 = 0;
  }

  //Debugging. Turn on by setting deBug to 1. Add/remove lines, depending on what you are interested in!
  if ((deBug == 1) && ((millis() - timerWait1) >= loopWait)) {
    Serial.println("DEBUG");
    Serial.print("time stamp ");
    Serial.println(millis());
    Serial.print("tankTemp ");
    Serial.println(tankTemp);
    Serial.print("sunShine ");
    Serial.println(sunShine);
    Serial.print("sunOn ");
    Serial.println(sunOn);
    Serial.print("turnOn1 ");
    Serial.print(turnOn1);
    Serial.print(" turnOn2 ");
    Serial.print(turnOn2);
    Serial.print(" delta ");
    Serial.println(turnOn2 - turnOn1);
    Serial.print("turnOff1 ");
    Serial.print(turnOff1);
    Serial.print(" turnOff2 ");
    Serial.print(turnOff2);
    Serial.print(" delta ");
    Serial.println(turnOff2 - turnOff1);
    Serial.print("tankUsed ");
    Serial.println(tankUsed);
    Serial.print("EEPROM tamkStored ");
    Serial.println(tankStored);
    Serial.print("tankPercent ");
    Serial.println(tankPercent);
    Serial.print("pumpRun ");
    Serial.println(pumpRun);
    Serial.print("timerWait2 ");
    Serial.println(timerWait2);
    timerWait1 = millis(); 
  }  
}

 

  • Greenie 3
Link to comment
Share on other sites

Jen I'm in awe how clever you are. What you've written you might have described how to get to the moon and back for all I understand.

 

Thought I'd mention this clever 8 year old from Mexico... 

 

Quote

This 8-Year-Old Mexican Girl Won a Prize for Making a Solar Heater From Recycled Objects

She’s been an inventor since age four.

 

At just 8 years old, Xóchitl Guadalupe Cruz has invented a device to help low-income families.

The little girl from Chiapas was recognized by UNAM’s Institute of Nuclear Sciences for her outstanding scientific achievement.

At just 8 years old, she has designed and constructed a solar-powered device to heat water, using only recycled materials, Cultura Colectiva reports.

In Xóchitl’s community in Chiapas, Mexico, resources are scarce. “People don’t have the money to buy heaters, so they chop down trees to get firewood, to heat the water”, she says. The 3rd grader took it upon herself to do something about it. 

Lupe2-300x193.jpg

A “veteran” of science projects – she’s been competing in science fairs since she was four – Guadalupe set about putting her knowledge and ingenuity to work. Using only discarded materials like bottles, wood, and plastics, she crafted a heating device that runs on a free and readily available energy source: the sun.

Her device not only functions to provide hot water to low-income families in her community, it also saves trees!

Xóchitl’s family helped her set up the device on their roof and have been using it to heat water to bathe. Guadalupe says she always bathes quickly though, “so the hot water will last for my little brother.”

The young inventor was recently recognized by the Nuclear Sciences Institute at Mexico’s Autonomous University for her solar-powered water-heating device, which has the potential to improve lives and the environment not only in rural Mexican communities, but in other areas around the world.

 

https://2019.makerfairerome.eu/en/8-year-old-mexican-girl-wins-nuclear-sciences-prize-for-her-invention/

Link to comment
Share on other sites

10 hours ago, system 4-50 said:

The following can be useful instead of millis();

https://www.pjrc.com/teensy/td_timing_elaspedMillis.html

Thanks. Looks like that could come in handy.

10 hours ago, Jennifer McM said:

https://2019.makerfairerome.eu/en/8-year-old-mexican-girl-wins-nuclear-sciences-prize-for-her-invention/

Very clever young lady. Easy to do stuff when you have access to lots of resources. Much harder when you are so limited in what's available.

 

Jen

  • Greenie 1
Link to comment
Share on other sites

4 hours ago, Bobbybass said:

I made this collector.....10mm pipe. Couldn't believe how well it worked. Burnt my fingers while bleeding it on an overcast day !

Always seems like magic to me. How can this thing possibly get so hot! Who makes the controller you've got?

 

Jen

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.