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.
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
}
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 ? ?
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.
How should be defined startTime ?
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 ?
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
}
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 );
}
}