Converting hex expressed as string into integers

Hi there

I have an 8 bit int called dimmerlevel which is defined thus;

dimmerlevel = 128 + rotaryvalue;

... where rotaryvalue is always less than or equal to 128.

I want to convert this 8 bit decimal into two 4 bit nibbles also expressed as decimals because I need to pass them to a function in that form. The function takes an array like this: byte lightsoff[] = {0,1,2,0,15,2,12,9,7,10}; and I want to insert my decimal versions of the two hex nibbles into the first two array values of the above.

So far I have converted it to a string as hex like so;

String dimmerstring = String(dimmerlevel, HEX);

For example let's say the rotary value is 140, then the string that gives would be "8c". I'd like to therefore get "8" converted into a decimal (i.e. 8 ) and insert it into the first value of the array mentioned above. Likewise I'd like to get "c" converted into the number 12 and insert it into the second value of the array.

So I thought all I need to do is select each character separately by addressing it in the string as an array, and convert it back to decimal. How hard could this be?

This post seemed helpful, but when I tried that in my case I simply had a whole load of different compile errors re types not being matched.

I won't post all the permutations and compile errors here, suffice to say I simply didn't manage to get it to work after a number of hours of head scratching in the area of matching value types; can someone suggest a solution?

Many thanks

Mat

can someone suggest a solution?

Dump String, use sscanf.

Thanks AWOL

I'm getting "invalid conversion from 'char' to 'const char*' when I do the following

int a;
sscanf(dimmerstring[0], "%x", a);
Serial.println(a);

This is presumably because I'm passing sscanf a character instead of a string, right?

I tried converting dimmerstring[0] into a string with a single character first;

dimmerstringfirst = dimmerstring[0];

... but that didn't work (same compile error as above)

How can I get my string to be a single character made out of a character of another string?

(Or am I asking the wrong question?)

int a;
sscanf(dimmerstring, "%x", &a);

AWOL

Appreciate your help so far.

I'm still getting a compile error with your code, to be honest I don't totally understand what the & does (does it reference a pointer?)

The compile error is

error: cannot convert 'String' to 'const char*' for argument '1' to 'int sscanf(const char*, const char*, ...)'

error: cannot convert 'String' to 'const char*' for argument '1' to 'int sscanf(const char*, const char*, ...)'

I thought I suggested dumping String?

I can't see your code.

I misunderstood what you meant about dumping String.

I thought sscanf took its first argument as a string, hence I can't dump String?

Here's what I have so far:

    dimmerlevel = 128 + rotarylevel;
    String dimmerstring = String(dimmerlevel, HEX);
    digitalWrite(13,LOW);
    delay(100);
    digitalWrite(13,HIGH);

    Serial.println(dimmerstring);

    int a;
    sscanf(dimmerstring, "%x", &a);

    Serial.println(a);

I thought sscanf took its first argument as a string,

It does - in C, a string is a char array, with a null terminator, so char string [10] = "123"; is effectively the same as char string [10] = {0x31, 0x32, 0x33, 0, 0, 0, 0, 0, 0, 0};

A String is a class, and in my opinion, should be avoided.

Given that you said that the 8-bit int named rotaryvalue may be equal to or less than 128

dimmerlevel = 128 + rotaryvalue;

what happens when rotaryvalue is 128? This is a bug waiting in the weeds to bite you in the butt down the road.

Also, while scanf() (and its friends) makes life easy, it is often an H-bomb-to-kill-an-ant approach. If memory's not an issue, it's great. However, if you're banging up against a memory limit, writing a simple function to do the conversion you're looking for might be an alternative approach.

@econjack Aye, I did notice that hence my actual code which is; dimmerlevel = 128 + ((newPosition-1) / 4);

Re memory, I hear your point. Memory currently isn't an issue. Either way though I still can't get it to work :( Will post back here again after trying some more things.

Give this a try:

void setup() {
  int val = 140;
  char buffer[20];
  
  Serial.begin(115200);

  decimalToHex(val, buffer);

  Serial.print("decimal 140 in hex is: ");
  Serial.println(buffer);
  
  Serial.print("First hex digit value: ");
  Serial.print(hexToDecimal(buffer[0]));
  Serial.print("    Second hex digit value: ");
  Serial.print(hexToDecimal(buffer[1]));
}

void loop() {
}

/*****
  Function that converts decimal value to a hex string
  
  Argument list:
    int n;        the decimal value to convert
    char hex[]    the buffer to hold the conversion
    
  Return value:
    void
    
  Caution: This function assumes the buffer is large enough to hold
  the output.

*****/

void decimalToHex(int n, char hex[]) 
{
  int i = 0, rem;
  while (n > 0)
  {
    rem = n % 16;
    switch (rem)
    {
      case 10:
        hex[i] = 'A';
        break;
      case 11:
        hex[i] = 'B';
        break;
      case 12:
        hex[i] = 'C';
        break;
      case 13:
        hex[i] = 'D';
        break;
      case 14:
        hex[i] = 'E';
        break;
      case 15:
        hex[i] = 'F';
        break;
      default:
        hex[i] = rem + '0';
        break;
    }
    ++i;
    n /= 16;
  }
  hex[i] = '\0';
  strrev(hex);   /* Reverse string */
}

/*****
  Function that converts hex digit to a decimal value
  
  Argument list:
    char hex       the hex digit to convert
    
  Return value:
    int
*****/
int hexToDecimal(char hex)   /* Function to convert hexadecimal to decimal. */
{
  hex = toupper(hex);
  int temp = hex - '0';
  
  if (temp < 10)
    return temp;
  else
    return  hex - 'A' + 10;
  
}
default:
        hex[i] = rem + '0';
        break;

Any reason for not doing a similar treatment for A-F? A switch/case just takes up so much space.

I thought of the hexToDecimal() code after I had already written the decimalToHex() code and was too lazy to rewrite it.

hazymat:
I want to convert this 8 bit decimal into two 4 bit nibbles

I feel some confusion here.

Suppose we have the decimal value 220. The 8-bit equivalent is 11011100. The two nibbles are 1101 and 1100.

What do you want to do with them.

…R

@Robin2 I wanted to take the 8 bit decimal 220 and express it as hex (0xDC) then take each of those hex numbers (which on their own bear no relation to the original number, but that’s the whole point - they are forming part of a message) and convert each hex number back into decimal, giving two integers 13 and 14.

@econjack & AWOL Thanks for the suggestions, I ended up iterating through the cases like that using a switch statement. I had thought this was a horribly inefficient way of going about things, but as econjack points out I suppose using a string function like sscanf is far sillier from a memory point of view.

Thanks

Will this do it? Just bust the number into nibbles and there you go.

'cause I think I remember that the scanf stuff is broken in the Arduino.

void setup() {
  Serial.begin(9600);
}

byte nibbleHi(byte inVal) {
  
  byte result;
  
  result = inVal & B11110000;  // Stomp the low nibble
  result = result >> 4;         // shift over the remainder bits..
  return result;               // Ship it off
}

byte nibbleLow(byte inVal) {
  
  byte result;
  
  result = inVal & B00001111;  // Stomp the high nibble
  return result;               // Ship it off 
}


void loop() {

  delay(15000);
  Serial.print("220 Hi nibble "); Serial.println(nibbleHi(220));
  Serial.print("220 low nibble "); Serial.println(nibbleLow(220));

}

-jim lee

hazymat: @Robin2 I wanted to take the 8 bit decimal 220 and express it as hex (0xDC) then take each of those hex numbers (which on their own bear no relation to the original number, but that's the whole point - they are forming part of a message) and convert each hex number back into decimal, giving two integers 13 and 14.

That's exactly what my example does and @jimLee has provded the code to achieve it.

Actually, the first part of @jimLee's code could be simplified to

byte nibbleHi(byte inVal) {
    
  return inVal >> 4;         // shift over the bits..

}

...R

I left it un-simple 'cause it was example type stuff.

-jim lee

Chaps

Many thanks for the great suggestions!

Mat

jimLee: I left it un-simple 'cause it was example type stuff.

Good idea.

...R