ok, i have pulled out my old project for the lights. here is the code. but i have to admit some facts:
-
this project was cooked up by a client, with specific and concrete ideas, so in lots of aspects not represents my opinions and thoughts. i just wrote the code to do exactly what he wanted to do
-
this is quite old stuff, since then lots of important things have changed even in blynk library (it was version 0.3 when i wrote this) and in even in app
-
for some reasons in this project were used arduino mega adk boards, not the standard mega 2560, this has some subtle differences, and i needed to make some modification is the uipethernet lib regarding the spi interface pins, etc
-
among just turning the lights on and off, there are lots off different tasks this program has to do, implying timed events, motion sensors, ups mode, accessory lights, sunlight monitoring opto sensors, panic function, all kind of automatic functions and lots of other stuff. so, i think it has no reason to post the whole sketch, because it would be very long confusing code and irrelevant for what you want to do
-
instead, i thought to present the basic problems and how i solved them, with the relevant code snippets
here is a map about the mega adk pins and functions:
so:
- there were lights triggered exclusively by motion sensors (acc lights 30-39)
- lights triggered by app / button / motion sensor (main with timeout 40-53)
- main lights, triggered by app / button (40-63)
- motion sensors are from 0-9
- push buttons are from 10-29
it further complicated the things, that on mega adk the uip ethernet lib does not worked on standard spi pins, only on pins 49, 50, 51, 52, so i had to reserve those pins for spi communication
header file:
// INPUTS
#define MOTIONmin 0 // define motion sensors starting / ending pins
#define MOTIONmax 9
#define BUTTONmin 10 // define buttons starting / ending pins
#define BUTTONmax 29
#define OPTO A14 // define the input pin for opto sensor
// OUTPUTS
#define LIGHTmin 30 // define lights starting / ending pins
#define LIGHTmax 63
#define BEEP 69 // (A15) define buzzer output pin
// GLOBALS
#define SHIFT 30 // difference between motion / acc pin pairs
#define SHIFT2 10 // difference between motion / button pin pairs
#define DEB 40 // debounce in MILLISECONDS
at the request of the client, the main lights should have a maximum timeout value (adjustable from the app). so, after the last app / button press, the respective light should stay on a predefined time. if a light was āonā longer than this timeout, it should automatically turn āoffā.
also, there was a separate timeout for the acc lights, hooked up to the motion sensors.
because of this, i had to monitor the last ābuttonā event of every light separately.
for this iāve used an array with 3 registers:
unsigned long inputs[BUTTONmax + 1][3]; // 'inputs' array has 3 registers: (stores data about every input pin)
// [0] -> timestamp of last activation
// [1] -> button was pressed in previus cycle? 0 == no, 1 == yes
// [2] -> store a blynk command 0 == not pressed, 1 == pressed
register [1] is for filtering the malicious user actions on buttons (kids playing, defective button, etc). for example if someone keeps pressed a button continuously, it ignores the respective button until itās released.
on startup (void setup) first the pin modes are assigned for every button / motion sensor / relay
during this, every relay turns on for a short time, to visually check for wiring problems.
the same applies for the push buttons assignment: the setup checks for stuck buttons or short to gnd between arduino & buttons. if problems found, stops and flashes respective relay light.
void setup()
{
pinMode(BEEP, OUTPUT);
for (byte light = LIGHTmin; light < LIGHTmax + 1; light++) { // setup lights as outputs
if ( light > 48 && light < 53) {
continue;
}
pinMode(light, OUTPUT);
digitalWrite(light, LOW); // check if all lights are hooked up correctly
delay(100);
}
delay(500);
lightsOff(LIGHTmax + 1);
for (byte motion = MOTIONmin; motion < MOTIONmax + 1; motion++) { // setup motion sensors as inputs
pinMode(motion, INPUT_PULLUP); // use internal pull-up resistors
}
for (byte button = BUTTONmin; button < BUTTONmax + 1; button++) { // setup buttons as inputs
pinMode(button, INPUT_PULLUP);
while (!digitalRead(button)) { // checks for stuck buttons or short circuits between arduino & buttons
if (button < 19) {
flash(button + SHIFT); // if problems found, stop and flash respective relay light
beep(100, 500);
}
else { // skipping pins of the ethernet module
flash(button + SHIFT + 4);
beep(100, 500);
}
}
}
Blynk.begin(auth); // configuring ethernet. timeout: 60 seconds
if (millis() < 50000) { // if Blynk.begin(auth) finished under 50 seconds == connected to network
beep(500, 0);
//Serial.println("connected to local network!");
tryConnecting(); // try to connect to blynk server
tryConnecting();
timer.setInterval(1000, updateBlynk); // set how often send the values to the blynk app
timer.setInterval(60000, tryConnecting); // if lost connection, try to connect time by time
//Serial.println("reconnect attempts to blynk server allowed in main loop");
}
else { // if Blynk.begin(auth) took more than 50 seconds == no network connection
blynkAllowed = false;
beep(2000, 0);
//Serial.println("no network access. no reconnect attempts allowed in main loop");
}
timer.setInterval(5000, checkOpto); // set how often read the opto sensor
}
this is how i monitored the buttons (some irrelevant code is removed):
void checkButton()
{
byte x = SHIFT;
for (byte button = BUTTONmin; button < BUTTONmax + 1; button++) {
if (!digitalRead(button) || inputs[button][2] == 1) { // checks if button is pressed, or command arrived from blynk
if (inputs[button][1] != 1 || inputs[button][2] == 1) { // checks if the same button was not pressed in previous cycle (to prevent 'missing events' if buttons are continuously pressed)
inputs[button][1] = 1;
inputs[button][2] = 0;
if (!digitalRead(button + x)) { // if light is on,
digitalWrite(button + x, HIGH); // turn off
mainLights[button + x] = false; // save light state (false == off, true == on)
delay(DEB);
}
else if { // if MAIN light is off, turn on
digitalWrite(button + x, LOW);
inputs[button][0] = millis();
mainLights[button + x] = true; // save light state (false == off, true == on)
delay(DEB);
}
}
}
else {
inputs[button][1] = 0; // if button was not pressed in this cycle, set 'last pressed?' value to 0
}
}
}
this is for motion sensors:
void checkMotion()
{
for (byte motion = MOTIONmin; motion < MOTIONmax + 1; motion++) {
if (!digitalRead(motion)) {
digitalWrite(motion + SHIFT, LOW);
inputs[motion][0] = millis(); // set timestamp for ACC light
inputs[motion + SHIFT2][0] = millis(); // set timestamp for button (MAIN light) with motion sensor
}
}
}
light timers:
void checkTimers()
{
byte x = SHIFT;
for (byte input = MOTIONmin; input < MOTIONmax + 1; input++) {
if (!digitalRead(input + SHIFT) && ((1000 * accSec) < millis() - inputs[input][0] || millis() - inputs[input][0] < 0)) {
digitalWrite(input + SHIFT, HIGH); // if ACC light is on and timeout or roll over, turn off light
}
}
for (byte input = BUTTONmin; input < BUTTONmax + 1; input++) {
if (input > 18) { // omit the pins used by ethernet module
x = SHIFT + 4;
}
if (!digitalRead(input + x) && ((60000 * lightMin) < millis() - inputs[input][0] || millis() - inputs[input][0] < 0)) {
digitalWrite(input + x, HIGH); // if MAIN light is on and timeout or roll over, turn off light
mainLights[input + x] = false; // save light state (false == off, true == on)
}
}
}
and finally main loop:
void loop()
{
checkBlynk();
checkPanicUps();
checkMotion();
checkButton();
checkTimers();
timer.run();
}
EDIT:
- sorry for the long and probably boring post
- i admit, the millis rollover monitoring is probably too primitive, there are much better solutions for this. it was not (very) critical in this application, so i didnāt bother to search for something elaborate.