Dear all,
Below you can see a sketch, it works well but after some days it crashes, it stops to work. Then after turning off and on the device it re-start to work. I don't undestand where is the problem. Can you help me?
/*
* HID RFID Reader Wiegand Interface for Arduino Uno
* Written by Daniel Smith, 2012.01.30
* Modified by F.A.N. 2013
* www.pagemac.com
*
* This program will decode the wiegand data from a HID RFID Reader (or, theoretically,
* any other device that outputs weigand data).
* The Wiegand interface has two data lines, DATA0 and DATA1. These lines are normall held
* high at 5V. When a 0 is sent, DATA0 drops to 0V for a few us. When a 1 is sent, DATA1 drops
* to 0V for a few us. There is usually a few ms between the pulses.
*
* Your reader should have at least 4 connections (some readers have more). Connect the Red wire
* to 5V. Connect the black to ground. Connect the green wire (DATA0) to Digital Pin 2 (INT0).
* Connect the white wire (DATA1) to Digital Pin 3 (INT1). That's it!
*
* Operation is simple - each of the data lines are connected to hardware interrupt lines. When
* one drops low, an interrupt routine is called and some bits are flipped. After some time of
* of not receiving any bits, the Arduino will decode the data. I've only added the 26 bit and
* 35 bit formats, but you can easily add more.
*
* f.a.n.= I've added some functions to send output in clock/data format using PIN 4 and PIN 5.
*/
#define MAX_BITS 100 // max number of bits
#define WEIGAND_WAIT_TIME 3000 // time to wait for another weigand pulse.
# define MAX_DIGIT_FCC 4
# define MAX_DIGIT_CC 9
unsigned char databits[MAX_BITS]; // stores all of the data bits
unsigned char bitCount; // number of bits currently captured
unsigned char flagDone; // goes low when data is currently being captured
unsigned int weigand_counter; // countdown until we assume there are no more bits
unsigned long facilityCode=0; // decoded facility code
unsigned long cardCode=0; // decoded card code
byte code_byte[32];
int length;
const int clock = 4; // CLOCK
const int data = 5; // DATA
// interrupt that happens when INTO goes low (0 bit)
void ISR_INT0()
{
//Serial.print("0");
bitCount++;
flagDone = 0;
weigand_counter = WEIGAND_WAIT_TIME;
}
// interrupt that happens when INT1 goes low (1 bit)
void ISR_INT1()
{
//Serial.print("1");
databits[bitCount] = 1;
bitCount++;
flagDone = 0;
weigand_counter = WEIGAND_WAIT_TIME;
}
void setup()
{
pinMode(13, OUTPUT); // LED
pinMode(2, INPUT); // DATA0 (INT0)
pinMode(3, INPUT); // DATA1 (INT1)
Serial.begin(9600);
Serial.println("RFID Readers");
// binds the ISR functions to the falling edge of INTO and INT1
attachInterrupt(0, ISR_INT0, FALLING);
attachInterrupt(1, ISR_INT1, FALLING);
pinMode(clock, OUTPUT);
pinMode(data, OUTPUT);
weigand_counter = WEIGAND_WAIT_TIME;
}
void loop()
{
// This waits to make sure that there have been no more data pulses before processing data
if (!flagDone) {
if (--weigand_counter == 0)
flagDone = 1;
}
// if we have bits and we the weigand counter went out
if (bitCount > 0 && flagDone) {
unsigned char i;
Serial.print("Read ");
Serial.print(bitCount);
Serial.print(" bits. ");
// we will decode the bits differently depending on how many bits we have
// see www.pagemac.com/azure/data_formats.php for mor info
if (bitCount == 35)
{
// 35 bit HID Corporate 1000 format
// facility code = bits 2 to 14
for (i=2; i<14; i++)
{
facilityCode <<=1;
facilityCode |= databits[i];
}
// card code = bits 15 to 34
for (i=14; i<34; i++)
{
cardCode <<=1;
cardCode |= databits[i];
}
printBits();
}
else if (bitCount == 26)
{
// standard 26 bit format
// facility code = bits 2 to 9
for (i=1; i<9; i++)
{
facilityCode <<=1;
facilityCode |= databits[i];
}
// card code = bits 10 to 23
for (i=9; i<25; i++)
{
cardCode <<=1;
cardCode |= databits[i];
}
printBits();
}
else {
// you can add other formats if you want!
Serial.println("Unable to decode.");
}
// cleanup and get ready for the next card
bitCount = 0;
facilityCode = 0;
cardCode = 0;
for (i=0; i<MAX_BITS; i++)
{
databits[i] = 0;
}
}
}
void printBits()
{
int j;
int divisorFCC= 1000;
unsigned long divisorCC=100000000;
unsigned long app;
for (j=0; j<32; j++)
{
code_byte[j]=0;
}
// I really hope you can figure out what this function does
Serial.print("FC = ");
Serial.print(facilityCode);
Serial.print(", CC = ");
Serial.println(cardCode);
app=facilityCode;
for (j=0; j<MAX_DIGIT_FCC; j++) //MAX_DIGIT_FCC=4
{
code_byte[j]=(byte)((app/divisorFCC)& 0xFF);
app=(app%divisorFCC);
divisorFCC=divisorFCC/10;
}
app=cardCode;
for (j=MAX_DIGIT_FCC; j<(MAX_DIGIT_FCC + MAX_DIGIT_CC); j++) //MAX_DIGIT_CC=9
{
code_byte[j]=(byte)((app/divisorCC)&0xFF);
app=(app%divisorCC);
divisorCC=divisorCC/10;
}
SendString (code_byte, (MAX_DIGIT_FCC+MAX_DIGIT_CC)); //transmit a string in CK DT
return;
}
void SendString (byte StringToSend[32], int num)
{
unsigned char CRC;
unsigned char Index;
unsigned char NumBit;
byte ByteSnd;
unsigned char BitSend;
//digitalWrite(cp, LOW);
delayMicroseconds(500); // pauses for 500 microseconds
CRC= 0x0B;
BitSend=0;
for (Index=0; Index<=14; Index++) //Transmit 15 clock
{
SendBit(BitSend);
}
ByteSnd=0x0B; //0x0B (Start sentinel)
NumBit=4;
SendByte(ByteSnd, NumBit); //Transmit the byte
for (Index=0; Index<num; Index++) //Transmit StringToSend
{
ByteSnd=(StringToSend[Index]); //Transmit il byte
CRC=CRC^ByteSnd; //Calculate CRC
NumBit=4;
SendByte (ByteSnd, NumBit);
}
ByteSnd=0x0F; //0x0F
CRC=CRC^ByteSnd; //Refresh CRC
NumBit=4;
SendByte (ByteSnd, NumBit); //Transmit last byte
ByteSnd=CRC; //CRC
NumBit=4;
SendByte (ByteSnd, NumBit); //Transmit CRC
BitSend=0;
for (Index=0; Index<=14; Index++) //Transmit 15 clock
{
SendBit(BitSend);
}
digitalWrite(data, HIGH);
delayMicroseconds(500); // pauses for 500 microseconds
//digitalWrite(cp, HIGH);
//Serial.write ('\n');
return;
}
void SendByte (byte Byte_to_Send, unsigned char Num_to_Send)
{
unsigned char CntPar=0;
unsigned char Bit_to_Send;
while ( 1 )
{
Bit_to_Send= (Byte_to_Send & 0x01) ;
CntPar= CntPar+Bit_to_Send;
SendBit(Bit_to_Send);
Num_to_Send= Num_to_Send-1;
if (Num_to_Send!=0)
{
Byte_to_Send=(Byte_to_Send>>1);
}
else
{
break;
}
}
if ((CntPar & 0x01)==1)
{
Bit_to_Send= 0;
}
else
{
Bit_to_Send= 1;
}
SendBit(Bit_to_Send);
return;
}
void SendBit (unsigned char Bit_to_Send)
{
if (Bit_to_Send==1)
{
digitalWrite(data, LOW);
}
else
{
digitalWrite(data, HIGH);
}
delayMicroseconds(250); // pauses for 250 microseconds
digitalWrite(clock, LOW );
delayMicroseconds(250); // pauses for 250 microseconds
digitalWrite(clock, HIGH );
// if (Bit_to_Send==1)
// {Serial.write ('1');}
// else
// {Serial.write ('0');}
return;
}
It doesn't look the best code in the world. I especially dislike this bit:-
// This waits to make sure that there have been no more data pulses before processing data
if (!flagDone) {
if (--weigand_counter == 0)
flagDone = 1;
}
For a start you don't have any control over how fast that loop will run. I suspect it runs faster than the time between pulses.
However, it could be a hardware problem with interference on the power lines or data input lines.
Is there a long run between the reader and the arduino?
unsigned char databits[MAX_BITS]; // stores all of the data bits
unsigned char bitCount; // number of bits currently captured
unsigned char flagDone; // goes low when data is currently being captured
unsigned int weigand_counter; // countdown until we assume there are no more bits
...
// interrupt that happens when INTO goes low (0 bit)
void ISR_INT0()
{
//Serial.print("0");
bitCount++;
flagDone = 0;
weigand_counter = WEIGAND_WAIT_TIME;
}
Variables used inside an ISR should be declared volatile.
Multi-byte variables (eg. weigand_counter) should be accessed inside critical sections.
"For a start you don't have any control over how fast that loop will run. I suspect it runs faster than the time between pulses."
Yes the loop runs faster then time between pulses. When the program works, it reads well.
"However, it could be a hardware problem with interference on the power lines or data input lines.
Is there a long run between the reader and the arduino?"
I don't think it's a hardware problem, the reader is very close to arduino (2-3 m).
Dear Nick Gammon
thanks for the link: "http://www.gammon.com.au/interrupts"
SO I HAVE TO DO THIS:
Variables only used outside an ISR should not be volatile.
Variables only used inside an ISR should not be volatile.
Variables used both inside and outside an ISR should be volatile.
TO SOLVE THE PROBLEM?
Anything used inside an ISR I declare volatile. Most of my variables are global variables and need to be accessed inside and out, but I noticed a lot of variables that are currently only used inside the ISR, I'm pretty sure they need to be volatile anyway though. From Gammon's page:
"Variables shared between ISR functions and normal functions should be declared "volatile". This tells the compiler that such variables might change at any time, and thus the compiler must reload the variable whenever you reference it, rather than relying upon a copy it might have in a processor register."
While it does sound like at first that only shared variables need to be volatile, based on the second sentence I'd say that if they are used by an ISR at all they should be volatile. The only downside I can see to having unnecessary volatiles would be a slightly slower program, but then again some people require fast programs so this could be a problem.
While it does sound like at first that only shared variables need to be volatile, based on the second sentence I'd say that if they are used by an ISR at all they should be volatile.
Why? The value in the register, copied from a memory location, can be different from what was copied only if an interrupt occurred. During an ISR, interrupts are disabled, so that can't happen.
While it does sound like at first that only shared variables need to be volatile, based on the second sentence I'd say that if they are used by an ISR at all they should be volatile.
Why? The value in the register, copied from a memory location, can be different from what was copied only if an interrupt occurred. During an ISR, interrupts are disabled, so that can't happen.
Based upon the syntax of the sentence Nick wrote: Variables shared between ISR functions and normal functions should be declared "volatile". He never mentioned variables only used within ISR (although I have not had any of these). Are these the variables that you are referring to? It would make sense based on what you just said that if there are indeed variables solely within an ISR that they need not be declared volatile. Thanks for clarifying.
cypherrage:
Anything used inside an ISR I declare volatile. Most of my variables are global variables and need to be accessed inside and out, but I noticed a lot of variables that are currently only used inside the ISR, I'm pretty sure they need to be volatile anyway though. From Gammon's page:
See further down that page:
I make it explicit:
Variables only used outside an ISR should not be volatile.
Variables only used inside an ISR should not be volatile.
Variables used both inside and outside an ISR should be volatile.
I agree with PaulS:
PaulS:
Why? The value in the register, copied from a memory location, can be different from what was copied only if an interrupt occurred. During an ISR, interrupts are disabled, so that can't happen.
The compiler knows it is an ISR. Non-shared variables do not need to be volatile. If anything making them volatile would slow it down, which is the last thing you want in an ISR.
fan7:
SO I HAVE TO DO THIS:
Variables only used outside an ISR should not be volatile.
Variables only used inside an ISR should not be volatile.
Variables used both inside and outside an ISR should be volatile.
TO SOLVE THE PROBLEM?
I'm not saying the problem will be solved. I am saying that is the first thing to fix. Then test it. Quite often problem results can be caused by multiple coding issues. May as well fix the obvious ones first.
void loop()
{
unsigned int weigand_counterCopy;
// This waits to make sure that there have been no more data pulses before processing data
// weigand_counter is updated inside an ISR so access it safely
noInterrupts ();
weigand_counter--;
weigand_counterCopy = weigand_counter;
interrupts ();
if (!flagDone) {
if (weigand_counterCopy == 0)
flagDone = 1;
}
...
Dear Nick, I downloaded the new sketch on Arduino, following your advices.
Unfortunatelly the problem occurred again, after about one week.
I don't understand where it is the problem. I don't know if it is a hardware or firmware problem and why it occurred after some days. I attach a simple scheme of the connections made.
Thanks in advance for your help.
/*
* HID RFID Reader Wiegand Interface for Arduino Uno
* Written by Daniel Smith, 2012.01.30
* Modified by F.A.N. 2013
* www.pagemac.com
*
* This program will decode the wiegand data from a HID RFID Reader (or, theoretically,
* any other device that outputs weigand data).
* The Wiegand interface has two data lines, DATA0 and DATA1. These lines are normall held
* high at 5V. When a 0 is sent, DATA0 drops to 0V for a few us. When a 1 is sent, DATA1 drops
* to 0V for a few us. There is usually a few ms between the pulses.
*
* Your reader should have at least 4 connections (some readers have more). Connect the Red wire
* to 5V. Connect the black to ground. Connect the green wire (DATA0) to Digital Pin 2 (INT0).
* Connect the white wire (DATA1) to Digital Pin 3 (INT1). That's it!
*
* Operation is simple - each of the data lines are connected to hardware interrupt lines. When
* one drops low, an interrupt routine is called and some bits are flipped. After some time of
* of not receiving any bits, the Arduino will decode the data. I've only added the 26 bit and
* 35 bit formats, but you can easily add more.
*
* f.a.n.= I've added some functions to send output in clock/data format using PIN 4 and PIN 5.
* f.a.n.= I've added the watchdog functions.
*/
#define MAX_BITS 100 // max number of bits
#define WEIGAND_WAIT_TIME 3000 // time to wait for another weigand pulse.
#define MAX_DIGIT_FCC 4
#define MAX_DIGIT_CC 9
volatile unsigned char databits[MAX_BITS]; // stores all of the data bits
volatile unsigned char bitCount; // number of bits currently captured
volatile unsigned char flagDone; // goes low when data is currently being captured
volatile unsigned int weigand_counter; // countdown until we assume there are no more bits
unsigned long facilityCode=0; // decoded facility code
unsigned long cardCode=0; // decoded card code
byte code_byte[32];
int length;
const int clock = 4; // CLOCK
const int data = 5; // DATA
// interrupt that happens when INTO goes low (0 bit)
void ISR_INT0()
{
//Serial.print("0");
bitCount++;
flagDone = 0;
weigand_counter = WEIGAND_WAIT_TIME;
}
// interrupt that happens when INT1 goes low (1 bit)
void ISR_INT1()
{
//Serial.print("1");
databits[bitCount] = 1;
bitCount++;
flagDone = 0;
weigand_counter = WEIGAND_WAIT_TIME;
}
void setup()
{
pinMode(13, OUTPUT); // LED
pinMode(2, INPUT); // DATA0 (INT0)
pinMode(3, INPUT); // DATA1 (INT1)
Serial.begin(9600);
Serial.println("RFID Readers");
// binds the ISR functions to the falling edge of INTO and INT1
attachInterrupt(0, ISR_INT0, FALLING);
attachInterrupt(1, ISR_INT1, FALLING);
pinMode(clock, OUTPUT);
pinMode(data, OUTPUT);
digitalWrite(clock, HIGH );
digitalWrite(data, HIGH );
weigand_counter = WEIGAND_WAIT_TIME;
}
void loop()
{
// This waits to make sure that there have been no more data pulses before processing data
unsigned int weigand_counterCopy;
// This waits to make sure that there have been no more data pulses before processing data
// weigand_counter is updated inside an ISR so access it safely
noInterrupts ();
weigand_counter--;
weigand_counterCopy = weigand_counter;
interrupts ();
if (!flagDone) {
if (weigand_counterCopy == 0)
flagDone = 1;
}
// if we have bits and we the weigand counter went out
if (bitCount > 0 && flagDone) {
unsigned char i;
Serial.print("Read ");
Serial.print(bitCount);
Serial.print(" bits. ");
// we will decode the bits differently depending on how many bits we have
// see www.pagemac.com/azure/data_formats.php for mor info
if (bitCount == 35)
{
// 35 bit HID Corporate 1000 format
// facility code = bits 2 to 14
for (i=2; i<14; i++)
{
facilityCode <<=1;
facilityCode |= databits[i];
}
// card code = bits 15 to 34
for (i=14; i<34; i++)
{
cardCode <<=1;
cardCode |= databits[i];
}
printBits();
}
else if (bitCount == 26)
{
// standard 26 bit format
// facility code = bits 2 to 9
for (i=1; i<9; i++)
{
facilityCode <<=1;
facilityCode |= databits[i];
}
// card code = bits 10 to 23
for (i=9; i<25; i++)
{
cardCode <<=1;
cardCode |= databits[i];
}
printBits();
}
else {
// you can add other formats if you want!
Serial.println("Unable to decode.");
}
// cleanup and get ready for the next card
bitCount = 0;
facilityCode = 0;
cardCode = 0;
for (i=0; i<MAX_BITS; i++)
{
databits[i] = 0;
}
}
}
void printBits()
{
int j;
int divisorFCC= 1000;
unsigned long divisorCC=100000000;
unsigned long app;
for (j=0; j<32; j++)
{
code_byte[j]=0;
}
// I really hope you can figure out what this function does
Serial.print("FC = ");
Serial.print(facilityCode);
Serial.print(", CC = ");
Serial.println(cardCode);
app=facilityCode;
for (j=0; j<MAX_DIGIT_FCC; j++) //MAX_DIGIT_FCC=4
{
code_byte[j]=(byte)((app/divisorFCC)& 0xFF);
app=(app%divisorFCC);
divisorFCC=divisorFCC/10;
}
app=cardCode;
for (j=MAX_DIGIT_FCC; j<(MAX_DIGIT_FCC + MAX_DIGIT_CC); j++) //MAX_DIGIT_CC=9
{
code_byte[j]=(byte)((app/divisorCC)&0xFF);
app=(app%divisorCC);
divisorCC=divisorCC/10;
}
SendString (code_byte, (MAX_DIGIT_FCC+MAX_DIGIT_CC)); //transmit a string in CK DT
return;
}
void SendString (byte StringToSend[32], int num)
{
unsigned char CRC;
unsigned char Index;
unsigned char NumBit;
byte ByteSnd;
unsigned char BitSend;
//digitalWrite(cp, LOW);
delayMicroseconds(500); // pauses for 500 microseconds
CRC= 0x0B;
BitSend=0;
for (Index=0; Index<=14; Index++) //Transmit 15 clock
{
SendBit(BitSend);
}
ByteSnd=0x0B; //0x0B (Start sentinel)
NumBit=4;
SendByte(ByteSnd, NumBit); //Transmit the byte
for (Index=0; Index<num; Index++) //Transmit StringToSend
{
ByteSnd=(StringToSend[Index]); //Transmit il byte
CRC=CRC^ByteSnd; //Calculate CRC
NumBit=4;
SendByte (ByteSnd, NumBit);
}
ByteSnd=0x0F; //0x0F
CRC=CRC^ByteSnd; //Refresh CRC
NumBit=4;
SendByte (ByteSnd, NumBit); //Transmit last byte
ByteSnd=CRC; //CRC
NumBit=4;
SendByte (ByteSnd, NumBit); //Transmit CRC
BitSend=0;
for (Index=0; Index<=14; Index++) //Transmit 15 clock
{
SendBit(BitSend);
}
digitalWrite(data, HIGH);
delayMicroseconds(500); // pauses for 500 microseconds
//digitalWrite(cp, HIGH);
//Serial.write ('\n');
return;
}
void SendByte (byte Byte_to_Send, unsigned char Num_to_Send)
{
unsigned char CntPar=0;
unsigned char Bit_to_Send;
while ( 1 )
{
Bit_to_Send= (Byte_to_Send & 0x01) ;
CntPar= CntPar+Bit_to_Send;
SendBit(Bit_to_Send);
Num_to_Send= Num_to_Send-1;
if (Num_to_Send!=0)
{
Byte_to_Send=(Byte_to_Send>>1);
}
else
{
break;
}
}
if ((CntPar & 0x01)==1)
{
Bit_to_Send= 0;
}
else
{
Bit_to_Send= 1;
}
SendBit(Bit_to_Send);
return;
}
void SendBit (unsigned char Bit_to_Send)
{
if (Bit_to_Send==1)
{
digitalWrite(data, LOW);
}
else
{
digitalWrite(data, HIGH);
}
delayMicroseconds(250); // pauses for 250 microseconds
digitalWrite(clock, LOW );
delayMicroseconds(250); // pauses for 250 microseconds
digitalWrite(clock, HIGH );
// if (Bit_to_Send==1)
// {Serial.write ('1');}
// else
// {Serial.write ('0');}
return;
}