10 years, 7 months ago.

Serial communication with RFID module: How do I implement a dynamic data buffer of unsigned char?

Greetings!

I'm implementing an UHF RFID reader using LPC1768 as the microcontroller.

My mbed communicates with a RFID module over serial, basically I can write data in the module registers and then I must start to read the serial to get the command response back.

There is a inventory command where the data size returned from the module can be bigger than 1024 bytes, but we don't have an exact number because this size will depend on the amount of tags read by the module, here is the open, write and read code:

RFID_RadioOpen

BOOL uart_serial::RFID_RadioOpen(const RadioPortInfo port, RadioHandle *handle, INT32U flags)
{
    PinName* pin_names = (PinName*) port;
    BufferedSerial* serial = new BufferedSerial(pin_names[0], pin_names[1]); // TX, RX
    serial->baud(115200);

    *handle = serial;
    return true;
}

RFID_RadioWrite

INT32U uart_serial::RFID_RadioWrite(RadioHandle handle, INT8U *data, INT32U nbytes)
{
    uartSerialDebug.traceOut("RFID_RadioWrite >>\n");
    BufferedSerial* serial = (BufferedSerial*) handle;

    int n;
    for (n = 0; n < nbytes; n++) {
        if(serial->writeable())
        {
            serial->putc(data[n]);
            uartSerialDebug.traceOut("RFID_RadioWrite - serial->putc  data[%d]= [%02lX]\n",n,data[n]);
        }
        else
        {
            uartSerialDebug.traceOut("RFID_RadioWrite << serial->writeable() == false  for n = [%d]\n",n);
            return n;
        }
    }
    uartSerialDebug.traceOut("RFID_RadioWrite << n = [%d]\n",n);
    return n;
}

RFID_RadioRead

unsigned long uart_serial::RFID_RadioRead(RadioHandle handle, unsigned char *data, unsigned long nbytes, unsigned long msTimeout, bool block)
{
    uartSerialDebug.traceOut("RFID_RadioRead >> nbytes[%d], block[%d]\n",nbytes,block);
    BufferedSerial* serial = (BufferedSerial*) handle;
    unsigned long totalRead = 0;
    unsigned long n = 0;
    unsigned char *buffer = data;
    int bytesToRead = nbytes;
    int readBytes = 0;

    while(serial->readable()) {
        buffer[n] = serial->getc();
        n++;  
        totalRead++;     
    }
    
    uartSerialDebug.traceOut("RFID_RadioRead: \n");
        for (n = 0; n < totalRead; n++) {
            uartSerialDebug.traceOut("buffer[%d]: %02lX = %d \n",n, buffer[n], buffer[n]);
        }
    uartSerialDebug.traceOut("\n");
        
    uartSerialDebug.traceOut("RFID_RadioRead << totalRead[%d]\n",totalRead);
    return totalRead;
}

I'm a newbie on C/C++ and what I have realized is that I'm not reading the entire data returned.

Do you guys have any guess on what I'm doing wrong?

Also, how should I access the "data" outside the function for example to iterate over it?

Thanks!

1 Answer

10 years, 7 months ago.

Dynamic memory allocation is done using malloc(size), it returns a void* to the start of the memory block or null if there isn't enough memory. Call free(pointer) to release the memory when you are finished with it.

These functions are fairly slow to call so avoid using them excessively.

You access the memory from another function by passing that function the pointer to the buffer.

e.g.

unsigned long uart_serial::RFID_RadioRead(BufferedSerial *handle) {

const int bufferSize = 1024; 
char *buffer = (char *) malloc(sizeof(char) * bufferSize);  // allocate memory
if (buffer == NULL) // failed to allocate memory 
  return 0; 

unsigned int bytesRead = readRadio(handle, buffer, bufferSize);
unsigned long totaBytes = 0;

while (bytesRead  > 0)  {  // while there are bytes left
  processBuffer(buffer,bytesRead,totalBytes );    // output them.
  totalBytes += bytesRead;                                   // update the counter
  bytesRead = readRadio(handle, buffer, bufferSize); // get the next set of data                                      
}

free (buffer); // release the memory
return totaBytes ;
}

// read until nothing left or we hit a limit and then return the number of bytes 
unsigned int uart_serial::readRadio(BufferedSerial *handle, char *buffer, unsigned int maxSize) {

unsigned int byteCount = 0;
while (handle->readable() && (byteCount<maxSize)) {
  *buffer = handle->getc();
  buffer++;
}

return byteCount;
}

// output the data in the buffer
uart_serial::processBuffer(char *buffer, unsigned int size, unsigned long priorBytes) {

unsigned int count = 0;
while (count < size) {
  uartSerialDebug.traceOut("buffer[%d]: %02lX = %d \n", priorBytes+count, *buffer, *buffer)
  buffer++;
  count++;
}

}

One potential issue is if you need to process all the data about one tag at the same time then what do you do if you get the data for 4 1/2 tags in your buffer? You can display the first 4 but what about the half a tag of data?

The best way to deal with that is to copy the remaining half tag of data to the start of the buffer and then modify the buffer pointer and max size passed to the read function so that it adds the next byte just after the data you had left, that way you'll get all the data in one block. The simple way is that if each tag takes exactly the same number of bytes make the buffer a multiple of that size so you never get half a tag to start with. Faster and simpler but less flexible.

And one style note, never pass something like a BufferedSerial as a parameter to a function, always pass a pointer to it. Not only does it save a few lines of code it saves a massive amount of memory and CPU time. As a general rule unless it's a primitive built in data type (int, char etc...) always pass a pointer rather than the actual object.

Accepted Answer

Hi Andrew, thanks for the fast reply! I'll test it now!

Thanks

posted by Alexandre Santos 25 Mar 2014

One other potential gotcha, the BufferedSerial will have it's own buffer that it is putting data into as it arrives on the wire, you need to make sure that either that buffer is large enough for the whole reply or that you can pull the data out fast enough that it never overflows or you'll lose data. That buffer looks like it defaults to 256 bytes (set on line 73 of Buffer.h, http://mbed.org/users/sam_grove/code/Buffer/file/cd0a1f4c623f/Buffer.h) in the buffer library used by BufferedSerial. You may want to change that or look at something like http://mbed.org/users/jarkman/code/SerialBufferedDemo/ which lets you set the buffer size when you open the serial port.

If the serial port buffer is large enough for everything then you want to keep your own reading buffer fairly small (e.g. one tag) to keep the memory usage down.

If on the other hand you are reading data fast enough to keep up with the serial port then there is a risk that you'll think the buffer is empty when in reality you've simply caught up with the data coming in. Most things like this will have a packet header or some other way of telling how many tags to expect data for, keeping track of that is a good way to make sure you've got everything.

posted by Andy A 25 Mar 2014