/* ZoneMotionDetector.ino
Version 4.0 | Updated 10/13/2023 | Cody Leisgang
This program implements a motion detection with a zone
lighting system with the ability to control 5 different
light zones. Specifically designed for a Garage system.
-----
Setup for below defined number of devices/sensors.
Change to number of devices used in setup, pay attention
to starting pins for proper PINOUT
*/
// define number of sensors/devices & PIN starts
#define noPIR 6
#define noLightZones 5
#define PIRstart 10
#define lightStart 16
#define selectorStart 49
//define PIR and light pins
byte pir[noPIR];
byte light[noLightZones];
byte selector[noLightZones];
//lighting variables
byte lightZones [] = {1, 1, 1, 1, 1};
byte lastLightZones[] = {1, 1, 1, 1, 1};
byte sensorState;
bool changeState = false;
byte lightState = 0;
unsigned long timeDelay = 600000;
unsigned long lastMotion = 0;
//off switch variables
#define manualSwitch 35
byte offState = 0;
unsigned long offDelay = 300000;
unsigned long lastPress = millis();
unsigned long offTime = 0;
int lastCount = 100;
void setup() {
Serial.begin(9600);
// designate all inputs and outputs
for (byte i = 0; i < noPIR; i++) {
pir[i] = PIRstart + i;
pinMode(pir[i], INPUT); // PIR Sensor inputs
}
for (byte i = 0; i < noLightZones; i++) {
light[i] = lightStart + i;
selector[i] = selectorStart + i;
pinMode(light[i], OUTPUT); // light zone relay outputs
pinMode(selector[i], INPUT); // zone selector switch inputs
}
pinMode(manualSwitch, INPUT); // manual off switch
}
void loop() {
if (millis() - lastPress > 500 && digitalRead(manualSwitch) == 1) {
manualMode();
}
switch (offState) {
case 0:
zoneCheck(); // check zone selectors
sensorRead(); // read PIR sensor values
switch (sensorState) {
case 1: // if sensor detects motion
switch (lightState) {
case 0: // and lights are not on
lightsOn(); // turn on lights
lightState = 1; // update lights on
break;
case 1: // if lights are already on
if (changeState == true) { // and zone state has changed
lightsOn(); // update lights on
changeState = false; // reset change state status
}
break;
}
break;
case 0: // if sensor does not detect motion
switch (lightState) {
case 1: // and lights are on
if (millis() - lastMotion > timeDelay) { // if lights have been on for time delay duration
lightsOff(); // turn off lights
lightState = 0; // update lights off
} else { // if time delay has not been met
if (changeState == true) { // and light state has changed
lightsOn(); // update lights on
changeState = false; // reset change state status
}
countDown("S: ", timeDelay, lastMotion);
}
}
}
break;
case 1:
if (millis() - offTime > offDelay) {
offState = 0;
lastMotion = timeDelay + 5000;
} else {
countDown("M: ", offDelay, lastPress);
}
}
}
// check if manual off engaged
void manualMode() {
offState = 1;
lastPress = millis();
offTime = millis();
toggleLightState();
}
void toggleLightState() {
switch (lightState) {
case 0:
lightState = 1;
lightsOn();
break;
case 1:
lightState = 0;
lightsOff();
}
}
// check zone selector values on/off
void zoneCheck() {
for (byte i = 0; i < noLightZones; i++) {
lightZones[i] = digitalRead(selector[i]); // store current zone selector values
}
for (byte i = 0; i < noLightZones; i++) {
if (lastLightZones[i] != lightZones[i]) { // if selector value changed
changeState = true; // set changed state to true
lastLightZones[i] = lightZones[i]; // update last light zone state
}
}
}
// read sensor motion
void sensorRead() {
for (byte i = 0; i < noPIR; i++) {
sensorState = digitalRead(pir[i]); // store current sensor state
if (sensorState == 1) { // if sensor detects motion
lastMotion = millis(); // store last motion time
break;
}
}
}
// Turn on selected lights
void lightsOn() {
// Turn on lights designated to be on
for (byte i = 0; i < noLightZones; i++) {
digitalWrite(light[i], lightZones[i]);
}
}
// Turn off all lights
void lightsOff() {
for (byte i = 0; i < noLightZones; i++) {
digitalWrite(light[i], 0);
}
}
// Counter for manual mode and sensor mode
void countDown(String mode, unsigned long delayTime, unsigned long lastMetric) {
if ((millis() - lastMetric) % 1000 == 0 && (millis() - lastMetric) / 1000 != lastCount) {
String timeDisplay = convertTime(delayTime, lastMetric);
Serial.print(mode);
Serial.println(timeDisplay);
lastCount = (millis() - lastMetric) / 1000;
}
}
// Convert countdown time value to M:SS format for display
String convertTime(unsigned long delayTime, unsigned long lastMetric) {
String timeDisplay;
int time = ((delayTime) - (millis() - lastMetric)) / 1000;
int minutes = 0;
int seconds = 0;
while (time >= 60) {
time = time - 60;
minutes = minutes +1;
}
seconds = time;
if (seconds < 10) {
timeDisplay = String(minutes) + ":0" + String(seconds);
} else {
timeDisplay = String(minutes) + ":" + String(seconds);
}
return timeDisplay;
}