2 way lighting circuit control with blynk and feedback updates

regarding the debounce, i have dealt with it a different way. will post sketch.

also, be aware about a very important thing:
it is not a good idea to put the low voltage cables and the mains cables in the same cable bed. ideally, they should be at least 20-30 cm apart on longer paralel runs (in our country this is even specified by building standards, forbidden to run high voltage and low voltage cables together). it is not advisable from safety pov, and also the low voltage cables on the long run can collect a lots of noise from mains.

in the house where it was installed this system, the owner forgot to design separate cable routing for low voltage, and at the begining we had some problems with ghost signals, turning lights on and off at random. in that house he put the 5v vires in the same tunnel as the mains, and was long run, even 20 m together in the wall. that is very bad idea!!

we had to use much stronger pullup resistors to filter the noise (the mcu builtin is around 20k, we had to use the builtin + physical 5k if i recall correctlyā€¦)

he also used double shielded alarm cables (they are very cheap here), with the shield grounded to the arduino gnd, altough iā€™m not sure how much contributed that to noise filtering. until we didnā€™t reduced the value of the pullups, the ghost signals were presentā€¦ on other forums i learned that most people use utp cat5 csble for this kind of wireing.

these are my experiences.

1 Like

Yeah thatā€™s all cool man and thanks for the info - just wish I was doing a new build and it would be all low voltage stuff!!!

Cheers

Kev

thanks again @wanek for the info and look forward to you code.

Cheers

Kev

For conventional switches we need only the LINE wire inside the switch hole on the wall but for the IoT application we need the wall hole to be with NEUTRAL as well. From this we can set up at/on site AC and DC (5 and 3.3) for us to GO with ESP / Blynk control at one place :slight_smile:

regarding the bouncing:

on the videos you posted, it is clearly a bouncing issue. that must be eliminated.

  1. it shouldnā€™t matter how hard / fast or how soft you press the button, in a production version it is not allowed to bounce in any circumstances

  2. i did not tested this theory, but iā€™m sure if you shorten the interval ā€œIn my code it is 250L ~ 250ms so we can shorten it by 100Lā€ the bouncing will be even worse, because the more frequently you check the button, the more bouncing you will catch.

actually, the simplest way to eliminate the bouncing, is to use a short delay (around 35-40 milliseconds in my experience is enough for most but the lowest quality push buttons) after the button is pressed. i know, i know, this is ā€œagainstā€ the blynk theory, ā€œdo not use delays anywhereā€. actually, such a short delay will not do any harm, iā€™m using even longer ones in my other projects without any issues.

why every veteran member on this forum says not to use delay, is because most people do not understand how much delay is too much, and how and where is safe to use them.

the other important thing, regarding delays, that the question is not necessarily is whether if they are present in code or not, but how often that part in the code is executed. for example, if one puts a delay in the main loop directly, without any condition, yes, that is a bad thing, because it is executed every time, forever and after forever.

but if you put a reasonable short delay in a special condition what is seldom becomes true (say, when you press a button) iā€™m sure that will not do any harm (at least this is my experience among ~2 years, since i use blynk).

the other and more ā€œcomplicatedā€ way to debounce switches, it is to use a timestamp, and monitor when was the last signal received from the respective button:

  • have a delta time variable, to calculate the time passed from the last button event
  • in the moment you received the first signal from the button, you store the millis into a variable.
  • if the delta time is bigger than a certain amount of time (the button bounce time) execute the code you want for the button press. something like this:
void crank()
{
  unsigned long deltaT = millis() - lastCrank;

  lastCrank = millis();

  if (deltaT > 100) {   // debounce reed relay
// do something here
  }
}

Eagerley awaiting your code!!! @wanek

I usually use this little circuit to make a hardware debounce:

So far it works flawlessly with every button thing I made.

1 Like

yeah, this is an rc circuit. it is very good for debouncing.
but if i can achieve the exactly same result with code, i will use the code. it is much faster / cheaper, and do not needs plus hardware and wires. if you need only for 1-2 button, it is ok to use rc component (okay, iā€™m actually too lazy to build this circuit even for one button :slight_smile: ). but in my project it would be needed around 30ā€¦

with code, i write only one function, no matter if there are 1 or 1000 buttons.

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.
1 Like

Cool man thanks for this all useful info I will be using motion detectors for the heating and detecting when there is zero occupancy and therefore setting the heating to a fallback away setting.

Cheers

Kev

pfffffffttt hidden p0rn0 links :wink:

ye, ye. iā€™ve waited for your thoughtful observations :))

2 Likes

HI @wanek,

Forgot to ask you - you mentioned when pressing a button and holding it a little longer it did another function - can you detail this for us ?

Cheers

kev

the method i used it is not applicable in your case, because it depends on other functions, which are not relevant in you case. this is exactly how i done:

else if ((ambient || panic || ups) && (1000 * overrideSec < millis() - inputs[button][0] || millis() - inputs[button][0] < 0)) {
        digitalWrite(button + x, LOW);   // if (ambient or panic or ups) and (override or override roll over), turn on light
        inputs[button][0] = millis();
        mainLights[button + x] = true;   // save light state (false == off, true == on)
        delay(DEB + 15);
      }

but this will not work for you. instead, i try to explain the basic idea how i would do:

because in the multi dimensional array ā€œinputsā€ it is stored the last button press timestamp @ ā€œinputs[button][0]ā€ and also stored that if the button was pressed or not in the last loop cycle @ ā€œinputs[button][1]ā€, one can easily monitor if a button is continuously kept pressed and for how long.

so, you should create a condition, that if a button was pressed continuously ("inputs[button][1]" is always true) for a certain amount of time (millis() - "inputs[button][0]" is > than X), do something else instead the default function in ā€œcheckbuttonā€.
(this would also involve some minor modification of the ā€œcheckButton()ā€ function.

hope this makes sense :wink:

@wanek: thanks for your advice that I am sure you have experienced much on those. I highly appreciate for those. Actually from my end, it happened sometime so let me revise my code to test on 12F that I may be coming back for further. Yes even very less cases it is still the issue for the customer side or it should be the thing to be improved.

In fact if you use a latching switch(with 5v) rather than a momentary button you get zero bounce :slight_smile:
you just toggle it from one way to the other so it either pulle the pin high to 5v or switches to low ground

Cheers

kev

i donā€™t think soā€¦ bouncing happens with all mechanical switches, it doesnā€™t matter if it is a push button or switch, etc.

ā€œBouncing is the tendency of any two metal contacts in an electronic device to generate multiple signals as the contacts close or open; debouncing is any kind of hardware device or software that ensures that only a single signal will be acted upon for a single opening or closing of a contact.ā€

if you really want to understand what is happening, read this.

1 Like

Hi @newdos, check the amazing code from @Jamin, you will find there the ā€œholdā€ funtion you are looking for.

Regards!

1 Like

Iā€™ll summerize the code as its quite handy.

int longHoldTimer;

BLYNK_WRITE(V0){
  if(param.asInt()){
    int pressTime = millis();
    longHoldTimer = timer.setTimeout(1000,longHoldCallback); // start a timer as long as the button is HIGH
  } else {
    if((millis() - pressTime) < 999){
       // do short press task
    }
    timer.disable(longHoldTimer); // if the button is let go, then stop the 1 second timer.
  }
}

void longHoldCallback(){
  // do an action here after holding the button for 1 second
}
1 Like

but this will execute BOTH tasks when button is long pressed?
i mean first will execute the // do a task when the button is pressed like normal line, and after the 1 sec timeout will run longHoldCallback()?