Hallo Leute, hab da mal wieder ne Frage an euch. Und zwar versuche ich den Raspberry Pi mit dem Onboard UART zu einem Modbus RTU Master hochzuziehen. Ich habe hierfür die modbuslib für Linux installiert, mit den Code Beispielen welche mit geliefert werden bin ich auch schon etwas voran gekommen. In den Beispielen wird jedoch immer mit einem USB-Adapter gearbeitet und nicht mit dem UART. Und eben das ist auch mein Problem, ich kann am UART keinerlei Flanken finden. Mein Code meldet soweit auch keine Probleme, die Rechte für den UART habe ich mir auch schon längst besorgt. Bringt aber leider alles nix. Ich möchte einfach so schnell wie möglich und möglichst billig erfolge erzielen, da ich das für eine SEMI-Arbeit brauche und durch Themen wie SDL, SPI und anderem schon viel Zeit verloren habe. Gruß an alle! Danke schon mal im voraus! ;)
:
Verschoben durch Moderator
Hier ist noch der Code ;) Ich weis schon das der sehr schlecht ist, aber vieles ist auch einfach aus den Beispielen Kopiert. #include <iostream> using namespace std; #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <modbus.h> #define SERVER_ID 17 #define INVALID_SERVER_ID 18 const uint16_t UT_BITS_ADDRESS = 0x13; const uint16_t UT_BITS_NB = 0x25; const uint8_t UT_BITS_TAB[] = { 0xCD, 0x6B, 0xB2, 0x0E, 0x1B }; const uint16_t UT_INPUT_BITS_ADDRESS = 0xC4; const uint16_t UT_INPUT_BITS_NB = 0x16; const uint8_t UT_INPUT_BITS_TAB[] = { 0xAC, 0xDB, 0x35 }; const uint16_t UT_REGISTERS_ADDRESS = 0x6B; /* Raise a manual exception when this adress is used for the first byte */ const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x6C; const uint16_t UT_REGISTERS_NB = 0x3; const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 }; /* If the following value is used, a bad response is sent. It's better to test with a lower value than UT_REGISTERS_NB_POINTS to try to raise a segfault. */ const uint16_t UT_REGISTERS_NB_SPECIAL = 0x2; const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x08; const uint16_t UT_INPUT_REGISTERS_NB = 0x1; const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A }; const float UT_REAL = 916.540649; const uint32_t UT_IREAL = 0x4465229a; int main (void) { cout << "Start" << endl; printf("Compiled with libmodbus version %s\n", LIBMODBUS_VERSION_STRING); printf("Linked with libmodbus version %d.%d.%d\n", libmodbus_version_major, libmodbus_version_minor, libmodbus_version_micro); modbus_t *ctx; modbus_mapping_t *mb_mapping; int header_length; uint8_t *query; int rc; ctx = modbus_new_rtu("/dev/ttyAMA0", 9600 , 'E', 8, 1); if (ctx == NULL) { cout << "We have Problems" << endl; fprintf(stderr, "Unable to create the libmodbus context\n"); return -1; } else { cout << "All fine" << endl; } if (modbus_set_slave(ctx, 1)== -1) { cout << "Slave not set" << endl; } else { cout << "Slave is set" << endl; } header_length = modbus_get_header_length(ctx); mb_mapping = modbus_mapping_new( UT_BITS_ADDRESS + UT_BITS_NB, UT_INPUT_BITS_ADDRESS + UT_INPUT_BITS_NB, UT_REGISTERS_ADDRESS + UT_REGISTERS_NB, UT_INPUT_REGISTERS_ADDRESS + UT_INPUT_REGISTERS_NB); if (mb_mapping == NULL) { cout << "Mapping Faild" << endl; } else { cout << "Mapping is fine" << endl; } if(modbus_connect(ctx) == -1) { cout << "Not connect" << endl; } else { cout << "Connected" << endl; } for (;;) { if (modbus_receive(ctx, query) == -1) { /* Connection closed by the client or error */ break; } } /* Read holding registers */ if (query[header_length] == 0x03) { if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 3) == UT_REGISTERS_NB_SPECIAL) { cout << "Set an incorrect number of values" << endl; MODBUS_SET_INT16_TO_INT8(query, header_length + 3, UT_REGISTERS_NB_SPECIAL - 1); } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1) == UT_REGISTERS_ADDRESS_SPECIAL) { cout << "Reply to this special register address by an exception\n" << endl; modbus_reply_exception(ctx, query, MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY); //continue; } } if (modbus_reply(ctx, query, rc, mb_mapping) == -1) { return -1; } }
Über welche Schnittstelle ist der Raspberry Pi mit dem Modbus-Master verbunden? Hast du einen RS-485 oder RS-232 Transceiver hinter den UART geschaltet?
Ich hatte beabsichtigt einen bzw. zwei Max3485 nach zu schalten. Der Raspberry soll dann selbst der Master werden. Ich kann aber bisher nicht mal erkennen, das der Raspberry etwas zu versenden versucht. Ich meine spätestens beim Connect müsste ja mal was passieren. Ich fürchte daher das ich die Schnittstelle nicht erreiche.
Nunja, dein Raspberry Pi, den du laut Quellcode ja als Modbus-Slave laufen lässt, wird von sich selbst aus nichts senden. Er wird nur auf Anfragen vom Master antworten. Die connect-Funktion sendet nichts, sie öffnet nur die serielle Schnittstelle. Implementiere ihn mal als Master und schau, ob du dann ein Signal erhälst. Kannst du denn über die serielle Schnittstelle so Daten senden? Mit minicom z.B.
Also ich hab jetzt mal nen Test mit minicom gemacht, ob überhaupt was geht und das sah soweit auch gut aus. Hab mir den ganzen Code nochmal genau angeschaut, auch den set_slave Befehl. Muss mich da mit einer Zusatz Funktion die der Befehl haben kann verlesen haben. Hab jetzt aufjedenfall noch mal einiges geändert. Jetzt bekomme ich als Antwort vom Raspi wenigstens mal ne gescheite Meldung. Start Compiled with libmodbus version 3.0.3 Linked with libmodbus version 3.0.3 All fine Opening /dev/ttyAMA0 at 1200 bauds (E, 8, 1) connected 3. Mod: modbus-rtu.c:119: _modbus_rtu_build_request_basis: Assertion `ctx->slave != -1' failed. Aborted Das ist die vollständige Ausgabe des Programms. Ich häng auch nochmal den verbesserten Code an. #include <iostream> using namespace std; #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <modbus.h> int main (void) { cout << "Start" << endl; printf("Compiled with libmodbus version %s\n", LIBMODBUS_VERSION_STRING); printf("Linked with libmodbus version %d.%d.%d\n", libmodbus_version_major, libmodbus_version_minor, libmodbus_version_micro); modbus_t *ctx; ctx = modbus_new_rtu("/dev/ttyAMA0", 1200 , 'E', 8, 1); modbus_set_debug(ctx, 1); if (ctx == NULL) { cout << "We have Problems" << endl; fprintf(stderr, "Unable to create the libmodbus context\n"); return -1; } else { cout << "All fine" << endl; } if(modbus_connect(ctx) == -1) { cout << "Can not connect" << endl; } else { cout << "connected" << endl; } printf("%d.\n", modbus_get_socket(ctx)); //uint16_t Buffer[512]; //modbus_read_registers(ctx, 42 , 10, Buffer); modbus_write_register(ctx, 42, 2000); }
Die modbus_set_slave() Funktion setzt im Master-Betrieb den Slave, an den die Daten gesendet werden sollen und im Slave-Betrieb, die Adresse, auf die der Slave reagieren soll. Die solltest du also schon benutzten. Ich weiß nicht, welche Adresse per default eingestellt ist. Wahrscheinlich Adresse 0 (Global Call). Auch kann es nicht schaden, mit modbus_free und modbus_close die Ressourcen wieder freizugeben. ;-)
Ja, so hatte ich es auch verstanden, aber Julian meinte ja, dass ich so wohl zum Slave würde. Zudem verstehe ich nicht ganz wozu man den Befehl als Master braucht. Mit modbus_write_register() kann ich ja ein Beliebiges uint16_t Register beschreiben, wobei ich auch die Salve Adresse angeben muss (z.B. 42).
Ich wollte nur eben bescheid geben, dass ich es heraus gefunden habe. Ich habe mir den Fehler nochmal angesehen und der verweist auf Z.119 in modbus_rtu.c des is dann das: assert(ctx->slave != -1); Also hab ich mal geschaut wie ich "slave" gültig machen kann. Und es liegt tatsächlich an modbus_set_slave(). Wieso des nicht gleich funktioniert hat weis ich jetzt auch nicht, aber sonst wäre es ja auch zu einfach ;) Ich denk mal, ohne euch wäre ich nie auf diese Fährte gekommen! ;)
Als Master brauchst du die Funktion modbus_set_slave() um bekannt zu machen, an welchen Slave du jetzt senden willst. Es könnte ja vorkommen, dass an deinem Bus mehr als nur zwei Teilnehmer hängen. Die Adresse, die du bei den read & write Funktionen angeben musst ist nur die Register-Adresse, die bei jedem Slave gleich sein kann.
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.