0

I am currently working on a RFID card reader. I need the reader to send the ID to a storage space.

What I am trying to achieve is a person swipes their card, enters their name on the serial monitor, and then have that data stored in a pair. The thing is that I do not know how many people will have cards. After that, I could call a person's name and it would show "Hello w, your ID number is xxx xxx xxx xxx xxx.

So I know there are no such things as "dynamic" variables. And I think I would need to pre-define a TON of arrays if I wanted to use them. So I am kinda at a stopping point. Is there no way to generate a new variable name and store data to that variable?

Also, I work with PLCs so I thought that indirect addressing might work, but I am unfamiliar on how to even do that in C++, only in ladder logic or if it would even work. Or how I would be able to call out an ID by name with that approach.

  • This would be an array of structs, plus storage space for all the names. – Ignacio Vazquez-Abrams Jun 3 '15 at 2:34
  • Array of structs? Could you please explain? I'm still only intermediate at programming. – TL140 Jun 3 '15 at 2:37
2

Use the EEPROM. This allows data to remain intact and unchanged even when power is disconnected.

As you say you're new to programming, and because I wrote a similar program just last week as an experiment for something bigger, I'll talk you through some of it. However, I'm not going to give you the whole code to copy-and-paste, I'm only going to give you information on dealing with EEPROM and structures. C-aficionados may grumble at some bits, and Arduinites at others - sic vita est. This is something of a crash-course so it's not gospel, but it should be more than enough to guide you. You may already know some or most of it; it's included nonetheless for completeness, for those who don't.


For those who don't know

In C, data relevant to an object can be stored in a structure, or struct. You can put all sorts of different types of data in a struct, like so:

struct personalProfile {
    char    firstName[8];
    char    lastName[8];
    uint    cardID;           // 'uint' is an unsigned int...
    bool    accessAllowed;
};

You can then use that structure to create a 'personal profile' for however many people you want:

// within your main program:

struct personalProfile Person1;
struct personalProfile Person2;

// Now you can directly access individual aspects as so:

Person1.firstName = "John";
Person1.lastName = "Brown";
Person1.accessAllowed = TRUE;

Person2.firstName = "Dodgy";
Person2.lastName = "Dave";
Person2.accessAllowed = NOT_A_HOPE_IN_HELL;

You can use the members of a structure in exactly the same way you'd use that member type if it wasn't part of a structure. Nice and easy.

The best way to store multiple instances of a particular variable type is in an array:

struct personalProfile listOfPeople[5];

You now have an array of five (blank) structs, which can be accessed by listOfPeople[n].firstName = Potato;, where 'n' is between 0 and the number-of-entries-minus-one. So far so potato.

An array is pretty basic. It's present in all sorts of programming languages because it's inherently essential to almost any program, but the trouble is that it's static. Using the code above, once the array has been created with five structs in it you can't add or remove any. One solution is to create a massive array, and set all the values in each struct to zero, or some other 'empty' identifier, so you're always going to have enough entries (up to a point). However, that's lazy, time inefficient (you've got to set aaaaalll the individual elements to your 'empty' signifier), resource inefficient (look at all that unused space!), and the data only lasts while the program is running. As soon as you cut the power to the Arduino, the information is lost.

Next comes containers. These are part of the Standard Template Library (STL) in C++. They're great for creating and managing dynamic-length arrays. There are different types for different purposes (see the link), but they don't exist in C (at least as far as this post is concerned) and they're not available as part of the Arduino IDE without installing a bunch of libraries or some other intervention. Therefore you've got to make your own version. It's actually pretty easy...


EEPROM basics

The EEPROM library that comes with Arduino is of course essential for this. It doesn't have many functions, and the ones that exist are fantastically easy to understand. EEPROM, for those who don't know, is non-volatile memory. That means that when you store data it stays there forever, even if you remove the power. The only way to get rid of the data is to write over it. The downside of EEPROM is that it has limited amount of times it can be written to - 100,000 times per bit typically so you don't need to start keeping count, it just means you shouldn't be using it as regular program memory. Reading data from EEPROM isn't limited in the same manner.

Using the function EEPROM.put(address, data) you can put ANY data type - an int, char or - you guessed it - a struct into the EEPROM, starting at the byte specified by address. Of course there has to be enough space so you can't put an int, which takes up more than 1-byte of space, into the last address without the function failing. (Or can you? I haven't tried it yet because I'd never do it in practice.)

So you've created your structure, and made sure that all it's members are a fixed size. That means the structure has a fixed size. If you know that size, you can start putting them into the EEPROM.

// within main program

struct personalProfile Person1;
struct personalProfile Person2;

// fill in the details for Person 1...
// fill in the details for Person 2...

// ...then put Person1 in the EEPROM at address 0.
EEPROM.put(0, Person1);

// Done. Now put in Person2. But where?
// You need to make sure you put them AFTER Person1.

int AddressJump = sizeof(personalProfile);

// AddressJump now equals how many bytes are need to store one person.

EEPROM.put(AddressJump, Person2);

Simple as that.

If you create a Person3, you're going to need to put them sizeof(personalProfile) bytes AFTER Person2, i.e. EEPROM.put(AddressJump*2, Person3);. So, you add PersonN to address AddressJump * (N-1).

Retrieving data is the same principle, except you use EEPROM.get():

// provided you've used the above snippet somewhere to actually
// populate the EEPROM with something!

struct personalProfile personA;

EEPROM.get(0, personA);

Serial.print("Person A is called:");
Serial.println(personA.firstName);

Again, you're going to need sizeof(personalProfile) to read the second, third, or Nth person. If you 'send EEPROM.get() to the wrong address it will still populate the personalProfile instance with data, it will just be gobbledygook. Once you start putting people in the EEPROM you're going to need to keep track of things otherwise you'll end up lost. You need to manage the list to make sure you don't put one person on top of another (you could get in trouble for that) and you don't want to get someone that isn't there.


List management

This is where I leave you, for now. When you have digested the information above, and you want to continue, I'll carry on.

Your first task is to determine what sort of array you want. Inevitably it's going to be a Linked List. This is an array of structs, where each struct element in the list contains the memory address of the following element, thus the list is linked. You need to consider:

  1. How the array will be linked (if at all);
  2. Whether or not the array is space-critical (does it have to be as small as possible);
  3. Where the array is going to start;
  4. How you're going to keep track of how many people are in the array.

@TL140 I await your response, good luck!

  • WOW! a lot of info here. Love it. I have dealt with class/objects in JS and Python, but never knew about Structs in C. The only thing that I am fairly concern about is would I have to create each object by hand? because that is what I am trying to get around. I understand the storing part of them and pushing them to the EEPROM, and indexing them to reference, but it is more on the creation part that I am getting hung up on. Such as sally scanning her RFID card, enter her name, the objects are then automatically created under that class, and stored after that. So far, it's looking like... – TL140 Jun 4 '15 at 5:38
  • I will have to use arrays with just the raw data and indexing. which I do not mind. But thank you for the tutorial on Structs, and the references. Also, Why can the EEPROM only be used 10k times? – TL140 Jun 4 '15 at 5:40
  • @TL140 As part of your 'list management' you write a function for adding new entries. When it begins you create a new structure struct personalProfile NewEntry then fill in the details how you like, i.e. the name fields are typed in by Sally herself and the card details are automatically entered from the RFID reader. Call EEPROM.put() which copies the data from NewEntry, byte for byte, into the EEPROM. When your add-new-entries function finishes, NewEntry falls out of scope (it gets forgotten about, rather then strictly deleted), but the version in the EEPROM remains forever. – CharlieHanson Jun 4 '15 at 12:55
  • And as to why EEPROM has limited write-cycles: Google it! It's related to the way that data is stored so that its integrity remains once you remove power to it. As with everything, it's a trade-off between features. – CharlieHanson Jun 4 '15 at 13:02
  • Awesome! I think im catching on little by little. Still foggy on structs but they are a new concept for a new language so I'll just need to practice. I'll probably use a big array to simulate the EEPROM since I dont want to ruin my board's. I'm guessing I should also store a pointer in the EEPROM to reference my list? or is there a way i can read my EEPROM and see just how much space is already stored in it, then use that? Because, wouldn't updating that counter every time a card was read ruin my EEPROM pretty fast? – TL140 Jun 5 '15 at 0:51
1

The "key" is creating a means to access a table of IDs that you can search through for a given name or a given ID number. There are various ways of doing this and if you are unclear on these that can be discussed in due course. But, to start, I'll assume that making and searching some form of table or array is not a problem for you.

You obviously MUST have enough storage space for the maximum number of simultaneous IDS that you wish to store at any time. Call this "IDCount".

You can allocate IDCount ID storage locations by whatever means suits you.
In unused locations you store a dummy or null ID number.
eg ID 000 000 000 000 000 is probably invalid so can be used as an 'empty' indicator.

To add a new ID either search through your table of IDs for an empty location and store the new ID there. To find a given ID match you search the table for the ID and , if it exists, retrieve the matching name.


"Improvements":

To reduce access times when the potential entries are large you can store all entries "at the bottom" and have a pointer to the 1st free location. New entries are added "at the end". If an entry is removed, all entries above it are moved down by one. eg if entry N is removed:
for (entry >= N and entry < end_pointer)
...move entry N + 1 into location N
...and step N by 1.

Another method is to use a "linked list" where each entry has a pointer to the next entry in the list. New entries take an address from the empty_locations list and add their address to the used_locations list. Removed entries do the opposite. This can be fast and effective but also mind blowing for beginners and, when it goes wrong it can go VERY wrong. Best avoided initially.

The simple "search for 1st empty entry" method will usually work very well.

  • So breaking it down, I basically have to create an array, give it a definite storage value, use a for loop to fill everything with a null value, and then fill in accordingly using a checker to see if the data is != said null value? Seems fairly simple. Now what about the pairs of data? Should I nest the array? how would I go about using a lookup in a multi dimensional array? – TL140 Jun 3 '15 at 4:04
  • @TL140 You can use a two dimensional array or if it suits use two arrays of the same size (number of entries)) but one dimensioned for names and the other for IDs as required. Whatever position you read or write from in the ID array, you can read and write from "blindly" in the name array as they are 2 parts of the same entry. – Russell McMahon Jun 3 '15 at 12:34

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.