问题
I got the little red "ELECHOUSE V3" kit off of eBay with a white card, and a blue keyfob transponder, and I've written a C program which creates and decodes packets and I can send it commands and it responds with AC packets, and I can read the version and the status. I'm not using any RFID library, I'm just using plain C and making my own simple library because I want to understand it and I want to release a simple single file demonstration for people who really want to understand it rather than just using some arduino lib or whatever. So that's all the questions I'm not asking.
So here's the question I am asking:
What is the exact commands to send to scan for the presence of passive (non powered) transponders? I do not know for sure what kind they are, but they came with the kit and are probably ISO 14443 / 14443A.
Actually, I tried the tags on my Samsung Galaxy S4, and it says they are ISO 14443-3A NXP MIFARE Classic 1K - Not Supported. But it still shows the serial numbers for them.
What are the exact commands to scan for all supported card types?
To send a command I use my function like this: sendcmd("0x4A 0x01 0x00"); (The TFI of 0xD4 is automatically added to the commands, and the preamble/len/lcs/checksums are all calculated and dealt with.)
I do get ACKS back for my commands, but can't figure out which commands to send to scan for cards or read them.
If I can get the PN532 to spit card scan data back at me I should be able to parse it using the PN532 datasheet.
Thank you very much,
Jesse
回答1:
Ahh OK.. After trying everything that seemed related indicated in the data sheet with no success, I turned ham radio on to 13.56Mhz CW/LSB and there was nothing to be heard.. So just for kicks I tried the RFRegulationTest command, and that unlocked the whole thing! Seems to be a test command which turns on the transmitter and leaves it on till you issue another command... But it initializes something as needed!
So here's the commands it takes to make the NXP NP532 scan for cards: (I'm using RS232 at 115200bps, but should be similar for other interfaces.)
You send to it:
0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0xFF 0x03 0xFD 0xD4 0x58 0x00 0xD4
and you'll get an ACK and transmitter will key on: 0x00 0x00 0xFF 0x00 0xFF 0x00
And at this point the transmitter will key up. Maybe let it do that for 100mS or something, then you can start scanning for cards:
0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0xFF 0x04 0xFC 0xD4 0x4A 0x02 0x00 0xE0
The chip will then turn on the radio transmitter and start scanning for cards until a card comes in range, then it reads the serial number, shuts down the transmitter, and gives you a packet which contains:
0x4B, 0x01/0x02 (Depending on whether 1 or 2 cards was detected), followed by various info about the card like it's serial number.
You can also set the max number of tries it'll try when given the 0x4A 0x02 0x00 command, by setting the number of tries to a number below 0xFF, like this:
sendcmd("0x32 0x05 0xFF 0x01 0x10")
in which case when you give the read command (0x4A 0x02 0x00) it'll try for a fraction of a second (0x10 times) then give up and send a packet that contains:
0x4B, 0x00
which means "Cards found: Zero."
See the datasheet for all the details, it does tell you everything you need to know, except I never could figure out how to enable the transmitter until I ran the rf test command.
Anyway, just keep sending the second command over and over a few times a second or slower if you like, and each time you send that command it will scan and let you know if there is a card within range!
Or if you set retries to 0xFF then it'll try forever until it detects a card, then you only have to re-send the scan-for-card command when it finds one.
The long strings of 0xFF is just to wake the device up because it goes to sleep and misses the first few bytes you send.
The examples I give of what to send it that start with a bunch of 0xFF's are the whole full packet, including preambles, length field, checksums and everything calculated. You can send them directly to make it scan for cards.
The initial RF test command and the retry set command only needs to be run once on powerup, after that just keep re-sending the read command as needed.
My PN532 chip reports itself as Version: 1.6
Here's my little sample program:
(The RS232 initialization part I lifted from an SO post - thanks to whoever wrote that!)
(It's for Linux. Compile with gcc nfc.c -o nfc.e )
(Should be able to port it to any platform, you just have to deal with the serial port - the rest is platform independent.)
(Also note that this simply scans for cards and returns serial number/NFCID, and only for Mifare, ISO/IEC14443-3 Type A cards at 106kbps in passive mode. If you want to actually read/write to the memory on the cards, you have to write more code, but this at least demonstrates what it takes to get started and how the command structure works, and provides some handy routines for sending and decoding packets.)
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int debug=0;
int fd;
void init_rs232(void);
void sendpacket(unsigned char * payload, int len);
void sendcmd(char * payload);
int main(void)
{
printf("Welcome!\n");
init_rs232();
int waitfor;
#define PACKET 1
#define ACK 2
sendcmd("0x58 0x00");waitfor=ACK; //RFRegulationTest -- This command is used for radio regulation test.
int cstate=0; //Chat state, config state, whatever..
int lc=0;
while(1)
{
lc++; //Send a scan command every second or so.
if(lc>1000)
{
lc=0;
sendcmd("0x4A 0x02 0x00"); //InListPassiveTarget -- The goal of this command is to detect as many targets (maximum MaxTg) as possible (max two) in passive mode.
}
if(cstate==1) //ACK's set the bottom bit in state, allowing it to go onto the next state.
{
sendcmd("0x02");waitfor=PACKET; //Read the version out of the PN532 chip.
cstate++;
}
if(cstate==3) //ACK's set the bottom bit in state, allowing it to go onto the next state.
{
sendcmd("0x04");waitfor=PACKET; //Get current status.
cstate++;
}
if(cstate==5)
{
waitfor=PACKET;
sendcmd("0x32 0x05 0xFF 0x01 0x10");//Max retries - last byte is for passive: 0=1 try, 1=2 tries, 254=255 tries, 0xFF=infinite retries.
//If last byte is 0xFF, then unit starts scanning for cards indefinitely. As soon as it detects a card, it stops scanning and returns info.
//If last byte is less than 0xFF, it tries scans and as soon as it finds a card returns info on it and stops trying, but
//if it never finds a card in the specified number of retries, it gives up and returns 0x4B, 0x00 (Cards found: Zero.)
cstate++;
}
//Alternative way to send a new scan command each time the previous one gives up or finds a card:
// if(cstate==7)
// {
// waitfor=PACKET;
// sendcmd("0x4A 0x02 0x00"); //InListPassiveTarget
// cstate--;
// }
static unsigned char buffin [1000024];
static unsigned char buf[1],bufo[1];
int n;
static int bi=0;
unsigned char len,lcs,tfi,dcs;
bufo[0]=buf[0];
n=read(fd,buf,sizeof(buf));
if(n!=0)
{
if(n>0)
{
if(bi<1000000)
{
static int state=0;
if(state==0) //Waiting for preamble..
{
if((bufo[0]==0)&&(buf[0]==0xFF)){state=10;}
}
else if(state==10) //Waiting for Len
{
len=buf[0];
state=20;
}
else if(state==20) //Waiting for len checksum
{
if((len==0xFF)&&(buf[0]==0xFF)){printf("ERROR: BIG PACKET. Bye.\n");exit(-2);}
if((len==0x00)&&(buf[0]==0xFF)){state=21;}
else if((len==0xFF)&&(buf[0]==0x00)){state=21;}
else
{
lcs=buf[0]+len;
if(lcs){printf("ERROR: len checksum failed! 0x%02X\n",buf[0]);}
state=30;
}
}
else if(state==21) //Waiting for the 0x00 after ack/nack..
{
state=0;
if(buf[0]==0x00)
{
if(bufo[0]==0xFF)
{
if(debug){printf("ACK!\n");}
if(waitfor==ACK){cstate=cstate|1;}
}
if(bufo[0]==0x00){printf("NACK!\n");}
}else{
printf("ERROR: Invalid length, or ack/nack missing postamble...\n");
}
}
else if(state==30) //Waiting for tfi..
{
tfi=buf[0];
//printf("tfi=0x%02X\n",tfi);
dcs=tfi;
bi=0;
state=40;
}
else if(state==40) //Saving bytes...
{
//printf("Saving payload byte 0x%02X\n",buf[0]);
buffin[bi++]=buf[0];
dcs=dcs+buf[0];
if(bi>=len){state=50;}
}
else if(state==50) //Calculating the checksum..
{
state=0;
dcs=dcs+buf[0];
if(dcs)
{
printf("ERROR: Data Checksum Failed! (0x%02X)\n",dcs);
}else{
if(waitfor==PACKET){cstate=cstate|1;}
//printf("Good Packet: tfi=0x%02X, len=%d\n",tfi,len-1);
if(tfi==0xD5)
{
if(buffin[0]==0x03){printf("PN532 Version: %d.%d, features:%d\n",buffin[2],buffin[3],buffin[4]);}
if(buffin[0]==0x05)
{
printf("Status: Last Error:%d, Field:%d, Targets:%d, SAM Status:0x%02X\n",buffin[1],buffin[2],buffin[3],buffin[len-2]);
static char bitrates[255][10]={"106kbps","212kbps","424kbps"};
static char modtypes[255][100];
strcpy(modtypes[0x00],"Mifare, ISO/IEC14443-3 Type A, ISO/IEC14443-3 Type B, ISO/IEC18092 passive 106 kbps");
strcpy(modtypes[0x10],"FeliCa, ISO/IEC18092 passive 212/424 kbps");
strcpy(modtypes[0x01],"ISO/IEC18092 Active mode");
strcpy(modtypes[0x02],"Innovision Jewel tag");
if(buffin[3]==1){printf("Target %d: rx bps:%s, tx bps:%s, modulation type: %s.\n",buffin[4],bitrates[buffin[5]],bitrates[buffin[6]],modtypes[buffin[7]]);}
if(buffin[3]==2){printf("Target %d: rx bps:%s, tx bps:%s, modulation type: %s.\n",buffin[8],bitrates[buffin[9]],bitrates[buffin[10]],modtypes[buffin[11]]);}
}
if(buffin[0]==0x4B)
{
printf("FOUND %d CARDS!\n",buffin[1]);
//ONLY VALID FOR Mifare/ ISO type A 106KBPS:
int i,ii,iii;
i=0;ii=2;
while(i<buffin[1])
{
printf("Target # %d:", buffin[ii++]);
printf("SENS_RES=0x%02X%02X, ",buffin[ii],buffin[ii+1]);ii++;ii++;
printf("SEL_RES=0x%02X, ",buffin[ii++]);
printf("NFCIDLength=%d, ",buffin[ii++]);
printf("NFCID=");
iii=0;
while(iii<buffin[ii-1])
{
printf("%02X",buffin[ii+iii]);
iii++;
if(iii<buffin[ii-1]){printf(":");}
}
ii=ii+iii;
printf("\n");
i++;
}
}
//Just a debugging thing for printing out the contents of valid packets.
//int i=0;while(i<(len-1)){printf("0x%02X, ",buffin[i++]);}printf("\n");
}
else if(tfi==0x7F)
{
printf("Received error packet 0x7F with zero size.\n");
}else{
printf("ERROR: Got unknown %d byte packet with tfi=0x%02X!\n",len-1,tfi);
}
}
}
else
{
printf("Uhoh!\n");
}
//printf("Got byte 0x%02X, now state is %d\n",(unsigned char)buf[0],state);
}else{
printf("ERROR: bi=%d which is too big.. Starting over.\n",bi);
bi=0;
}
}else{
printf("ERROR %d while reading serial port: %s\n",errno,strerror(errno));
exit(-1);
}
}
usleep(1000);
}
return(0);
}
void init_rs232(void)
{
char *portname = "/dev/ttyUSB0";
fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
printf("error %d opening %s: %s", errno, portname, strerror (errno));
exit(-1);
}
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
printf("error %d from tcgetattr(%s)\n", errno,strerror(errno));
exit(-1);
}
cfsetospeed (&tty, B115200);
cfsetispeed (&tty, B115200);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // disable break processing
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 0; // 0.5 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= 0; //This was parity
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
printf("error %d from tcsetattr(%s)\n", errno,strerror(errno));
exit(-1);
}
}
void sendpacket(unsigned char * payload, int len)
{
int tfi;
static unsigned char buffer[66000];
int i,bo;
unsigned char lcs,dcs;
tfi=0xD4;
i=0;
bo=0;
while(i<=8){i++;buffer[bo++]=0xFF;} //Pre-padding.. 8-800 OK, 900 too much, 7 too little. Needs to be 0xFF I guess.. Probably wakes it up.
buffer[bo++]=0x00; //Preamble.
buffer[bo++]=0xFF; //Preamble.
len++;
lcs=-len; //Length Checksum.. (yes...)
buffer[bo++]=len;
buffer[bo++]=lcs;
buffer[bo++]=tfi;
dcs=tfi;
i=0;
while((i<65900)&&(i<(len-1)))
{
buffer[bo]=payload[i];
dcs=dcs+buffer[bo];
bo++;
i++;
}
dcs=(-dcs);
buffer[bo++]=dcs;
write(fd,buffer,bo);
//printf("Sent %d bytes\n",bo);
//printf("Whole packet: ");
//i=0;
//while(i<bo)
//{
// printf("0x%02X ",buffer[i]);
// i++;
//}
//printf("\n");
}
void sendcmd(char * payload) //Accepts space separated argument list like "0xFF 0x0A 255 'USERID' 0 0"
{ //strings are quoted in half quotes. half quotes inside a string are escaped \\'
int i,v; //full quotes inside a string are escaped like \"
static unsigned char buffer[1024]; //back slashes inside a string are escaped like \\\\ .
static int bo; //(The first escape or escape pair is for the C compiler, the second for this function:
bo=0; // The actual contents of the string are just \' and \\)
i=0; // Numeric formats supported are hex (0xNN), base ten (123), and octal (0377).
if(debug){printf("sendcmd: ");}
while(payload[i])
{
if((payload[i]!='\'')&&(payload[i]!=' '))
{
v=strtoul(&payload[i],NULL,0);
buffer[bo++]=v;
if(debug){printf("0x%02X, ",v);}
while(payload[i]>' '){i++;}
}
else if(payload[i]=='\'')
{
i++;
int keeprun;
keeprun=1;
while(keeprun)
{
if(payload[i]=='\\')
{
i++;
}else{
if(payload[i]=='\''){keeprun=0;}
}
if((keeprun)&&(payload[i]))
{
buffer[bo++]=payload[i];
if(debug){printf("%c",payload[i]);}
}
i++;
}
if(debug){printf(", ");}
}
else
{
i++;
}
}
if(debug){printf("\n");}
sendpacket(buffer,bo);
}
来源:https://stackoverflow.com/questions/43563378/how-do-i-read-passive-nfc-rfid-units-with-pn532