RFID tag as _one_ variable
Erlend
Posts: 612
I want to handle the RFID tag as one variable instead of 10 byte values. The byte codes seem to be in the range 0 - 99, but I am not sure it is always so. If it is, and I just string all the numbers together ino one integer (i,e, 44 52 87 ... becomes 445287...) the value could be as high as 10EE10 (-1), obviously blowing the LONG capacity. I may be forced to split into two LONGs and I suppose I could live with that too, if there is no, more elegant solution out there. But, that would mean I need to add some PUB to take care of comparison and lookup, etc. Same goes for a solution based on storing the tag code as a 20 char string. Ideas to a more elegant solution is appreciated.
Erlend
I am using the RFID reader object by Brandon Nimon, and the RFID hardware from Parallax.
Erlend
I am using the RFID reader object by Brandon Nimon, and the RFID hardware from Parallax.

Comments
Spin has enough string support for simple moves and compares, and a fixed stringsize makes this really easy:
Andy
How can this be? One of my cards reads: 48 49 48 48 65 54 52 56 66 68. Can't see how this could fit into 10x 4 bit?
Ariba,
I had forgotten the 'native' strcomp. This looks good, and based on the above code, all the bytes are printable characters. But it would be better to think of the tag not as a string as such, but an array of bytes, and then it does not matter if they are printable or not, right? No reason to behave and only use strcomp for real strings.
I will define thus: DAT refTag1 BYTE 48, 49, 48, 48, 65, 54, 52, 56, 66, 68 -, and next, same way for refTag2, ...etc. After that I can deal with refTag as a string, right?
And I will modify the RFID reader object to write the 10 codes into the (global) tag[] by means of passing @tag to it, right?
My Main code will have a CASE block where the action depends on which of the refTag matches the value of tag. Am I on the right track?
You can define the tag numbers in a DAT section with the ASCII charcters in quotes: But you can also write 10 decimal values as in your example, but you need to add a zero as 11th byte.
I don't know how you get the number from the RFID reader. If you read it serially byte per byte then this may work: If you have the tag characters in the @tag string then you can serach it in the refTags like that:
repeat i from 0 to MAX_REF_TAGS - 1 if strcomp(@tag, @refTags+i*11) quit if i < MAX_REF_TAGS 'found refTag number in i else 'not thereAndy@kuroneko
I didn't realise the 48 49 48 ... were ASCII codes for the character form of the hex code, I thought they were numerals. Sorry. that's why I could not understand how 4 bits could do 48 49 48 etc. I guess I should have recognized it from the familiar number range. I'm learning : ).
@Ariba
Thanks for the guideline. Knowing they are all ASCII codes makes it so much easier. I'm on my wings now.
Erlend
Is there a problem with using the built in functionality?
You could build a list in RAM or as part of a DAT that is formatted list this:
'' Key# Key Byte values Key ID tags {1} byte "1234567890" ' 0098765432 {2} byte "2345678901" ' 0087654321 {3} byte "3456789012" ' 0076543210 {4} byte "4567890123" ' 0065432109 {5} byte $23, $89, $6F, $58, $69, $11, $12, $A2, $7C, $41 ' 0023569832 <--- notice here, these are the ASCII values you are referring to {6} byte "5678901234" ' 0054321098I could alter the software to allow tags to be zero-terminated strings and just add a parameter in the start method to reflect the option.
Bobb,
I was debugging my setup with a RFID reader written in spin, but now it is up and running, and I understand what is going on, and it is time to move over to a permanent solution. I want to use your object for that. I've seen it has lots of functionality, and I have only a few thoughts; I think it is 'cleaner' when the object is not specific to one application, so I would like to avoid putting DAT data in there. Better to pass a pointer to DAT stuff instead. And, my application is not only looking to check if the card has a valid code, but must also find which card it is. I thought I would try to implement this as an additional functionality in the object, but PASM is hard and slow for me to work on.
What I would ideally want is:
Define the ok tags in DAT in the Main code, pass a pointer to the RFID object
Have the RFID reader continously running in a separate cog, where it reads tag id, checks which of the ok tags is matching, and then writes this to a global valueID variable (an index only, i.e. 1, 2, 3,..) in Main. After 10 sec (or a parameter time) it resets the valueID by writing a -1 to it. This way it will only contain the id of a recent card swipe, and not hold on to old ones.
This would allow me - in the Main code - to anywhere in my algorithms check "who is here", by looking at valueID. No more fuzz.
I would very much appreciate help to do this!
Erlend
Using the tagloc parameter, you supply the object with an address (a "table") that has the valid tags run end-to-end as 10-byte arrays (no zero termination/delimitation).
With the tagcount parameter, you tell the object how many tags are in that table.
When a tag is read, the supplied output address contains (1) if the tag is in the supplied table, and (2) if the tag is not (or (3) if there has been a wrong tag and a correct tag scanned since the last time the value was cleared). These values are not cleared by the object, you'll have to do it in the parent object.
At the same time, the rec_tag address is populated with the 10-byte string of the tag's value, and the tag_ret address gets the tag's index (that's why -- in my tags table example above -- I have the tags numbered).
None of the return values are cleared by the object, they do get overwritten when a new tag is read though. That's all up to the parent object to detect and control that.
Thanks. I suspected tag_ret could be what I was looking for. Now all I have to sort out is the automatic clearing of an old card tag value. I don't like to have this kind of housekeeping in my Main, though. If you don't mind too much I will tweak the code to take one more parameter - auto_clear - and if >0 it will clear tag_ret to -1 after auto_clear seconds.
I am doing it this way for all the inputs in my project. Inputs are read, converted, and digital ones timetagged - outside the Main cog. Results are written back to global variables that the Main code can use without worrying about houskeeping.
Erlend
Just FYI, when you dig into it, the PASM COG just sits at a WAITPEQ Dat_p, Dat_p until a tag is read, so you would have to replace that with a loop that jumps out if the Dat_p goes high, then have a timer for when the last tag was read, and clears the values based on that. It shouldn't be too difficult.
waitrfid TEST Dat_p, INA WC ' test Dat_p IF_C JMP #moveon ' if it is high, go to rest of program MOV waitlen, cnt ' get now ADDS waitlen, strt ' difference of start and now CMP waitlen, waitprd WZ, WC ' if a waitperiod hasn't passed, do another loop IF_B JMP #waitrfid ' do it again! WRWORD negone, tagnoAddr ' put -1 (actually 32767 into the tag number (maybe you should use 0?) WRBYTE zero, doutAddr ' put 0 into the output value '' write a loop here to clear the tag string if you need it. WAITPEQ Dat_p, Dat_p ' if there's no need to wait for time to pass, just wait high moveonAnd add this right after BigLoop:
MOV strt, cnt ' start timer NEG strt, strt ' get negative (used in place of SUB later on)Just to make sure I've got it (I never did get a grip on assembly, nor on Forth), here's the stiched together code:
DAT ORG entry MOV p1, PAR SHR p1, #1 RDWORD DoutAddr, p1 ' get address ADD p1, #2 RDWORD tagnoAddr, p1 ' get address ADD p1, #2 RDWORD TagAddr, p1 ' get address MOV TagAddr2, TagAddr ' save copy ADD p1, #2 RDWORD tagsAddr, p1 ' get address MOV tagsAddr2, tagsAddr ' save copy MOV DeltaT2, DeltaT SHR DeltaT2, #1 ADD DeltaT2, DeltaT SUB DeltaT2, #4 ' DeltaT2 := DeltaT / 2 + DeltaT - 4 MOV OUTA, En_p MOV DIRA, En_p BigLoop MOV strt, cnt ' start timer NEG strt, strt ' get negative (used in place of SUB later on) MOV time, cnt ADD time, pauselen WAITCNT time, #0 ' kind of debounce for RFID MOVD set_tag, #tag MOV TagAddr, TagAddr2 ' restore backup ReadRFID MOV idx1, #12 ' recieve 12 bytes (only middle 10 are used) MOV OUTA, #0 ' /enable RFID waitrfid waitrfid TEST Dat_p, INA WC ' test Dat_p IF_C JMP #moveon ' if it is high, go to rest of program MOV waitlen, cnt ' get now ADDS waitlen, strt ' difference of start and now CMP waitlen, waitprd WZ, WC ' if a waitperiod hasn't passed, do another loop IF_B JMP #waitrfid ' do it again! WRWORD zero, tagnoAddr ' put 0 into the tag number WRBYTE zero, doutAddr ' put 0 into the output value '' write a loop here to clear the tag string if you need it. WAITPEQ Dat_p, Dat_p ' if there's no need to wait for time to pass, just wait high moveon WAITPNE Dat_p, Dat_p ' wait low MOV time, cnt ' mark time MOV OUTA, En_p ' shutdown RFID as soon as possible MOV idx2, #8 ' get eight bits MOV val_out, #0 ' clear last value ADD time, DeltaT2 ' wait for middle of first bit (and all subsequent bits) getbit WAITCNT time, DeltaT ' wait for center of next bit TEST Dat_p, INA WC RCL val_out, #1 ' shift value in (LSB first) DJNZ idx2, #getbit REV val_out, #24 ' reverse byte set_tag MOV tag, val_out WRBYTE val_out, TagAddr ' write to hub so parent object can see which RFID ADD TagAddr, #1 ADD set_tag, add1dest DJNZ idx1, #waitrfid MOV TagAddr, TagAddr2 ' restore backup TJZ tagc2, #nocheck ' if no tags to check MOV tagsAddr, tagsAddr2 ' restore backup mainloop MOV tagc, tagc2 ' restore backup bloop MOVD cmp_bytes, #tag+1 MOV idx, #10 ' check 10 bytes check_byte RDBYTE tagsbyte, tagsAddr ' byte from DAT section below ADD tagsAddr, #1 cmp_bytes CMP tag, tagsbyte WZ, NR ADD cmp_bytes, add1dest IF_NZ JMP #skip_tag ' if one byte is wrong, skip the rest of tag DJNZ idx, #check_byte MOV p1, tagc2 ADD p1, #1 SUB p1, tagc ' calculate RFID tag number WRWORD p1, tagnoAddr MOV p1, #|< 0 ' RFID match WRBYTE p1, DoutAddr JMP #BigLoop ' on success, back to beginning skip_tag_ret DJNZ tagc, #bloop ' check next tag WRBYTE zero, tagnoAddr nocheck MOV p1, #|< 1 ' no matching RFID WRBYTE p1, DoutAddr JMP #BigLoop ' after failure, back to beginning skip_tag ADD tagsAddr, idx ' move to next tag address SUB tagsAddr, #1 JMP #skip_tag_ret deltaT LONG 0 pauselen LONG 0 add1dest LONG 1 << 9 zero LONG 0 tagc2 LONG 0 Dat_p LONG 0 En_p LONG 0 DeltaT2 RES tag RES 12 tagc RES tagsbyte RES DoutAddr RES tagnoAddr RES tagsAddr RES tagsAddr2 RES TagAddr RES TagAddr2 RES p1 RES idx RES idx1 RES idx2 RES time RES val_out RES FIT DAT '' Key# Key Byte values Key ID tags {1} byte "1234567890" ' 0098765432 {2} byte "2345678901" ' 0087654321 {3} byte "3456789012" ' 0076543210 {4} byte "4567890123" ' 0065432109 {5} byte $23, $89, $6F, $58, $69, $11, $12, $A2, $7C, $41 ' 0023569832 {6} byte "5678901234" ' 0054321098Must admit I cannot follow it more than I can understand German, i.e. some. Where is tagno (:= tag_retAddr) given a value? And where&how is waitprd defined? And I cannot find tags (DAT) anywhere in the code.
Sorry for being PAStupidM.
Erlend
Though they help you appreciate what the high-level languages you are used to working with actually do behind the scenes (almost all high-level languages break down to languages like PASM -- SPIN is no exception).
It looks like you have the program how I described, but I am unsure it will work. I may be able to test it later today or tomorrow, and get it to work if it isn't (let me know).
Just to answer your questions:
tagnoAddr is assigned an address in SPIN, and the COG gets that address during initialization on line RDWORD tagnoAddr, p1. Then the value is populated on this line: WRWORD p1, tagnoAddr where p1 is the assigned value, and is calculated by the few lines before it. For WRWORD, the first value (the source) is the value written, and the second value (the destination) is the HUB address to write it to.
WAITPEQ, or more importantly the Dat_p (a 32-bit mask where specific bits are assigned to instruct it to wait for specific pins -- i.e. if you wait for pin 3 to go high, Dat_p has a value of %...000_0000_1000) register is assigned all the way back up in the SPIN code that starts the COG. You can store values to the DAT section in SPIN (which is a HUB address), then when the cog is started, those values are loaded into the COG as a local variable (changing the value in the PASM cog won't affect the HUB value). The mask is assigned using the |< operator.
The tags address is passed by assigning the tagcheckAddr variable. And as with tagnoAddr, the address is gathered when the COG starts on this line: RDWORD TagAddr, p1. Though, just the address is passed, the values are read from the HUB memory each time a tag is read using this line: RDBYTE tagsbyte, tagsAddr. This means the tags could actually be changed after the COG has started and the new tags would then be compared.
Erlend
PUB start_rfid (Ser_p, Ena_p, outputAddr, rec_tagAddr, tag_retAddr, tagcount, tagloc, debounce, resettime) '' Start RFID Cog. ''****************************************** ''* Ser_p is RFID serial data line ''* Ena_P is RFID /ENABLE line ''* outputAddr is address of byte or longer, returns %01 on RFID match, %10 on read, but no RFID match ''* rec_tagAddr is address of 12 bytes (array) where to place recieved RFID tag (regaurdless of valid or not) ''* tag_retAddr is address of word or longer, where matching tag number is returned (1 through NO_OF_TAGS -- 0 on no match) ''* tagcount is the number of tags to recognize, needs to match the table supplied ''* tagloc is 0 for when using tags table at bottom of this program, else it is an address of a tag table (same format as below) ''* debounce is the number of seconds after a card is read before the program continues ''* resettime is the number of seconds (correct timing only if same or longer than debounce) before the output and rec_tag get reset. 0 will disable this feature. ''****************************************** stop pauselen := (clkfreq * debounce) #> 9 ' time to "debounce" RFID reads so a card doesn't get read twice in quick succession (minimum 9 clocks) waitprd := clkfreq * resettime enable_p := Ena_p Dat_p := |< Ser_p En_P := |< Ena_p tagc2 := tagcount Dout := outputAddr tagno := tag_retAddr outtagAddr := rec_tagAddr IF (tagloc == 0) tagcheckAddr := @tags ELSE tagcheckAddr := tagloc deltaT := clkfreq / 2400 cogon := (cog := cognew(@entry, @Dout << 1)) > 0 RETURN cogon PUB stop '' Stop cogs if already in use. if cogon~ cogstop(cog) PUB disable '' Disable RFID reader by setting the /ENABLE pin high IF (cogon) OUTA[enable_p]~~ DIRA[enable_p]~~ PUB enable '' Enable RFID reader by setting IO as input (allowing PASM cog to control the /ENABLE pin) IF (cogon) DIRA[enable_p]~ DAT ORG entry MOV p1, PAR SHR p1, #1 RDWORD DoutAddr, p1 ' get address ADD p1, #2 RDWORD tagnoAddr, p1 ' get address ADD p1, #2 RDWORD TagAddr, p1 ' get address MOV TagAddr2, TagAddr ' save copy ADD p1, #2 RDWORD tagsAddr, p1 ' get address MOV tagsAddr2, tagsAddr ' save copy MOV DeltaT2, DeltaT SHR DeltaT2, #1 ADD DeltaT2, DeltaT SUB DeltaT2, #4 ' DeltaT2 := DeltaT / 2 + DeltaT - 4 MOV OUTA, En_p MOV DIRA, En_p BigLoop MOV strt, cnt ' start timer NEG strt, strt ' get negative (used in place of SUB later on) MOV time, cnt ADD time, pauselen WAITCNT time, #0 ' kind of debounce for RFID MOVD set_tag, #tag MOV TagAddr, TagAddr2 ' restore backup ReadRFID MOV idx1, #12 ' recieve 12 bytes (only middle 10 are used) MOV OUTA, #0 ' /enable RFID nextbyte WAITPEQ Dat_p, Dat_p ' high is initiated when RFIT starts waitrfid TJZ waitprd, #wait ' if not setup to reset, just wait TEST Dat_p, INA WC ' test Dat_p IF_NC JMP #moveon ' if it is low, go to rest of program MOV waitlen, cnt ' get now ADDS waitlen, strt ' difference of start and now CMP waitlen, waitprd WZ, WC ' if below one waitperiod, do another loop IF_B JMP #waitrfid ' do it again! WRBYTE zero, DoutAddr ' put 0 into the output value WRWORD zero, tagnoAddr ' put -1 (actually 65535 into the tag number (maybe you should use 0?) '' write a loop here to clear the tag string if you need it. wait WAITPNE Dat_p, Dat_p ' wait low moveon MOV time, cnt ' mark time MOV OUTA, En_p ' shutdown RFID as soon as possible MOV idx2, #8 ' get eight bits MOV val_out, #0 ' clear last value ADD time, DeltaT2 ' wait for middle of first bit (and all subsequent bits) getbit WAITCNT time, DeltaT ' wait for center of next bit TEST Dat_p, INA WC ' test pin RCL val_out, #1 ' shift value in (LSB first) DJNZ idx2, #getbit ' get next bit TJZ val_out, #BigLoop ' if false read (recieves 0 value), send back to beginning REV val_out, #24 ' reverse byte set_tag MOV tag, val_out WRBYTE val_out, TagAddr ' write to hub so parent object can see which RFID ADD TagAddr, #1 ADD set_tag, add1dest DJNZ idx1, #nextbyte ' go to next byte MOV TagAddr, TagAddr2 ' restore backup TJZ tagc2, #nocheck ' if no tags to check MOV tagsAddr, tagsAddr2 ' restore backup mainloop MOV tagc, tagc2 ' restore backup bloop MOVD cmp_bytes, #tag+1 MOV idx, #10 ' check 10 bytes check_byte RDBYTE tagsbyte, tagsAddr ' byte from DAT section below ADD tagsAddr, #1 cmp_bytes CMP tag, tagsbyte WZ, NR ADD cmp_bytes, add1dest IF_NZ JMP #skip_tag ' if one byte is wrong, skip the rest of tag DJNZ idx, #check_byte MOV p1, tagc2 ADD p1, #1 SUB p1, tagc ' calculate RFID tag number WRWORD p1, tagnoAddr RDBYTE p1, DoutAddr OR p1, #|< 0 ' RFID match WRBYTE p1, DoutAddr JMP #BigLoop ' on success, back to beginning skip_tag_ret DJNZ tagc, #bloop ' check next tag WRBYTE zero, tagnoAddr nocheck RDBYTE p1, DoutAddr OR p1, #|< 1 ' no matching RFID WRBYTE p1, DoutAddr JMP #BigLoop ' after failure, back to beginning skip_tag ADD tagsAddr, idx ' move to next tag address SUB tagsAddr, #1 JMP #skip_tag_ret deltaT LONG 0 pauselen LONG 0 add1dest LONG 1 << 9 zero LONG 0 tagc2 LONG 0 Dat_p LONG 0 En_p LONG 0 waitprd LONG 0 waitlen RES strt RES DeltaT2 RES tag RES 12 tagc RES tagsbyte RES DoutAddr RES tagnoAddr RES tagsAddr RES tagsAddr2 RES TagAddr RES TagAddr2 RES p1 RES idx RES idx1 RES idx2 RES time RES val_out RES FITCMP tag, lead_byte WZ ' make sure tag value has a valid first byte IF_Z CMP tag+11, end_byte WZ, NR ' make sure tag value has a valid last byte IF_NZ JMP #BigLoop ' if not valid, go back to beginning...before bloop, and: ...after waitprd, you get a valid RFID card test.I have had some errant RFID reads from due to interference or the card being on the edge of reception, so this will help (I'll be adding this and the debounce parameter to a soon to be released update on the OBEX).
Thanks a thousand. I'll sit down with this code and the textbook PASMchapter until I understand it all. But, I need to learn to walk before I fly, and I am still in the learning process wrt the COG mechanism and how it relates to the Main code. My background is from Pascal, some C, and digital Control System programming - all quite high level abstract languages - and a looong time ago. With ideas, logical flow, and program control I am fine, but the stuff specific to prop must be learned - which is part of the fun for sure. For me the prop's cog functionality is perfect because it allows me to put all the hard labor housekeeping code into 'independent lives', and concentrate on implementing my ideas in the Main code, keeping it uncluttered.
A bit off-topic, I guess.
Erlend