Interruprs or millis

Hello,

I'm newbie and i struggling with time in Arduino uno.
I have device on input which sending signal impulses. Those impulses have 100ms (device manual) time period in 0 state. I want to filter those signal to accept only pulses with those 100 ms. My program counting time too. It will count time in scale od few hours. To counting time i use timer interrupts. For checking impulse length i'm using millis.
And here is the problem. When i check the length of pulse it is lottery from 60-100 ms.

When i use test program to just check the length of impulse using only timer interrupt the impulse in fact have 100ms.

Does time interrupt interfere with millis ? If yes - what time functions should i use ?


#include <TimerOne.h>
#include <Arduino.h>
#include <TM1637Display.h>
#include <EEPROM.h>

// Module connection pins (Digital Pins)
#define CLK 3
#define DIO 4

TM1637Display display(CLK, DIO);

int minutes=3;
int hours=0;
int state=0;
int difference=0;
unsigned long Time;
unsigned long TimeMark;

void setup() {
                    // pinMode(2, INPUT_PULLUP);
 Serial.begin(500000);
 display.setBrightness(0x0f);
 
 //EEPROM.put(0, total_amount);
 //EEPROM.get(0, total_amount);
 display.clear();
 Timer1.initialize(1000000); // 1 second
 Timer1.attachInterrupt(countDownOneSecond);

}
void countDownOneSecond() {
  minutes++;
  hours++;                              //just counting for now
}

void incomingImpuls()
{
  Serial.println("timemark");
  Serial.println(TimeMark);
  state = digitalRead(2);
  Serial.println("state before delay");
  Serial.println(state);
  while(1)
  {    
    Time = millis();
    Serial.println("Time");
    Serial.println(Time);
    state = digitalRead(2);
    Serial.println("difference");
    difference= Time-TimeMark;
    Serial.println(difference);
    Serial.println("state");
    Serial.println(state);
    
    if ((difference >= 99) && (state == 0))
    {
      Serial.println("checkpoint1");
      TimeMark = millis();
      while(1)
      {
        Serial.println("checkpoint2");
        Time = millis();
        state = digitalRead(2);
        if ((Time-TimeMark <= 2) && (state == 1))
        {
          minutes += 4;
          break;
        }
        if ((Time-TimeMark > 2) && (state == 0))
        {
          break;
        }
      }
      attachInterrupt(digitalPinToInterrupt(2),incomingImpuls, FALLING);
      break;
    }
  }
}
  

//EEPROM.put(0, total_amount);
void loop() {
  state = digitalRead(2);
  Serial.println(state);
  if (state == 0)
  {
    TimeMark=millis(); 
    incomingImpuls();
  }
  display.showNumberDecEx(hours, 0b01000000, false, 2, 0);
  display.showNumberDecEx(minutes, 0b01000000, false, 2, 2); 

100ms is long enough that you don't need interrupts at all.
if you look at the State Change Detection example you'll get an idea on how to monitor this and upon the first front of interest you would note the time (millis) and do the same when you detect a change of state. The difference is the duration you are interested in, if it's close to 100ms then it's a hit

What is the duration of a pulse ?
If it is very short, then you can connect it to a interrupt input and get the millis() value in the interrupt routine.

I suggest to start again.
Never wait. Don't hang around in a while-loop to wait for something.
Use millis() to get a timestamp of a pulse and the next time you know the difference between the timestamps.

Make a sketch that measures the timing of the pulses.
If that works, then add other things. The timing for a few hours can be done with millis() as well. That is just as accurate if it is done well.

time of a pulse is 100ms. I tried yesterday with interrupt and milis. The detection of impulse falling i created on interrupt

<attachInterrupt(digitalPinToInterrupt(2),incomingImpuls, FALLING);>

, then function counting the lenght of impulse starts using millis, but millis() doesnt work in interrupts :frowning: ...

typed here so fully untested, but I'd do something like this - no need for interrupt, just polling pin 2 from time to time.

const byte signalPin = 2;

const unsigned long expectedDuration = 100;  // in ms
const unsigned long errorMargin = 3;  // in ms

enum t_state : byte {SIGNAL_ON, SIGNAL_OFF} currentState;
unsigned long startTime, endTime;

inline t_state getState() {
  return (digitalRead(signalPin) == LOW) ? SIGNAL_OFF : SIGNAL_ON;
}

void signalIsValid() {
  // do something here, we just got a valid signal
}

void signalIsInvalid() {
  // do something here, we just got an invalid signal
}

void validateSignal() {
  unsigned long deltaT = endTime - startTime;
  if ((deltaT > (expectedDuration - errorMargin)) && (deltaT <= (expectedDuration + errorMargin))) {
    signalIsValid();
  } else {
    signalIsInvalid();
  }
}

void handleSignal() {
  t_state newState = getState();
  if (newState != currentState) {   // state change!
    switch (currentState) {         // decide what to do depending on what stage we were in
      case SIGNAL_ON: endTime = millis(); validateSignal(); break;
      case SIGNAL_OFF: startTime = millis(); break;
    }
    currentState = newState;
  }
}

void setup() {
  pinMode(signalPin, INPUT);
  while (getState() !=  SIGNAL_OFF); // active wait until signal is off to get started
  currentState = SIGNAL_OFF;
}

void loop() {
  handleSignal();
  // do other stuff here as long as it's not blocking and not too time consuming
}
1 Like

i think the posted code is much too complicated to count 100 msec pulses.

consider

#undef MyHW
#ifdef MyHW
byte pinInp = A1;
#else
byte pinInp = 2;
#endif

byte inpLst;
int  count;

#define PulseWidth  100
unsigned long msec;
unsigned long msecLst;

void
loop (void)
{
    msec     = millis ();
    byte inp = digitalRead (pinInp);
    if (inpLst != inp)  {
        inpLst = inp;

        if (HIGH == inp)
            msecLst = msec;
        else  if (msec - msecLst > PulseWidth)  {
           count++;
           Serial.println (count);
        }
    }
    
}

void
setup (void)
{
    Serial.begin (9600);

    pinMode (pinInp, INPUT_PULLUP);
    inpLst = digitalRead (pinInp);
}
1 Like

millis() does not change whilst in an ISR but its value will be correct on entry to the ISR. What do you mean by

Hello
This is small sketch has to been checked out before lunch time :slight_smile:

constexpr byte InputPin {A0};
void setup() {
  Serial.begin(9600);
  pinMode (InputPin,INPUT_PULLUP);
}
void loop() {
  static bool stateCurrent=false;
  static unsigned long timeStamp=millis(); 
  bool stateNew=!digitalRead(InputPin);
  if (stateNew!=stateCurrent) {
    stateCurrent=stateNew;
    if (stateNew){
      Serial.println(millis()-timeStamp);
      timeStamp=millis();
    }
  }
}

Have a nice day and enjoy coding in C++.

1 Like

Is the pulse 100ms high and then 100ms low ? Sorry, but I still don't get it.

high all the time, then100ms low and back to high

you have 3 codes to play with :slight_smile:

1 Like

YEaah i have 3 codes/lessons for now :smiley: Thx everyone I need time to think it over :stuck_out_tongue:
i'm analyzing now the first one- yours for now and i have question.

enum t_state : byte {SIGNAL_ON, SIGNAL_OFF} currentState;

is that mean- create variable "currentState" which is byte type (0-255 ) which is in type of just created t_state type which has two possible values - signal_on=0, and signal_off=1 ? :smiley: ?

Almost, :slight_smile:

it means create a currentState variable, whose type is a t_state which is a newly define type as an enumeration with fixed underlying type of byte, that should take only two values listed in the enumeration SIGNAL_ON or SIGNAL_OFF.

Under the hood indeed the compiler will assign values to enumerators, 0 to SIGNAL_ON and 1 to SIGNAL_OFF but it's best to consider this irrelevant and not take advantage of this (in most cases).

The reason I created the enum is that if later on you want to deal with more states (unknown, error, etc) you could do it in a simple way and that's a good construct to build a state machine.

ok, i got your idea... allmost. What i'm missing here is that for me the signal always be invalid, because when the state will switch it will check signal only once when delta=0.

  1. How should be defined startTime ?

  2. With your idea which method should i use to count time ? millis() is precision enough ?

I have not tested my code but the idea was to track only a LOW to HIGH transition which defines the start time and then the HIGH to LOW which would be the end of signal. The code does not measure the duration at LOW

Ok, co i check all propositions and still don't know what method use to count time independently. Should i use millis to count seconds, minutes, and hours ? Or should i use timer interrupt like in my code, or not, cause it somehow distorts time in millis ?

The OP's signal is the other way around. HIGH -> LOW == start, LOW -> HIGH == end

1 Like

You do not need to use interrupts to track time. mliis() does a great job. If you want more precision, you can use micros().

1 Like

This should have the proper transistions

const byte signalPin = 2;

const unsigned long expectedDuration = 100;  // in ms
const unsigned long errorMargin = 3;  // in ms

enum t_state : byte {SIGNAL_ON, SIGNAL_OFF} currentState;
unsigned long startTime, endTime;

inline t_state getState() {
  return (digitalRead(signalPin) == LOW) ? SIGNAL_OFF : SIGNAL_ON;
}

void signalIsValid() {
  // do something here, we just got a valid signal
  Serial.println(" Valid");
}

void signalIsInvalid() {
  // do something here, we just got an invalid signal
  Serial.println(" Invalid");
}

void validateSignal() {
  unsigned long deltaT = endTime - startTime;
  Serial.print("Pulse: ");
  Serial.print(deltaT);
  if ((deltaT > (expectedDuration - errorMargin)) && (deltaT <= (expectedDuration + errorMargin))) {
    signalIsValid();
  } else {
    signalIsInvalid();
  }
}

void handleSignal() {
  t_state newState = getState();
  if (newState != currentState) {   // state change!
    switch (currentState) {         // decide what to do depending on what stage we were in
      case SIGNAL_OFF:
        // OFF to ON == end of pulse
        endTime = millis(); validateSignal();
        break;
      case SIGNAL_ON:
        // ON to OFF == start of pulse
        startTime = millis();
        break;
    }
    currentState = newState;
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(signalPin, INPUT);
  while (getState() !=  SIGNAL_ON); // active wait until signal is on to get started
  currentState = SIGNAL_ON;
}

void loop() {
  handleSignal();
  // do other stuff here as long as it's not blocking and not too time consuming
}
1 Like

You can use Interrupts if your loop time is several milliseconds and you want it more exactly. But you must not measure within the ISR. You start your measurement with an ISR, and you stop it with the next. You need an Interrupt for the falling edge, and one for the rising edge.
You can do that with one ISR function. A short example to show the principle ( you can also use micros(), to get it more exactly):

const int pulsePin = 2;
volatile bool pulseEnd = false;
volatile unsigned long pulseStart;
volatile unsigned long pulseDuration;

void pulseISR () {
    if ( pulseStart ) {
        // so this is the end
        pulseDuration = millis() - pulseStart;
        pulseStart = 0;
        pulseEnd = true;
        // check for pulse start again
        attachInterrupt( digitalPinToInterrupt(pulsePin), pulseISR, FALLING );
    } else {
        // new pulse starting
        pulseStart = millis();
        // check for pulse end
        attachInterrupt( digitalPinToInterrupt(pulsePin), pulseISR, RISING );
    }
}

void setup() {
    // put your setup code here, to run once:
    Serial.begin( 115200);
    pinMode( pulsePin, INPUT_PULLUP );
    // check for pulse start
    attachInterrupt( digitalPinToInterrupt(pulsePin), pulseISR, FALLING );
}

void loop() {
    // put your main code here, to run repeatedly:
    if ( pulseEnd ) {
        unsigned long printValue;
        noInterrupts();
        printValue = pulseDuration;
        interrupts();
        pulseEnd = false;
        Serial.print( "New pulse: "); Serial.println( printValue );
    }

}