1

I'm working on an RFID system using a MFRC522 with this Library : https://github.com/miguelbalboa/rfid Here's the code I have right now:

int A=(mfrc522.uid.uidByte[0]);
int B=(mfrc522.uid.uidByte[1]);
int C=(mfrc522.uid.uidByte[2]);
int D=(mfrc522.uid.uidByte[3]);

Serial.println("UID is %x %x %x %x", A, B, C, D);

This results in the following output:

UID is 35 C5 5A 59

My issue is - how do I first reverse that UID : from 35 C5 5A 59 to 59 5A C5 35 - and second convert and print the corresponding decimal value of that reversed UID. In this case, it would be 1499120949.

I should mention that I need it as a string at the end to add it to the end of a POST command.

1

4 Answers 4

1

A standard way to convert individual bytes to a number is to use bit shifts and bitwise OR, as shown in chrisl's answer. When doing this, however, one has to take care not to overflow the bit shifts. On the AVR-based Arduinos, an int is 16 bits long. Shifting it by 24 bit positions to the left is guaranteed to overflow, which in C++ leads to undefined behavior.

The solution, as explained in Majenko's comment, is to cast to the final type before shifting, like this:

uint32_t uid = (uint32_t) mfrc522.uid.uidByte[0] << 0
             | (uint32_t) mfrc522.uid.uidByte[1] << 8
             | (uint32_t) mfrc522.uid.uidByte[2] << 16
             | (uint32_t) mfrc522.uid.uidByte[3] << 24;

Note that I got rid of the useless intermediate variables A through D.

Once you have have the numerical value, you may try to use it without explicitly converting to a string. Instead, you rely on the print() or println() methods to implicitly do the conversion only when needed, as in

Serial.print("UID = ");
Serial.println(uid);

or even, on a network connection:

client.print(uid);

This will save you the memory needed to store the string. If you really need to have the converted string in memory, which could happen if you need to provide a Content-Length before sending the data, you can do so with the function ultoa() from the avr-libc:

char uid_str[11];  // max = 10 digits + terminating NUL
ultoa(uid, uid_str, 10);  // convert to base 10

Please note that ltoa() is not appropriate here, as calling it will implicitly convert the value to a signed long, which has the potential to overflow and yield a negative result.

This “C string” – a NUL-terminated character array – can then be converted to a String object if needed, but I advice you against doing so if at all possible. String objects are more convenient to manipulate, but they are absolutely not memory friendly, which can quickly become an issue on most Arduinos.

0

Your UID has 4 bytes, which correspond to 32 bits. To get a decimal value from these bytes, you have to put them together in one variable. For this you can use an unsigned long. To put the bytes together, you can shift the values to the byte boundaries:

unsigned long dec_value = (D << 24) | (C << 16) | (B << 8) | A;

<< shifts the bits of the byte by the specified amount of bits to the left; The bitwise OR | puts the now not overlapping bytes together in one value. I have reversed the order of the bytes in this code, as you wrote, that this is necessary.

To convert this unsigned long to a string you can use the function ltoa():

char buf[50];
ltoa(dec_value, buf, 10);  // 10 is the base value not the size - look up ltoa for avr

If you want to use a String object (which should be avoided because of heap fragmentation), you can contruct the String directly from the long value:

String my_UID = String(dec_value);
5
  • Fantastic! I tried printing the result just to test with Serial.println("%s\n", my_UID); but I'm getting an error: no matching function for call to 'HardwareSerial::println(const char [4], String&)' Aug 2, 2018 at 21:29
  • Serial.println() does not work like sprintf(), so you can't use that syntax there. But you should be able to get the correct result by simply writing Serial.println(my_UID);. There is really no need to format the string again (also because println already adds a line break at the end, so that you can ditch the extra "\n").
    – chrisl
    Aug 2, 2018 at 21:32
  • Ah. Managed to get it to print - but I'm getting 4294952245 as my decimal answer. An online converter said that 595AC535 = 1499120949. I'm testing it using fixed hex bytes - pastebin.com/i2NPjv60 Aug 2, 2018 at 21:36
  • 2
    You need to use "unsigned long" instead of "int" for your A,B,C and D - otherwise the shifting overflows.
    – Majenko
    Aug 2, 2018 at 21:40
  • 1
    dec_value is a poorly chosen variable name, as it implies there is something “decimal” about it. It's not decimal: it's just a number, internally stored in binary, just as any other numerical variable is stored in binary. Aug 3, 2018 at 8:44
0

I think this does what you need. You already have the data in an array so to reverse it you go backwards through the list and assemble the number. To fit 4 bytes into 32bits you need to multiply each byte by (256 * its position), which is what the left shift << does. So by placing the code in a small loop you get some compact code that should do the job.

int A=(mfrc522.uid.uidByte[0]);
int B=(mfrc522.uid.uidByte[1]);
int C=(mfrc522.uid.uidByte[2]);
int D=(mfrc522.uid.uidByte[3]);

Serial.println("UID is %x %x %x %x", A, B, C, D);
// ^ Your code
uint32_t reversed = 0;
for (int x = 3; x > -1; --x) // go backwards through the array
{  // Take the existing value and multiple by 256 (left shift by 8) then add the latest byte on.
   reversed = (reversed << 8) + mfrc522.uid.uidByte[x];
}
String decValue = String (reversed);  // Covert the number to a string
0

To add a bit to the whole hex and decimal story: Those are just representations of numerical values your computer/microprocessor does not really care about.

int x = 0
int y = 0b0
int z = 0x0

these are all the same value and internally stored as binary, just like any other number.

You specifically told the println function to display said number(s) in hexadecimal representation with the format specifier %x Serial.println("UID is %x %x %x %x", A, B, C, D); using %d or %i(for integer) will print out decimal values for each number.

Converting byte array to larger data type

If you actually want them to be reversed, you could simply cast the array variable (which is nothing but a pointer) to an uint32_t pointer:

uint32_t dec_value = *(uint32_t*)mfrc522.uid.uidByte

Note the *s We first cast the pointer to a pointer of a different type (uint32_t*) and then access it's value, by prepending another *, which is then stored in dec_value, equaling 1499120949 now.

This works because in C/++ data structures in memory are guarenteed to be in the order you specified. Therefore, you essentially tell the computer to 'see' these very 4byte (32bit) as a single value rather than a independent bytes.

Usually, when converting byte arrays to larger data types, you will find the values you receive are not what you would expect. This is because they are stored in a different order (depending on the machine) known as endianness

Here is a function that addresses this issue (similiar to the other answers)

uint32_t bytearray_to_uint32(unsigned char *buffer)
{
    uint32_t value = 0;

    value |= (uint32_t)buffer[0] << 24;    // cast to target type before 
    value |= (uint32_t)buffer[1] << 16;    // shifting to avoid overflow
    value |= (uint32_t)buffer[2] << 8;     // no parentheses needed due to operator precedence
    value |= buffer[3];

    return value;
}

Applying this function to your case would result in 902126169 (0x35c55a59) instead of the desired 1499120949 (0x595ac535).

Also check out ntohl when dealing with endianness still puzzles you.

4
  • 1. As already stated in Majenko's comment to chrisl's answer, and restated in my own answer, buffer[0] << 24 will overflow on most Arduinos. 2. ntohl() is not be available on most (maybe all) Arduinos. Aug 3, 2018 at 9:51
  • I referred to ntohl for educational purposes only. Shifting an 8 bit value by > 8 will not overflow unless the target is too small (WinAVR). Again, the function utilizing shifting is just included for educational purposes, as it is not suited for OP's application in it's given form.
    – mystery
    Aug 3, 2018 at 10:42
  • This may be just my personal opinion, but I don't think “educational purposes” excuses undefined behavior. Aug 3, 2018 at 12:06
  • Behavior depends on target architecture and used compiler. Any decent one will either cope or throw a shift-count-overflow instead.
    – mystery
    Aug 3, 2018 at 13:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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