Byte Array in String konvertieren

Hi!

Ich bin recht neu in der Arduino Welt und habe sehr wenig C++ Erfahrung. Was ich aktuell tun will, ist eine Verbindung zu einem alten (pre OBDII) Subaru Motorsteuergerät aufzubauen. Das Gerät hat einen RX/TX Kanal mit 5v Pegel und 1953er Baud rate.

Ich habe einen kleinen Beispielcode gefunden, welcher ein Kommando an das Steuergerät sendet und die Antwort abfängt:

bool ECU_GetROMID(byte * buffer) {
  char readCmd[4] ={0x78,0x00,0x00,0x00}; // Read Command
  char romidCmd[4]={0x00,0x46,0x48,0x49}; // Get RomId Command
  char romid[3]   ={0};

  ECU_Stop();

// Send read command to ECU...muss lt. Protokoll immer als erster Schritt gemacht werden
  Serial.write(readCmd[0]);
  Serial.write(readCmd[1]);
  Serial.write(readCmd[2]);
  Serial.write(readCmd[3]);
  
  
  int retries = 0;
  while (retries<8) {
      
    int nbytes = Serial.readBytes(romid,3);
    
//  If ECU response is complete => Exit    
    if ((nbytes == 3) &&(romid[0]!=0x00))
      break;
    
//  Send getRomId Command to ECU... schickt das Kommando 00 0F 0H 0I HEX an das Steuergerät
    Serial.write(romidCmd[0]);
    Serial.write(romidCmd[1]);
    Serial.write(romidCmd[2]);
    Serial.write(romidCmd[3]);
    ++retries;
  }
  
// Tell ECU to stop sending data 
  ECU_Stop();
  
// Return determined result
  buffer[0] = romid[0];
  buffer[1] = romid[1];
  buffer[2] = romid[2];
   
  // If max tries > 8 and ECU didnt response => Return false
  if (romid[0] == 0x00) {
      return false;
  }
    
  return true;
}

Es wird in HEX ein Kommando an das Steuergerät geschickt ({0x00,0x46,0x48,0x49}) und die ECU antwortet lt Protokoll auch in HEX.

Die obige Funktion liefert TRUE zurück, also scheint eine Antwort vorzuliegen. Was ich nicht verstehe ist, in welcher Form der Inhalt am Schluss in buffer vorliegt und wie ich diesen in einen String zur Ausgabe konvertieren kann.

Vielleicht kann mir an der Stelle jemand kurz weiterhelfen?

VG
Jens

j.w.: Was ich nicht verstehe ist, in welcher Form der Inhalt am Schluss in buffer vorliegt und wie ich diesen in einen String zur Ausgabe konvertieren kann.

Hallo Jens, wenn Serial mit dem Steuergerät verbunden ist, wohin willst Du etwas ausgeben?

Hi!

Ich habe ein kleines i2e LCD angeschlossen, darauf will ich die ermittelten Motorkenndaten später ausgeben. Eine Art Bordcomputer soll das Ergebnis sein. Nutze dazu die liquidcrystal_i2e.

LG

print()/println() kann Zahlen mit HEX formatiert ausgeben:
https://www.arduino.cc/en/Serial/Print

Ansonsten kann man auch sprintf() verwenden, damit kann man z.B. angeben ob man führenden Nullen möchte oder bei den Buchstaben Groß und Klein wählen

Mhhhh ich will ja eigentlich nicht auf Serial schreiben, bräuchte die Response eigentlich als Float in ner Variable. Man muss die Response mit einem definierten Faktor multiplizieren, um das tatsächliche Ergebnis, z.B. die Wassertemperatur, zu bekommen.

Sprintf schau ich mir mal an! :-)

... Was genau passiert denn in diesem Schritt?

buffer[0] = romid[0];

Da wird ein Char in ein byte geschoben. In dem Char müsste sich ein HEX wert stehen.. Macht das denn Sinn?

j.w.: Mhhhh ich will ja eigentlich nicht auf Serial schreiben

print() geht nicht nur mit Serial sondern mit allen Klassen die von Print abgeleitet sind. Dazu zählen auch die Standard LCD Libraries. Vielleicht haßt du was exotisches

bräuchte die Response eigentlich als Float in ner Variable

Wieso sagst du dann das du sie in einen String konvertieren willst? Das ist etwas völlig anderes

"Antwortet in HEX" ist übrigens nicht korrekt. Es werden drei Byte gesendet. Wie du das interpretierst steht dir völlig frei. Hex ist nur eine bestimmte Schreibweise. Man könnte das gleiche Byte auch Dezimal oder in ASCII interpretieren.

Du sagst du weißt nicht was die empfangenen Bytes genau darstellen. Dann kann man auch nicht sagen wie man daraus einen Float bastelt.

Da wird ein Char in ein byte geschoben. In dem Char müsste sich ein HEX wert stehen.. Macht das denn Sinn?

Nicht wirklich. Das Ergebnis wird erst mal in einem lokalen Array gespeichert und dann in das Array kopiert das als Parameter übergeben wurde. Man hatte das aber auch gleich da rein schreiben können.

Wie Serenifly schon schreibt, ist die Antwort der Steuerung einfach 3 Byte. Wie die zu interpretieren sind, dass kann man aus dem Programm nicht entnehmen. Das musst DU wissen, sonst kannst Du das nicht weiterverarbeiten. HEX ist keine Interpretation der Daten, sondern nur eine Schreibweise der Byte. Die von dir gepostete Funktion gibt die 3 Byte einfach an das aufrufende Programm weiter. Und nun?

Ah, verstehe. Ich wollte den zurückgelieferten Wert erst mal irgendwie sehen. Um die Umrechnung hätte ich mich dann später gekümmert, wenn er plausibel gewesen wäre. Daher die Anfrage nach Konvertierung in String. Verstehe ich es dann richtig, dass ich wissen muss, in welchen Zieldstentyp ich konvertieren will, da das zu unterschiedlichen Resultaten führt?

Die Infos zum Protokoll habe ich hier ( https://www.sites.google.com/site/ecumonitor/subaru-diagnose-protokoll ) gefunden. Da steht: "Das für uns Wichtige ist das Byte 3. Dort ist der Wert enthalten der unserer Batteriespannung entspricht. Mit dem Wert AA können wir aber noch keine Spannung erkennen, wir müssen es also umrechnen. Wir brauchen also einen Umrechnungsfaktor aus dem dann das passende Ergebnis wird und müssen unser Byte 3 von HEX in dezimal umrechnen und mit dem Faktor verrechnen: (HEX) AA umgerechnet ergibt 170 dezimal der Faktor für die Batteriespannung ist * 0,08 ergibt 170*0,08 = 13,6 Volt"

Dann muss ich irgendwie den Inahlt von romid[2], der in Hex Schreibweise vorliegt, in einen dec konvertieren?

ir brauchen also einen Umrechnungsfaktor aus dem dann das passende Ergebnis wird und müssen unser Byte 3 von HEX in dezimal umrechnen

Nein. Da muss nichts umgerechnet werden. Rein Binär ist Hex und Dezimal genau das gleiche. Wie man das darstellt ist nur für den Anwender relevant. Hex wird gerne verwendet weil man die genaue Bitfolge daraus leicht ablesen kann. So hat jede Hex-Ziffer 4 Bit. Aber den Prozessor interessiert das nicht. Ein Byte ist ein Byte.

Wenn du ein Byte mit einer Zahl multiplizieren willst dann mach das einfach

Das ist das gleiche:

float value = 0xAA * 0.08;
float value = 170 * 0.08;

Hallo, wenn man bei Dir die Antworten in letzter Zeit so mitliest, dann lernt man richtig etwas. Schönen Dank dafür. Gruß und Spaß Andreas

Serenifly: ... So hat jede Hex-Ziffer 4 Byte. ...

Nicht das alle noch verwirrt werden ;) . Ich denke da handelt es sich um einen Tippfehler, und es sind 4 Bit gemeint.

Ja. 4 Bit natürlich. Habe es inzwischen selbst gemerkt und ausgebessert :)

Hi,

super, es hat funktioniert!! Hier ein Bild vom LCD: Dropbox - 1 photo :slight_smile:

Allerdings ist es aktuell viiiel zu langsam. Bis ein Wert da ist, dauet es etwa 1 Sekunde. Ich will gerne 4 Werte je LCD Screen (es gibt einen Minitaster zum “Umblättern” um die nächsten vier Werte zu sehen) anzeigen. Ein Aktualisierungsdurchlauf für alle 4 Werte dauert jetzt 4 Sekunden. Das ist für bspw. die Wassertemperatur okay, aber bei der Drehzahl macht das natürlich keinen Sinn. Ein weiteres Problem ist, dass mein Minitaster zum Umblättern des Screens im Betrieb an der ECU nicht mehr richtig reagiert. Ich vermutete urspünglich, dass das an der niedrigen Baud Rate von 1953 liegt und, dass der Arduino permanent auf die Response von der ECU wartet und daher in dieser Zeit nicht auf den Minitaster reagiert.

Grundsätzlich wird ein Lesekommando an die ECU gesendet, welches aus 4 Byte besteht. Die ECU Antwortet mit einer 3 Byte Response (wovon nur das 3. interessant ist). Anschließend muss man der ECU noch ein 4 Byte Stop-Kommando senden, damit sie aufhört die Response zu senden. Diese Schritte müssen für alle 4 angezeigten Werte durchlaufen werden, um sie zu aktualisieren.

Kann ich das so rechnen oder bin ich auf dem Holzweg?
Request = 4byte * 8 = 32bit
Response = 3byte * 8 = 24bit
Stop = 4 byte * 8 = 32bit
=> 88 Bit je Wert

Dann müsste ich doch theoretisch gut 20 Werte je Sekunde bei einer Baud rate von 1953 aktualisieren können, ohne warten zu müssen.

Hat der Code irgendwelche überflüssigen Bremsen? Das Serial.flush() will ich raus nehmen. Habe gelesen, dass es nur den TX Buffer aufräumt und die Laufzeit um Faktor 4 erhöht.

bool EcuAdapter::readEcu(char address[4], unsigned char * value) {
    unsigned char response[3] = {0};
    int retries = 0;

    while (retries < 4) {

        int nbytes = Serial.readBytes(response, 3);

        //  If ECU response is complete => Exit    
        if ((nbytes == 3) &&(response[0] != 0x00))
            break;

        Serial.write(address[0]);
        Serial.write(address[1]);
        Serial.write(address[2]);
        Serial.write(address[3]);
        ++retries;
    }

    // Tell ECU to stop sending data 
    ECU_Stop();

    // If max tries > 4 and ECU didn't response => Return false
    if (response[0] == 0x00) {
        return false;
    }

    *value = response[2];
    return true;
}

void EcuAdapter::ECU_Stop() {

    // This is the "Stop command", which tells the ECU to stop sending data
    byte txbuf[4] = {0x12, 0x00, 0x00, 0x00};

    Serial.write(txbuf[0]);
    Serial.write(txbuf[1]);
    Serial.write(txbuf[2]);
    Serial.write(txbuf[3]);

    delay(5);//50

    Serial.flush();
}

Viele Grüße
Jens

Seriell ist üblicherweise mindestens 10 bit/byte, bei 1953 Bd (?) dauert ein Byte also mindestens 5.1 ms. Deine 11 Byte hin und her also mindestens 56 ms.

Serielle Übertragung heisst asynchron, weil der Sender das Stopbit bliebig lang machen darf: Startbit muss 1 bit lang sein, Stopbit muss mindestens 1 bit lang sein (bei 2 Stopbit ist 2 bit Länge das Minimum). Wann der Sender mit dem Startbit für ein Byte anfängt, ist frei. Stopbit und Trödelzeit sind nicht unterscheidbar. Ausserdem ist unklar, wie lange dein Steuergerät sich Zeit lässt mit der Antwort.

Für 4 Werte brauchst du also eigentlich deutlich weniger als 1 Sekunde reine Übertragungszeit, das sollte für eine LCD - Anzeige reichen. Wo es tatsächlich hängt, müsstest du mit { Oszi / LA / Zusatz-Code im Arduino } erforschen.

Wenn es erheblich schneller werden soll, müsstest du darüber nachdenken:

Anschließend muss man der ECU noch ein 4 Byte Stop-Kommando senden, damit sie aufhört die Response zu senden

Ohne Stop-Kommando kriegst du wohl nur 1 Wert, den aber deutlich häufiger.