Logic Daten an serielle Schnittstelle senden

Ich habe absichtlich den Sachverhalt so einfach wie möglich gehalten, damit nicht abgeschweift wird. Da es mir um eine Arduino Software Lösung auf einer MCU geht - das wozu das Forum da ist. Nicht um ein anderes BMS, nicht, damit andere User nach irgendwas suchen, weil das habe ich schon zu genüge getan.

@Rentner du liegst absolut richtig. Da ich leider selbst nicht fit genug in Sachen coding bin, aber bereit mich in Sachen einzulesen, habe ich die Frage hier gestellt, ob und wie meine Idee umgesetzt werden kann.
Wie tolerant der Empfänger bzgl. timing ist, müsste ich bei einem Versuch herausfinden.

@ec2021

Eine DIY Batterie soll dem Roller das orig. BMS vorgaukeln - statische Daten reichen schon.

Hallo
Dennoch, wenn du da nicht nur eine Simulation mit konstanten Daten benötigst , könnte es ja sein das z.B auch eine Prüfsumme in den Daten steckt. Zudem musst du wissen wo sich die Nutzdaten in welchem Format befinden. Um welche Nutzdaten handelt es sich überhaupt? In dem Fall kommst du also nicht darum herum dich mit den Daten-Inhalten und Datenformat zu beschäftigen.

Du hast doch die Daten mal mitgeschrieben. Hast du mal versucht die Bytes zumindest teilweise als ASCII Zeichen zu interpretieren ? Ergibt das eventuell sinnvolle Daten Werte.

Danke. An Prüfsumme habe ich auch schon gedacht, aber da ich ja den Inhalt eines Frames nicht verändern will (es sind SoC und Temperatur, vllt. noch mehr enthalten), würde sich die Prüfsumme ja nicht ändern, sondern könnte mit "simuliert" werden.
ASCII habe ich geschaut, es ist nichts sinnvolles + einige nicht darstellbare Zeichen, z.B. kommt oft 0x12 vor.

Bitrate 1920b/s ist ja schon ungewöhnlich und ziemlich klein. Denke das Protokoll wird sehr simpel sein.

Also wenn es konstant sein soll dann ist es Recht einfach
Du kannst für den ESP da interne filesystem nutzen littleFS.
Die Daten selbst kannst du mittels IDE als Datei darauf speichern

Mit dem Sketch liest du die erste Datei byteweise aus und gibst sie seriell aus bis Dateiende.Dann eine Pause . Nächste Datei . Usw bis alle Frames ausgegeben sind . Eventuell dann alles von vorne.
Ob das jetzt Sinn macht musst du entscheiden , aber es kann ja ein Anfang sein.

Klingt gut, probiere ich gerne aus.

Hättest du ein Sketch fürs Auslesen, wenn nicht, werde ich morgen mal suchen.

Hallo
Naja wenn du die Daten mit dem ESP erst mal auslesen willst ist das ja der umgekehrte Weg . Oder wir reden aneinander vorbei. Man muss dann nur richtig den Anfang erkennen.

Kannst du denn mit deinem logik Analysator keine Daten in eine Datei schreiben ?

Doch, mit dem Logik Analyzer kann ich auslesen, die Dateien sehen dann aber wie im ersten Beitrag aus. Entweder proprietäres Salea Format, oder CSV.

Vllt wäre es sinnvoll, jetzt wo ich die die bitrate kenne, gleich mit dem ESP den Stream auszulesen und wegzuspeichern.

Wenn man den Exporter des Async Serial Analyzers nimmt, das CSV nicht direkt mit Excel öffnet, sondern über Daten "aus Text/CSV" importiert und am Ende die zweite Spalte einfach in Notepad++ kopiert, kommt eine schöne Textdatei mit einem Wert pro Zeile raus.

Musst Du Dir nur noch überlegen, was Du mit den "Error" markierten Werten machst...

werte.txt (3,0 KB)
daten.xlsx.kein.txt (29,7 KB)

1 Like

Hallo
Na gut geht auch, wie gesagt es geht darum zu erkennen wann das Telegram anfängt.

Man könnte die Pause erkennen und dann das nächste Byte als Anfang eines Paketes nehmen oder halt die Startsequenz.
Pause erkennen ist recht einfach das macht man mit milies().

Da wird man ein bisschen probieren müssen.

Ich glaube jetzt ehr nicht das du was fertiges findest mit dem du eine bestimmte Anzahl an Frames auslesen kannst .

Na da kommt doch schon das richtige bei raus , jetzt noch in mehrere Dateien aufteilen und das war's dann doch schon

Yep.
Wenn er einfach wiederholt ausgeben will, braucht er nicht mal aufteilen. Ggf. Zeilen aus der Datei am Anfang und/oder Ende entfernen und gut ist.

Der Rest ist im LITTLEFS verfügbar: Datei öffnen, mit file.read() eine Zeile lesen, mit sscanf() in eine Variable lesen und dann auf eine der drei (ESP32) seriellen Schnittstellen pusten. Ob man die allerdings auf die krumme Baudrate und 7N1 einstellen kann, weiß ich nun wieder nicht. Aber vielleicht ist ein ESP sogar schnell genug, das bitweise auf einen Port zu schieben.

Der ESP hat doch so viel Speicher, warum dann noch erst in eine Datei.
Die Seqenzen einfach in ein 2 Dim Array und gut ist.

Wobei ich immer noch nicht verstehe, von wo nach wo die Daten gesendet werden (unidirektional) und wozu das gut sein soll. Manchmal hilft da auch ein Blockschaltbild um den Helfern zu helfen.

Hallo,
ich hatte die Variante mit den einzelnen Dateien im FS bevorzugt da sich damit das Timing für die Pausen zwischen den einzelnen Frames recht einfach realisieren lässt, falls es erforderlich ist. Zudem lassen sich die Daten an den nötigen Stellen mit einem Texteditor leicht trennen und z.B "1.txt", "2.txt" usw benennen. Dann die Dateien mittels IDE Tool auf den ESP laden. Das lässt sich dann im Prinzip mit einer Laufvariablen ausgeben und wenn es sein muss die Pause auch mit einem delay() machen. Der T0 scheint Anfänger zu sein, aber das sollte er auch hinbekommen, Datei öffnen und byteweise auslesen und das Byte serial ausgeben da gibt es Beispiele zu.

Die Frage wozu das Ganze gut sein soll, stelle ich mir nicht mehr, der T0 will darauf nicht eingehen, also lässt er es bleiben und bekommt einen einfachen Lösungsvorschlag.

Gruß Heinz

Siehe oben ...

Bisheriger Datenfluss:

  • Batteriesensoren -> BMS -> E-Roller-Elektronik

Gewünschter Datenfluss (ohne echte Messung von Werten der DIY-Batterie):

  • ESP32 --> E-Roller-Elektronik

So stellt sich der Plan des TO für mich zzgl. der folgenden Annahmen dar:

  • Das Protokoll wurde aus den Messdaten (ausreichend) korrekt ermittelt.
  • Es gibt nur eine unidirektionale Schnittstelle vom BMS zum E-Roller, Reaktionen auf Anfragen der E-Roller-Firmware sind nicht erforderlich.
  • Die gewonnenen Daten wiederholen sich alle 3 Sekunden und stellen die Firmware des E-Rollers zufrieden.

Wenn eine oder mehrere der o.a. Bedingungen nicht zutreffen, funktioniert es nicht.

Man kann es ja probieren.

Gruß
ec2021

1 Like

P.S.: Bin Deiner Meinung, diese Datenmenge lässt sich bei einem ESP32 locker im Code verarbeiten. Einlesen von SD Karte wäre nur sinnvoll, wenn man damit rechnet, dem ESP häufiger neue Daten übergeben zu müssen.

  • 1920 Bd und 7n1 ergeben maximal 240 Zeichen pro Sekunde
  • Bei einer 3 Sekunden Sequenz wären das 3 *240 Byte = 720 Zeichen

Unter der Annahme, dass

  • die Zeiten zwischen zwei Zeichen (annähernd) identisch sind,
  • also nur die als "framing" gekennzeichneten Bytes einem anderen Pausentiming unterliegen,

benötigt man nach der vorliegenden Tabelle maximal 10 bis 12 unsigned long Werte, dieses Sondertiming zu speichern (unsigned long Pausenarray[]).

Da die Sendedaten 7 Bit-Werte sind, kann man jetzt prima das hochwertige Bit (MSb) verwenden, um die framing-Bytes zu kennzeichnen (byte Sendearray[]);

Also:

  • sendeIndex = 0
  • pausenIndex = 0

LOOP

  • sendeArray(sendeIndex) ohne MSb senden
  • sendeArray(sendeIndex) auf gesetztes MSb prüfen:
    • nicht gesetzt:
      • Warte Standardpause
    • gesetzt:
      • Warte Pausenarray[pausenIndex]
      • pausenIndex++
  • sendeIndex++
  • wenn sendeIndex >= Anzahl zu sendender Bytes
    • sendeIndex = 0 und pausenIndex = 0 setzen
  • gehe zu LOOP

(Nach pausenIndex++ muss ein Überlauf nicht zwingend geprüft werden, wenn man bei der Vergabe der MSb keine Fehler gemacht hat ... :wink: )

Sollte kein Hexenwerk sein, wenn ich mich nicht täusche ...

Falls das Timing aller Zeichen tatsächlich kritisch ist, braucht es ggf. das 2D-Array oder ein Array of struct (byte Wert, unsigned long pause) ...

Gruß
ec2021

So, zum Abschluss meinerseits aufgrund Zeitmangels:

unsigned long pauseArray = {
433,500,107,365,498,486,499,93,
365,498,433,340,499,93,365,498
}

uint8_t sendArrayHx = {
0x52,0x1B,0x13,0x52,0x5A,0x12,0x52,0x53,
0x1A,0x1B,0x12,0x12,0x12,0x12,0x12,0x52,
0x12,0x12,0x12,0x12,0x12,0x52,0x5B,0x13,
0x80,0x52,0x1B,0x13,0x52,0x5A,0x1A,0x5B,
0x52,0x52,0x1A,0x12,0x1A,0x52,0x12,0x53,
0x12,0x1A,0x12,0x9A,0x00,0x52,0x1B,0x13,
0x52,0x5A,0x12,0x52,0x53,0x1A,0x1B,0x12,
0x12,0x12,0x12,0x1A,0x52,0x1B,0x13,0x5B,
0x5B,0x5B,0x53,0x5A,0x1B,0x80,0x52,0x1B,
0x13,0x52,0x5A,0x1A,0x13,0x5A,0x12,0x53,
0x1B,0x12,0x12,0x1A,0x13,0x1A,0x12,0x5B,
0x12,0x52,0x52,0x52,0x52,0x1A,0x1A,0x1B,
0x1A,0x13,0x1A,0x52,0x5A,0x52,0x52,0x13,
0x13,0x13,0x1A,0x52,0x1A,0x5A,0x1A,0x5A,
0x1B,0x5A,0x52,0x12,0x5B,0x52,0x52,0x13,
0x13,0x5A,0x13,0x53,0x1B,0x52,0x52,0x53,
0x1A,0x5A,0x52,0x13,0x1B,0x53,0x1B,0x5B,
0x92,0x52,0x1B,0x13,0x52,0x5A,0x1A,0x5B,
0x52,0x52,0x1A,0x12,0x1A,0x52,0x12,0x53,
0x12,0x1A,0x12,0x9A,0x00,0x52,0x1B,0x13,
0x52,0x5A,0x12,0x52,0x53,0x1A,0x1B,0x12,
0x12,0x12,0x12,0x1A,0x52,0x12,0x12,0x12,
0x12,0x12,0x1A,0x52,0x13,0x80,0x52,0x1B,
0x13,0x52,0x5A,0x1A,0x5B,0x52,0x52,0x1A,
0x12,0x1A,0x52,0x12,0x53,0x12,0x1A,0x12,
0x9A,0x00,0x52,0x1B,0x13,0x52,0x5A,0x12,
0x52,0x53,0x1A,0x1B,0x12,0x12,0x12,0x12,
0x1A,0x52,0x12,0x12,0x12,0x12,0x12,0x1A,
0x52,0x13,0x80,0x52,0x1B,0x13,0x52,0x5A,
0x1A,0x13,0x5A,0x12,0x1B,0x12,0x12,0x12,
0x1A,0x13,0x1A,0x12,0x5B,0x12,0x52,0x52,
0x53,0x52,0x1A,0x1A,0x1B,0x1A,0x13,0x1A,
0x52,0x5A,0x52,0x52,0x13,0x13,0x13,0x1A,
0x52,0x1A,0x5A,0x1A,0x5A,0x1B,0x5A,0x52,
0x12,0x5B,0x52,0x52,0x13,0x13,0x5A,0x13,
0x53,0x1B,0x52,0x52,0x53,0x1A,0x5A,0x52,
0x13,0x1B,0x53,0x1B,0x5B,0x93,0x52,0x1B,
0x13,0x52,0x5A,0x1A,0x5B,0x52,0x52,0x1A,
0x12,0x1A,0x52,0x12,0x53,0x12,0x1A,0x12,
0x9A,0x00,0x52,0x1B,0x13,0x52,0x5A,0x12,
0x52,0x53,0x1A,0x1B,0x12,0x12,0x12,0x12,
0x1A,0x52,0x12,0x12,0x12,0x12,0x12,0x1A,
0x52,0x13,0x80,0x52,0x1B,0x13,0x52,0x5A,
0x1A,0x5B,0x52,0x52,0x1A,0x12,0x1A,0x52,
0x12,0x53,0x12,0x1A,0x12,0x9A,0x52,0x1B,
0x13,0x52,0x5A,0x1A,0x5B,0x52,0x52,0x1A,
0x12,0x1A,0x52,0x12,0x53,0x12,0x1A,0x12,
0x9A,0x00,0x52,0x1B,0x13,0x52,0x5A,0x12,
0x52,0x53,0x1A,0x1B,0x12,0x12,0x12,0x12,
0x1A,0x52,0x12,0x12,0x12,0x12,0x12,0x1A,
0x52,0x13,0x80,0x52,0x1B,0x13,0x52,0x5A,
0x1A,0x13,0x5A,0x12,0x1B,0x12,0x12,0x12,
0x1A,0x13,0x1A,0x12,0x5B,0x12,0x52,0x52,
0x53,0x52,0x1A,0x1A,0x1B,0x1A,0x13,0x1A,
0x52,0x5A,0x52,0x52,0x13,0x13,0x13,0x1A,
0x52,0x1A,0x5A,0x1A,0x5A,0x1B,0x5A,0x52,
0x12,0x5B,0x52,0x52,0x13,0x13,0x5A,0x13,
0x53,0x1B,0x52,0x52,0x53,0x1A,0x5A,0x52,
0x13,0x1B,0x53,0x1B,0x5B,0x93,0x52,0x1B,
0x13,0x52,0x5A,0x1A,0x5B,0x52,0x52,0x1A,
0x12,0x1A,0x52,0x12,0x53,0x12,0x1A,0x12,
0x9A,0x00,0x52,0x1B,0x13,0x52,0x5A,0x12,
0x52,0x53,0x1A,0x1B,0x12,0x12,0x12,0x12,
0x1A,0x52,0x12,0x12,0x12,0x12,0x12,0x1A,
0x52,0x13}

uint8_t sendArrayDz = {
82,27,19,82,90,18,82,83,
26,27,18,18,18,18,18,82,
18,18,18,18,18,82,91,19,
128,82,27,19,82,90,26,91,
82,82,26,18,26,82,18,83,
18,26,18,154,0,82,27,19,
82,90,18,82,83,26,27,18,
18,18,18,26,82,27,19,91,
91,91,83,90,27,128,82,27,
19,82,90,26,19,90,18,83,
27,18,18,26,19,26,18,91,
18,82,82,82,82,26,26,27,
26,19,26,82,90,82,82,19,
19,19,26,82,26,90,26,90,
27,90,82,18,91,82,82,19,
19,90,19,83,27,82,82,83,
26,90,82,19,27,83,27,91,
146,82,27,19,82,90,26,91,
82,82,26,18,26,82,18,83,
18,26,18,154,0,82,27,19,
82,90,18,82,83,26,27,18,
18,18,18,26,82,18,18,18,
18,18,26,82,19,128,82,27,
19,82,90,26,91,82,82,26,
18,26,82,18,83,18,26,18,
154,0,82,27,19,82,90,18,
82,83,26,27,18,18,18,18,
26,82,18,18,18,18,18,26,
82,19,128,82,27,19,82,90,
26,19,90,18,27,18,18,18,
26,19,26,18,91,18,82,82,
83,82,26,26,27,26,19,26,
82,90,82,82,19,19,19,26,
82,26,90,26,90,27,90,82,
18,91,82,82,19,19,90,19,
83,27,82,82,83,26,90,82,
19,27,83,27,91,147,82,27,
19,82,90,26,91,82,82,26,
18,26,82,18,83,18,26,18,
154,0,82,27,19,82,90,18,
82,83,26,27,18,18,18,18,
26,82,18,18,18,18,18,26,
82,19,128,82,27,19,82,90,
26,91,82,82,26,18,26,82,
18,83,18,26,18,154,82,27,
19,82,90,26,91,82,82,26,
18,26,82,18,83,18,26,18,
154,0,82,27,19,82,90,18,
82,83,26,27,18,18,18,18,
26,82,18,18,18,18,18,26,
82,19,128,82,27,19,82,90,
26,19,90,18,27,18,18,18,
26,19,26,18,91,18,82,82,
83,82,26,26,27,26,19,26,
82,90,82,82,19,19,19,26,
82,26,90,26,90,27,90,82,
18,91,82,82,19,19,90,19,
83,27,82,82,83,26,90,82,
19,27,83,27,91,147,82,27,
19,82,90,26,91,82,82,26,
18,26,82,18,83,18,26,18,
154,0,82,27,19,82,90,18,
82,83,26,27,18,18,18,18,
26,82,18,18,18,18,18,26,
82,19}

Ungetestete Arrays, die ich mittels eines kleinen Pythonskripts aus der Rohdaten-Tabelle habe erstellen lassen:

inf = open('rohdaten.txt','r')

sendArrayHx = []
sendArrayDz = []
pauseArray = []
lastTime = 0.0
lastChar = ''
for line in inf:
    if 'Async' in line:
        txt = line.split(chr(9))
        time = float(txt[2])
        duration = 0.0
        char = txt[4]
        if lastTime > 0.0:
            duration = time-lastTime
        if (duration > 0.050):
            lc = int(lastChar,0)+128
            lastChar =  "0x{:X}".format(lc)
            pause    = round(duration*1000)
            pauseArray.append(pause)
        if lastChar > "":
            sendArrayHx.append(lastChar)
            sendArrayDz.append(int(lastChar,0))
            
        lastTime = time
        lastChar = char

print("unsigned long pauseArray = {")
cCount = 0
count = 0
for p in pauseArray:
    print(p,end="")
    count += 1
    cCount += 1
    if count < len(pauseArray):
        print(",",end="")
    if cCount > 7:
        print()
        cCount = 0
print("}")

print("uint8_t sendArrayHx = {")
cCount = 0
count = 0
for p in sendArrayHx:
    print(p,end="")
    count += 1
    cCount += 1
    if count < len(sendArrayHx):
        print(",",end="")
    if cCount > 7:
        print()
        cCount = 0
print("}")

print("uint8_t sendArrayDz = {")
cCount = 0
count = 0
for p in sendArrayDz:
    print(p,end="")
    count += 1
    cCount += 1
    if count < len(sendArrayDz):
        print(",",end="")
    if cCount > 7:
        print()
        cCount = 0
print("}") 
inf.close()

Bitte das Copy/Paste bei den Tabellenerstellungen zu verzeihen ... geht schöner, aber es ist ja nur ein einmal benötigtes Hilfsmittel.

Die Pausenzeiten sind in [ms] dargestellt.

Jetzt muss nur noch jemand den Pseudocode von oben umsetzen ...

Gruß
ec2021

[Edit] Die zu sendenden Bytes beinhalten bereits das gesetzte MSb dort, wo im Anschluss ans Senden die jeweilige spezielle Pause aus dem pauseArray zu verwenden ist! Erkennbar beispielsweise an 0x80 oder 0x92 etc. Beim Senden einfach immer binär mit 0x7F maskieren, beim Prüfen für die Pause mit 0x80 ...

Moin und danke euch,

mir ist nicht alles klar, was @ec2021 geschrieben hat, deswegen ein paar Gedanken die mir dazu kommen.

MsB oder LsB, in den Logic Analyzer Settings hatte ich den Standardwert LsB gesetzt, weißt nicht, inwiefern das für das Senden der Daten relevant ist.

Ok verstehe, nach jedem Frame wird der Msb gesetzt. Daran soll das Program erkennen, dass es den nächsten Wert aus dem pauseArray nehmen soll?

Braucht es die Daten auch in Dezimal? Oder kann man es in Hex an einen GPIO als Hexdaten senden? Es müsste ja eh in binär umgesetzt werden.

MSb ist das höchstwertige Bit in einem Byte.

Die Werte werden vom Compiler sowohl in Hex oder auch Dezimal akzeptiert. Es handelt sich nur um unterschiedliche Schreibweisen für Menschen...

Die Pause zwischen zwei Bytes habe ich oben "Standardpause" genannt, den Wert kann man aus den Daten leicht ermitteln.

Das gesetzte MSb in einem Datenbyte signalisiert, dass nach dem Senden die nächste Pause aus dem PausenArray zu nehmen Ist.

Der Ablauf sollte für einen einigermaßen geübten Entwickler keine große Herausforderung bedeuten.

Ich habe die nächsten Tage keinen Zugriff auf einen PC, bin daher erstmal raus.

Gruß
ec2021

Ist nur dann relevant, wenn Du anders sendest als Du aufgezeichnet hast.

0x52 ist binär (7 Bit) 010 0010.
Da Du LSB first aufgenommen hast, sind die Bits in umgekehrter Reihenfolge auf dem Draht erschienen: 0100 010. So solltest Du dann auch wieder senden.
Als MSB first interpretiert ist die Bitfolge nämlich 0x22 - also ein anderer Wert.

@wno158 ,

da bei den üblichen Seriellen Schnittstellen LSB first übertragen wird, sollte das eigentlich passen.

Andererseits kann der TO ja auch mal einen ESP32 per RX und mit den ermittelten Parametern anstelle des Logic Analyzers anklemmen.

Dazu müsste die Schnittstelle noch passend konfiguriert werden ->HardwareSerial mit SERIAL_7N1 und Baudrate 1920.

siehe https://randomnerdtutorials.com/esp32-uart-communication-serial-arduino/

Dann sollte der ESP die gesuchten Werte einlesen können...