NON-BLOCKING CODE to turn a light on

Good Morning / Afternoon / Evening peeps!

I was wondering if I could get some (more) help. I have this piece of code that works as the master unit for a garage door opener. What happens is there is are a master and slave units that are connected via bluetooth (HC-05.) The master is in the garage and the slave is in the house. When I press a button on the slave, a relay at the master unit is closed for as long as the button is pressed. As it stands everything works great.

I recently redesigned the project to add another relay so when the button is pressed at the slave unit, the second relay at the master unit is closed for a pre-determined amount of time (turning a light on) and then opening again. I thought about using millis but I have no clue how to implement that, or if there's a better way. I don't think I need to have this controlled by the button at the slave, I thought that maybe when the reed switch opens or closes that it could trigger the second relay. Anyhoo below is my code and parts list. Fire any questions at me, I tried to be as thorough as possible. Thanks in advance.

Relay
Arduino Nano
HC-05 Bluetooth Module
TP4056 Charging Module
Buck Converter
Step-Up Converter
2N2222 Transistors
LED's
Diodes
Capacitors
Resistors

MASTER CODE

#include <TimerOne.h>
#include <SoftwareSerial.h>

#define redLed 6
#define greenLed 9
#define blueLed 10
#define GarSwitch 4
#define relay1 5
#define relay2 11

int garageState = 0;
int lastGarageState = 0;

char ch;
String HC05_Awake = "ON";

SoftwareSerial mySerial(7, 8);  // Rx | Tx

void setup() {

  Timer1.initialize(40000000);
  Timer1.attachInterrupt(KeepAlive);
  Serial.begin(115200);
  mySerial.begin(38400);

  pinMode(GarSwitch, INPUT_PULLUP);
  lastGarageState = digitalRead(GarSwitch);

  pinMode(relay, OUTPUT);
  pinMode(redLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(blueLed, OUTPUT);
  digitalWrite(redLed, LOW);
  digitalWrite(greenLed, LOW);
  digitalWrite(blueLed, LOW);
  digitalWrite(relay1, HIGH);
  digitalWrite(relay2, HIGH);
  digitalWrite(greenLed, HIGH);
}

int counter;

void loop() {

  // Sending
  // read input once
  garageState = digitalRead(GarSwitch);  // LOW = pressed
  if (garageState != lastGarageState) {
    if (garageState == LOW) {  // switch got pressed

      Serial.print(counter);
      counter++;
      Serial.println(" Print an 'a'");

      mySerial.print('a');

      digitalWrite(blueLed, LOW);
      digitalWrite(redLed, LOW);
      digitalWrite(greenLed, HIGH);
    } else {  // switch got released

      Serial.print(counter);
      counter++;
      Serial.println(" Print a 'c'");

      mySerial.print('c');

      digitalWrite(blueLed, LOW);
      digitalWrite(greenLed, LOW);
      digitalWrite(redLed, HIGH);
    }
  }


  if (mySerial.available()) {
    char ch = mySerial.read();
    Serial.write(ch);

    if (ch == 'b') {
      Serial.print(counter);
      counter++;
      Serial.println(" Print a 'b'");
      mySerial.print('b');
      digitalWrite(blueLed, LOW);
      digitalWrite(redLed, HIGH);
      digitalWrite(greenLed, LOW);
      digitalWrite(relay1, LOW);
      delay(1000);
      } else {
        Serial.print(counter);
        counter++;
        Serial.println(" Print a 'd'");
        mySerial.print('d');
        digitalWrite(blueLed, LOW);
        digitalWrite(redLed, LOW);
        digitalWrite(greenLed, HIGH);
        digitalWrite(relay1, HIGH);
      }
    }
    lastGarageState = garageState;
    delay(20);  // poor man's debouncing
  }


void KeepAlive() {
  if (HC05_Awake = "ON") {
    mySerial.print('m');
    Serial.print('m');
  }
}

SLAVE CODE

// slave

# include <SoftwareSerial.h>

SoftwareSerial mySerial(7, 8);  // Rx | Tx

# define redLed 5
# define greenLed 6
# define blueLed 10
# define Button 3
# define pwrLed 9

char ch;

int buttonState = 0;
int lastButtonState = 0;
int counter;


void setup() {
  Serial.begin(115200);
  mySerial.begin(38400);
  pinMode(pwrLed, OUTPUT);
  pinMode(redLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(blueLed, OUTPUT);
  pinMode(Button, INPUT_PULLUP);
  analogWrite(pwrLed, 3);

}

void loop() {

  // read input once
  buttonState = digitalRead(Button);  // LOW = pressed

  if (buttonState != lastButtonState) {
    mySerial.print('a');
    if (buttonState == LOW) {  // switch got pressed

      Serial.print(counter);
      counter++;
      Serial.println(" Print a 'b'");

      mySerial.print('b');

      digitalWrite(blueLed, HIGH);
      digitalWrite(redLed, LOW);
      digitalWrite(greenLed, LOW);
    } else {  // switch got released

      Serial.print(counter);
      counter++;
      Serial.print(" Print a 'd'");

      mySerial.print('d');

      digitalWrite(blueLed, LOW);
      digitalWrite(greenLed, LOW);
      digitalWrite(redLed, HIGH);
    }

  }  //...

    // Receiving
  if (mySerial.available()) {
    char ch = mySerial.read();

    Serial.write(ch);

    if (ch == 'a') {
      Serial.print(counter);
      counter++;
      Serial.println(" Print an 'a'");
      analogWrite(redLed, 0);
      analogWrite(greenLed, 50);
      analogWrite(blueLed, 0);
    } 
    else if (ch == 'c') {
      Serial.print(counter);
      counter++;
      Serial.println(" Print a 'c'");
      mySerial.print('c');
      analogWrite(redLed, 255);
      analogWrite(greenLed, 0);
      analogWrite(blueLed, 0);
    }
  }

  lastButtonState = buttonState;
  delay(20);  // poor man's debouncing
}

I get that, but how do I implement it without affecting the other parts of the code? Thanks for the response by the way.

unsigned long currentMillis;   // unsigned long because you don't want the clock to run out
unsigned long previousMillis;  // store last time we checked millis() free running clock
const int delayTime = 1000;    // const because we always want the delay this long

void setup(){
// your stuff
}
void loop(){
// your other stuff
  // start our named (currentMillis), built in, free running, no blocking timer (millis() function)
  currentMillis = millis();
  if (currentMillis - previousMillis > delayTime) {
    previousMillis = currentMillis; // like pressing 'lap' on a stopwatch
 // do the thing for the timed interval
  }
// maybe some of your other stuff
}

If you want a delay time of more than 30 seconds, change the delayTime variable
to const long because on an Uno R3 anyway, int rolls over (into negative numbers) after 32,767.

Cool thanks. Gonna give it a try. I'll keep you posted.

Here's something to play around with to help understand what's going on. I had a really hard mental barrier to understanding this millis timer concept.

This is the most boring game in the world. All that happens is, you watch the serial monitor and it increments the game level every 5 seconds, no user interaction required, but also no external hardware required so there's that at least.

Take 'er for a rip if you like, just don't give me a negative review on Steam Games, please :wink:

/* April 03, 2024
    Brandon Ruys
    State/Mode Machine - timed leveled events
    Working as intended
*/

byte level; // you could have 0 - 255 levels if you wanted.
unsigned long currentTime;
unsigned long lastTimeAround;

void setup() {
  Serial.begin(115200);
  Serial.println(F("millisTimerLeveledEvents"));
  delay(500);
  Serial.println();
  level = 0; // start at level 0
  currentTime = 0;
  lastTimeAround = 0;
}

void loop() {
  // start free running timer
  currentTime = millis();
  // set conditional statement whose condition is to check clock and report every 5 seconds
  if (currentTime - lastTimeAround > 5000) {
    switch (level) {
      case 0:
        zero();
        break;
      case 1:
        one();
        break;
      case 2:
        two();
        break;
      case 3:
        three();
        break;
      case 4:
        four();
        break;
    }
    // synch the time marker to the free running clock
    lastTimeAround = currentTime;
  }
}
void zero() {
  Serial.print("Game runtime: ");
  Serial.println("5 sec");
  level += 1; // increment level by one, exit to level (case) 1
}
void one() {
  Serial.print("Game runtime: ");
  Serial.println("10 sec");
  level += 1; // exit to level 2
}
void two() {
  Serial.print("Game runtime: ");
  Serial.println("15 sec");
  level += 1;
}
void three() {
  Serial.print("Game runtime: ");
  Serial.println("20 sec");
  level += 1;
}
void four() {
  Serial.print("Game runtime: ");
  Serial.print("25 sec");
  Serial.println(" - Resetting to Level 0");
  level = 0; // reset game to level 0
}

1 Like

Lol, I would never do such a thing to someone who is trying to help me. I truly appreciate your help.

1 Like

there´s an excelent tutorial in this site showing how to handle many things (n buttons) without blocking. read it and it will clarify many points...

Thanks again but I think I'm going to restart this and go in a different direction. I've decided to make the relay close on motion detection (Using This Motion Sensor), so I'll have to figure out how to code it and then I'm sure I'll be back here to ask for more help lol. I already have the sensors, now I just need to mock it up then order the new boards.

check these two on the matter of "several things at the same time"

1 Like

Wiring diagram?

Could always just rig a servo to press the existing interior garage door button. I mean, it works, I've done it...

look this over

# include <SoftwareSerial.h>

const int  PinLed  [] = { 6, 9, 10 };
const int  PinRly  [] = { 5, 11 };
const int  PinGarSw   =   4;

const int Nled = sizeof(PinLed)/sizeof(int);
const int Nrly = sizeof(PinRly)/sizeof(int);

enum { Red, Green, Blue };
enum { Off = HIGH, On = LOW };

int  swState;
bool tmr;

const unsigned long MsecPeriod = 1000;
      unsigned long msec0;
      unsigned long msec;

String HC05_Awake = "ON";

SoftwareSerial mySerial (7, 8);  // Rx | Tx

// -----------------------------------------------------------------------------
void
setLeds (
    int  redLed,
    int  grnLed,
    int  bluLed )
{
    digitalWrite (PinLed [Red],   redLed);
    digitalWrite (PinLed [Green], grnLed);
    digitalWrite (PinLed [Blue],   bluLed);
}

// -----------------------------------------------------------------------------
void actA ()
{
    Serial.println (__func__);
    mySerial.println ('a');
    setLeds (On, On, Off);
    digitalWrite (PinRly [0], On);

    tmr   = true;
    msec0 = msec;
}

void actB ()
{
    Serial.println (__func__);
    mySerial.println ('b');
    setLeds (Off, Off, On);
    digitalWrite (PinRly [0], Off);
}

// -----------------------------------------------------------------------------
void loop ()
{
    msec = millis ();

    if (tmr && msec - msec0 >= MsecPeriod)  {
        tmr = false;
        actB ();
    }

    // monitor garage switch
    int sw = digitalRead (PinGarSw);
    if (swState != sw)  {
        swState  = sw;
        delay (20);                 // debounce

        if (swState == LOW)     // pressed
            actA ();
    }

    // monitor serial input
    if (mySerial.available()) {
        char ch = mySerial.read ();

        if ('b' == ch)
            actB ();
        else if ('a' == ch)
            actA ();
    }
}

// -----------------------------------------------------------------------------
void KeepAlive () {
    if (HC05_Awake == "ON") {
        mySerial.print ('m');
        Serial.print ('m');
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (115200);
    mySerial.begin (38400);

    pinMode (PinGarSw, INPUT_PULLUP);
    swState = digitalRead (PinGarSw);

    for (unsigned n = 0; n < Nrly; n++)  {
        pinMode (PinRly [n], OUTPUT);
        digitalWrite (PinRly [n], Off);
    }

    for (unsigned n = 0; n < Nled; n++)
        pinMode (PinLed [n], OUTPUT);
    setLeds (Off, Off, Off);
}

I'm looking this over with interest.
Many new things, forms I haven't seen before.

void
setLeds (
    int  redLed,
    int  grnLed,
    int  bluLed )
{
    digitalWrite (PinLed [Red],   redLed);
    digitalWrite (PinLed [Green], grnLed);
    digitalWrite (PinLed [Blue],   bluLed);
}

At first I thought it's a void with no name.
Looking at it I eventually saw that it's --

void setLeds (int redLed, int grnLed, int bluLed )
{ 
  digitalWrite (PinLed [Red],   redLed);
  digitalWrite (PinLed [Green], grnLed);
  digitalWrite (PinLed [Blue],  bluLed);
}

Holy Crap! (Can I say that?)

Looks like you replicated my entire sketch and it compiled super fast. I have to test it but wow! Looks very interesting. I guess I have some learning to do lol. Thanks for that, much appreciated.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.