Attachinterrupt & Software timer

Hello
a few weeks ago I opened a post on volatile variables with interrupts. Starting from there I ran some tests using timer software.
I need to have a delay of happening in the event. Using an on - off switch connected to the negative pole and to D0 and D1 for the tests. With the skecht attached sometimes the device freeze or the led output D8 does not turn off.
Unfortunately I can’t find a correct solution, does anyone have any suggestions to solve the problem or some corrections to do in my skecht?
Thanks Valentino

#include "Particle.h"
int Inscent=D0; // CENTR INS.
int DInscent=D1; // CENTR DINS.
int LedInsCent=D8;
int StatoInscent=0;
int StatoDInscent=0;
int StatoC=0;
int Uscita=0;
//**********************
int Zona1 = D3;  // Input Zona1
int StatoZ1=0;    
int AStatoZ1= 0;
int LedZona1 = D4;
void CentrOn()
{
    if (StatoInscent=1)
   {
    digitalWrite(LedInsCent,HIGH);
    }
}
void CentrOff()
{   
    if (StatoDInscent=1)
    {
    digitalWrite(LedInsCent,LOW);
    }
}
//declare timer object one shot
Timer TCentrOn(5000, CentrOn, true);
Timer TCentrOff(500, CentrOff, true);
void setup() {
    attachInterrupt(Inscent,CentrIntOn,CHANGE); //RISING
    attachInterrupt(DInscent, CentrIntOff,CHANGE);//FALLING
    Serial.begin(9600);
    pinMode(LedInsCent,OUTPUT);
    pinMode(Inscent,INPUT_PULLUP);
    pinMode(DInscent,INPUT_PULLUP); 
    
    pinMode(Zona1,INPUT_PULLDOWN);    
    pinMode(LedZona1,OUTPUT);
    StatoZ1=0;
    AStatoZ1=1;
    Particle.variable("Z1",AStatoZ1);
}
void loop()
{
    StatoZ1=digitalRead(Zona1);
    StatoInscent=digitalRead(Inscent);
    StatoDInscent=digitalRead(DInscent);
    Uscita=digitalRead(D8);
    Serial.print("StatoC : ");Serial.println(StatoC);
    Serial.print("Uscita ");Serial.println(Uscita);
    Serial.print("Stato on: ");Serial.println(StatoInscent);
    Serial.print("Stato off: ");Serial.println(StatoDInscent);
    delay(1000);
}
    void CentrIntOn()
    {
        TCentrOn.startFromISR();
    }
    void CentrIntOff()
    {
        TCentrOff.startFromISR();        
    }

@Tino52, first you are long enough in this community to know how to format code blocks - I know I have reminded you before to use the “Preformatted text” image feature - please do.

For the question at hand: Do you really need the exact timing down to a few microseconds?
If not, you can well use the interrupt to set a flag but then act upon it in your loop().
Starting a software timer from an ISR should be the exception not the first option.

First kick out that dreaded delay() at the end of your loop() and wrap the delayed actions in a millis() timing block.
When that is taken care of, you can implement the flag idea rather comfortably

something like this

bool  startNeeded[2] = { false, false };                // flags for ISR to tell loop() to initiate the timer [0]..off, [1]..on
Timer timer[2]       = { Timer( 500, CentrOff, true)    // array of timers to wrap similar actions in one loop
                       , Timer(5000, CentrOn , true) };

void loop() {
  static uint32_t ms1Sec = 0;

  for (int i = 0; i < 2; i++) {                         // traverse timer array
    if (startNeeded[i] && !timer[i].isActive()) {       // when a start is needed and not currently running
      startNeeded[i] = false;                           // reset the start flag
      timer[i].start();                                 // start the timer
    }
  }

  if (millis() - ms1Sec >= 1000) {                      // when last execution is at least one second in the past
    ms1Sec        = millis();
    StatoZ1       = digitalRead(Zona1);
    StatoInscent  = digitalRead(Inscent);
    StatoDInscent = digitalRead(DInscent);
    Uscita        = digitalRead(D8);
    Serial.printlnf("StatoC   : %5d\r\n"
                    "Uscita   : %5d\r\n"
                    "Stato on : %5d\r\n"
                    "Stato off: %5d\r\n"
                   , StatoC
                   , Uscita
                   , StatoInscent
                   , StatoDInscent
                   );
  }
}

void CentrIntOn() {
  startNeede[1] = true;
}
    
void CentrIntOff() {
  startNeeded[0] = true;
}

BTW, you’d usually first set the pinMode() for the pins before you attach an interrupt to said pin. Otherwise you may be getting erroneous triggers of the floating pins.
I’d also not use a CHANGE trigger for this. Usually I’d opt for the button release to trigger any action with time component.
(for button handling you may have a look at Rick’s brilliant library - debouncing is also something your code should do)

Also this is wrong

and

Here you are not checking the state of your flags but setting them.

You may also need to give the re-trigger logic some more thought.
Your code (and hence my derivative of it too) does not properly address at least two possible states:

  • what if the button was pressed again while the timer was running?
  • what if the button stays pressed for the duration of the timer running (or while the timer is about to expire)?

You first need to formulate the intent for all cases and based on that develop your code.

1 Like

Hello
usually I always do a small flowchart to better understand where I want to go, this time too it has been done.
I edited the sketch as suggested and it works fine with an on and off switch.
Your question:
what if the button was pressed again while the timer was running?
With the switch works, it turns off and does not turn on again, while during the test with the buttons it turns on again after the set time has elapsed (Timer timer [2] = {Timer (1000, CentrOff, true)).
Attached is the photo of the switch used to better understand us.
However, I have to thank for the good help you always give me.
Regards
Valentino

1 Like

Hello @ScruffR
After several tests I felt that for my purpose using millis would have been ideal.
In the tests I found that if I impose for example: 30000 and measuring the time the action occurs before 30000 have elapsed. In my tests I arrived around 14-20 seconds.
I attach my skecht and I ask you if there is an error in using the millis function.
Also I wanted to know if I declare ‘static uint32_t ms1Sec = 0;’ can I also use it for other millis settings?
(millis () - ms30Sec> = 30000)
(millis () - ms1Sec> = 1000)
Thanks Valentino

#include "Particle.h"
// ZONA2
int Zona2 = A1;
int StatoZ2=0;
int AStatoZ2= 0;
int LedZona2 = D5;
int LedZ2=0;
int Inscent=D12; // CENTR INS.
int LedInsCent=D7;
int StatoInscent=0;
int AStatoC=0;

void setup() {
    Serial.begin(9600);
    //ZONA2
    pinMode(Zona2,INPUT_PULLUP);
    pinMode(LedZona2,OUTPUT);
    Particle.variable("Z2",AStatoZ2);
    attachInterrupt(Zona2,Z2Interrupt,RISING); //RISING
    //************
    pinMode(LedInsCent,OUTPUT);
    pinMode(Inscent,INPUT_PULLDOWN);//INPUT_PULLDOWN);
}

void loop() {
    StatoInscent = digitalRead(Inscent);
    static uint32_t ms1Sec = 0;
    static uint32_t ms30Sec = 0;
    if (StatoInscent == 1) {
        digitalWrite(LedInsCent, HIGH);
        if (StatoZ2 == 1) {
            if (millis() - ms30Sec >= 30000) {
                ms30Sec          = millis();
                digitalWrite(LedZona2, HIGH);
            }
        }
    }
    else { // if (StatoInscent != 1)
        digitalWrite(LedZona2,LOW);
        StatoZ2 = 0;
        digitalWrite(LedInsCent, LOW);
        StatoInscent = 0;
    }    
    if (millis() - ms1Sec >= 1000) {
        ms1Sec          = millis();
        Serial.print("Stato Z2:");Serial.println(StatoZ2);
        Serial.print("Centr:");Serial.println(StatoInscent);
    }
}

void Z2Interrupt() {
    StatoZ2=1;
}

I’m not entirely sure what your code intends to do, but having if(millis() - ms30Sec >= 30000) inside another condition that may already be false when the 30sec are over does feel somewhat obscure (to me).

You can have as many independent time variables as you want.

Hello @ScruffR
I’m replacing part of a burglary center. The radio receiver part for the wireless sensors (infrared) works, in this there are some closed relays that open every x seconds at the time of an infringement and my device (Argon) should trigger an alarm.
I put this line (if (StatoInscent == 1)) because only if the burglary control unit is active can an alarm be activated.
Have a nice day
Valentino

Hello
I add that I want to put a delay on Zone2 so that the alarm only goes on after x seconds.
Best and Thanks
Valentino

I see.
This calls for some clarification then.

This construct isn’t quite giving you what you intend because it does not take into account when your triggering event occured but only when the condition was last checked and rendered true.
If you want to make one thing dependent on something happening somewhere else you’d need slightly different approach.

e.g.

bool     AStatoZ2 = false;
uint32_t msZona2  = 0;        // make this global (could be volatile but doesn't have to ;-) @joel
                              //   replaces int ZonaZ2 = 0; 
void Z2Interrupt() {
    ms30Sec = millis();       // record when the interrupt happened
}

void loop() {
    static uint32_t ms1Sec = 0;

    if (StatoInscent = digitalRead(Inscent)) { // assginment "=" on purpose (not only a check "==")
        if (msZona2 && millis() - msZona2 >= 30000) {
            AStatoZ0 = true;
        }
    }
    else {
        msZona2 = 
        AStatoZ0 = 0;
    }
    digitalWrite(LedInsCent, StatoInscent);
    digitalWrite(LedZona2, AStatoZ2);

    if (millis() - ms1Sec >= 1000) {
      ms1Sec = millis();
      Serial.printlnf("Centr   : %s \r\n"
                      "Stato Z2: %s (%.1f)\r\n"
                      , StatoInscent ? "ON" : "OFF"
                      , AStatoZ2     ? "ON" : "OFF"
                      , msZone2      ? (millis() - msZone2) / 1000.0 : 0.0 // sec since trigger 
                      ); 
    }
}

Hello @ScruffR
thanks in the next few days I will do some tests with your suggestions.
Have a good day for now
Valentino

1 Like

Hello @ScruffR
I finally had time to review the program and make the necessary changes. Now it works well, I am attaching the skecht so that those who want to use it have an idea.
Thanks again for the valuable help.
Valentino

#include "Particle.h"
// ZONA2
int Zona2 = A1;
int StatoZ2=0;
//int AStatoZ2= 0;
int LedZona2 = D5;
int LedZ2=0;

int Inscent=D12; // CENTR INS.
int LedInsCent=D8;
int StatoInscent=0;
int AStatoLedC=0;
int AStatoC=0;

bool     AStatoZ2 = false;
uint32_t msZona2  = 0;        // make this global (could be volatile but doesn't have to ;-) @joel
                              //   replaces int ZonaZ2 = 0; 
//static uint32_t ms30Sec = 0;
void Z2Interrupt() {
    msZona2 = millis();         // record when the interrupt happened
}
void setup() {
    Serial.begin(9600);
    //ZONA2
    pinMode(Zona2,INPUT_PULLUP);
    pinMode(LedZona2,OUTPUT);
    Particle.variable("Z2",AStatoZ2);
    attachInterrupt(Zona2,Z2Interrupt,RISING); //RISING
    //************
    pinMode(LedInsCent,OUTPUT);
    pinMode(Inscent,INPUT_PULLDOWN);//INPUT_PULLDOWN);
}
void loop() {
    static uint32_t ms1Sec = 0;

    if (StatoInscent = digitalRead(Inscent)) { // assgniment "=" on purpose (not only a check "==")
        if (msZona2 && millis() - msZona2 >= 10000) {
            AStatoZ2 = true;
        }
    }
    else {
        msZona2 = 
        AStatoZ2 = 0;
    }
    digitalWrite(LedInsCent, StatoInscent);
    digitalWrite(LedZona2, AStatoZ2);
    
    if (millis() - ms1Sec >= 1000) {
      ms1Sec = millis();
      Serial.printlnf("Centr   : %s \r\n"
                      "Stato Z2: %s (%.1f)\r\n"
                      , StatoInscent ? "ON" : "OFF"
                      , AStatoZ2     ? "ON" : "OFF"
                      , msZona2      ? (millis() - msZona2) / 1000.0 : 0.0 // sec since trigger 
                      ); 
    }
}
1 Like

Hello @ScruffR
one last thing about this project that I have not found on the internet in the Printlnf specifications

Serial.printlnf ("Centr:% s \ r \ n"
                       "Z2 state:% s (% .1f) \ r \ n"
                       , StatusInscent? "ON": "OFF"

The question mark has what function (if?)
Regards
Valentino

For one, there should be a blank between the variable name and the question mark and no blanks after % or \.
And this hasn’t got anything to do with snprintf() but is a standard C construct called “ternary operator” - in a nutshell it’s a one-line if…else statement that returns a specific value for either of the two branches.

1 Like

Thanks I don’t know because maybe with the cut & paste it has been modified, it is still in the sketch as indicated by you.
Good day & Grazie!
Valentino

[quote="Tino52, post:10, topic:59598"]
`, StatoInscent ? "ON" : "OFF"`
[/quote]
1 Like