Controlling relays with UV sensor Guva-S12SD

Hi there

I need help, been searching all over for last few days to get this working.

I needed something to automate my house solar instead of manually flipping switches all day.
With reading the UV light intensity and using some relays i can control the amount of sun light vs amount of power that is available.
So in the mornings example i can only use a kettle, later on in day heating/cooling and pumps - so limited on output depending on sunlight available
I only need like 2 or 3 relays but here i am going 8

Code works somewhat so far, getting to an override button for manually on off for one relay, cloudy days and so on proves more challenging as delays is required else the relay will switch continuously on and off whole day when one cloud goes past.

Any code help to get this working will be much appreciated!

// Solar panel relay controller depending on UV light received using Guva-S12SD
// When early morning little sun, start switching on relays, more sunlight later onwards switch other relays on
// Later in late afternoon, start switching off relays as sunlight gets less (ANALOG_THRESHOLD)
// When dark cloudy days(no sun) = relays to be off
// When some clouds, delay needed before relay responds, wait few minutes before switching off some relays
// Manual emergency overide if needed for like water pumps - press button to activate and to deactivate relay eg 9, however needs to run normally again next day or by pressing button

// constants won't change
const int BUTTON_PIN = 11; // on/off button's pin
const int BUTTON_PIN2 = 12; // Reset code pin

// Reset button state
int rButton = 1;

int relayState = LOW;   // the current state of relay
int lastButtonState;    // the previous state of button
int currentButtonState; // the current state of button

int analogValue;

const int RELAY_PIN2 = 2;
const int RELAY_PIN3 = 3;
const int RELAY_PIN4 = 4;
const int RELAY_PIN5 = 5;
const int RELAY_PIN6 = 6;
const int RELAY_PIN7 = 7;
const int RELAY_PIN8 = 8;
const int RELAY_PIN9 = 9;

const int LIGHT_SENSOR_PIN = A0;

const int ANALOG_THRESHOLD2 = 200;
const int ANALOG_THRESHOLD3 = 300;
const int ANALOG_THRESHOLD4 = 400;
const int ANALOG_THRESHOLD5 = 500;
const int ANALOG_THRESHOLD6 = 600;
const int ANALOG_THRESHOLD7 = 700;
const int ANALOG_THRESHOLD8 = 800;
const int ANALOG_THRESHOLD9 = 900;

void setup() 
// set arduino pin to output mode
  pinMode(RELAY_PIN2, OUTPUT);
  pinMode(RELAY_PIN3, OUTPUT);
  pinMode(RELAY_PIN4, OUTPUT);
  pinMode(RELAY_PIN5, OUTPUT);
  pinMode(RELAY_PIN6, OUTPUT);
  pinMode(RELAY_PIN7, OUTPUT);
  pinMode(RELAY_PIN8, OUTPUT);
  pinMode(RELAY_PIN9, OUTPUT);

  pinMode(BUTTON_PIN, INPUT_PULLUP); // set arduino pin to input pull-up mode
//  pinMode(BUTTON_PIN2, INPUT_PULLUP); // set arduino pin to input pull-up mode

 // button.setDebounceTime(1000); // set debounce time to 50 milliseconds
   currentButtonState = digitalRead(BUTTON_PIN); 

void(* resetFunc) (void) = 0; //Declare reset function

void loop() {

  lastButtonState    = currentButtonState;      // save the last state
  currentButtonState = digitalRead(BUTTON_PIN); // read new state

  if(lastButtonState == HIGH && currentButtonState == LOW) {
    Serial.println("The button is pressed");
    // toggle state of relay
    relayState = !relayState;

    // control relay arccoding to the toggled state
    digitalWrite(RELAY_PIN9, relayState);  

// How to reset this loop?
  float sensorVoltage; 
  float sensorValue; 
  sensorValue = analogRead(LIGHT_SENSOR_PIN);
  sensorVoltage = sensorValue/1024*5.0;
  Serial.print("sensor reading = ");
  Serial.print("        sensor voltage = ");
  Serial.println(" V");

   analogValue = sensorValue; // read the input on analog pin

//2nd relay pin
  if(analogValue < ANALOG_THRESHOLD2)
    digitalWrite(RELAY_PIN2, HIGH); // turn on Relay
if(analogValue < ANALOG_THRESHOLD2)
    digitalWrite(RELAY_PIN2, LOW);  // turn off Relay
    if(analogValue > ANALOG_THRESHOLD2)
 // other relays code omitted for testing
//9th relay pin USING relayState !!
  if(analogValue < ANALOG_THRESHOLD2)
    digitalWrite(RELAY_PIN9, relayState); // turn on Relay
if(analogValue < ANALOG_THRESHOLD2)
    digitalWrite(RELAY_PIN9, relayState);  // turn off Relay
    if(analogValue > ANALOG_THRESHOLD2)
// Reset button
rButton = digitalRead(12); // Read INPUT from pin12 to the button state
if (rButton == LOW) {
   resetFunc();  //call reset

Hi, @boerseun_petrus
Welcome to the forum.

What is your solar system, do you have storage batteries?
What size is your battery bank?
What do you flip switches too?
How much PV do you have(Watts, Voltage)?
How much are your individual loads (Watts)?

Its seems you want to switch in certain loads when you have the PV energy to do it?

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

Hi Tom

Solar does have battery bank @ 12Kw
Solar panels total 16Kw

Sometimes i forget to put off the water pumps(1.3Kw) and then my batteries drains completely
2 x water heaters @ 2Kw each
1 x airconditioner

The rest is normal appliances running off solar/battery which is sufficient.

I have 2 choices
1 increase the battery bank substantial
2 Adruino board

So to limit the load if there is little to no sun available therefore not draining the batteries.
Override button comes in when i realize my water tank is empty at night and need to pump water.

I think you'll have to get rid of your delays. When you check the amount of sunlight and there's enough, turn on the relay and note the time using millis.

When you notice that there isn't enough light on a subsequent check, check that sixty seconds have passed since you saw enough light. Only then turn the relay off.

About that millis()...
i tried but the serial output ended doing the same as delay - code pauses

Most likely i need an example, cannot wrap my head around the millis() function

So to update further, my button is working, added led light to show override
just the delay part needs code help

This is what I had in mind:

  //3rd relay pin
  if(analogValue < ANALOG_THRESHOLD2)
    digitalWrite(RELAY_PIN3, HIGH); // turn on Relay
  else if(millis() - Relay3StartTime> 60000UL)
    digitalWrite(RELAY_PIN3, LOW);  // turn off Relay        

where Relay3StartTime is an unsigned long global.

You could make your life easier if you learn about arrays - anytime you're using a numeric suffix on variable names is a strong indication of that.

Yes, correct diy beginner

On your code, delay works when output is going low,
also adding delay before going high?

Same mechanism:

  //3rd relay pin
  if(analogValue < ANALOG_THRESHOLD2)
    if(millis()-Relay3StopTime > 60000UL)
      digitalWrite(RELAY_PIN3, HIGH); // turn on Relay
  else if(millis() - Relay3StartTime > 60000UL)
    digitalWrite(RELAY_PIN3, LOW);  // turn off Relay
1 Like

Thanks @wildbill

Great, output works as expected!

Just one more question:
Added these lines
unsigned long Relay3StartTime = 0;
unsigned long Relay3StopTime = 0;

Then What exactly does this part do?
if(millis()-Relay3StopTime > 60000UL)

What i see:
millis() -
Relay3StopTime (0) greater than 60sec

If you can short explain this it will really help me

Millis() tells you how long it is since the Arduino was powered up.

I'm using Relay3StartTime and Relay3StopTime to store the time when the relay was supposed to be on or off.

When the light level is such that the relay should be turned on, that code checks to see whether sixty seconds have passed since it was last turned off because millis tells me time now and Relay3StopTime tells me when it was turned off.

There's a slight complication to it because if the light level says that the relay should be on I turn it on again and note the time I did it, at least in the first code I gave you. Now it does the same thing, but only if a minute has elapsed since it was off.

So if the sun is shining, relay3 stays on and on and on and on. When a cloud passes by, the code checks how long it was since it was light enough to justify the use of whatever relay3 controls. If the cloud passes by during that minute, the relay stays on. If it look longer to go away, off it goes.

1 Like

Regarding the waterpump

Lets say its 11pm at night, water level gets low and sends a signal pulse 1 time only;
I do not need water immediately however when sunlight threshold is enough then only to activate the output 5 - timed for 4 hrs on time total
This one time pulse needs to activate output 5, when analogValue > ANALOG_THRESHOLD5 sometime but not skip the pulse and lets put it this way forgets to switch on 5

It takes 4 hours to fill up water tanks

waterpump_in = digitalRead(BUTTON_PIN9);

  if((analogValue < ANALOG_THRESHOLD5) || (waterpump_in == HIGH))
    if(millis()-RelayStopTime > 1000UL)
      digitalWrite(RELAY_PIN5, HIGH); // turn on Relay
  else if(millis() - RelayStartTime > 1000UL)
    digitalWrite(RELAY_PIN5, LOW);  // turn off Relay

You're going to need more complex logic to deal with the water tanks. What you have there is repeatedly reading pin9 by the look of it. If you're lucky, you'll catch the pulse with a read, but on a subsequent check it will have passed and you'll abandon filling.

Is there another indicator to tell you that the tank is full?

Well said for "complex logic"

The tank is 100 meters away, sending a wireless signal. The wireless receiver relay i can select to output example 5 / 10 or 30 seconds

Currently i'm thinking input 9 (waterpump_in) received pulse - timing started using millis().
If millis() start time is still in range let say 1000ul * 60 * 60 * 16 for 16 hours waterpump_in is still valid

After this code pressing an override button should do
To put to into logical code?

Like this :frowning: - not working as expected

const int BUTTON_PIN9 = 9; // Water level sensor input button's pin
int waterpump_in;
int waterpump_out;
byte waterpump_in_State = LOW;

const int buttonInterval = 5000;
unsigned long previousButtonMillis = 0;
   if (millis() - previousButtonMillis >= buttonInterval) {

    if (digitalRead(waterpump_in) == HIGH) {
     waterpump_in_State  = ! waterpump_in_State ; // this changes it to LOW if it was HIGH 
                                           //   and to HIGH if it was LOW
      previousButtonMillis += buttonInterval;
  // if(digitalRead(BUTTON_PIN9) == LOW) {
         //5th relay pin water level ANALOG_THRESHOLD5
         // BUTTON_PIN9 = low water level from tanks given once
  if((analogValue < ANALOG_THRESHOLD5) && (previousButtonMillis == 1))
    if(millis()-RelayStopTime > 1000UL)
      digitalWrite(RELAY_PIN5, HIGH); // turn on Relay
  else if(millis() - RelayStartTime > 1000UL)
    digitalWrite(RELAY_PIN5, LOW);  // turn off Relay

Looks like you have a typo here:

// BUTTON_PIN9 = low water level from tanks given once
if ((analogValue < ANALOG_THRESHOLD5) && (previousButtonMillis == 1))

I don't think you intended to use previousButtonMillis in that if. waterpump_in_State perhaps?


I have tried many ways: waterpump_in = (input pin 9)
The last one previousButtonMillis was attempt to get state previousButtonMillis inside the delay

So if delay was active get previousButtonMillis to be 0 or 1

What does the signal from the tank mean? Is it "I'm not full"?

Tank almost empty

Ignoring code for a moment, what is the desired functionality for the water tank?

I would guess that when you detect low water, you have a need to run the pump for some time (four hours?). So when there is enough sun, the pump should run and you should keep track of the time spent pumping.

Once four hours of pump are done, you might expect the tank to be full (or at least close to it). On a cloudy day, that might not be true because you used some in the ten hours it took to get a full four hours of pumping. It would be good to know when the tank is actually full.

Maybe when you pump, there should be a minimum run time (a minute?) even if clouds roll in.

Then there is an override that says pump now, irrespective of how much sun there is. I expect that this is a fixed amount of time - how long? Maybe that should vary based on time of day and state of battery charge if you know it.

It sounds like there is a hierarchy of things that can be used, based on how much sunlight you have to share out among the various electrical devices. Do those priorities change when water is low?


There are enough solar panels to generate electricity for charging the batteries, running borehole pump even on cloudy days, very dark days = only charging possible

Sunny days = max 14Kw available

Getting to pumps and heaters that consumes in the Kw range can quickly drain the system if not controlled.
Currently i have only normal timers on geysers that switches on/off from 10:30am - 3pm, so i have to manually switch off when thick clouds roll in.

Borehole pump is manually switched on

At minimum pump needs to run at least 30min per day to keep up in bad conditions.
This provides water for house and farm animals.
Currently only filling once every 4 to 5 days or so - 10 000L tank, if overflowing that's fine

Override button for pump to be pressed for on and off

1 - Pump
2 - Water heater 1
3 - Water heater 2
4 - Spare (future use)

Indication light when pump override active
Indication light - failed to pump, delay is active?