/*
3/1/26 - garden light controller
R. Parnell
Note: GPS clock to be separate module to provide night/day output
Inputs:
*duress alarm (from siren or alarm strobe?)
*light beam sensor
*mains detector (from existing mains light circuit)
*Day/night sensor (via GPS clock or other means)
*Test button (set DARK or DAY and check beam)
Outputs:
*Light string A - driveway
*Light string B - front and side garden
*Letterbox lights
*House lights - 12v system
*Night or day LED indicator
*heartbeat - indicator
*/
const int duress=A0; // pin A0
const int beam=A1; // A1
const int mains=A2; // A2 mains voltage detection
const int daynight=A3; // ambient light detection
const int test=A4; // beam walk-through test at any time
bool duressActive = false;
bool beamActive = false;
bool mainsActive = false;
bool testActive = false;
bool lightsON = false;
int duressActiveVal = 1;
int beamActiveVal = 1;
int mainsActiveVal = 1;
int testActiveVal = 1;
int lightsONVal = 1;
//int daynightVal = 0;
bool dark = false; // day/night flag
int relayON = LOW; // swap high/low to suit relay modules
int relayOFF = HIGH;// swap high/low to suit relay modules
int stringA=4; // pin D4
int stringB=5; // pin D5
int stringC=6; // pin D6
int houselights=7; // pin D7. (turn house lights ON from this module)
int NDind=8; // pin D8 (night/day indicator)
int HB=9; // pin D9 heartbeat indicator
unsigned long HBstartMillis; // for heartbeat timer
unsigned long HBcurrentMillis; // what will be the current value to compare countdown to
unsigned long HBtimer = 500; // heartbeat timer
boolean HBtoggle = false; // HB LED toggle
unsigned long BeamStartMillis; // for beam trigger start timer
unsigned long BeamCurrentMillis; // to keep track of beam time
unsigned long beamtimer = 300000; // 5 minute lights ON period
int HOUSEwereON = false; // were house lights on?
int BeamBlocked = false; // beam blocked for prolonged period
/* Conditions:
1. Light beam triggered - pulse
2. Light beam triggered - continuously (car parked across it)
3. House lights ON
4. Duress alarm triggered
5. sunrise and sunset times for each month
When it is dark:
1. For light beam pulse - turn garden lights and house lights ON for set duration
If lights are already on when another pulse is detected, restart time ON period.
2. If beam is triggered continuously (still on beyond ON time, then consider it blocked and turn all lights OFF. Ignore the beam until the state changes)
3. House lights ON detected - if the house lights are turned ON, turn on the garden lights, irrespective of beam trigger.
If house light switch is turned OFF, turn off all lights.
If the beam is triggered when the house lights are already on, leave the garden lights ONLY on, until the time period runs out.
4. Duress alarm triggered
At all times of the day, flash garden lights on/off alternatively during the active duress. Flash letterbox number at a different rate.
If house lights are ON with duress active, flash one set of garden lights and house number - leave driveway garden lights on steady.
*/
// ############# initialisation complete ##############
void setup() {
Serial.begin(9600); // debugging
pinMode(stringA, OUTPUT);
pinMode(stringB, OUTPUT);
pinMode(stringC, OUTPUT);
pinMode(houselights, OUTPUT);
pinMode(HB, OUTPUT);
pinMode(NDind, OUTPUT);
pinMode(duress, INPUT_PULLUP);
pinMode(mains, INPUT_PULLUP);
pinMode(beam, INPUT_PULLUP);
pinMode(daynight, INPUT_PULLUP);
pinMode(test, INPUT_PULLUP);
digitalWrite(stringA,relayON);
digitalWrite(stringB,relayON);
digitalWrite(stringC,relayON);
digitalWrite(houselights,relayON);
delay (800);
digitalWrite(houselights,relayOFF);
delay (200);
digitalWrite(stringC,relayOFF);
delay (200);
digitalWrite(stringB,relayOFF);
delay (200);
digitalWrite(stringA,relayOFF);
HBstartMillis = millis(); //start timing code for heartbeat
} // end of setup() #########################
void loop() {
HBcurrentMillis = millis();
if (HBcurrentMillis - HBstartMillis >= HBtimer){
HBtoggle = !HBtoggle; // invert the status from true/false to false/true
digitalWrite(HB,HBtoggle); // turn HB on/off
HBstartMillis = HBcurrentMillis;
} // end of heartbeat indicator
readinputs();
Serial.println("Main loop");
// if it's dark, respond to the other conditions. If not, nothing happens.
// if (dark == true){respondtoinputs();}
// // otherwise, it must be light. Therefore, ensure all lights off and go back to the start.
// if (dark == false){allLightsOFF(); }
if (testActive == true){testbeam();}
} // end of loop() ##############
void respondtoinputs(){
// you are here because it is either "dark"
if (duressActive == true){ duressactive() ; }
if (mainsActive == true){
Serial.println("Mains input active");
allLightsON();
HOUSEwereON = true;
}
if (mainsActive == false){
// turn all lights OFF, if they were on and it is time to do so
if (lightsON==true){
if (millis()-BeamStartMillis >= beamtimer){
digitalWrite(stringA,relayOFF);
digitalWrite(stringB,relayOFF);
digitalWrite(stringC,relayOFF);
digitalWrite(houselights,relayOFF);
lightsON= false;
}
}
} // end of section if mains lights were not on
// beam has been activated for 3 cycles of lighting timer. Consider that it is blocked.
if (millis()-BeamStartMillis >= (beamtimer*3)){
BeamBlocked = true;
}
} /// end of responding to inputs
void duressactive() {
// this section is working 3/1/26
// arrived here because it is dark && the duress is active
// flash garden lights and also letterbox number
digitalWrite(stringA,relayON);
digitalWrite(stringB,relayOFF);
digitalWrite(stringC,relayON);
digitalWrite(houselights,relayOFF);
delay(1000);
digitalWrite(stringA,relayOFF);
digitalWrite(stringB,relayON);
digitalWrite(stringC,relayOFF);
digitalWrite(houselights,relayON);
delay(1000);
} // end of duressactive()############
void beamactive(){
// turn all garden lights & external house lights on sequentially if beam tripped and it's dark
if (beamActive == true && BeamBlocked == false && lightsON == false){
// beam is active
BeamStartMillis = millis(); // reset start time for lightsON timer
digitalWrite(stringA,relayON);
digitalWrite(stringB,relayON);
digitalWrite(stringC,relayON);
digitalWrite(houselights,relayON);
lightsON = true;
}
}// end of beamactive()##################
void readinputs(){
if (digitalRead(duress) == false ) {
duressActive = true;
} else duressActive = false;
if (digitalRead(beam) == false ) {
beamActive = true;
} else beamActive = false;
if (digitalRead(mains) == false ) {
mainsActive = true;
} else mainsActive = false;
if (digitalRead(daynight) == false ) {
dark = true;
digitalWrite(NDind, LOW); }
else { dark = false;
digitalWrite(NDind, HIGH);}
if (digitalRead(test) == false ) {
testActive = true;
Serial.println("test active low");
} else {testActive = false;
Serial.println("test active high");
}
} // end of readinputs()#################
void testbeam(){
Serial.println("this is the test beam void");
// test switch has been set
// allows walk through of beam to align and test
// working 3/1/26
allLightsOFF();
checkbeam:
HBcurrentMillis = millis();
if (HBcurrentMillis - HBstartMillis >= HBtimer){
HBtoggle = !HBtoggle; // invert the status from true/false to false/true
digitalWrite(HB,HBtoggle); // turn HB on/off
HBstartMillis = HBcurrentMillis;
} // end of heartbeat indicator
// heartbeat added here to continue beating with time delays for beam check
beamActive = digitalRead(beam);
if (beamActive == true){
allLightsON();
delay (100);
}
if (beamActive == false){
allLightsOFF();
BeamBlocked=false;
delay (100);
}
if (digitalRead(test) == false){ goto checkbeam; } // false is "low". Exits loop if "high/true".
testActive = false;
allLightsOFF();
} // end of testBeam() void ##############
void allLightsON(){
digitalWrite(stringA, relayON);
digitalWrite(stringB, relayON);
digitalWrite(stringC, relayON);
digitalWrite(houselights,relayON);
lightsON = true;
}
void allLightsOFF(){
digitalWrite(stringA, relayOFF);
digitalWrite(stringB, relayOFF);
digitalWrite(stringC, relayOFF);
digitalWrite(houselights,relayOFF);
lightsON = false;
}duress
beam
mains lights on
Day/night
Test (force night time)
A B C D
H/B, D/N