Go Down

Topic: Here's a SD card hex dump :) (Read 559 times) previous topic - next topic

neiklot

Mar 25, 2019, 04:59 pm Last Edit: Mar 27, 2019, 09:24 am by neiklot
Back in the day I worked extensively with hex dumps of print files, teasing out obscure format codes etc. I was fluent in Ascii and Ebcdic back then, and could see obscure formatting characters at 100 yards.

Just as a curiosity I wonder if anyone wrote an Arduino sketch to "dump" an SD file in the hex dump format shown below with the position in the file at the left, data as hex in the middle, and ascii translation at the right.

Like the below, which is just a random screen shot of one I found in Google, but they all look something along these lines:




Paul_KD7HB

Back in the day I worked extensively with hex dumps of print files, teasing out obscure format codes etc. I was fluent in Ascii and Ebcdic back then, and could see obscure formatting characters at 100 yards.

Just as a curiosity I wonder if anyone wrote an Arduino sketch to "dump" an SD file in the hex dump format shown below with the position in the file at the left, data as hex in the middle, and ascii translation at the right.

Like the below, which is just a random screen shot of one I found in Google, but they all look something along these lines:




I remember that! Also useful to see what other software people had included in their commercial software packages that they did not disclose to the buyer.

I think that would be an EXCELLENT Arduino project.

Paul

Lucario448

#2
Mar 26, 2019, 06:40 pm Last Edit: Mar 26, 2019, 06:43 pm by Lucario448
Just as a curiosity I wonder if anyone wrote an Arduino sketch to "dump" an SD file in the hex dump format shown below with the position in the file at the left, data as hex in the middle, and ascii translation at the right.
Well... is not that difficult. In fact, I'll give you an example of how I would do that:

Code: [Select]
// Assuming the file is already opened for reading.

const byte bytesPerRow = 16; // 4 as minimum

char hexVal[9];
byte buffer[bytesPerRow];

Serial.print(F("File: "));
Serial.println(file.name());
Serial.println();

// Writting header
Serial.print(F("Offset    "));
Serial.print(F("Hexadecimal ");

// Pad with spaces depending on the amount of bytes per row (it should align with the text below)
for (unsigned int spaces = (bytesPerRow - 4) * 3; spaces; spaces--)
  Serial.write(' ');

Serial.println(F(" ASCII"));


file.seek(0);
// Typing file's content
while (file.available()) {

  // Print offset
  sprintf(hexVal, "%08lX", file.position());
  Serial.print(hexVal);

  byte amount = file.read(buffer, bytesPerRow);

  // Print hex values
  for (byte i = 0; i < amount; i++) {
    sprintf(hexVal, "%02X ", buffer[i]);
    Serial.print(hexVal);
  }

  // Fill with spaces in case we couldn't fill an entire row (due to reaching the end of the file)
  for (unsigned int spaces = (bytesPerRow - amount) * 3; spaces; spaces--)
    Serial.write(' ');

  // Print ASCII values
  for (byte i = 0; i < amount; i++) {
    // Printable characters appear as they are, non-printable appear as a dot/period (.)
    Serial.write(buffer[i] > 31 && buffer[i] != 127 ? buffer[i] : '.');
  }

Serial.println(); // Next line
} // End of while loop
file.close();

Depending on the terminal console's column width (count), you may accomodate more or less bytes per row (4 minimum).

neiklot

Well... is not that difficult. In fact, I'll give you an example of how I would do that
Thanks- I'll give that a whirl.


neiklot

#4
Mar 27, 2019, 09:20 am Last Edit: Mar 27, 2019, 10:17 am by neiklot
Wow well that was simple.

Here is Lucario448's code from above (2 or 3 minor changes, all commented), wrapped up as a sketch:

Code: [Select]
//27 march 2019

// sd card hex dump
// based on Lucario448's code in #2 here https://forum.arduino.cc/index.php?topic=605875
//

#include <SPI.h>
#include <SD.h>
const int chipSelect = 4;
File file;

void setup()
{
  Serial.begin(9600);
  Serial.println(".... sd card hex dump ....");
  Serial.print("Created: ");
  Serial.print(__TIME__);
  Serial.print(", ");
  Serial.println(__DATE__);

  //turn off L13
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  openFileOnSD(); //remember to edit the file name into the function <<<<<<<<<<<<<<<<
  if (file) dumpTheOpenedFile();

} //setup

void loop()
{

} //loop

void openFileOnSD()
{
  Serial.print("Initializing SD card... ");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present ******************");
    // don't do anything more:
    return;
  }
  Serial.println("Card initialized.");

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  file = SD.open("blah001.dat"); //<<<<<<<<<<<<<<<<<<< set the name here <<<<<<<<<<<<<<<

  if (file)
  {
    Serial.println("File opened");
    delay(500);
  }
  // if the file isn't open, pop up an error:
  else
  {
    Serial.println("Error opening file... check the name ******************");
  }
}

void dumpTheOpenedFile()
{
  const byte bytesPerRow = 16; // 4 as minimum

  char hexVal[9];
  byte buffer[bytesPerRow];

  Serial.print(F("File: "));
  Serial.println(file.name());
  Serial.println();

  // Writting header
  Serial.print(F("Offset    "));
  Serial.print(F("Hexadecimal ")); //added )

  // Pad with spaces depending on the amount of bytes per row (it should align with the text below)
  for (unsigned int spaces = (bytesPerRow - 4) * 3; spaces; spaces--)
  {
    Serial.write(' '); //put inside a {}
  }

  Serial.println(F(" ASCII"));


  file.seek(0);
  // Typing file's content
  while (file.available())
  {

    // Print offset
    sprintf(hexVal, "%08lX", file.position());

    Serial.print(hexVal);
    Serial.print(" "); // to leave a space between offset and first hex character
    byte amount = file.read(buffer, bytesPerRow);

    // Print hex values
    for (byte i = 0; i < amount; i++)
    {
      sprintf(hexVal, "%02X ", buffer[i]);
      Serial.print(hexVal);
    }

    // Fill with spaces in case we couldn't fill an entire row (due to reaching the end of the file)
    for (unsigned int spaces = (bytesPerRow - amount) * 3; spaces; spaces--)
      Serial.write(' ');

    // Print ASCII values
    for (byte i = 0; i < amount; i++)
    {
      // Printable characters appear as they are, non-printable appear as a dot/period (.)
      Serial.write(buffer[i] > 31 && buffer[i] != 127 ? buffer[i] : '.');
    }

    Serial.println(); // Next line
  } // End of while loop
  file.close();
  Serial.println("                  **File closed**");
} //dumpTheOpenedFile



And here's some output:

Code: [Select]
.... sd card hex dump ....
Created: 10:29:14, Mar 27 2019
Initializing SD card... Card initialized.
File opened
File: blah001.dat

Offset    Hexadecimal                                      ASCII
00000000 31 38 39 2C 31 33 36 2C 31 35 32 2C 61 62 63 0D 189,136,152,abc.
00000010 0A 32 30 31 2C 31 35 32 2C 31 36 30 2C 78 79 7A .201,152,160,xyz
00000020 0D 0A 31 34 39 2C 31 31 35 2C 31 32 36 2C 65 74 ..149,115,126,et
00000030 63 0D 0A 31 35 34 2C 31 31 39 2C 31 32 34 0D 0A c..154,119,124..
00000040 31 33 33 2C 31 31 30 2C 31 31 35 0D 0A 31 31 38 133,110,115..118
00000050 2C 39 32 2C 39 36 0D 0A 31 32 37 2C 31 30 37 2C ,92,96..127,107,
00000060 31 31 30 0D 0A 39 30 2C 37 32 2C 37 39 0D 0A 0D 110..90,72,79...
00000070 0A                                              .
                  **File closed**


K++, for sure.



neiklot

#5
Mar 27, 2019, 10:03 am Last Edit: Mar 27, 2019, 10:19 am by neiklot
For those who might never have worked with a hex dump before, and are wondering why this is a big deal...

I've noticed a few threads lately where folk get "funny" characters on their monitors and especially LCDs. LCDs, for example, cannot honour the printer control codes such as carriage returns and line feeds, and so if you willy-nilly send print control characters to an LCD, it will try to print them. Then we get a "aaaargh my LCD is printing garbage".

A dump like shown here, allows you to print the file's raw hex characters in the middle of the screen and the Ascii representation on the right.

So if you look at an Ascii table such as below, you will see that the digits 1, 8, and 9 are represented as hex 31, 38 and 39. Look at the first line in the dump in the previous post, and just under the word "Hexadecimal" you will see 31 38 39 2C and so on. At the right of the dump you will see 189,136 etc. Check the Ascii table, and 2C is the , character.

What does it do with characters that don't print for humans, such as printer controls? Well scan to the end of the first line of hex characters, and you'll see 0D. It prints as a . character over on the far right after the abc. Next line starts 0A, which is also a . over at the right, before the 201.

Look in the Ascii table: hex 0D and 0A, halfway down the first column, are "CR" and "LF", that is "Carriage Return" and "Line Feed". They on a printer or serial monitor will not show, they will be acted upon. In the dump we get a . as a place holder. All unprintable characters, as well of course as the printable full stop / period Ascii 2E, will print as a . in the dump. On an LCD, which doesn't work in lines, it will print some or other representation of the CR, LF as "garbage". Same goes for other non-printables.



So... if you get some "funnies" or "garbage" when you print out a file from an SD card, use the above utility to dump it out and have a look at what's inside. Then, when you know what "funnies" are in there, handle them with "if" statements and discard them before the become "garbage" in your output.

But as the man says, Use it.... Don't use it ;)

Huge thanks to Lucario448.






Lucario448

I'm glad my suggestion was useful to you :)

However, although it's something minor I still will point out that I've made some little mistakes aligning header with data (to make it look more like a table), as seen here:
And here's some output:

Code: [Select]
.... sd card hex dump ....
Created: 10:29:14, Mar 27 2019
Initializing SD card... Card initialized.
File opened
File: blah001.dat

Offset    Hexadecimal                                      ASCII
00000000 31 38 39 2C 31 33 36 2C 31 35 32 2C 61 62 63 0D 189,136,152,abc.
00000010 0A 32 30 31 2C 31 35 32 2C 31 36 30 2C 78 79 7A .201,152,160,xyz
00000020 0D 0A 31 34 39 2C 31 31 35 2C 31 32 36 2C 65 74 ..149,115,126,et
00000030 63 0D 0A 31 35 34 2C 31 31 39 2C 31 32 34 0D 0A c..154,119,124..
00000040 31 33 33 2C 31 31 30 2C 31 31 35 0D 0A 31 31 38 133,110,115..118
00000050 2C 39 32 2C 39 36 0D 0A 31 32 37 2C 31 30 37 2C ,92,96..127,107,
00000060 31 31 30 0D 0A 39 30 2C 37 32 2C 37 39 0D 0A 0D 110..90,72,79...
00000070 0A                                              .
                  **File closed**

The header is OK, it's just that I forgot to place an extra space between sections (between "Offset", "Hexadecimal" and "ASCII"); so the corrected function should look like this:

Code: [Select]
void dumpTheOpenedFile()
{
  const byte bytesPerRow = 16; // 4 as minimum

  char hexVal[9];
  byte buffer[bytesPerRow];

  Serial.print(F("File: "));
  Serial.println(file.name());
  Serial.println();

  // Writting header
  Serial.print(F("Offset    "));
  Serial.print(F("Hexadecimal ")); //added )

  // Pad with spaces depending on the amount of bytes per row (it should align with the text below)
  for (unsigned int spaces = (bytesPerRow - 4) * 3; spaces; spaces--)
  {
    Serial.write(' '); //put inside a {}
  }

  Serial.println(F(" ASCII"));


  file.seek(0);
  // Typing file's content
  while (file.available())
  {

    // Print offset
    sprintf(hexVal, "%08lX", file.position());

    Serial.print(hexVal);
    Serial.print(F("  ")); // to leave two spaces between offset and first hex character
    byte amount = file.read(buffer, bytesPerRow);

    // Print hex values
    for (byte i = 0; i < amount; i++)
    {
      sprintf(hexVal, "%02X ", buffer[i]);
      Serial.print(hexVal);
    }

    // Fill with spaces in case we couldn't fill an entire row (due to reaching the end of the file)
    for (unsigned int spaces = (bytesPerRow - amount) * 3; spaces; spaces--)
      Serial.write(' ');

    Serial.write(' '); // Another extra space I've missed before
    // Print ASCII values
    for (byte i = 0; i < amount; i++)
    {
      // Printable characters appear as they are, non-printable appear as a dot/period (.)
      Serial.write(buffer[i] > 31 && buffer[i] != 127 ? buffer[i] : '.');
    }

    Serial.println(); // Next line
  } // End of while loop
  file.close();
  Serial.println("                  **File closed**");
}

And... whoops, I've never placed before any space between the offset and the first hex value.

Well... being honest I've never actually tested what I wrote, so that's why I overlooked those mistakes.



Nevertheless, I'm still glad I've made something useful, so... you're welcome! ;)

Go Up