/* Garage - Stage 1
This program is to implement stage one of the garage
project.
Stage 1: Motion Sensing lights [10 min delay, zones 1-5]
Stage 2: Nextion Display Install/implementation
Stage 3: Zone 5 addition
Stage 4: Add ons - TBD */
// Ultrasonic senor pins
#define PIN_TRIG 3
#define PIN_ECHO 2
#define pirStartPin 10 //PIR sensor pins start sequentially
#define lightZoneStartPin 5 //Light Zone pins start here sequentially
#define noPIR 4 //Enter number of PIR sensors here
#define noLightZones 5 //Enter number of light zones here
unsigned long lightDelay = 10; //set Delay time (min) here
int ultrasonicReadFrequency = 2000; //Ultrasonic sensor read frequency in ms
byte pir[noPIR]; //initialize PIR sensor pins array
byte lightZone[noLightZones]; //initialize light zone pins array
byte mostPins; //initialize variable to determine most pins in setup
bool lightState = 0; //initial light state is [ OFF | 0 ]
bool lightZoneState [] = {1,1,1,1,0};
bool heatRunState = 1; //Initial heat run state is [ ON | 1 ]
bool lastHeatRunState = 1; //Store last state for changed state status
String lastMessage = "";
//unsigned long tracker; //[debug] use to debug actual delay time vs set delay time
unsigned long lastSensorMotion = 0; //initialize variable to store last motion time
unsigned long lastUltrasonicRead = 0;
void setup() {
Serial.begin(9600);
//Ultrasonic Sensor pin setup
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
//Convert input time delay to minutes (or 2nd option seconds, min 5 seconds)
lightDelay = convertTimeDelayInput();
//Determine most pins for "for loop" i variable
mostPins = detMostPins();
//Designate PIR sensor and Light zone Pins
for (byte i = 0; i < mostPins; i++) {
if (i < noLightZones) {
lightZone[i] = i + lightZoneStartPin;
}
if (i < noPIR) {
pir[i] = i + pirStartPin;
}
}
// Set inputs and outputs
for (byte i = 0; i < 4; i++) {
pinMode(pir[i], INPUT);
pinMode(lightZone[i], OUTPUT);
}
}
void loop() {
//Check sensors for motion
bool sensorState = sensorRead();
//run logic to determine if lights are on or off; return resulting light on/off state
lightState = lightSelectLogic(sensorState, lightState);
//Read ultrasonic sensor every 5 seconds
if (millis() - lastUltrasonicRead > ultrasonicReadFrequency || lastUltrasonicRead == 0) {
heatRunState = ultrasonicRead();
lastUltrasonicRead = millis();
}
//Send heat run state to thermostat managing function
thermostatManage(heatRunState);
}
//[SETUP] Convert time Delay Input
unsigned long convertTimeDelayInput() {
//Convert time delay to minutes
lightDelay = lightDelay * 60; //comment this line to use seconds (min 5)
//Account for PIR Sensor delay, convert delay time from milliseconds
lightDelay = ((lightDelay * 1000) - 5000);
return lightDelay;
}
//[SETUP] Determine most pins
byte detMostPins() {
//Determine which array is larger for "for loop"
if (noLightZones >= noPIR) {
mostPins = noLightZones;
} else {
mostPins = noPIR;
}
return mostPins;
}
//Read PIR sensors
bool sensorRead() {
bool sensorState = 0; //Default no motion detected
for (byte i = 0; i < 4; i++) {
if (digitalRead(pir[i]) == 1) { //If sensor detects motion
sensorState = 1; //Change to motion detected
break; //Break loop if any sensor detects motion
}
}
return sensorState; //Return motion detected [ Y/N | 1/0 ]
}
//Lights on/off logic
bool lightSelectLogic(bool sensorState, bool lightState) {
switch (sensorState) {
case 1: //If motion has been detected
switch (lightState) {
case 0: //If lights are currently off
lightsOn(); //Turn on lights
lightState = 1; //Save light state as on
lastSensorMotion = millis(); //Save last sensor motion to now
//tracker = millis(); //[debug] Use to check actual time (s) lights stay on
break;
case 1:
lastSensorMotion = millis();
}
break;
case 0:
// If lights are on and delay time is met
if (millis() - lastSensorMotion >= lightDelay && lightState == 1) {
lightsOff(); //Turn off lights
lightState = 0; //Ave light state as off
//tracker = millis()-tracker; //[debug] determine how much time since tracker init
//Serial.println(tracker); //[debug] display actual delay, compare to set delay
}
}
return lightState; //Return current light state [ ON/OFF | 1/0 ]
}
//Turn on lights
void lightsOn() {
for (byte i = 0; i < noLightZones; i++) {
digitalWrite(lightZone[i], lightZoneState[i]);
}
}
//Turn off lights
void lightsOff() {
for (byte i = 0; i < noLightZones; i++) {
digitalWrite(lightZone[i], 0);
}
}
//Read Ultrasonic Sensor for heat run state
bool ultrasonicRead () {
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
int distance = pulseIn(PIN_ECHO, HIGH); //Read sensor
if ((distance / 148) <= 7) { //If distance is less than 7 inches
heatRunState = 0; //Do not run heater
} else {
heatRunState = 1; //Otherwise let it run
}
return heatRunState; //Return run/don't run status
}
//Thermostat to deterimine if heat should run
void thermostatManage(bool heatRunState) {
String message = ""; //Initialize message storage variable
switch (heatRunState) {
case 0:
message = "Heat will not run"; //Replace with thermostat off code
break;
case 1:
message = "Heat will be managed by thermostat"; //replace with conditional thermostat code
}
if (message != lastMessage) {
Serial.println(message);
lastMessage = message;
}
}