Convert string sent over serial to array of hex numbers

Being new at C, I am looking for a way to send to the Arduino the following command via the serial console (note that the commands can be of varying length):

PROCESS 0000 0067 0000 000d 0060 0018 0030 0018 0030 0018 0030 0018 0030 0018 0018 0018 0030 0018 0018 0018 0030 0018 0018 0018 0018 0018 0018 0018 0018 03de

The Arduino should then recognize that a "PROCESS" command was sent, and feed the following string into an array of hex numbers:

uint16_t array_of_hex[] = 
    {0x00, 0x67, 0x00, 0x0d,
     0x60, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 
     0x30, 0x18, 0x18, 0x18, 0x30, 0x18, 0x18, 0x18, 
     0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
     0x18, 0x3de };

And hand it over to the process() function:

process(array_of_hex);

Then it should continue to listen on the serial line for further commands.

Can you help me please? In case you are wondering what I am trying to do, I would like to send the Arduino infrared remote codes in the industry standard Pronto Hex format via the serial line, and have the Arduino send them out via an attached IR LED.

The code I would like to use is available at

Hello,

Here is how I would do it basically:

First, store the whole serial message in a char array (from first character until a '\r' is found, for example).

Example code:

void loop()
{
  if ( Serial.available() > 0 ) 
  {
    static char input[64];
    static uint8_t i;
    char c = Serial.read();

    if ( c != '\r' && i < 64-1)
      input[i++] = c;

    else
    {
      input[i] = '\0';
      i = 0;
      
      // Here you have the char array "input" containing the whole Serial message (minus the last '\r')
      // So you can do whatever you want with it, for example:
      Serial.println( input );
    }
  }
}

Then, if the message starts by "PROCESS" (using strncmp function), in a while loop use the strchr function (to find position of the next ' ') and strtol function to convert the string (at the position returned by strchr) into a number, that will then be stored in the next slot of the array.

Use this website if you don't know functions parameters: avr-libc: Modules

Thanks guix. The strol function will take care that the string "03de" coming from the serial line is correctly stored as hex 0x3de = 990? I figure that I need to use something like this

uint16_t value= strtol ("03de",NULL,6);

Would that be correct?

Here is an interesting example, which I modified a bit:

/* strtol example */
#include <stdio.h>      /* printf */
#include <stdlib.h>     /* strtol */

int main ()
{
  char input[] = "0000 0067 0000 000d";
  char * pEnd;
  long int li1, li2, li3, li4;
  li1 = strtol (input,&pEnd,16);
  li2 = strtol (pEnd,&pEnd,16);
  li3 = strtol (pEnd,&pEnd,16);
  li4 = strtol (pEnd,NULL,0);
  printf ("The decimal equivalents are: %ld, %ld, %ld and %ld.\n", li1, li2, li3, li4);
  return 0;
}

How could I change this to be a loop rather than hardcoded li1, li2,...?

How could I change this to be a loop rather than hardcoded li1, li2,...?

Once you have the string, the strtok() function can be used, in a while loop to extract the tokens. Call strtol() on each token.

Note that guix's code assumes an array length of 64 which does not appear to be enough to hold all of your data.

Here is lil example of what I described:

~~http://codepad.org/kFkVJsm3~~

Better example, handles multiple spaces and only prints existing values:

http://codepad.org/99hKFScP

Still having trouble... this compiles on the Arduino, but doesn't give any output when it should print the members of the array. What am I doing wrong?

http://codepad.org/1ysav622

I think you forgot to increment j.

Well spotted, thanks. But it still doesn't work :0

http://codepad.org/jylhWIuX

Make sure that the Line Ending setting in the Serial monitor, is set to Carriage return :slight_smile:

And as PaulS suggested, your string is longer than 64 characters, so you must change the array size accordingly. If you try your ctual code (the one that you posted on codepad) with a string such as "PROCESS 0000 0067 0000 000d 0060 0018", it will work.

Change:

static char input[64];
static uint8_t i;
...
    if ( c != '\r' && i < 64-1)

to

static char input[512];
static uint16_t i;
...
    if ( c != '\r' && i < 512-1)

or better:

const uint16_t inputLenght = 512;
...

static char input[inputLenght];
static uint16_t i;
...
    if ( c != '\r' && i < inputLenght-1)

I tested your code with this modification, worked perfecly :slight_smile:

This works, but only exactly once. If I send the same string a second time over the serial line, it doesn't work anymore.

I must be missing something obvious...

Weird, because it's working for me (I tested with that sketch you posted) I can send same string many times, shorter strings, longer strings..never failed :S

(Once again make sure the Line Ending setting is set to Carriage Return, not Both NL and CR)

Awesome!

That was it indeed. I had "both"...

Also I notice that when the last character is a blank (" "), then it doesn't work. But that's not a problem if you know it :slight_smile: Thank you so much for your help.

NP, and you could make more robust code to handle these little problems if needed, just experiment :slight_smile:

In the meantime, I have added some code to actually send Pronto Hex IR codes.

I think with really long strings like

SEND 0000 006C 0022 0002 015B 00AD 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0041 0016 0016 0016 0016 0016 0041 0016 0016 0016 0041 0016 0041 0016 0016 0016 0016 0016 0016 0016 0041 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0041 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 0041 0016 0679 015B 0057 0016 0E6C

it crashes, even if I set the inputLength to something like 512. Using freeRam() I suspect I am running out of RAM. Idea: Can the routine be modified so that instead of reading the entire line into a string and processing the string, each chunk (up to the next space) is processed until everyting is in the array?

In your code, you forgot to replace

static uint8_t i;

with

static uint16_t i;

uint8_t will accept values of range 0 to 255. If you put a greater value in it, rollover happen so if you set i to 256 it's like i = 0, set i to 257, i = 1, etc... Try to understand what the code do. That "i" variable is used as an index of the input array. You have more than 255 characters in that long string, so when it reaches the 256th character, it starts filling the array from the beginning, so it's erasing the "PROCESS" (now "SEND") message from the array, and so the rest of the code isn't triggered :slight_smile:

Awesome!

This kind of stuff is really hard to spot for someone coming from the Python world... Too bad compilers can't give an error in these cases... Again, thanks for your spot-on help!

Too bad compilers can't give an error in these cases.

Actually, they can and do. Well, a warning, actually, since you may have actually considered what is happening, and decided that that is what you want to have happen.

For instance, calling analogWrite() with a value over 255 causes overflow, which the function author decided was exactly the correct behavior.

But, because the author did not explicitly handle the overflow with the use of casts, the warning that the compiler generates is shown every time the analogRead() function is used.

The Arduino team doesn't want you to see that, so they disable all warnings.