Hallo zusammen, ich stehe gerade mit einem Brett vorm Kopf vor meinem Code, und weiß aktuell nicht weiter. Mein Ziel ist es 32 Relais über SCPI über USB mittels eines Arduino Mega 2560 zu schalten. Ich habe zu diesem Thema im Internet folgende Seite gefunden. https://community.element14.com/challenges-projects/project14/open-arduino/b/blog/posts/arduino-in-test-instrumentation---intro-scpi-programmable-switch Ich habe nun sein Programm genommen und die Funktionsweise nachvollzogen. anschließend habe ich nun zum Test ein 8 Relay Module zum Testen hergenommen, und 8 Kanäle im Programm implementiert. Ergebnis: Alles funktioniert perfekt! Daraufhin erweiterte ich den Code auf 32 Kanäle. Leider funktioniert es nicht ab Kanal 10. Sobald ich über SCPI den Befehl: :DIGI:SWITCH10 ON an den Arduino sende, bekomme ich die Antwort OFF… eigentlich sollte bei diesem Befehl Relay 10 Schalten. Zusammengefast funktionieren die ersten 9 Kanäle und alle über 9 nicht. Ich komme damit nun etwas an meine Grenzen, was Embedded Software betrifft. Meine Vermutung ist, dass es an den Datentypen liegt, um die ich mir bei beispielsweise Python recht wenig Gedanken mache. Ich hoffe mir kann hier jemand helfen und mir das Ganze auch erklären wo mein Fehler liegt. Danke im Voraus!
Ich hab deinen Code nicht im Detail durchsucht. Aber beim Aufruf von scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH1?", 8, "SWIT1?", 6, get_digital_1); und von scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH10?", 8, "SWIT10?", 6, get_digital_10); fällt mir eine Inkonsistenz auf. Der vierte Aufrufparameter ist long_name_length. Du nutzt dazu jeweils 8, obwohl "SWITCH10?" ein Zeichen länger ist als "SWITCH1?" Wenn nur die ersten 8 Zeichen des Kommando überprüft werden, dann ist "SWITCH10?" (Abfrage des Registerwerts) identisch zu "SWITCH10" (schreiben des Registerwerts. Und deswegen wird wohl beim Senden von "SWITCH10" versehentlich "SWITCH10?" durchgeführt. Passe mal die Längenangaben der Kommandostrings an deren tatsächliche Länge an.
Tim T. schrieb: > Mein Ziel ist es 32 Relais über SCPI über USB mittels eines Arduino Mega > 2560 zu schalten. Wenn das sicher sein soll wäre es besser dafür zwei von denen zu nehmen: https://www.controllino.com/product/controllino-mega-pur/
Tim T. schrieb: > Sobald ich über SCPI den Befehl: :DIGI:SWITCH10 ON an den Arduino sende, > bekomme ich die Antwort OFF… eigentlich sollte bei diesem Befehl Relay > 10 Schalten. Dann mach doch die Gegenprobe und lass die ersten 9 einfach weg. Evtl. geht bei der Masse Zeug eine Spannung in die Knie oder die Zeit reicht nicht?
Tim T. schrieb: > Ich hoffe mir kann hier jemand helfen und mir das Ganze auch erklären wo > mein Fehler liegt. Naja, man kann ein paar Hinweise geben. Ich hab gestern Abend mal ein bisschen mit dem Code gespielt. Der Fehler liegt ca. hier
1 | scpi_error_t
|
2 | scpi_execute_command(struct scpi_parser_context* ctx, const char* command_string, size_t length) |
3 | {
|
4 | struct scpi_command* command; |
5 | struct scpi_token* parsed_command; |
6 | |
7 | parsed_command = scpi_parse_string(command_string, length); |
8 | command = scpi_find_command(ctx, parsed_command); |
9 | // TESTAUSGABE HIER!!!
|
10 | |
11 | if(command == NULL) |
12 | {
|
13 | return SCPI_COMMAND_NOT_FOUND; |
14 | }
|
15 | |
16 | if(command->callback == NULL) |
17 | {
|
18 | return SCPI_NO_CALLBACK; |
19 | }
|
20 | |
21 | |
22 | return command->callback(ctx, parsed_command); |
23 | }
|
Ich hab mir an der oben per Kommentar gekennzeichneten Stelle die beide strucs ausgeben lassen. Achtung, das sind POINTER auf structs! Dabei sieht man, daß die Zerlegung (Parsing) des Strings funktioniert, allerdings die Dekodierung nicht. Also muss man den Fehler in scpi_find_command suchen.
Im Debugger könnte man auf Zeile 11 einen Breakpoint setzen und sich den Inhalt der Strukturen anzeigen lassen ... Wenn man nur mit Bootloadern arbeitet, muss man hier na Achim S. schrieb: > Ich hab deinen Code nicht im Detail durchsucht. Aber beim Aufruf von > > scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH1?", 8, "SWIT1?", > 6, get_digital_1); > > und von > > scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH10?", 8, "SWIT10?", > 6, get_digital_10); > > fällt mir eine Inkonsistenz auf. Der vierte Aufrufparameter ist > long_name_length. Du nutzt dazu jeweils 8, obwohl "SWITCH10?" ein > Zeichen länger ist als "SWITCH1?" Es wäre sinnvoll, die Funktion scpi_register_command zu verändern, so daß sie die Stringlänge aus den übergebenen Texten bestimmen kann, statt sie redundant und --hier auf jeden Fall-- fehlerträchtig getrennt anzugeben. (Auszug ab Zeile 208 von scpiparser.cpp)
1 | current_command->long_name = long_name; |
2 | current_command->long_name_length = long_name_length; |
3 | |
4 | current_command->short_name = short_name; |
5 | current_command->short_name_length = short_name_length; |
ändern in
1 | current_command->long_name = long_name; |
2 | current_command->long_name_length = long_name_length ? long_name_length : strlen(long_name); |
3 | |
4 | current_command->short_name = short_name; |
5 | current_command->short_name_length = short_name_length ? short_name_length : strlen(short_name); |
Dann kann für die Stringlänge eine 0 übergeben werden und die Funktion kümmert sich selbst drum.
1 | scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH10?", 0, "SWIT10?", 0, get_digital_10); |
Achim S. schrieb: > Passe mal die Längenangaben der Kommandostrings an deren tatsächliche > Länge an. Naja, besser der Op lernt was über die übliche Stringverarbeitung in C bzw. C++. Die kommt in den allermeisten Fällen OHNE Längenangabe aus, weil Nullterminiert. Auch diverse andere Stellen des Programm sind eher "anders". Das liegt aber am Originalautor, nicht am OP, der hat nur übernommen und kopiert.
BINGO! Mit einer kleinen Änderung geht es jetzt!
1 | struct scpi_command* |
2 | scpi_register_command(struct scpi_command* parent, scpi_command_location_t location, |
3 | const char* long_name, size_t long_name_length, |
4 | const char* short_name, size_t short_name_length, |
5 | command_callback_t callback) |
6 | {
|
7 | |
8 | struct scpi_command* current_command; |
9 | |
10 | if(location == SCPI_CL_CHILD) |
11 | {
|
12 | current_command = parent->children; |
13 | }
|
14 | else
|
15 | {
|
16 | current_command = parent; |
17 | }
|
18 | |
19 | if(current_command == NULL) |
20 | {
|
21 | parent->children = (struct scpi_command*)malloc(sizeof(struct scpi_command)); |
22 | current_command = parent->children; |
23 | }
|
24 | else
|
25 | {
|
26 | while(current_command->next != NULL) |
27 | {
|
28 | current_command = current_command->next; |
29 | }
|
30 | |
31 | current_command->next = (struct scpi_command*)malloc(sizeof(struct scpi_command)); |
32 | current_command = current_command->next; |
33 | }
|
34 | |
35 | current_command->next = NULL; |
36 | current_command->children = NULL; |
37 | |
38 | current_command->long_name = long_name; |
39 | current_command->long_name_length = strlen(long_name); |
40 | |
41 | current_command->short_name = short_name; |
42 | current_command->short_name_length = strlen(short_name); |
43 | |
44 | current_command->callback = callback; |
45 | |
46 | return current_command; |
47 | }
|
Ich empfehle trotzdem, die Sache mit den Längen aus den Funktionen rauszuschmeißen und wie der Rest der Welt Strinverarbeitung zu praktizieren.
Hallo zusammen, @ Achim S. Ich habe die Längen angepasst und es funktionierte bis zu Switch13… nach mehrmaligem kompilieren habe ich den code editor gewechselt zur original Arduino IDE. Und dann funktionierte alles perfekt. Ich arbeite normalerweise mit Visual Studio Code mit Platform IO. Da ich aktuell mehr in Python arbeite scheint dort etwas mit den Erweiterungen durcheinandergekommen sein. Nach Neuinstallation von Platform IO ging es dann auch damit. Das war der richtige Ansatz den ich brauchte! Danke @ Lothar Das Produkt merke ich mir vor und ist interessant für ein sich in Planung befindliches Projekt. @ falk Ich habe es nach deinen Angaben versucht mit dem code ohne angepasste Längen und mit angepassten Längen. Mit angepassten Längen funktioniert die Dekodierung. Mit nicht angepassten längen klappt es nicht. Was ich dabei jetzt aber nicht kapier ist das die Dekodierung so schief geht, dass ein anderer Befehl daraus wird. Also aus einem Set ein Get Befehl wird und er mir über den Seriellen Monitor ein OFF ausspuckt. @ DerEgon & falk Ich werde die Änderungen am Montag Einpflegen und testen! @ all Ich danke euch für eure kompetente Hilfe! Es ist nicht selbstverständlich wie ich aus anderen Foren schmerzvoll lernen musste. Nun noch mal was zu mir bezüglich des Kommentar von falk: >Naja, besser der Op lernt was über die übliche Stringverarbeitung in C >bzw. C++. Die kommt in den allermeisten Fällen OHNE Längenangabe aus, >weil Nullterminiert. Auch diverse andere Stellen des Programm sind eher >"anders". Das liegt aber am Originalautor, nicht am OP, der hat nur >übernommen und kopiert. Ich würde so gerne das ganze von null auf selber schreiben, weil man nur so am besten lernt wie das ganze funktioniert. Für mich ist es wichtig, das wenn ich code von anderen übernehme ich ihn auch wirklich verstehe! Ich programmiere seit jetzt ca. 4 jahren mehr oder weniger komplexe Dinge. Gelernt habe ich S7 und Arbeite aktuell viel mit WAGO SPS. Zudem schreibe ich viel in Python. Da bin ich ganz froh, dass ich auf fertiges zurückgreifen kann. Leider scheitere ich sehr oft bei eigenem code an den so unterschiedlichen Syntax von AWL (SPS),C/C++ und python, was zeitlich gesehen oft schwierig ist. Solange Zeit ist gelingt mir das meiste. :-) Das nächste was ich implementieren möchte ist, dass mit einem Befehl (Cell_1) zwei der Ausgänge Switch1&2 gleichzeitig anspreche. Mit einer gegenseitigen Verriegelung, dass nur jeweils Cell_1 oder Cell_2 (Switch 3&4) usw. angehen und niemals mehr als ein Cell_x gleichzeitig geschaltet werden kannen. Das einzelne ansprechen der Kanäle muss dabei erhalten bleiben. Ich hoffe das ich bald die Gelegenheit habe mich damit in ruhe auseinander zu setzen. Vielleicht hat ja der ein oder andere dazu eine Idee?
Tim T. schrieb: > Ich würde so gerne das ganze von null auf selber schreiben, weil man nur > so am besten lernt wie das ganze funktioniert. > Für mich ist es wichtig, das wenn ich code von anderen übernehme ich ihn > auch wirklich verstehe! Ich programmiere seit jetzt ca. 4 jahren mehr > oder weniger komplexe Dinge. Gelernt habe ich S7 und Arbeite aktuell > viel mit WAGO SPS. Zudem schreibe ich viel in Python. Naja, wenn gleich die elementaren Grundlagen der meisten Programmiersprachen sehr ähnlich sind, hat man bei SPS selten was mit Stringverarbeitung zu tun ;-) > Da bin ich ganz froh, dass ich auf fertiges zurückgreifen kann. Leider > scheitere ich sehr oft bei eigenem code an den so unterschiedlichen > Syntax von AWL (SPS),C/C++ und python, was zeitlich gesehen oft > schwierig ist. Solange Zeit ist gelingt mir das meiste. :-) Klingt mehr nach Überleben als nach Programmieren. Mach es richtig. Schnapp dir ein Grundlagenbuch zu C, arbeite das durch, dann bist du halbwegs fit. C++ würde ich dir erstmal nicht empfehlen, das ist zu komplex, wenn gleich natürlich in dem Code schon einiges an C++ drinsteckt und die Stringverarbeitung in C++ etwas anders läuft. Hmmm.
Noch ein Hinweis. Die "Millionen" Funktionen für SWITCH1...SWICHTxy stechen dem fortgeschrittenen Programmierer ARG ins Auge. Das macht man anders. Man muss im Befehl den Kanal dekodieren und dann an EINE Funktion übergeben. Dann braucht es nämlich nur zwei lausige Funktionen für set und get. Das macht den Code nicht nur kürzer, sondern auch übersichtlicher und weniger fehlerträchtig. Copy & Paste ist zu 99% Bäääääähhhh!!! Wie das bei SCPI im Befehl offiziell kodiert wird, weiß ich nicht. Vielleicht so? :DIGITAL:SWITCH99 on :DIGITAL:SWITCH:99 on
Moin, ich war mal so frei, das Ganze ein wenig aufzuräumen. Siehe Anhang. - alle Anwenderfunktionen in eine separate Datei incl. Header verlagert (scpi_user.c), dadurch deutlich mehr Übersicht, scpi.ino wird sehr kurz - Testfunktionen zur Ausgabe der wichtigen Datenstrukturen in test.c - die unsägliche Verwendung einer zusätzlichen Längenangabe bei der Stringverarbeitung komplett entfernt, das war der größte Aufwand - Stringverarbeitung konsequent mittels libc-Funktionen und gemäß gängiger Praxis in C - diverse Kleinigkeiten verbessert und aufgeräumt - Parser des Kommandostrings ist jetzt robuster, mehrfach hintereinander liegende Trennzeichen (':' und ' ') werden gefiltert - Ausgänge für Relais sind jetzt HIGH aktiv, d.h. nach einem Reset sind alle Ausgänge auf LOW. Das ist eigentlich der Normalfall, wenn man normale Treiber ala ULN2803 nutzt. Jetzt funktioniert es sehr gut, stabil und ist auch leicht erweiterbar. Die Funktion scpi_parse_numeric habe ich nicht angefaßt, die wird hier nicht verwendet, da ist auch viel zu tun, wenn man es denn braucht. Sie ist per #ifdef auskommentiert. Was könnte man verbessern? - Der Kommandobaum ist im Normalfall statisch, da braucht es keine dynamischen Funktionen für den Aufbau. Man könnte das alles als Konstanten definieren, das spart vor allem beim AVR viel RAM, der im Moment verschwendet wird (die konstanten Strings in den vielen Funktionen scpi_register_command() verbrauchen Flash UND RAM!). - Die Tokenliste könnte man vereinfacht über ein Array von Pointern machen, eine verkettete Liste ist hier ohne Vorteil, hat eher den Nachteil des zusätzlichen Aufwands und Resourcenverbrauchs durch dynamische Speicherverwaltung - die vielen Funktionen für SWITCH könnte man mit einer allgemeinen Funktion lösen, das spart viel Schreiberei. Dazu muss man aber die Dekodierung des Befehls intelligenter machen. Viel Spaß damit
Beitrag #7122446 wurde vom Autor gelöscht.
Sooo, und weil's so schön ist, hier die 2. Version ein paar Verbesserungen. - Command tree rein statisch, liegt zu 100% im Flash. Da muss man zwar an einigen Stellen beim AVR mit den pgm_read_word() Funktionen arbeiten, was den Code etwas schlechter lesbar macht, ist aber noch OK. Der Lohn dafür ist eine massive Einsparung an RAM - Die Tokens sind nur noch ein einfaches Array aus Pointern, keine verkettete Liste mehr. Damit entfällt die komplette, dynamische Speicherverwaltung mit malloc() - Der Zustand der Kanäle wird komprimiert in Bits gespeichert, nicht einfachen bool, denn die brauchen 1 Byte/Element, spart immerhin 28 Bytes Speicherbedarf Version 1, dynamische Speicherverwaltung im RAM Flash: 8234 Bytes RAM: 1566 Bytes Wobei das nur der STATISCHE Verbrauch ist! Der dynamische wird von der IDE nicht angezeigt und liegt deutlich höher, schätzungsweise bis 3kB! Denn es werden ca. 70x10 Bytes für SCPI Kommandos benötigt, dazu noch die dynamische Liste der Tokens mit ca 5x5 Bytes, plus der Speicherbedarf für die Verwaltung von malloc(). Version 2, statischer command tree ohne dynamische Speicherverwaltung Flash: 7616 Bytes RAM: 395 Bytes Damit läuft das auch auf einem eher kleinen Arduino UNO mit noch reichlich Reserven. Ok, die Ansteuerung der Relais müßte man dann auf Schieberegister umbauen, denn er hat keine 32 freien IOs.
:
Bearbeitet durch User
Ich empfehle als SCPI lib die Vrekrer_scpi_parser_v2.h zu nehmen,
Tim T. schrieb: > Was ich > dabei jetzt aber nicht kapier ist das die Dekodierung so schief geht, > dass ein anderer Befehl daraus wird. Also aus einem Set ein Get Befehl Ich hatte versucht, das schon oben zu beschreiben. Der Set-Befehl lautet "SWITCH10", der Get-Befehl lautet "SWITCH10?". Wenn du nur die ersten 8 Buchstaben davon auswertest, unterscheiden sich die SCPI-Kommandos für Set und Get nicht mehr, in beiden Fällen wird nur noch auf "SWITCH10" geprüft. Wenn (aufgrund der Reihenfolge im Code) der Set-Befehl zuerst als Match erkannt wird, dann wird er ausgeführt (sowohl für "SWITCH10" als auch für "SWITCH10?"). Aber bei dir wird halt der Get-Befehl zuerst als Match erkannt, und deshalb der Get-Befehl ausgeführt.
Ich möchte mich einmal bei allen bedanken! Das Gerät funktioniert perfekt. Ich bin noch auf ein Hindernis gestoßen bezüglich der automatischen Resetfunktion des Arduino Mega. Nach dem dies gelöst war habe ich das ganze erfolgreich in Betrieb nehmen können. @Falk Ich danke dir für deine Mühen, es ist echt klasse zu sehen wie das ganze vernünftig auszusehen hat und wie man das Strukturiert. In den kommenden Tagen wird hoffentlich mein Buch eintreffen „C Programmieren für Einsteiger“ vom BMU Verlag. Anhand des Buchs werde ich versuchen deine Herangehensweise zu verstehen und künftige Projekte strukturiert von Null auf zu beginnen und es richtig zu machen. Ich bin bisher mit den Büchern vom BMU Verlag ganz gut gefahren mit Python und Git, daher hoffe ich dass dieses Buch mich weiterbringt. Noch mal in die Runde gefragt, giebt es noch andere Bücher die man empfehlen kann bezüglich C? @Achim S. Der Groschen ist bei mir nun gefallen. Danke dir @ chris Diese SCPI lib ist mir bekannt, weil einem diese auch direkt vorgeschlagen wird wen man bei Platform IO oder der Arduino IDE im Lib-Manager nach SCPI sucht. Ich hatte mir aus Zeitnotgründen an einem bestehenden Projekt dessen Link ich zu Anfang gepostet habe orientiert.
Tim T. schrieb: > Noch mal in die Runde gefragt, giebt es noch andere Bücher die man > empfehlen kann bezüglich C? Der K&R in der zweiten Ausgabe ("Programmieren in C", Hanser-Verlag, 1990) Definitiv abzulehnen ist das Machwerk eines gewissen "Schellong".
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.