Forum: PC-Programmierung CRC berechnen - Wo ist der Fehler?


von D.F. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich versuche mich seit heute morgen an einem CRC-Code, blicke inzwischen 
aber überhaupt nicht mehr durch.

Ich würde in naher Zukunft gerne mit einem Gerät kommunizieren, dessen 
Doku ich vorliegen habe. Die "crc16"-Funktion ist eins zu eins 
(zumindest sehe ich das so) aus dieser Doku entnommen.

Umgesetzt werden soll eine CRC-16-CCITT (0xFFFF) mit dem Algorithmus 
x¹⁶+x¹²+x⁵+1.
Bei der übergebenen Byte-Folge sollte ich laut Doku folgende Ergebnisse 
erhalten:
crc_lo = F1 (hex) = 241 (dez)
crc_hi = D1 (hex) = 225 (dez)  (!!!)

Da die letzte Gleichung offensichtlich nicht stimmt (und auch bei Byte2 
bis Byte6 (s.Bild CRC) hex- und dez-Zahlen sich nicht entsprechen), ist 
entweder die Doku fehlerhaft oder ich habe ein grundlegendes 
Verständnisproblem.
Mein Programm, zwei andere Codebeispiele hier aus dem Forum und ein 
Onlinerechner für die CRC-Umsetzung liefern mir alle verschiedene 
Ergebnisse.

Könnt ihr mir sagen, wo der Fehler ist??

: Verschoben durch User
von Bernd K. (prof7bit)


Lesenswert?

Besorg am besten mal einen echten Mitschnitt von einem solchen Paket. 
Die Doku ist reichlich strange formuliert und enthält Fehler in den 
Testvektoren. Ich hab eben grad auch ein bisschen rumgespielt mit den 
Testvektoren und hab keinen gängigen Algorithmus gefunden (auch keine 
Variante von ccitt16) die das Ergebnis liefert das die da unten 
abdrucken.

Normalerweise wenn man sowas unmißverständlich dokumentieren will gibt 
man den Namen des Algorithmus, einen knappen funktionsfähigen 5-Zeiler 
in C (und nicht haufenweise umschweifige Reimprosa) der genau das 
implementiert und dazu ein oder zwei Testvektoren ohne Tippfehler die 
dazu passen.

von Kaj (Gast)


Lesenswert?

Das Problem faengt ja schon bei den Testvektoren an:
Msg (Decimal) 0 -> Msg (Hex) 30
...
Aha, also doch nicht Dezimal 0 sonder das ASCII-Zeichen 0, also '0' -> 
0x30.

Abgesehen davon interpretiere ich Punkt 2 als Schleife die ueber alle 
Zeichen der Nachricht laeuft. Das muss sogar so sein, weil ja sonst (so 
wie in deiner Implementierung) CRC_L und CRC_H immer ueberschreiben 
werden. Das wuerde keinen Sinn ergeben.

Hier mal mein Versuch:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
uint16_t crc16_ccitt(uint8_t *New, uint8_t len);
5
6
7
int main(void)
8
{
9
    uint8_t
10
        rx_msg[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35};
11
12
    uint16_t
13
        crc = 0;
14
15
16
    crc = crc16_ccitt(rx_msg, 6);
17
    printf("CRC: 0x%x\n\n", crc);
18
19
    return 0;
20
}
21
22
23
uint16_t crc16_ccitt(uint8_t* New, uint8_t len)
24
{
25
    uint8_t
26
        Bcc_Lo = 0xff,          /*  1   */
27
        Bcc_Hi = 0xff,          /*      */
28
        CRC_L  = 0,             /*      */
29
        CRC_H  = 0;             /*      */
30
31
    uint16_t
32
        Tmp = 0;
33
34
                                /*  2   */
35
    for (uint8_t i = 0; i < len; i++) {
36
        New[i] ^= Bcc_Lo;       /*  a   */
37
        Tmp = New[i] << 4;      /*  b   */
38
        New[i] ^= Tmp;          /*  c   */
39
40
        Tmp = New[i] >> 5;      /*  d   */
41
        Bcc_Lo = Bcc_Hi;        /*  e   */
42
        Bcc_Hi = New[i] ^ Tmp;  /*  f   */
43
44
        Tmp = New[i] << 3;      /*  g   */
45
        Bcc_Lo ^= Tmp;          /*  h   */
46
        Tmp = New[i] >> 4;      /*  i   */
47
48
        Bcc_Lo ^= Tmp;          /*  j   */
49
    }
50
51
    CRC_L = ~Bcc_Lo;            /*  3   */
52
    CRC_H = ~Bcc_Hi;            /*      */
53
54
    printf("CRC_H: 0x%x | CRC_L: 0x%x\n", CRC_H, CRC_L);
55
56
    return ( (CRC_H << 8) | CRC_L) ;
57
} //  crc16_ccitt

Fuer die RX-Message passt es, da kommt fuer die CRC 0x4468 raus (High: 
0x44 Low: 0x68)

Aber fuer die TX-Message passt es nicht.

von D.F. (Gast)


Lesenswert?

Vielen Dank euch schonmal.

@Bernd K.
Ich werd jetzt übers Wochenende nicht dazu kommen, werd dann aber mal 
versuchen, die Kommunikation mitzuschneiden (wenn sie denn möglich sein 
sollte) - habe den Code bisher nur auf meinem Rechner getestet.

Kann ich denn vom Gerät überhaupt eine vollständige Antwort erwarten, 
wenn das TX-Paket unvollständig bzw. mit falscher CRC gesendet wird?

Kennst du empfehlenswerte Software zum Mitschneiden der Kommunikation 
vorzugsweise unter Linux (Windows geht zur Not aber auch)?

Würde sonst entweder auf irgendein kostenfreies Tool zurückgreifen, oder 
Wireshark mit USB-PlugIn mal versuchen. Habe aber leider weder mit dem 
einen noch dem anderen bisher Erfahrung gemacht.

@Kaj
Das ist doch schonmal ein Lichtblick:) Dann will ich mal auf Fehler im 
TX-Paket in der Doku hoffen und deinen Code nächste Woche mal in der 
Praxis ausprobieren.

Kaj schrieb:
> Abgesehen davon interpretiere ich Punkt 2 als Schleife die ueber alle
> Zeichen der Nachricht laeuft. Das muss sogar so sein, weil ja sonst (so
> wie in deiner Implementierung) CRC_L und CRC_H immer ueberschreiben
> werden.
Das macht Sinn - und das Ergebnis scheint dir Recht zu geben.

von Bernd K. (prof7bit)


Lesenswert?

D.F. schrieb:
> Kennst du empfehlenswerte Software zum Mitschneiden der Kommunikation
> vorzugsweise unter Linux (Windows geht zur Not aber auch)?

Wenn Du nur einen PC hast aber zufällig zwei serielle Schnittstellen 
(z.B. zwei USB-RS485 Adapter) kannst Du einfach über den einen Adapter 
deren Software ganz normal laufen lassen und den zweiten Adapter 
parallel klemmen und dort mit nem simplen seriellen "Terminal"-Programm 
das auch hex anzeigen kann den Traffic direkt abgreifen und anzeigen.

Du könntest aber natürlich auch sagen: Ok, Versuch und Irrtum, ich 
vermute ich habs laut Doku richtig implementiert, ich schick jetzt 
einfach mal so ein Paket mit meiner selbst berechneten CRC los und schau 
was das Gerät antwortet. Wenns geht und Du die Antwort-CRC ebenfalls 
korrekt berechnen kannst bist Du an dem Punkt schon fertig.

von Bert3 (Gast)


Lesenswert?

Was für ein Gerät ist es denn?

1. Hersteller
2. Typ
3. Verbindungsart: (RS232, RS485?)
4. gibt es bestehende Software vom Hersteller? für welche 
Betriebssysteme (DOS, Windows, Linux,...)

also alles was schon mal so da ist - vielleicht gibt es andere Projekte 
wo dieses Gerät auch mal zum Einsatz kam

ich habe z.B. nach wenig hilfreicher Doku schon mal einen 
CRC-Algorithmus  direkt aus dem Programmcode des Herstellers extrahiert 
- ist manchmal gar nicht so schwer wie es sich anhört - deswegen ist es 
gut einen Überblick über die vorhandenen Programme zu haben (manche 
Testprogramme für Windows oder DOS sind winzig und trivial zu 
analysieren)

und die Möglichkeit Logs der Kommunikation zu erstellen wäre auch sehr 
gut

von D.F. (Gast)


Lesenswert?

Soo, zurück aus dem Wochenende...

Bernd K. schrieb:
> Du könntest aber natürlich auch sagen: Ok, Versuch und Irrtum, ich
> vermute ich habs laut Doku richtig implementiert, ich schick jetzt
> einfach mal so ein Paket mit meiner selbst berechneten CRC los und schau
> was das Gerät antwortet. Wenns geht und Du die Antwort-CRC ebenfalls
> korrekt berechnen kannst bist Du an dem Punkt schon fertig.
So hatte ich das erstmal vor. Einen zweiten Adapter habe ich nicht.

@Bert3
Der Adapter ist der DIGITUS DA-70157 von reichelt:
https://www.reichelt.de/USB-Konverter/DIGITUS-DA-70157/3/index.html?ACTION=3&LA=446&ARTICLE=122187&GROUPID=6105&artnr=DIGITUS+DA-70157&SEARCH=usb%2Brs%2B485

Da gibt es tatsächlich einige Threads zu hier im Forum.

Bert3 schrieb:
> ich habe z.B. nach wenig hilfreicher Doku schon mal einen
> CRC-Algorithmus  direkt aus dem Programmcode des Herstellers extrahiert
Das klingt sehr gut. Es gibt eine Software, die Daten des Gerätes 
ausliest und visualisiert - die funktioniert zuverlässig allerdings nur 
bis Windows XP. Habe ich auch noch nicht testen können - ist alles etwas 
umständlich, weil ich das Gerät nicht bei mir zuhause habe. Muss mal 
sehen, was ich in diese Richtung herausfinden kann; ich dachte 
eigentlich, dass man nicht so einfach in den Code blicken kann, ist aber 
definitiv einen Versuch wert.

Ich denke, es ist am sinnvollsten, wenn ich mich einfach mal dran setze 
und ein bisschen ausprobiere. Ich melde mich, sobald ich was neues weiß!

von Bert3 (Gast)


Lesenswert?

>Der Adapter ist der DIGITUS DA-70157 von reichelt

muss du für den Adapter musst du die Checksumme berechnen?
wenn nicht - für welches Gerät ist die Checksumme?

>Ich denke, es ist am sinnvollsten, wenn ich mich
>einfach mal dran setze und ein bisschen ausprobiere.
>Ich melde mich, sobald ich was neues weiß!

hört sich gut an

>ich dachte eigentlich, dass man nicht so einfach
>in den Code blicken kann

kann man auch nicht - aber wir sind ja da zum helfen :)

von D.F. (Gast)


Angehängte Dateien:

Lesenswert?

Die Weihnachtszeit ist nicht so glücklich gewählt für das Vorhaben, 
deswegen hat es jetzt leider ein bisschen gedauert..

Ich habe jetzt mal den USB-Traffic zwischen 
PC(WinXP)-Digitus-Wechselrichter (@Bert3: die CRC ist für einen 
Wechselrichter) mit Wireshark mitgeschnitten. Ausschnitte davon habe ich 
angehängt. Die TX-Pakete scheinen wie erwartet zu funktionieren (Aufbau 
der Pakete s. Anhang im ersten Thread).
Die Berechnung der Checksumme funktioniert ebenfalls einwandfrei - 
nochmal danke, Kaj.

Wo ich noch nicht so ganz durchblicke, ist der Aufbau der 
RX-Nachrichten. Grundsätzlich lässt sich auch da der Aufbau aus der Doku 
wiedererkennen, allerdings sieht es für mich so aus, als ob auch da ein 
10Byte-Paket ankommt - und nicht wie angegeben, 8Byte.
Zudem verstehe ich noch nicht, warum der Empfänger meiner TX-Nachricht 
so häufig und dann mit verschiedenen, zum Teil mit nur 2Byte großen, 
Datenpaketen antwortet.

Ich habe dann mal versucht, das Gerät über den angehängten Python-Code 
anzusprechen. So wie ich das sehe, wird das TX-Paket gesendet. Die 
Antwort kann ich mir aber nicht erklären. Es wird einfach nichts für 
"response" ausgegeben (s. Anhang). Woran kann das liegen?

Habe mal einen anderen COM-Port versucht -> Fehler
Daraus schließe ich, dass der COM-Port richtig angegeben ist.

Habe dann mal andere TX-Pakete gesendet -> das gleiche Ergebnis: "read:" 
und dann nichts -> Liegts am Code?

Zum Anhang:
RX123 -> Erste, zweite und dritte Nachricht nach TX
RX4 -> vierte ... ihr wisst schon Bescheid ;)

von D.F. (Gast)


Angehängte Dateien:

Lesenswert?

Oh sorry, der Code war noch ne alte Version.
Mit dem hier wurde getestet.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

D.F. schrieb:
> Die Berechnung der Checksumme funktioniert ebenfalls einwandfrei -
> nochmal danke, Kaj.
Kein Problem, dafuer ist das Forum ja da :)

Wenn du das in Python machst, kannst du das auch so machen:
1
def crc16_ccitt(new, len):
2
    ...
3
    return crc_l, crc_h
4
5
6
tx_msg[8], tx_msg[9] = crc16_ccitt(tx_msg[:], 8)
Ist doch doof die crc erst zusammen zu quetschen nur um sie dann wieder 
auseinander zu nehmen.

Nur so als kleiner verbesserungsvorschlag :)

von D.F. (Gast)


Lesenswert?

Kaj G. schrieb:
> Ist doch doof die crc erst zusammen zu quetschen nur um sie dann wieder
> auseinander zu nehmen.

Ist geändert - sehr praktisch, zwei return-Werte..

von Bert3 (Gast)


Lesenswert?

>@Bert3: die CRC ist für einen Wechselrichter

nur der Ordnung halber: für welchen genau?

von D.F. (Gast)


Lesenswert?

Ist für einen ABB UNO-2.0/2.5-I-OUTD.

Kann denn jemand etwas mit dem USB-Verlauf anfangen?

von Bert3 (Gast)


Lesenswert?

Es würde ungemein helfen wenn du einfach die Logdateien (pcap) mit 
reinstellst dann kann man das einfacher mit Wireshark anschauen

von D.F. (Gast)


Lesenswert?

Die Datei ist angehängt.

Mein Python-Programm scheint tatsächlich einen leeren String zu 
empfangen. Wenn ich nach dem Senden des TX-Paketes "not response" 
abfrage (also einen leeren String) erhalte ich "True" als Ergebnis.

Inzwischen habe ich zwei Programme gefunden, die auch unter Linux 
laufen.
Eins davon lässt eine Verbindung zu und liest mir z.B. die "Inverter 
Version" koorekt aus, Energiemesswerte konnte ich hier allerdings bisher 
auch nicht empfangen. Ich blicke durch den Programmcode nicht durch - 
hab schon vergeblich die Definition der TX-Pakete gesucht..

Hier der Link zu den Programmen:
http://auroramonitor.sourceforge.net/

"Aurora Monitor" ganz oben auf der Seite (lässt sich bisher nicht 
kompilieren)
"aurora" ganz unten auf der Seite (funktioniert bisher nur teilweise)

Könnt ihr dem Code etwas entnehmen?

von D.F. (Gast)


Angehängte Dateien:

Lesenswert?

Jetzt ist sie angehängt

von Bert3 (Gast)


Lesenswert?

1. in beiden Quellen findet man auf jeden Fall leicht die CRC Berechnung 
- und die Aufrufstellen müssen die Paketerzeugung sein

2. du solltest dich klarer Ausdrücken

>Ich blicke durch den Programmcode nicht durch
>(lässt sich bisher nicht kompilieren)
>(funktioniert bisher nur teilweise)

keine Ahnung was das helfen soll - bis du technisch in der Lage C Code 
zu lesen und zu kompilieren oder hast du da schon keinen Plan

3. bist du sicher in der RS485 Kommunikation oder kann es sein das dein 
ganzer Code auch noch falsch ist - denke schon sonst würde das 1. 
Programm wohl nicht laufen

4. wie wäre es wenn du dich erstmal nur auf das auslesen der "Inverter 
Version"
konzentrierst - weil das geht ja definitiv mit dem anderen Programm - 
solange das nicht problemlos funktioniert brauchst du gar nicht weiter 
machen

von Bert3 (Gast)


Lesenswert?

im auroramonitor comms.cpp

Zeile 435 ist die crc16-Funktion

Zeile 523 ist die Communicate-Funktion - diese sendet den Inhalt von 
SerialBuf und empfaengt dann die Antwort auch wieder in SerialBuf

und dann gibt es viele Funktioen die einfach die SerialBuf belegen, die 
Communicate-Funktion nutzen um Befehle abzusetzen und dann das Ergebnis 
verarbeiten

Beispiele:
Zeile 734 GetCEdata
Zeile 915 GetInverterInfo
usw. eben die ganzen Get-Funktionen

ist nicht sauber und auch nicht super schön - aber man sieht schon was 
so passiert

von Bert3 (Gast)


Lesenswert?

..und weil man dir ja alle Details so aus der Nase ziehen musst:

hast du einen Link zu dem Dokument wo die CRC und die anderen 
Protokoll-Daten vollständig beschrieben sind?

von D.F. (Gast)


Lesenswert?

Ich sehe mich grundsätzlich in der Lage C-Code zu lesen, habe in dem 
verlinkten Code aber nach einer Art zentralen Stelle gesucht, an der 
alle TX-Pakete definiert sind - und zwar so wie sie auf die Leitung 
geschickt werden. Etwa in der Art:

Byte0    ... Byte8/9
Adresse        CRC

Und die habe ich eben nicht gefunden. CRC- und Communicate-Funktion 
konnte ich auch schon finden. Nur, wie gesagt, kann ich eben den Aufbau 
der einzelnen Pakete nicht finden. Irgendwo müssen die ja schließlich 
definiert werden, oder nicht?

Ich habe mich dann heute nochmal an mein Python-Programm gesetzt und es 
endlich zum Laufen bekommen. Der Fehler war, dass die einzelnen 
hex-Zahlen in der TX-Liste auch nur ziffernweise in Bitstrings umgesetzt 
wurden (z.B. wurde 0x41 als zwei Byte interpretiert und nicht wie 
gewollt als eins). Jetzt berechne ich aus den hex-Zahlen die CRC und 
konvertiere die Listenelemente dann in einen hex-String. Der wird dann 
in gewünschter Form empfangen. Jetzt kann ich auch meine Daten aus dem 
Wechselrichter auslesen.
Ich werde das Programm in leicht abgespeckter Version (s.u.) nach 
Weihnachten noch hochladen.

Bert3 schrieb:
> ..und weil man dir ja alle Details so aus der Nase ziehen musst:
Ich habe mich mit den Informationen bewusst zurückgehalten, weil ich die 
Doku per Mail vom Hersteller erhalten habe. Der hat mich ausdrücklich 
darum gebeten, diese nicht weiterzugeben. Mir ist schon klar, dass das 
nicht so optimal ist. Umso mehr danke ich dir, dass du immer noch hier 
bist.

Das hier:
Bert3 schrieb:
> ich habe z.B. nach wenig hilfreicher Doku schon mal einen
> CRC-Algorithmus  direkt aus dem Programmcode des Herstellers extrahiert
würde mich nämlich noch brennend interessieren.
Und ich würde natürlich den Programmcode der Aurora-Software gerne noch 
besser verstehen.

von Bert3 (Gast)


Lesenswert?

>Nur, wie gesagt, kann ich eben den Aufbau
>der einzelnen Pakete nicht finden.
>Irgendwo müssen die ja schließlich
>definiert werden, oder nicht?

du solltest die Codezeilen die ich nennen wenigstens
grob ueberfliegen das Protokoll steht dort sauber drinn

Zeile 435 ist die crc16-Funktion

Zeile 523 ist die Communicate-Funktion - das ist Lesen/Schreiben auf 
RS485
1. über den globalen Puffer SerialBuf erhält diese Funktion einen 
vorbereiteten Befehl (das Protkoll)
2. die Funktion haengt NOCH die CRC drann - deswegen ist auch nur hier 
die crc16 drinn
3. sendet den Inhalt des Puffers an den Inverter
4. liesst dann das Ergebnis vom Inverter wieder in den globalen Puffer 
SerialBuf zurück
5. die CRC der Antwort wird geprüft

alle Funktionen welche die Communicate-Funktion nutzen sind "Kommandos" 
fuer den Inverter
alle Get...-Funktionen sind solche

Beispiel:

Zeile 734 GetCEdata - hatte ich schon gepostet - und da ich die 
Dokumentation nicht habe kann ich dir nicht die Seite nennen wo der 
GetCEdata-Befehl beschrieben ist
1
int GetCEdata(int addr, int param)
2
{//===============================
3
//der befehlspezifische Aufbau von SerialBuf fuer "GetCEdata"
4
    strcpy(SerialBuf, SerialBufSpaces);
5
    SerialBuf[0] = addr;
6
    SerialBuf[1] = opGetCE;
7
    SerialBuf[2] = param;
8
    SerialBuf[3] = 0;
9
//jetzt fehlt noch die CRC
10
    if(Communicate(1) <= 0) //CRC drann, senden, antwort empfangen, CRC pruefen, fertig - fuer alle Befehle gleich daher hier drinn
11
    {
12
        return(-1);  // failed
13
    }
14
//hier noch die befehlsspezifische Antwort aus dem SerialBuff(die Antwort) verarbeiten
15
    return(ConvertLong(&SerialBuf[2]));
16
}

ist das dein Dokument?:
https://forums.xilinx.com/xlnx/attachments/xlnx/CONN/10023/1/AuroraCommunicationProtocol_4_2.pdf 
Version 4.2
http://documentslide.com/documents/auroracommunicationprotocol-4-7-public.html 
Version 4.7

noch eine Perl-Library fuer die Inverter: 
http://search.cpan.org/~freman/Device-Inverter-Aurora-0.05/lib/Device/Inverter/Aurora.pm

jetzt sollte das aber echt klappen - 2 funktionierende C-Projekte, eine 
funktionierende Perl-Library, haufenweise Doku - viel mehr an Hilfe geht 
kaum noch :)

von Bert3 (Gast)


Lesenswert?

ich hab den GetCEdata-Befehl doch noch gefunden
in AuroraCommunicationProtocol_4_2.pdf auf Seite: 13 unten

>78) Cumulated energy readings (Aurora grid-tied inverters only)

---
[0] ist die Adress
[1] ist die 0x78 - Funktion "Cumulated energy readings"
[2] ist Parameter - siehe Doku
 0) Daily Energy
 1) Weekly Energy
 2) Not used
 3) Month Energy (Energy from the first day of current calendar month)
 4) Year Energy (Energy from the first day of current calendar year)
 5) Total Energy (total lifetime)
 6) Partial Energy (cumulated since reset)
[3] Byte ist 0
---
[4-7] scheint auch 0 zu sein - oder egal
[8-9] crc

Antwort
[0] tr-state
[1] global state
[2-5] in einen Long konvertiert die Wh
[6-7] = crc

ich kann also direkt den Befehl(Protokoll) dem C Code zuordnen

von Bert3 (Gast)


Lesenswert?

Hat jetzt alles geklappt oder gibt es noch Fragen?

von ♪Geist (Gast)


Lesenswert?

Ich habe letztens auch CRC16 gebraucht und bin auf folgendes gestoßen, 
was auch tatsächlich funktioniert hat:
http://www.idothink.com/2008/07/c-crc16-ccitt.html

Ist zwar C#, läßt sich aber einfach ins C übersetzen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.