0

Hi SE (and Majenko in particular, if I'm lucky) - I have a setup with an SD module (on SPI) and an Elechouse PN532 NFC reader (on I2C). I would like to use a master tag to enter MASTER MODE [sic] to add tags if they are not present in a file on my SD card, or remove tags which are present in the same file. I am currently checking tag UID against a hardcoded master UID, and it works well enough for now. What trips me up is making the tag database in the most arduino-friendly way. How would you advise me to do this in the most spartan way? And when removing existing tags from the database, how would you suggest I update it, to remove unwanted UIDs? I am using a flat text database now, but it could be a binary file - I am just puzzled by what is the best method to remove an entry(line) from a file using SD library.

  • 1
    I an considering using EEPROM instead, if it can have a decent lifespan (I will only rarely add or remove tags), but it would be convenient to have the UIDs in a text file on microsd. – user400344 Aug 14 '16 at 14:46
  • 1
    If the list isn't too large you could just read all the entries into memory, modify the list, and write back the modified list. Alternatively you could overwrite the entry with zeros instead of actually removing it. – Gerben Aug 14 '16 at 15:11
3

You got me ;)

Since a UID is a fixed size you don't need to think in "lines", only in bytes.

The most efficient way of storing the UIDs is as pure bytes. I am not familiar with that specific card and reader combination, but I will assume as an example an 8 byte UID (though this all holds true for any other length you care to choose).

You treat your file on SD card like a kind of database with a number of records in it. Each record is the exact same size (8 bytes in this case) . To read or write a specific record you just "seek" to the record number multiplied by the record size - so multiples of 8 in this example.

myFile.seek(recordNumber * 8);

If .seek() returns false then you have reached the end of the file.

So to find a tag you can:

myFile.seek(0); // Start at the beginning of the file
int recno = 0;
char foundUID[8];
bool found = false;
while (myFile.read(foundUID, 8)) {
    if (strncmp(foundUID, myUID, 8) == 0) { // 8 bytes match
        found = true;
        break;
    }
    recno++;
}

At the end of that found will be true if there is a match, and recno will be set to the record number. Now the magic begins...

If it has been found and you want to delete it, think again. That's a very wasteful idea. Instead you just want to "invalidate" it - that is, replace the UID in the database with something the UID can never be - all 0xFF is a good choice. Simply:

myFile.seek(recno * 8);
for (int i = 0; i < 8; i++) {
    myFile.write(0xFF);
}

Now the tag has gone from the database. You may want to add a filter at the card reading phase to stop anyone using a card that has a UID of all 0xFF, of course.

Now for adding a new card... This is where the fun starts. You could just add 8 bytes to the end of the file, but then the file will grow and grow and grow. Instead, you can first search through the file for a tag entry that is all 0xFF and replace that data with your new UID. Just the same as when you deleted it - seek to 8x the record number and write 8 bytes.

It might help to write a function to wrap the tag seeking routine so you can easily call it multiple times from multiple places. Returning the record number would be most useful - and you could return -1 if it's not found.

If you really want it to be plain text you can do the exact same thing but work with the ASCII representation of the UID bytes instead of the raw values - maybe as HEX. So your 8 byte records would become 18 bytes (16 bytes for 2 ASCII characters per UID byte, plus two for the \r\n line ending). Doing the conversion between bytes and text and back to bytes again though is wasteful if you don't really need it.


In case you're wondering why I recommended 0xFF as the deleted tag fill value and not 0x00 or anything else, it is simply because when you erase flash memory it is set to 0xFF. When you change the contents of a block on an SD card the block is read, it is modified in memory, the block is then erased (the whole lot set to 0xFF), then the modified block re-written back to the card. The act of erasing the block slightly damages it as it forces electrons through bits they don't really want to go through to make it all 0xFF. By setting your empty records to 0xFF, when it comes time to filling them again, the flash doesn't have to work as hard to erase that block since those bits are already in the erased state. Therefore the lifetime of the flash card should be extended slightly.

  • 1
    Excellent. I won't have time to do it now, but I'll use a binary db. I will read this answer many times to fully appreciate it. Thanks! – user400344 Aug 14 '16 at 17:27
  • You are a lesser deity - this is an excellent method! – user400344 Aug 29 '16 at 4:43
  • Majenko is next to godliness. – user2497 Aug 13 '17 at 16:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.