Counting pulses, read inputs, and act accordingly.

Hi to everyone,

These are my first steps on programming, and age has dropped my learning curve, my English are not enough, so please be patient! :slight_smile:

I decided to extend the abilities of a card reader- logger, supporting 2 doors, to an alarm system.
The problem has 2 parts, this is the first one.

  1. When the card reader reads an unknown card, produces approximately 3x 250 sine-wave pulses during 500ms (2,5KHz):
    3 short beeps during 500ms, 750 pulses.
    This should be converted to WrongID output (to serial, and pin7).

  2. When a door sensor alarm occurs, outSensorState (5) and/or inSensorState(6), the reader produces constand sinewave 2,5KHz (1250 pulses during 500ms).
    This should be converted to outSensorAlarm output (to serial, and pin8) and /or inSensorAlarm output (to serial, and pin9).

All these should happen, only if alarmState (pin 4) is LOW (alarm is active).

Borrowing the code provided by dc42 at : Frequency Counter Library - Science and Measurement - Arduino Forum
and some others, and adding what I understood it should be right, I came up with this code:

// Frequency counter sketch, for measuring frequencies low enough to execute an interrupt for each cycle
// Connect the frequency source to the INT0 pin (digital pin 2 on an Arduino Uno)

volatile unsigned long firstPulseTime;
volatile unsigned long lastPulseTime;
volatile unsigned long numPulses;
const int ledPin  = 13;        //If I want to see when and for how long it counts
const int alarmState = 4;      //in   LOW equals ALARM ON
const int outSensorState = 5;  //in   LOW equals OUTDOOR OPENED
const int inSensorState = 6;   //in   LOW equals INDOOR OPENED
const int WrongID = 7;         //out  HIGH ACTIVE
const int outSensorAlarm = 8;  //out  HIGH ACTIVE
const int inSensorAlarm = 9;   //out  HIGH ACTIVE

void isr()
{
  unsigned long now = micros();
  if (numPulses == 1)
  {
    firstPulseTime = now;
  }
  else
  {
    lastPulseTime = now;
  }
  ++numPulses;
}

void setup()
{
  Serial.begin(9600);    // this is here so that we can print the result
  pinMode(4, INPUT);     // reads the alarm state, on or off. Should be LOW alarm on maybe pullup res internally
  pinMode(5, INPUT);     // reads the outer sensor alarm , HIGH, door closed.
  pinMode(6, INPUT);     // reads the inner sensor alarm , HIGH, door closed.
  pinMode(7, OUTPUT);    // IF alarm on: if pulses are : 650 < numPulses < 1000 @500ms, then "unknown ID"
  pinMode(8, OUTPUT);    // IF alarm on: if pulses are : numPulses >= 1000 @500ms, and 5=LOW  then "out sensor alarm" & 8=HIGH
  pinMode(9, OUTPUT);    // IF alarm on: if pulses are : numPulses >= 1000 @500ms, and 6=LOW  then "out sensor alarm" & 9=HIGH
  digitalWrite(7, LOW);
  digitalWrite(8, LOW);
  digitalWrite(9, LOW);
  pinMode(3, OUTPUT);     // put a PWM signal on pin 3, then we can connect pin 3 to pin 2 to test the counter
  analogWrite(3, 128);
}

// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
unsigned int readFrequency(unsigned int sampleTime)
{
  numPulses = 0;                      // prime the system to start a new reading
  digitalWrite(ledPin, LOW);
  attachInterrupt(0, isr, RISING);    // enable the interrupt
  delay(sampleTime);
  detachInterrupt(0);
  digitalWrite(ledPin, HIGH);
  return (numPulses < 3) ? 0 : (1000000UL * (numPulses - 2))/(lastPulseTime - firstPulseTime);
}

void loop()
{
  unsigned int freq = readFrequency(500); // put here the sample time
  Serial.println(freq);
  Serial.println(numPulses);
  if ((alarmState == LOW) && (numPulses > 650) && (numPulses < 1000))
     {
      Serial.println("Unknown ID");
      digitalWrite(WrongID, HIGH);
      delay(1000);
     }
  if ((alarmState == LOW) && (numPulses >= 1000) && (outSensorState == LOW))
     {
      Serial.println("OUT SENSOR ALARM");
      digitalWrite(outSensorAlarm, HIGH);
      delay(1000);
     }
   if ((alarmState == LOW) && (numPulses >= 1000) && (inSensorState == LOW))
     {
      Serial.println("IN SENSOR ALARM");
      digitalWrite(inSensorAlarm, HIGH);
      delay(1000);
     }
   
   delay(1000);
}

1st problem: this thing counts again and again and again. What should I do to make it start counting, when the first or second pulse arrives? Duration should be 500ms.

2nd problem: The only thing I get, is on serial, frequency and pulses. For the test, I use pin 3 output, and change the 650 to 65 and 1000 to 100 numPulses.

Probably, I do something wrong with the ifs'
I tried other combinations:

void loop()
{
  unsigned int freq = readFrequency(500); // put here the sample time
  Serial.println(freq);
  Serial.println(numPulses);
  if (alarmState == LOW)
    {
      if ((numPulses > 65) && (numPulses < 100))
      {
      Serial.println("Unknown ID");
      digitalWrite(WrongID, HIGH);
      delay(1000);
      }
      if (numPulses >= 100)
      {
        if (outSensorState == LOW)
        {
          Serial.println("OUT SENSOR ALARM");
          digitalWrite(outSensorAlarm, HIGH);
          delay(1000);
        }
        if (inSensorState == LOW)
        {
          Serial.println("IN SENSOR ALARM");
          digitalWrite(inSensorAlarm, HIGH);
          delay(1000);
        }
      }
    }
   
    
  delay(1000);
}

nothing better.
For the sine to square conversion, I will try NE555, and I think it will be ok, except if there is a better way.

The second part, takes the 3 outputs, and puts them to a GSM shield, with 4 inputs and 4 outputs, a 2 way communication with SMS. I found with inputs, found with outputs, but not both...

Sorry for the detailed explanation, usually I start from Adam and Eve!

Thanks for your time and your patience !

I forgot to mention that I'm using Arduino nano for the tests.

I hope someone will spent a little time to check the code and see what is wrong with it.

Thanks again,
Stelios.

5B4ALR:

  1. When the card reader reads an unknown card, produces approximately 3x 250 sine-wave pulses during 500ms (2,5KHz):
    3 short beeps during 500ms, 750 pulses.
    This should be converted to WrongID output (to serial, and pin7).

  2. When a door sensor alarm occurs, outSensorState (5) and/or inSensorState(6), the reader produces constand sinewave 2,5KHz (1250 pulses during 500ms).
    This should be converted to outSensorAlarm output (to serial, and pin8) and /or inSensorAlarm output (to serial, and pin9).

I don't understand the context in which this occurs.
What sort of card reader and sensor are you using - post links to their data sheets.

What happens when there is NOT a problem - is there any signal then?
Do both the RFID and the door sensor trigger the same interrupt pin?

You are only counting pulses occasionally - how do you know when to start the ISR?

I think it would be a great help if you describe in English, step by step, how the project is supposed to work.

...R

Robin 2, thanks for the reply!
I will try to clarify things.

This is the RFID Reader:
http://www.ebay.com/itm/Economic-Door-Rfid-Proximity-Reader-Access-Control-Keypad-10-ID-Cards-Brand-NEW-/141384320006?

It can control 2 doors.

Door sensor will be a simple, common reed magnet sensor.

The RFID Reader has 2 inputs, programmable, will be used to read the doors sensors.
Has 2 outputs, will be used to unlock the doors.
So, no more outputs!

Though, it has a built in buzzer that produces:
1 burst of sinewaves, 100ms @ 2,5KHz (equals to 250 pulses)
if a valid card is detected.

3 burst of sinewaves, 3x100ms @ 2,5KHz during 500ms (equals to 750 pulses)
if an invalid card is detected.

constant sinewaves @ 2,5KHz (equals to 1250 pulses during 500ms)
if a door is opened illegally.

I will upload an excel file with the true table of what I want to achieve.

In order to have, let say, an outdoor alarm HIGH at pin 8, should:
1)have pin 4 LOW (alarm on)
2)pin 5 LOW (out sensor state on)
3)pulses count > 1000 on a sample 500ms
ALL of them (AND).

There is a problem with counting: I want it to start, when the pulses start coming at pin 2. Duration of sample: 500ms.

I hope that I expressed better what I have in mind!
Stelios.

ALARM_TRUE_TABLE.xlsx (10.1 KB)

I noticed this on the link you posted

Note: If you want to connect this product on your computer, you should use an extra 485 converter as the following link shows:

Maybe that would be worth pursuing.

I have some reservations about how reliably you would count the sound pulses.

I don't think I would bother trying to count the individual pulses. Instead I would detect the fact that a buzz (or 3 buzzes or a very long buzz) has happened by looking for gaps between the buzzes.

I think I would use my ISR to start a timer (using millis() ) when the very first pulse is received and then keep resetting the short term timer for every pulse - in other words the timer will only show a long time between pulses. You could use that to detect the intervals and the long term timer would be reset at each gap so it would show when no gaps had been detected due to an intrusion alarm. I hope that makes some sense. I know it's not my best paragraph.

Edit to add ... I think this code might work - it's neither complete nor tested

attachInterrupt(0, doorISR, RISING);

void loop() {
  currentMillis = millis();
  
  checkDoor();

}

void checkDoor() {
  if (doorDetect  && doorWaiting){ // doorWaiting is when nothing happens
    doorDetect = false;
    doorWaiting = false;
    pulseCount = 0;
    doorStartTime = currentMillis;
  }
  if (currentMillis - doorStartTime >= gapBetweenPulses) {
     pulseCount ++;
  }
  if (pulseCount == 3) {
	  codeError = true;
  }
  if (pulseCount == 1) {
    if (currentMillis - doorStartTime >= timeFor3pulses) {
	   codeValid = true;
	}
  }
  if (currentMillis - doorStartTime >= longBuzzTime) {
     intrusionDetected = true;
  }

}

void doorISR() {
  doorStartTime = currentMillis;
  doorDetect = true;

}

...R

Edit to correct doorTime which should have been doorStartTime ...R
And a second time ...R

Hmmm...... It becomes more complicated.
I need to "decode" this in my head, and read about millis() to see how to use this code portion.
If I got it well, you define states.

pulseCount is how many ..... "beeps" ? (forgive my simplicity!)

I don't understand the first "if", is an AND, but I don't get it yet. The result is in the { } , so is ok.

What is the "doorTime" ?

"gapBetweenPulses" is a number in milli-seconds?
Also, "timeFor3pulses" and "longBuzzTime" ?

RS485 just connects it to pc, but nothing obvious happens live. I think it only sends packets of data stored on Elf. I thought about it, but nothing obvious.

I will convert the sinewave to square-waves using a 555.
My problem is THE CODE!!!
It's way out of my knowledge, unfortunately. I never did any programming, except in "Basic", back in 1992!

I will read, and wait for a reply,
Thanks!
Stelios

5B4ALR:
pulseCount is how many ..... "beeps" ? (forgive my simplicity!)

Yes - 1 beep, 3 beeps or a very looooonnnng beep

I don't understand the first "if", is an AND, but I don't get it yet. The result is in the { } , so is ok.

The idea of the doorWaiting variable is to have some way of knowing that we are in between attempts to enter - maybe for hours

What is the "doorTime" ?

Sorry - should be doorStartTime. I have corrected it. It is the time when the attempt to enter begins

"gapBetweenPulses" is a number in milli-seconds?
Also, "timeFor3pulses" and "longBuzzTime" ?

Yes. And all vaues connected with millis must be declared as unsigned long

And I am assuming all the variable will be global variables for simplicity

RS485 just connects it to pc, but nothing obvious happens live. I think it only sends packets of data stored on Elf. I thought about it, but nothing obvious.

I did not read about its capability. I thought it might send messages equivalent to what you are trying to detect from the beeps

I will convert the sinewave to square-waves using a 555.

A schmidt trigger buffer would be better and much simpler e.g. 74HC14. Or an OP Amp
Or read about the analog comparator built into the Atmega 328. Read the Atmel datasheet.

My problem is THE CODE!!!
It's way out of my knowledge, unfortunately. I never did any programming, except in "Basic", back in 1992!

But think of the fun while you learn.

If you really are new to the Arduino start with the simple examples that come with the IDE.

...R

Ok, Here are some questions, after some reading:

void doorISR() {
doorTime = currentMillis;
doorDetect = true;

Sould it also be doorStartTime?

if (doorDetect && doorWaiting){ // doorWaiting is when nothing happens
doorDetect = false;
doorWaiting = false;
pulseCount = 0;
doorStartTime = currentMillis;

Sould it be doorDetect == doorWaiting ?
doorDetect is the state of the door?
doorStartTime is when pulses start?

I'm a little mixed up... Also with the maths with millis() but I think it will be clear if I understand the meaning of doorStartTime.

I have a little issue with "if's"... When the program finds one that is true, does it keep on looking for the next "if" to check weather is true or not, or it just quits?

Thanks for your time,
Stelios.

It is now a long time since I wrote that code and I will have to re-learn it to answer your questions ...

5B4ALR:
Sould it also be doorStartTime?

Oops - Yes - I have corrected the earlier code (again)

if (doorDetect && doorWaiting){ // doorWaiting is when nothing happens
doorDetect = false;
doorWaiting = false;
pulseCount = 0;
doorStartTime = currentMillis;

Should it be doorDetect == doorWaiting ?

No. They are two separate things. It is the same as if (doorDetect == true && doorWaiting == true)

doorDetect is the state of the door?
doorStartTime is when pulses start?

doorDetect is poorly named. It is just a flag to say that an interrupt has been detected - isrDetect may be a better name.
doorStartTime may also be poorly named. It is the time when the last interrupt happens. If interrupts don't happen it stays the same and eventually the difference between it and currentMillis is big enough to trigger something. If the interrupts do happen they will keep updating doorStartTime. Maybe lastInterruptTime would be a better name.

As an aside this is a great example of the value of choosing variable names carefully so they are completely self explanatory. And an example of the complications when it is not practical to devote a long time to preparing some example code. And how problems slip through that would be immediately obvious if one tried to compile the code.

I have a little issue with "if's"... When the program finds one that is true, does it keep on looking for the next "if" to check weather is true or not, or it just quits?

Think of the code as being very short sighted. It can see the current instruction and the next one. It evaluates an IF and then moves on the the next piece of code depending on the evaluation. It has no notion of whether it will be faced with more IF statements in the future - until it actually comes across one.

...R

It is now a long time since I wrote that code and I will have to re-learn it to answer your questions ...

Sorry, I didn't think of that, as obvious as it shows! I preferred to read and learn as much as I could before I bother you or the community again. But the delay brought the opposite result!

I take the answer for the "if"'s as :
All of them are examined and executed if they meet the requirements,
in the order they have been placed.
Please correct me if I understood wrong.

After your advices, and a little correcting on IDE:

const int alarmState = 4;      //in   LOW equals ALARM ON
const int outSensorState = 5;  //in   LOW equals OUTDOOR OPENED
const int inSensorState = 6;   //in   LOW equals INDOOR OPENED
const int WrongID = 7;         //out  HIGH ACTIVE
const int outSensorAlarm = 8;  //out  HIGH ACTIVE
const int inSensorAlarm = 9;   //out  HIGH ACTIVE
volatile unsigned long pulseCount;
volatile unsigned long currentMillis;
boolean codeError = false;
boolean codeValid = false;
boolean intrusionDetected = false;
boolean alarmArmed = false;
boolean pulseDetect = false;
int pin = 2;
volatile int pulseStartTime = LOW;

void setup()
{
  Serial.begin(9600);    // this is here so that we can print the result
  pinMode(4, INPUT);     // reads the alarm state, on or off. Should be LOW alarm on 
  pinMode(5, INPUT);     // reads the outer sensor alarm , HIGH, door closed.
  pinMode(6, INPUT);     // reads the inner sensor alarm , HIGH, door closed.
  pinMode(7, OUTPUT);    // "unknown ID" & 7=HIGH
  pinMode(8, OUTPUT);    // "out sensor alarm" & 8=HIGH
  pinMode(9, OUTPUT);    // "in sensor alarm" & 9=HIGH
  digitalWrite(7, LOW);
  digitalWrite(8, LOW);
  digitalWrite(9, LOW);
  attachInterrupt(0, pulseISR, RISING);
}

void loop()
{ 
  currentMillis = millis();
  digitalWrite(WrongID , LOW);
  digitalWrite(outSensorAlarm , LOW);
  digitalWrite(inSensorAlarm , LOW);
  codeError = false;
  codeValid = false;
  intrusionDetected = false;
  
  while (digitalRead(alarmState) == LOW)
    {
     alarmArmed = true;
     pulseState();
     if (codeError == true) 
       { 
        digitalWrite(WrongID, HIGH);
        Serial.println("Unknown ID");
       }
     if (codeValid == true) 
       { 
        Serial.println("Valid ID");
       }
     if (intrusionDetected && outSensorState && inSensorState)
       {
        digitalWrite(outSensorAlarm, HIGH);
        digitalWrite(inSensorAlarm, HIGH);
        Serial.println("SENSORS ALARM");
       }
     if (intrusionDetected && outSensorState)
       {
        digitalWrite(outSensorAlarm, HIGH);
        Serial.println("OUT SENSOR ALARM");
       }
     if (intrusionDetected && inSensorState)
       {
        digitalWrite(inSensorAlarm, HIGH);
        Serial.println("IN SENSOR ALARM");
       }
     delay(1000);
    }
}

void pulseState()
{
  if (pulseDetect && alarmArmed)  
    { 
     pulseDetect = false; 
     alarmArmed = false;
     pulseCount = 0;
     pulseStartTime = currentMillis; 
    }

  if (currentMillis - pulseStartTime >= 90) 
    {
     pulseCount ++;
    }

  if (pulseCount == 3) 
    {
     codeError = true;
    }

  if (pulseCount == 1) 
    {
     if (currentMillis - pulseStartTime < 500) 
       {
        codeValid = true;
       }
     if (currentMillis - pulseStartTime >= 500)
       {
        intrusionDetected = true;
       }
     }
}

void pulseISR() 
{
  pulseStartTime = currentMillis;
  pulseDetect = true;
}

It appears to be correct, but is untested yet. I will test it during weekend.
Please have a look, for obvious mistakes.
I changed some variable names, to represent better themselves.

And an example of the complications when it is not practical to devote a long time to preparing some example code. And how problems slip through that would be immediately obvious if one tried to compile the code.

I didn't understand that really, It has to do probably with my poor English. But this is a programming lesson!!!

This would be real FUN, 3 kids and 15 years ago!! Though, it still is!!

Thanks,
Stelios.

5B4ALR:
I take the answer for the "if"'s as :
All of them are examined and executed if they meet the requirements,
in the order they have been placed.
Please correct me if I understood wrong.

I'm a little concerned that line 2 above may be an over simplification.
The Arduino only deals with an IF statement when it comes across it in the process of working through the code. It is quite common for some IF statements to cause the code to by-pass several other IF statements that are then never tested - at least on that iteration of the loop.

Also a compound IF statement such as if (colour == 'R' && temperature > 35) will NOT bother checking temperature if the colour test fails.

I haven't studied your revised code. Try it and see what happens and then report back. The Arduino is a great system for learning-by-doing.

And an example of the complications when it is not practical to devote a long time to preparing some example code. And how problems slip through that would be immediately obvious if one tried to compile the code.

I didn't understand that really, It has to do probably with my poor English. But this is a programming lesson!!![/quote]

Sorry that was a comment about the hurried way in which I produced the example code. I hope you can learn from my mistakes. If I had been writing it for myself I would have developed it piece by piece checking each bit as I went along. And it probably would have occurred to me that the variable names were not as good as they could be.

...R

Edit to correct = to == ...R

Also a compound IF statement such as if (colour = 'R' && temperature > 35) will NOT bother checking temperature if the colour test fails.

To understand why this is might be an issue, consider what happens if that statement is:

if (getColour() == 'R' && getTemperature() > 35)

instead. (I assume that the original was meant to have ==).

If the value returned by the first function call is 'G', the second function will not be called. That may, or may not, cause problems, depending on whether or not you are relying on both functions being called (because of side effects of the functions).

PaulS:
To understand why this is might be an issue, consider what happens if that statement is:

if (getColour() == 'R' && getTemperature() > 35)

instead. (I assume that the original was meant to have ==).

Thanks, I have corrected my =.

You have neatly expressed a problem that I have accidentally avoided because my normal approach would be

col = getColour();
temp = getTemperature();
if (col == 'R' && temp > 35) {

The only reason I do it like this is that it allows me to print the values if I need to debug the IF statement.

...R

What you both say is clear, and "natural" since this if has AND inside. If the first fails, is useless to check the second part.
My worries is, how it reacts for example when more than one of "if"'s on the row, are true (assuming that none of the results points it to finish the execution of the loop). Does it execute all of them?

     if (intrusionDetected && outSensorState && inSensorState)
       {
        digitalWrite(outSensorAlarm, HIGH);
        digitalWrite(inSensorAlarm, HIGH);
        Serial.println("SENSORS ALARM");
       }
     if (intrusionDetected && outSensorState)
       {
        digitalWrite(outSensorAlarm, HIGH);
        Serial.println("OUT SENSOR ALARM");
       }
     if (intrusionDetected && inSensorState)
       {
        digitalWrite(inSensorAlarm, HIGH);
        Serial.println("IN SENSOR ALARM");
       }

if the whole 3 of them (intrusionDetected, outSensorState, inSensorState) are true,
will I see on serial:

SENSORS ALARM
OUT SENSOR ALARM
IN SENSOR ALARM
?

Oh, and another question, is this:

if (intrusionDetected && outSensorState && inSensorState)

correct? Can I put more than 2 variables in there?

For the story, the first test of the code wasn't successful....

My worries is, how it reacts for example when more than one of "if"'s on the row, are true (assuming that none of the results points it to finish the execution of the loop). Does it execute all of them?

There is only one if statement "on the row". Each if statement has multiple conditions. For the statement to be true, and the body of the statement executed, all of the conditions must be true.

Each if statement is evaluated independently. If you want at most one of the if blocks performed, you'd use if/else if/else.

if the whole 3 of them (intrusionDetected, outSensorState, inSensorState) are true,
will I see on serial:

SENSORS ALARM
OUT SENSOR ALARM
IN SENSOR ALARM
?

Yes.

Oh, and another question, is this:

if (intrusionDetected && outSensorState && inSensorState)

correct? Can I put more than 2 variables in there?

Yes and yes.

For the story, the first test of the code wasn't successful....

Oh, no, the dreaded (and lame) "it didn't work..."

5B4ALR:
My worries is, how it reacts for example when more than one of "if"'s on the row,

You need to go back to what I said about the Arduino being very short sighted. Every IF statement is like a junction on a railway track. The Arduino out the test and moves on to the next instruction indicated by the test. It has not concept at all of what the test means in your mind. All it knows is "continue with the instruction on the next line" or "skip forward to some later instruction". It will have no notion of what it might have skipped over. It's a bit like you deciding to holiday in Florida and not having any idea what Wyoming is like.

You can put several tests in a single IF statement with various && (and) and || (if) connections. But the logic can very quickly get out of control. Each of those tests represents 2 options so if you have if (aa && bb && cc) there are potentially 8 outcomes (2**3) and it can be very difficult to be sure you have chosen the right one. I prefer to do one test at a time where I can. Usually one of them is much more significant and can be used to exclude a whole bunch of the others. For example if I have just finished eating I can ignore all sorts of questions about what main-course I would like or which table I want to sit at.

...R