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
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.
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.
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.
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.
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
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ß!
>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 :)
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 ;)
Oh sorry, der Code war noch ne alte Version. Mit dem hier wurde getestet.
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 :)
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..
>@Bert3: die CRC ist für einen Wechselrichter
nur der Ordnung halber: für welchen genau?
Ist für einen ABB UNO-2.0/2.5-I-OUTD. Kann denn jemand etwas mit dem USB-Verlauf anfangen?
Es würde ungemein helfen wenn du einfach die Logdateien (pcap) mit reinstellst dann kann man das einfacher mit Wireshark anschauen
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?
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
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
..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?
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.
>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 :)
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.