Go Down

Topic: Print hex with leading zeroes (Read 14949 times) previous topic - next topic

Mike Jackson

You can print hex using the serial library with Serial.print(data,HEX), but it does not include leading zeroes, so 0x01 will come out as 1, etc. This can get confusing if you print an array, so I put together some simple functions to include leading zeroes. In case they are useful to others, here they are:

Code: [Select]

/*
 PrintHex routines for Arduino: to print byte or word data in hex with
 leading zeroes.
 Copyright (C) 2010 Kairama Inc

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex with leading zeroes
{
       Serial.print("0x");
       for (int i=0; i<length; i++) {
         if (data[i]<0x10) {Serial.print("0");}
         Serial.print(data[i],HEX);
         Serial.print(" ");
       }
}

void PrintHex16(uint16_t *data, uint8_t length) // prints 16-bit data in hex with leading zeroes
{
       Serial.print("0x");
       for (int i=0; i<length; i++)
       {
         uint8_t MSB=byte(data[i]>>8);
         uint8_t LSB=byte(data[i]);
         
         if (MSB<0x10) {Serial.print("0");} Serial.print(MSB,HEX); Serial.print(" ");
         if (LSB<0x10) {Serial.print("0");} Serial.print(LSB,HEX); Serial.print(" ");
       }
}


Example of how to use them:

Code: [Select]
void setup()
{
       Serial.begin(9600);
       Serial.print("Setup: Examples of PrintHex usage\n\n");
}

void loop()
{
       uint8_t ByteData[5]={0x01, 0x0F, 0x10, 0x11, 0xFF};
       Serial.print("With uint8_t array:  "); PrintHex8(ByteData,5); Serial.print("\n");

       uint8_t ByteDatum=0x01;
       Serial.print("With uint8_t scalar: "); PrintHex8(&ByteDatum,1); Serial.print("\n");

       uint16_t Shorts[5]={0x0001, 0x00FF, 0x0100, 0xAAAA, 0xFFFF};
       Serial.print("With uint16_t array: "); PrintHex16(Shorts,5); Serial.print("\n");

       Serial.print("==========================================\n");
       delay(10000);
}


The output of which is:
Code: [Select]
Setup: Examples of PrintHex usage

With uint8_t array:  0x01 0F 10 11 FF
With uint8_t scalar: 0x01
With uint16_t array: 0x00 01 00 FF 01 00 AA AA FF FF
==========================================

bootsector

Hi there!

Thanks for the hint!

My suggestion would be doing something like:

Code: [Select]

void printHex(int num, int precision) {
     char tmp[16];
     char format[128];

     sprintf(format, "0x%%.%dX", precision);

     sprintf(tmp, format, num);
     Serial.print(tmp);
}

void loop()
{
     printHex(375, 8);
     for(;;);
}


Hope it helps!

Regards,

bootsector

Mike Jackson

sprintf is so much cleaner - I didn't know it was available! Thx a lot.

bootsector

np man!

Just be careful with the array limits. Maybe adding some size checks at the beginning of the function would help!

Regards,

bootsector

Mike Jackson

New code based on sprintf as suggested (but without any bounds checking :()...

Code: [Select]
/*
 PrintHex routines for Arduino: to print byte or word data in hex with
 leading zeroes.
 Copyright (C) 2010 Kairama Inc

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex with leading zeroes
{
     char tmp[16];
       for (int i=0; i<length; i++) {
         sprintf(tmp, "0x%.2X",data[i]);
         Serial.print(tmp); Serial.print(" ");
       }
}

void PrintHex16(uint16_t *data, uint8_t length) // prints 16-bit data in hex with leading zeroes
{
       char tmp[16];
       for (int i=0; i<length; i++)
       {
         sprintf(tmp, "0x%.4X",data[i]);
         Serial.print(tmp); Serial.print(" ");
       }
}


Use it the same way...
Code: [Select]
void setup()
{
       Serial.begin(9600);
       Serial.print("Examples of PrintHex usage\n\n");
}

void loop()
{
       uint8_t ByteData[5]={0x01, 0x0F, 0x10, 0x11, 0xFF};
       Serial.print("With uint8_t array:  "); PrintHex8(ByteData,5); Serial.print("\n");

       uint8_t ByteDatum=0x01;
       Serial.print("With uint8_t scalar: "); PrintHex8(&ByteDatum,1); Serial.print("\n");

       uint16_t Shorts[5]={0x0001, 0x00FF, 0x0100, 0xAAAA, 0xFFFF};
       Serial.print("With uint16_t array: "); PrintHex16(Shorts,5); Serial.print("\n");

       Serial.print("==========================================\n");
       delay(10000);
}


and get these results:
Code: [Select]
Examples of PrintHex usage

With uint8_t array:  0x01 0x0F 0x10 0x11 0xFF
With uint8_t scalar: 0x01
With uint16_t array: 0x0001 0x00FF 0x0100 0xAAAA 0xFFFF

Moore313

Thanks,

This helped out alot. I used it for this http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1263301069, and altered it a bit so that my MD5 hash would print out as a continuous string of Hex, as we're used to seeing it. Check it out.

Mestarismies


Robert Bares

For anyone not wishing to include sprintf, here is a micro-optimisation which will do the conversion directly. In my personal tests it runs (comparing both without output) approximately 15x faster, and since we avoid sprintf the resulting binary is almost 1.5kb lighter, leaving you with room for more important code. First block of code provides lower-case hex suitable for use with some applications requiring MD5 hashes (such as digest access authentication):

Code: [Select]
void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex
{
     char tmp[length*2+1];
     byte first;
     byte second;
     for (int i=0; i<length; i++) {
           first = (data[i] >> 4) & 0x0f;
           second = data[i] & 0x0f;
           // base for converting single digit numbers to ASCII is 48
           // base for 10-16 to become lower-case characters a-f is 87
           // note: difference is 39
           tmp[i*2] = first+48;
           tmp[i*2+1] = second+48;
           if (first > 9) tmp[i*2] += 39;
           if (second > 9) tmp[i*2+1] += 39;
     }
     tmp[length*2] = 0;
     Serial.println(tmp);
}


Adapting this to uint16 is trivial. If you want the original formatting seen in Mike Jacksons latest post then see the following modifications:

Code: [Select]
void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex
{
     char tmp[length*5+1];
     byte first;
     byte second;
     for (int i=0; i<length; i++) {
           first = (data[i] >> 4) & 0x0f;
           second = data[i] & 0x0f;
           // base for converting single digit numbers to ASCII is 48
           // base for 10-16 to become upper-case characters A-F is 55
           // note: difference is 7
           tmp[i*5] = 48; // add leading 0
           tmp[i*5+1] = 120; // add leading x
           tmp[i*5+2] = first+48;
           tmp[i*5+3] = second+48;
           tmp[i*5+4] = 32; // add trailing space
           if (first > 9) tmp[i*5+2] += 7;
           if (second > 9) tmp[i*5+3] += 7;
     }
     tmp[length*5] = 0;
     Serial.println(tmp);
}

note: I deleted my post from yesterday & reposted to include some new changes to the code

robtillaart

#8
Jan 11, 2011, 10:56 pm Last Edit: Jan 11, 2011, 11:19 pm by robtillaart Reason: 1
@Robert
15 times faster is very impressive, so I felt challenged and fabricated this optimized version based upon your code. Most was won by concentrating all actions per half byte. The footprint is a bit smaller (48bytes if i'm correct) and my measurements indicated ~25% off (measured without the actual printing). Thanks for the inspiration ;)

Code: [Select]
void PrintHex83(uint8_t *data, uint8_t length) // prints 8-bit data in hex
{
 char tmp[length*2+1];
 byte first ;
 int j=0;
 for (uint8_t i=0; i<length; i++)
 {
   first = (data[i] >> 4) | 48;
   if (first > 57) tmp[j] = first + (byte)39;
   else tmp[j] = first ;
   j++;

   first = (data[i] & 0x0F) | 48;
   if (first > 57) tmp[j] = first + (byte)39;
   else tmp[j] = first;
   j++;
 }
 tmp[length*2] = 0;
 // Serial.println(tmp);
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Robert Bares

Thanks robtillaart. Im running out of space so those 48 bytes might make all the difference :D . Just finished implementing digest access authentication for my webserver, between ethernet, MD5, and SD libs there isnt much room for control code.

robtillaart

#10
Jan 12, 2011, 03:12 pm Last Edit: Jan 12, 2011, 03:13 pm by robtillaart Reason: 1
Maybe other functions can be optimized too. If you have one that could be optimized just post it on the forum. Preferably together with a testapp that measures the speed. Something simple like:

Code: [Select]
void loop()
{
 long start = millis();
 for (int i=0; i< 10000; i++) PrintHex8(ar, 15);
 Serial.println(millis() - start);

 start = millis();
 for (int i=0; i< 10000; i++) PrintHex83(ar, 15);
 Serial.println(millis() - start);
 delay(3000);
}  
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up