How to compress an hexadecimal number into a short string?

With a Arduino uno (Sparfun Redboard) and a SparkFun Simultaneous RFID Reader - M6E Nano. I try to scan 50 UHF RFID tags

During the scan, each RFID tag reports its TID (20 bits long) several times. Each RFID tag has a unique TID. The TID cannot be edited.

To verify that all TIDs of the 50 RFID tags have been received. I use an array.
Each received TID is checked in a for loop to see if it is already contained in the array, if not it will be added.

The Arduino uno has only 2 KB SDRAM, so I need compress a hexadecimal number into a short string.

The best thing I found on the net is a javscript solution. I ask if someone can help me translate it to c++. So I can use it on the Arduino.

Original solution comes from here.

Working codepen version is based on the first response from stackoverflow

i hope this is the best approach to implement my plan or has someone a better idea how i can check if all 50 RFID tags have been scanned.

function hexToString(hex) {
   var string = "";

   while (hex.length % 8 !== 0) {
      // we need it to be multiple of 8
      hex = "0" + hex;
   }

   for (var i = 0; i < hex.length; i += 8) {
      string += String.fromCharCode(
         parseInt(hex.substring(i, i + 4), 16),
         parseInt(hex.substring(i + 4, i + 8), 16)
      );
   }

   return string;
}

function stringToHex(string) {
   var hex = "";
   for (var i = 0; i < string.length; i++) {
      hex += ((i === 0 ? "" : "000") + string.charCodeAt(i).toString(16)).slice(
         -4
      ); // get character ascii code and convert to hex a string, adding necessary 0s
   }

   return hex.toUpperCase();
}

var str = hexToString("0140FC0000CC2FA0090B013C00055FFBFFFFDC50");
alert(str);
alert(stringToHex(str));

Post the code you've written to do this task and folks here will help you with it.

Why does it need to be a string?
Why not store it as binary?

The Uno also has 8192 bits of EEPROM.
That could prove useful.

20 bits requires 3 bytes, times 50 requires 150 bytes of SDRAM? You need to either post your code (preferably) or be more specific with the data types.

TolpuddleSartre:
Why does it need to be a string?
Why not store it as binary?

Its just a idea, give me a better way.
After the scan i try to get a console output with all scanned TID without duplicates

pekabo:
Its just a idea, give me a better way.

Store the code as binary

pekabo:
During the scan, each RFID tag reports its TID (20 bits long) several times. Each RFID tag has a unique TID. The TID cannot be edited.

The first thing you need to do is post the code that reads the TID from the tags and also post some examples of the values it gets from the tags.

There is probably a very simple answer to this problem.

...R

Enclosed the code to read the EPC (12 bytes) instead of TID (20 bytes) both has Hex values
I scanned just 5 RFID Tags for this answer.

for most of the code goes the credits to Sparkfun

#include <SoftwareSerial.h>

SoftwareSerial softSerial(2, 3);

#include "SparkFun_UHF_RFID_Reader.h"
RFID nano;

#define BUZZER1 10
#define BUZZER2 9

boolean tagDetected;
long lastSeen = 0;
int counter = 0;

void setup()
{
  Serial.begin(115200);

  pinMode(BUZZER1, OUTPUT);
  pinMode(BUZZER2, OUTPUT);

  digitalWrite(BUZZER2, LOW);

  while (!Serial);

  if (setupNano(38400) == false)
  {
    Serial.println(F("Das Modul hat nicht reagiert. Bitte überprüfen Sie die Anschlüsse."));
    while (1);
  }

  nano.setRegion(REGION_NORTHAMERICA);


  nano.setReadPower(2700);


  nano.startReading();

  Serial.println("Scannprozess läuft!");

  lowBeep();
  tagDetected = false;
}

void loop()
{
  byte myEPC[12];
  byte myEPClength;
  byte responseType = 0;

  if (nano.check() == true)
  {
    byte responseType = nano.parseResponse();

    if (responseType == RESPONSE_IS_TAGFOUND)
    {
      long timeStamp = nano.getTagTimestamp();
      byte tagEPCBytes = nano.getTagEPCBytes();

      Serial.print(F("RFID-Tag detektiert: "));
      Serial.println(counter++);


      Serial.print(F("RFID-Daten EPC["));
      for (byte x = 0 ; x < tagEPCBytes ; x++)
      {
        if (nano.msg[31 + x] < 0x10) Serial.print(F("0"));
        Serial.print(nano.msg[31 + x], HEX);
        Serial.print(F(" "));
      }
      Serial.print(F("]"));

      Serial.print(F("@ time["));
      Serial.print(timeStamp);
      Serial.print(F("]"));

      if (tagDetected == false)
      {
        tagDetected = true;
        highBeep();
      }
      else if (millis() - lastSeen > 250)
      {
        highBeep();
      }
      lastSeen = millis();

    }
  }

  if (tagDetected == true && (millis() - lastSeen) > 1000)
  {
    Serial.println(F("Kein RFID-Tag gefunden..."));

    tagDetected = false;
    lowBeep();
  }


  if (Serial.available())
  {
    nano.stopReading();

    Serial.read();
    Serial.println("Scannen angehalten. Drücken Sie die Taste, um fortzufahren.");
    while (!Serial.available());
    Serial.read();

    nano.startReading();
  }
}



boolean setupNano(long baudRate)
{
  nano.begin(softSerial);

  softSerial.begin(baudRate);
  while (!softSerial);

  while (softSerial.available()) softSerial.read();

  nano.getVersion();

  if (nano.msg[0] == ERROR_WRONG_OPCODE_RESPONSE)
  {

    nano.stopReading();

    Serial.println(F("Modul liest kontinuierlich. Modul soll kontinuierlich lesen beenden...."));

    delay(1500);
  }
  else
  {

    softSerial.begin(115200);

    nano.setBaud(baudRate);

    softSerial.begin(baudRate);
  }


  nano.getVersion();
  if (nano.msg[0] != ALL_GOOD) return (false);


  nano.setTagProtocol();

  nano.setAntennaPort();

  return (true);
}

void lowBeep()
{
  tone(BUZZER1, 130, 150);
}

void highBeep()
{
  tone(BUZZER1, 2093, 150);
}

serial monitor output

Modul liest kontinuierlich. Modul soll kontinuierlich lesen beenden....
Scannprozess läuft!
RFID-Tag detektiert: 0
RFID-Daten EPC[E2 00 00 17 22 0A 00 39 14 70 7F 9F ]@ time[300]RFID-Tag detektiert: 1
RFID-Daten EPC[E2 00 00 17 22 0A 00 37 14 70 7F 9E ]@ time[306]RFID-Tag detektiert: 2
RFID-Daten EPC[E2 00 00 17 22 0A 00 37 14 70 7F 9E ]@ time[348]RFID-Tag detektiert: 3
RFID-Daten EPC[E2 00 00 17 22 0A 00 39 14 70 7F 9F ]@ time[412]RFID-Tag detektiert: 4
RFID-Daten EPC[E2 00 00 17 22 0A 00 39 14 70 7F 9F ]@ time[420]RFID-Tag detektiert: 5
RFID-Daten EPC[E2 00 00 17 22 0A 00 38 14 70 7F 97 ]@ time[524]RFID-Tag detektiert: 6
RFID-Daten EPC[E2 00 00 17 22 0A 00 71 14 70 7F DF ]@ time[537]RFID-Tag detektiert: 7
RFID-Daten EPC[E2 00 00 17 22 0A 00 51 14 70 7F BD ]@ time[771]RFID-Tag detektiert: 8
RFID-Daten EPC[E2 00 00 17 22 0A 00 39 14 70 7F 9F ]@ time[775]RFID-Tag detektiert: 9
RFID-Daten EPC[E2 00 00 17 22 0A 00 71 14 70 7F DF ]@ time[771]RFID-Tag detektiert: 10
RFID-Daten EPC[E2 00 00 17 22 0A 00 38 14 70 7F 97 ]@ time[788]RFID-Tag detektiert: 11
RFID-Daten EPC[E2 00 00 17 22 0A 00 71 14 70 7F DF ]@ time[832]RFID-Tag detektiert: 12

As you will notice, some RFID-Tags will be output multiple times. There were only 5 different RFID-Tags in total.

What I try to do. I scan for RFID tags for about a minute. Afterwards try to get the following output. without duplicates

E2000017220A003814707F97
E2000017220A007114707FDF
E2000017220A003714707F9E
E2000017220A003914707F9F
E2000017220A005114707FBD

If only the last 10 hex digits ever change, then 5 bytes is sufficient to contain the unique part of each tag ID as a binary number, e.g. just this part:

5114707FBD

A hash function can be used to convert a long string into a short number. But this looks like only 11 bytes so it should not take a lot of space to store that.

This (from Reply #7)
E2000017220A003814707F97

should be viewed like this
E2 00 00 17 22 0A 00 38 14 70 7F 97

In other words it is just 12 bytes. Storing 50 sets of 12 bytes would only take up 600 bytes - have you not got enough space for that?

If not, then the suggestion by @jremington just to store the last 5 bytes for each tag (the green part) and just store the first 7 bytes (the blue part) once as they seem to be the same for all the tags.

I suspect the problem with seeing a solution to this is the fact that the demo code just prints the values in HEX format and does not actually store the bytes as bytes.

...R

Some "string" functions can analyze occurrence of "substring" within a string starting at selected position, even "from the end" toward the beginning of the string.
( i did not look on your implementation of "analyzing loop", but using substring analysis could be more fun).

Hex notation is just a convenient shorthand , it is still binary number occupying 4 bits, all you can really do is
to remove unwanted spaces before storing the string.

all you can really do is to remove unwanted spaces before storing the string

You can actually do much better: convert two ASCII hex digits to binary (0 to 255 in decimal notation), and store each pair in one byte rather than two bytes.

Please see replies #5, #8, #9 and #10 for more information.

jremington:
You can actually do much better: convert two ASCII hex digits to binary (0 to 255 in decimal notation), and store each pair in one byte rather than two bytes.

Please see replies #5, #8, #9 and #10 for more information.

Hex notation is just a convenient shorthand , it is still binary number occupying 4 bits,,,

232:
Hex notation is just a convenient shorthand , it is still binary number occupying 4 bits, all you can really do is
to remove unwanted spaces before storing the string.

There is NO string to be stored.

Just 12 (or 5) bytes.

...R

I tried follwing code changes. In the old code i converted it to HEX, now i get it raw.

old code
Serial.print(nano.msg[31 + x], HEX);

new code
Serial.print(nano.msg[31 + x])

for (byte x = 0 ; x < tagEPCBytes ; x++)
      { 
        Serial.print(nano.msg[31 + x]);
        Serial.print(F(" "));
      }
      Serial.print(F("]"));

I get this in Serial Monitor

226 0 0 23 34 10 0 55 20 112 127 158
226 0 0 23 34 10 0 57 20 112 127 159
226 0 0 23 34 10 0 56 20 112 127 151
226 0 0 23 34 10 0 81 20 112 127 189
226 0 0 23 34 10 0 113 20 112 127 223

How to store the Values in a 2 dimensional array[50][12] without duplicates and with a minimum on Bytes

Never mind about converting the bytes to something a human can read - which is the purpose of Serial.prin().

Just save the bytes into an array

byte myArray[5][12]; // creates 5 arrays each with 12 bytes (I hope)
for (byte x = 0 ; x < 12 ; x++)
{ 
         myArray[0][x] = nano.msg[31 + x]; // stores the values in the first of the 5 rows
}

...R

If I try

byte tagEPCBytes[50][12];
for (byte x = 0 ; x < tagEPCBytes ; x++)
      { 
        tagEPCBytes[counter][x] = nano.msg[31 + x];
        Serial.print(nano.msg[31 + x]);
        Serial.print(F(" "));
      }

I get the following errors

error: invalid types 'byte {aka unsigned char}[int]' for array subscript

         tagEPCBytes[counter][x] = nano.msg[31 + x];


                            ^


invalid types 'byte {aka unsigned char}[int]' for array subscript

I tried to change the type of tagEPCBytes[50][12]; to int or char. No one works.

pekabo:

for (byte x = 0 ; x < tagEPCBytes ; x++)

{
        tagEPCBytes[counter][x] = nano.msg[31 + x];
        Serial.print(nano.msg[31 + x]);
        Serial.print(F(" "));
      }

As you have not posted the complete program I can't tell how the variable counter is defined

...R

I wrote all the code in post #7.
From then on, only the code that was changed

int counter = 0;

after each scanned TAG the counter is increased by 1

Serial.println(counter++);

I think, I've got the wrong type. I am trying to store the an int in a byte. I try to cast it to byte

tagEPCBytes[(byte)counter][x] = nano.msg[31 + x];

this gives me following error

error: invalid types 'byte {aka unsigned char}[byte {aka unsigned char}]' for array subscript

         tagEPCBytes[(byte)counter][x] = nano.msg[31 + x];

now i am confused :astonished: