Funktion to HEX - Ausgabe führende Null

Hallo,
ich nutze folgende Funktion um mit die Hexwerte auszugeben.
Grund das ich diese Variante nutze und keine sprinntf sind die Größeneinsparungen beim Sketch.

void printHex(unsigned int numberToPrint) {
  if (numberToPrint >= 16)
    printHex(numberToPrint / 16);
  /* line is needed, no line - no output !!! */
  Serial.print("0123456789ABCDEF"[numberToPrint % 16]);
}

Nun würde ich gern das selbe Prinzip nutzen aber da brauche ich die führende Null mit in der Ausgabe.
Somit möchte ich anstatt
Bsp: F ein 0F erhalten.

Kann ich diese Funktion modifizieren, das ich dies hinbekommen?
Leider erhielt ich noch keine Erfolge.

MfG

Du musst erst mal checken, wieviele Hexzeichen raus kommen sollen und dann die fehlenden mit if (wert < ...) mit 0 ausgeben.
Oder Du gibst die erwartete Zeichenzahl als Parameter mit.

Gruß Tommy

weil ich heute das schon ähnlich gepostet habe:

void printHex(byte actual)             // helper function to print out hex values with pre-zero
{
  Serial.print(" 0x");
  if (actual < 0x10) Serial.print("0");
  Serial.print(actual, HEX);
}

ArduNewBee:
ich nutze folgende Funktion um mit die Hexwerte auszugeben.

void printHex(unsigned int numberToPrint) {

if (numberToPrint >= 16)
   printHex(numberToPrint / 16);
   Serial.print("0123456789ABCDEF"[numberToPrint % 16]);




Nun würde ich gern das selbe Prinzip nutzen aber da brauche ich die führende Null mit in der Ausgabe.

In welchem Bereich bewegst Du Dich denn? Arduino unsigned int wäre uint16_t somit 0-65535 dec bzw. FFFF hex.
Willst Du dann eine Ausgabe 0F 0F 0F 0F für 65535?

Hallo,

ich hätte da was im Angebot für unsigned. Die Abstufung ist im switch case beliebig einstellbar.
Auf signed/unsigned geändert.

/*
  Doc_Arduino - german Arduino Forum
  IDE 1.8.13
  avr-gcc 10.2.0
  Arduino Mega2560
  19.10.2020
  License: GNU GPLv3
  https://forum.arduino.cc/index.php?topic=709714.0
*/

void setup (void)
{
  Serial.begin(115200);
  Serial.println("\nStart");
  
  for (long i = -66666; i < 66666; i=i+11)
  {
    Serial.print(i); Serial.print('\t');
    drucken(i);
  }
}

void loop (void)
{

}

// ****** Funktionen ****** //

template <class T>
void printInteger(const T var)
{
  char buffer[11];
  if (var < 0) {
    ltoa(var, buffer, 16);       // negativ
  }
  else {
    ultoa(var, buffer, 16);      // positiv
  }

  const byte ZEICHEN = strlen(buffer);
  //Serial.print("Zeichen: "); Serial.println(ZEICHEN);

  byte LEERSTELLEN {0};

  switch (ZEICHEN)
  {
    case 0 ... 2: LEERSTELLEN = 2 - ZEICHEN; break;
    case 3 ... 4: LEERSTELLEN = 4 - ZEICHEN; break;
    case 5 ... 8: LEERSTELLEN = 8 - ZEICHEN; break;
    default: LEERSTELLEN = 8 - ZEICHEN; break;
  }

  //Serial.print("Leerstellen: "); Serial.println(LEERSTELLEN);
  for (byte i = 0; i < LEERSTELLEN; ++i)
  {
    Serial.print('0');
  }

  Serial.println(buffer);
}


void drucken(const int32_t var)
{
  printInteger(var);
}

void drucken(const uint32_t var)
{
  printInteger(var);
}

Was mir Sorgen macht ist, dass die Funktionsüberladung nicht funktioniert wenn ich es auf 16Bit limitiere aber der Testwert noch 16Bit groß ist. Folgendes erzeugt ein

Serial_HEX_Formatierung_02.ino: In function 'void setup()':
Serial_HEX_Formatierung_02:5:16: error: call of overloaded 'drucken(long int)' is ambiguous
    5 |   drucken(65535);
      |                ^
Serial_HEX_Formatierung_02.ino:27:6: note: candidate: 'void drucken(int16_t)'
   27 | void drucken(const int16_t var)
      |      ^~~~~~~
Serial_HEX_Formatierung_02.ino:32:6: note: candidate: 'void drucken(uint16_t)'
   32 | void drucken(const uint16_t var)
      |      ^~~~~~~
exit status 1
call of overloaded 'drucken(long int)' is ambiguous
void setup (void)
{
  Serial.begin(115200);
  Serial.println("\nStart");
  drucken(65535);
}

void loop (void)
{

}

// ****** Funktionen ****** //
template <class T>
void printInteger(const T var)
{
  char buffer[11];
  if (var < 0) {
    ltoa(var, buffer, 16);       // negativ
  }
  else {
    ultoa(var, buffer, 16);      // positiv
  }
  Serial.println(buffer);
}

void drucken(const int16_t var)
{
  printInteger(var);
}

void drucken(const uint16_t var)
{
  printInteger(var);
}

Was mir Sorgen macht ist, dass die Funktionsüberladung nicht funktioniert wenn ich es auf 16Bit limitiere aber der Testwert noch 16Bit groß ist. Folgendes erzeugt ein

Das kann ich dir sagen...

drucken(65535);

Die Zahl passt nicht in int16_t
Wird deswegen auf long erweitert.
Dafür gibts aber keine Funktion

drucken(65535U);

Dann passt sie in uint16_t
Und darum keine Meldung

Vorschlag aus meiner Wühlkiste:
(ganz andere Richtung)

class PrintStr: public String, public Print
{
  public:
  using String::String;
  using String::operator=;

  virtual size_t write(byte value) override
  {
    *this += (char)value;
    return 1;
  }

  void format(char fueller, size_t stellen)
  {
    size_t strLen = length();
    if(strLen >= stellen) return;
    char temp[strLen+1];
    toCharArray(temp, strLen+1);
    *this = "";
    for(unsigned i = 0; i <= stellen - strLen; i++) *this += fueller;
    *this += temp;  
  }
  
};

PrintStr test;

void setup() 
{
  Serial.begin(9600);
  test.reserve(30);

  test.print(32767,HEX);
  test.format('#',12); // String wird mit vorlaufenden Lattenkreuzen aufgefüllt
  Serial.println(test);

// rechtsbuendige Ausgabe
  unsigned long array[] {345,56565,3333,66,1,676766,3534342};
  for(auto &d:array)
  {
    test = d; 
    test.format(' ',12);
    Serial.println(test);
  }
  
}

void loop() 
{

}

Hallo,

Danke. Das alte Problem mit dem Literal und Standard int. Das dafür noch niemand eine automatische Erkennung erfunden hat ... :wink:

Übrigens, die Ursprungsfunktion würde auch arbeite mit führender Null wenn man einfach
das if ausbaut :-).
Allerdings für so etwas Rekursion zu nutzen ist schon gewagt :wink:

void printHex(unsigned int numberToPrint) {
  printHex(numberToPrint / 16); // vordere Ziffer < 16 ist die Division halt 0 wie gewollt
  /* line is needed, no line - no output !!! */
  Serial.print("0123456789ABCDEF"[numberToPrint % 16]);
}

Ulli

Allerdings für so etwas Rekursion zu nutzen ist schon gewagt :wink:

Insbesondere bei Rekursionen mit fehlerhafter (oder wie hier, ganz ohne) Abbruchbedingung. Da versagt nicht nur ein kleiner Arduino, das kann der größte Großrechner nicht.

Der Trick mit

  Serial.print("0123456789ABCDEF"[numberToPrint % 16]);

ist allerdings heftig. Danke dafür.
Kommt so allein auf 26 byte Flash weniger alsSerial.print(numberToPrint & 0x0F, HEX);durch den Verzicht auf die Standard print(int,HEX) Funktionalität. ( Für einen 328 kompiliert )

michael_x:
Der Trick mit

  Serial.print("0123456789ABCDEF"[numberToPrint % 16]);

ist allerdings heftig. Danke dafür.
Kommt so allein auf 26 byte Flash weniger

Sollte aber 17 Byte RAM fressen, insofern, nein danke.

Ein Byte RAM ist wertvoller als FLASH, daher geb ich dir Recht.

Außerdem muss man aufpassen, dass der superschlaue Compiler beim Test nicht
"0123456789ABCDEF"[numberToPrint % 16] schon zur CompileZeit ausrechnet (und den 17 Byte Text gar nicht speichert), weil er den Wert von numberToPrint beim Aufruf kennt.

//const char* digits {"0123456789ABCDEF"};
char digit(char x) {return x + ((x < 10)? '0': 'A'-10);} // wandelt einen Wert 0 .. 0x0F -> '0' .. 'F' 
void setup() {
  Serial.begin(9600);
  while (!Serial.available());
  byte numberToPrint = Serial.read();
  // Serial.write(digits[numberToPrint / 16]); 
  // Serial.write(digits[numberToPrint % 16]); // Size: 1522 / 202
//  if (numberToPrint < 16) Serial.write('0');
//  Serial.print(numberToPrint, HEX); // Size: 1594 / 184
  Serial.write(digit(numberToPrint/16));
  Serial.write(digit(numberToPrint%16));  // Size: 1512 / 184
}
void loop() { }

3 Varianten, ein Byte in Hex mit führender Null auszugeben

Bei der Gelegenheit: Warum ist /16 sogar besser als >>4 ?

schon zur CompileZeit ausrechnet

volatile int numberToPrint = 11;

Macht ihm das recht unmöglich.

Wobei sich zeigt, dass

Serial.println("0123456789ABCDEF"[numberToPrint % 16]);

Nicht die optimale Variante ist, was den Flash Verbrauch angeht.

Diese drei sind funktional gleichwertig benötigen aber nicht die Math Libc Funktionen.

  Serial.println("0123456789ABCDEF"[numberToPrint & 15]);
  Serial.println(*("0123456789ABCDEF"+(numberToPrint & 15)));
  Serial.println((numberToPrint & 15)["0123456789ABCDEF"]);

Ich habe mal eine Funktion zusammengezimmert, die von Byte bis long long alles mit führenden Nullen für die jeweilige Größe des Typs ausgibt.
Ein Problem habe ich damit allerdings noch. Print-Routinen sollen ja die Anzahl der ausgegebenen Zeichen zurückgeben. Das macht sie nicht. Intern wird die Anzahl richtig ermittelt.

Kann mir da jemand auf die Sprünge helfen?

// Printerweiterung führende 0 für Hexzahlen > 0

template <class T>  size_t printHex(Stream &out, const T &value, const boolean isLF = false) {
  size_t count = 0;
  uint8_t len = sizeof(value) * 2;
  uint8_t nibble;
  T aktVal;
  char puffer[9]; // max. longlong = 8 + '\0'
  memset(puffer,0,sizeof(puffer));
  // keine negativen Zahlen und max. 8 Byte groß
  if (value < 0 || len > 8) {
    return 0; 
  } 
  aktVal = value;
  
  for(int8_t i=len-1;i>=0;i--) {
    nibble = aktVal % 16;
    aktVal = aktVal / 16;
    puffer[i] = (nibble > 9) ? nibble-10+'A' : nibble+'0';
  } 
  count = out.print(puffer);
  if (isLF) count += out.println();
  // Serial.print("\nTest innen ");
  // Serial.println(count);
  return count;
}

void setup(){
byte b1 = 0x08, b2 = 0x4c;
int i1 = 0x82, i2 = 0x1234;
long l1 = 0x1234, l2 = 0x561234, l3 = 0x56781234;
long long ll = 0x8103502181035021;
size_t bytes; 
  Serial.begin(115200);
  Serial.println("Start");
  printHex(Serial,b1,true);
  printHex(Serial,b2,true);
  printHex(Serial,i1,true);
  printHex(Serial,i2,true);
  printHex(Serial,l1,true);
  printHex(Serial,l2,true);
  printHex(Serial,l3,true);
  bytes = printHex(Serial,ll,true);
  Serial.print("Bytes: ");
  Serial.println(bytes);
}

void loop() {}

Gruß Tommy

nur geraten: vieleicht auch den return Typ der Funktion als size_t definieren?

https://github.com/esp8266/Arduino/blob/master/cores/esp8266/Print.cpp

Danke, der war vorher size_t. Das int war nur ein Test. Ich korrigiere es.
Das Problem tritt auch bei size_t auf.

Gruß Tommy

Tommy56:
Ein Problem habe ich damit allerdings noch. Print-Routinen sollen ja die Anzahl der ausgegebenen Zeichen zurückgeben. Das macht sie nicht. Intern wird die Anzahl richtig ermittelt.

Kann mir da jemand auf die Sprünge helfen?

  // keine negativen Zahlen und max. 8 Byte groß

if (value < 0 || len > 8) {
    return 0;
  }

Das ist doch wieder so ein Ding, was irgendwo versteckt mit den Typen verbastelt ist.
Wenn Du den Codeschnipsel oben auskommentierst, bekommst Du 24 und ne Zeile vorher nur Kauderwelsch.

00:19:03.268 -> 0)(+-(*)0)(+-(*(%&⸮:
00:19:03.268 -> Bytes: 24

machst Du aus dem long long ein unsigned long long

00:20:03.747 -> 8103502181035021%&|
6
00:20:03.747 -> Bytes: 24

Da läuft irgendwas über.

Ahhh. Moment:
Wenn ich umstelle auf b1, kommt:

00:28:58.987 -> 08
00:28:58.987 -> Bytes: 4

Setz mal Deinen Puffer höher, dann bekomme ich bei LL

00:33:36.920 -> Bytes: 18

Aber immernoch Kauderwelsch.

Tommy56:
Print-Routinen sollen ja die Anzahl der ausgegebenen Zeichen zurückgeben. Das macht sie nicht. Intern wird die Anzahl richtig ermittelt.

Also wenn Du b1 durchlaufen lässt, bekommst Du 08 und 4 Bytes. Das ist richtig, denn Du hast 2 Bytes im Puffer und 1x CR + LF. Bei I1 sinds 6 Bytes.

len ist unglücklich, denn

 uint8_t len = sizeof(value) * 2;

weiter unten:

if (value < 0 || len > 8) {

Da ist len aber schon doppelt so gross...

Dazu passt dann auch nicht

char puffer[9];

Hier läuft es in jedem Fall über.

Ich hätte len mit sizeof(value) gefüttert und den Puffer mit
char puffer[len * 2];
erstellt.

Allerdings, während nach der Änderung von b2 in
b2 = 0xff
noch eine Ausgabe FF mit 4 bytes kommt, ist bei einer Änderung von
i2 = 0xffff
value -1

Das ist was für die Spezies unter Euch.
Ich könnte schwören ich las sowas ähnliches in den Beiträgen der letzten Tage.

return len; statt return count; wäre auch richtig.

Dein Problem scheint irgendwas mit size_t zu tun zu haben.
Nimmt man den Datentyp byte als Rückgabetyp, und liefert len zurück, scheint es immer zu funktionieren.

Da du ja gleich nach out ausgibst, brauchst du eigentlich den char puffer[9] garnicht, oder?

Und wie du das Ganze auf Arrays erweitern willst, wäre meine nächste Frage :slight_smile:

@michael_x: Die Werte des Arrays in einer Schleife mit der Funktion ausgeben?
@all: Danke für das erfolgreiche Entfernen der Tomaten. Jetzt geht es.

// Printerweiterung führende 0 für Hexzahlen > 0

template <class T>  size_t printHex(Stream &out, const T &value, const boolean isLF = false) {
  size_t count = 0;
  uint8_t len = sizeof(value) * 2;
  uint8_t nibble;
  T aktVal;
  char puffer[17]; // max. longlong = 8 * 2 + '\0'
  memset(puffer,0,sizeof(puffer));
  // keine negativen Zahlen und max. 8 Byte groß
  if (value < 0 || len > 16) {
    count =  out.print("Error");
    if (isLF) count += out.println();
    return count; 
  } 
  aktVal = value;
  
  for(int8_t i=len-1;i>=0;i--) {
    nibble = aktVal % 16;
    aktVal = aktVal / 16;
    puffer[i] = (nibble > 9) ? nibble-10+'A' : nibble+'0';
  } 
  count = out.print(puffer);
  if (isLF) count += out.println();
  return count;
}

void setup(){
uint8_t b1 = 0x08, b2 = 0x4c;
uint16_t i1 = 0x82, i2 = 0xffff;
uint32_t l1 = 0x1234, l2 = 0x561234, l3 = 0x56781234;
uint64_t ll = 0x8103502181035021;
size_t bytes; 
  Serial.begin(115200);
  Serial.println("Start");
  printHex(Serial,b1,true);
  printHex(Serial,b2,true);
  printHex(Serial,i1,true);
  printHex(Serial,i2,true);
  printHex(Serial,l1,true);
  printHex(Serial,l2,true);
  printHex(Serial,l3,true);
  bytes = printHex(Serial,ll,true);
  Serial.print("Bytes: ");
  Serial.println(bytes);
}

void loop() {}

Gruß Tommy

Tommy56:
@all: Danke für das erfolgreiche Entfernen der Tomaten. Jetzt geht es.

Die führende 0 ist weg. i2 gibt FFFF mit 6 Byte aus.
Schuld ist

for(int8_t i=len-1;i>=0;i--) {[/code}

edit: Ich muss mich noch an die neuen Automatiken gewöhnen...
Mein Vorschlag:

template <class T>  size_t printHex(Stream &out, const T &value, const boolean isLF = false) {
  size_t count = 0;
  // uint8_t len = sizeof(value);
  uint8_t nibble;
  T aktVal;
  char puffer[sizeof(value)*2+2]; // max: 2* laenge + 0 + '\0'
  memset(puffer,0,sizeof(puffer));
  // keine negativen Zahlen und max. 8 Byte groß
  if (value < 0 || sizeof(value) > 8) {
    count =  out.print("Error");
    if (isLF) count += out.println();
    return count;
  }
  aktVal = value;
 
  for(int8_t i=sizeof(value)*2-1;i>=0;i--) {
    nibble = aktVal % 16;
    aktVal = aktVal / 16;
    puffer[i] = (nibble > 9) ? nibble-10+'A' : nibble+'0';
  }
  count = out.print(puffer);
  if (isLF) count += out.println();
  return count;
}

void setup(){
uint8_t b1 = 0x08, b2 = 0x4c;
uint16_t i1 = 0x82, i2 = 0xffff;
uint32_t l1 = 0x1234, l2 = 0x561234, l3 = 0x56781234;
uint64_t ll = 0x8103502181035021;
size_t bytes;
  Serial.begin(115200);
  Serial.println("Start");
  printHex(Serial,b1,true);
  printHex(Serial,b2,true);
  printHex(Serial,i1,true);
  printHex(Serial,i2,true);
  printHex(Serial,l1,true);
  printHex(Serial,l2,true);
  printHex(Serial,l3,true);
  bytes = printHex(Serial,ll,true);
  Serial.print("Bytes: ");
  Serial.println(bytes);
}

void loop() {}