Archiv verlassen und diese Seite im Standarddesign anzeigen : Befehlsprotokoll
Fabian E.
03.05.2009, 02:12
Hallo,
hat hier jemand eine Idee mit welchem Protokoll, also in welcher Formatierung ich am besten Daten vom RP6 zum PC schicke und andersherum?
Es geht mir darum, dass ich dem RP6 Steuerbefehle schicke. Das kann für die Motoren sein, aber auch für ganz andere Sachen. (vielleicht ein Greifarm oder Ähnliches)
Gleichzeitig soll der RP6 auch noch Sensordaten zum PC schicken, die ich dann dort anzeige.
Hat jemand eine Idee wie ich das ganze am sinnvollsten verschicke?
Also nicht falsch verstehen, über UART halt, klar, nur wie verpacke ich das ganze schön in einen String oder so?
Ich hatte mir überlegt folgendes zu verwenden:
Z.B.: "1 160 160" Die "1" steht in dem Fall für die Motorregelung, die anderen beiden Werte für die Geschwindigkeiten für Links und Rechts.
Wenn ich etwas für den Greifer übertragen will steht dann am Anfang halt keine "1" sondern was anderes.
Ich muss ganz ehrlich sagen, dass dieses Vorhaben aber irgendwie etwas an meinen C-Kenntnissen gescheitert ist.
Z.B. habe ich keine Ahnung wie ich ordentlich mit dem Parametern umgehen kann, es gibt ja keine Strings in C... :'(
Geschweige denn wie ich die gut aufsplitten kann...
Vielleicht kann mir ja jemand von euch helfen.
Wie gesagt, ich suche eigentlich nur eine anständige Formatierung mit der ich die Datenetwas strukturiert übertragen kann.
Liebe Grüße,
Fabi
vklaffehn
03.05.2009, 03:41
Moin!
Man könnte z.B. Bytes nehmen (oder chars, wenns Dir lieber ist) z.B. ein Startbyte, ein Befehlsbyt und dann ´die Parameter...
MfG
Volker
Da haben sich ein paar Leute Gedanken gemacht.
https://www.roboternetz.de/wissen/index.php/Network_Controller/PC_Praxis
Nehme aber an, dass das mehr ist, als du im Moment zu brauchen glaubst.
oberallgeier
03.05.2009, 10:19
Hi, Fabi,
ich habe ähnliche Sorgen auch gehabt. Danach habe ich mir ein eigenes, halbwegs sinnvolles Protokoll nach meinen Gegebenheiten zusammengeschustert. Da wurde ein String zusammengesetzt aus den gewünschten Daten - alle in ASCII (auch die Zahlenwerte!!), vom PC zum Controller oder umgekehrt gesendet.
Wichtig ist es, beim Zusammensetzen des Sendestrings im Controller den String mit einem "NUL" : int nNUL = 0; // Null = Ende Textpuffer abzuschließen. Andernfalls gibt das Fehler bei der Übertragung. Der String den das Terminal sendet, wird "automatisch" mit so einem "End-of-string"-Byte versehen. Die Übermittlung von Zahlenwerten als "reine Zahlen" - 1 oder 2 Byte (real hatte ich nie versucht) hatte ich , wie gesagt, nicht geschafft. Angefangen hatte ich mit "Steuerzeichen", die bei mir in ASCII sind:
unsigned int conS = 83; // "Control"-Zeichen 83="S" = Steuerwert
unsigned int conL = 76; // "Lösch"-Zeichen 77="L" = Steuerwert
unsigned int conZ = 90; // "Senden"-Zeichen 90="Z" = Steuerwert
... weil mir das bei der Auswertung einfacher vorkam und weil ich das am Terminal besser, weil eben Klartext, lesen konnte. Dieses Zeichen zu Beginn des Strings dient bei mir u.a. dazu, im Controller die Länge des Kommandos zu erkennen und aus dem Empfangspuffer die entsprechende Anzahl der Bytes zu lesen.
Fabian E.
03.05.2009, 20:25
Hallo zusammen,
nun habe ich es nach etlichen Versuchen endlich geschafft, zumindest schonmal die Sensordaten ordentlich zu übertragen.
Diese werden dann schön am PC dargestellt. Dazu aber nachher mehr.
Nun stellt sich halt nur noch die Frage des Befehlesendens...
Bei den Sensoren sah das ganze so aus:
void StartFrame(void)
{
writeString_P("START\n");
}
void EndFrame(void)
{
writeString_P("END\n");
}
void printSensors(void)
{
StartFrame();
writeString_P("SpeedL:");
writeInteger(mleft_speed, DEC);
writeString_P("\n");
writeString_P("SpeedR:");
writeInteger(mright_speed, DEC);
writeString_P("\n");
writeString_P("Bat:");
writeInteger(adcBat, DEC);
writeString_P("\n");
[...]
writeString_P("ObsL:");
writeInteger(obstacle_left, DEC);
writeString_P("\n");
writeString_P("ObsR:");
writeInteger(obstacle_right, DEC);
writeString_P("\n");
EndFrame();
}
Das kann ich am PC sehr einfach wieder an den "\n" und den Doppelpunkten aufsplitten und auslesen.
Das ist also kein Problem. Aber auch nur, weil ich mit C# wesentlich besser umgehen kann als mit C.
Kann mir jemand sagen wie ich das ganze nun am besten mit C auf dem RP6 mache? Also so nur vom Code her.
Am besten wäre es natürlich, wenn ich in der selben Art und Weise auch zurücksenden würde.
Wie sähe denn z.B. die Syntax aus, um einen Text, ähnlich dem oben erzeugten, wieder zu zerlegen?
Ich hoffe ihr könnt mir helfen! =)
Achja, hier noch mein Leseprogramm für die Sensorwerte.
http://img22.imageshack.us/img22/4308/aufzeichnenb.th.png (http://img22.imageshack.us/img22/4308/aufzeichnenb.png)
Dazu wüsste ich gerne noch was genau die gemessen ADC-Werte bedeuten...
Also z.B. was 150 bei dem Motorstromsensoren bedeutet. Kann man das irgendwo nachlesen?
Liebe Grüße & vielen Dank,
Fabi =)
Fabian E.
05.05.2009, 23:05
So, prinzipiell klappt die Übertragung jetzt schon sehr gut.
Ich habe nur noch ein großes Problem.
Ich übertrage auf den RP6 Strings die z.B. so aussehen: "#1:160:160:0:*"
Die Eins bezeichnet die Art des Befehls. Hier z.B. die Motorsteuerung. Die beiden "160" und die "0" sind die Parameter für den Befehl. Hier erst die Speedwerte für links und rechts und danach noch die Richtung.
Zum Schluss noch ein "*" um zu signalisieren, dass der Befehl und alle Parameter übertragen sind.
So, nun stehe ich nur vor dem Problem diesen String an den ":" zu zerteilen und in einzelnen Strings oder in einem Array zu speichern.
Ich habe Code dazu, doch leider scheint der ein Memoryleak zu haben, da nach ca. 70 Befehlen der Roboter keine Befehle mehr an nimmt...
Das Programm an sich läuft weiter und auch die Sensordaten werden noch zum PC übertragen.
Ich habe leider keine Ahnung was da schief läuft...
Vielleicht kann ja mal jemand von euch über den Code gucken. Das wäre echt lieb =)
Liebe Grüße,
Fabi
char text[50];
int counter = 0;
char ** parameter;
char ** strsplit(char sep, size_t max_tokens)
{
free(parameter);
char ** tokens = NULL;
char * str = NULL;
tokens = malloc(sizeof(char *) * (max_tokens + 1));
if(!tokens) goto failed;
size_t size = strlen(text) + 1;
str = malloc(size);
if(!str) goto failed;
memcpy(str, text, size);
tokens[0] = str;
char ** current = &tokens[1];
for(; *str; ++str)
{
if(*str != sep) continue;
if(!--max_tokens) break;
*str = '\0';
*current++ = str + 1;
}
*current++ = NULL;
tokens = realloc(tokens, (char *)current - (char *)tokens);
if(!tokens) goto failed;
return tokens;
failed:
if(tokens) free(tokens);
if(str) free(str);
return NULL;
}
int getCommand(void)
{
if(text[strlen(text)-1] == '*') // Falls das letzte Zeichen das Zeichen für das Befehlsende ist
{
parameter = strsplit(':',10); //Den gesamten Befehl aufsplitten
text[0] = '\0'; //Den Befehl wieder löschen
counter = 0;
return atoi(parameter[0]);//Und das Befehlskommando zurückgeben
}
return 0;
}
#define CMD_SET_SPEED 1
#define CMD_SOUND 2
#define CMD_LEDS 3
#define CMD_LEDS_RP6 4
void task_Commands(void)
{
if(getBufferLength()) //Sind Zeichen im Buffer des seriellen Ports vorhanden?
{
char tmp = readChar(); //Ein Zeichen des Buffers lesen
if (tmp =='#') //Falls ein neuer Befehl gestartet wird
{
mSleep(10); //Warum auch immer man hier warten muss, ohne klappts nicht. Oo
counter = 0; //Laufvariable wieder zurücksetzen, da wir wieder am Anfang des Strings schreiben wollen
text[0] = readChar(); //Noch ein Zeichen aus dem Buffer lesen und als erstes Zeichen des Strings verwenden
}
else
{
text[counter] = tmp; //Falls nicht einfach das ausgelesene Zeichen einfügen
}
text[counter + 1] = '\0';
counter++;
}
int cmd = getCommand(); //Hier wird auch parameter initialisiert...
if(cmd)
{
setCursorPosLCD(0,0);
writeIntegerLCD(cmd,DEC);
switch(cmd)//Schnell was mit den Token machen...
{
case CMD_SET_SPEED: moveAtSpeed(atoi(parameter[1]),atoi(parameter[2])); changeDirection(atoi(parameter[3])); break;
case CMD_SOUND: sound(atoi(parameter[1]),atoi(parameter[2]),0);break;
case CMD_LEDS: setLEDs(atoi(parameter[1]));break;
case CMD_LEDS_RP6: setRP6LEDs(atoi(parameter[1]));break;
}
//Joar, und hier dann wieder irgendwie freigeben...
}
}
task_Command() wird die ganze Zeit aus der Hauptschleife aufgerufen.
Hallo,
malloc und free fragmentieren dir den Speicher. Aber soweit ich das gelesen habe sollte das nicht zu einem "memoryleak" führen, da freigegebene Speicherfragmente beim nächsten malloc wieder verwendet werden (sofern Sie passen). Siehe: "avr-libc-user-manual --> "Memory Areas and Using malloc()"
Deinen Speicherverbrauch kannst du mit https://www.roboternetz.de/wissen/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc bestimmen - allerdings nicht die Fragmentierung. Du kannst ja den jeweils aktuellen Wert auf das LCD ausgeben...
Ich habe mir für solche Probleme eine C "Testumgebung" auf meinem Rechner aufgebaut, um den Code auch im graphischen Debugger anschauen zu können. Man muss halt die hardwarespzifischen Funktionen durch "dummys" ersetzen.
Zu deinem Code:
Wenn du mit einer festen Syntax (4 werte pro commando) und Zahlen arbeitest würde an deiner Stelle "parameters" mit einem Array aus festen char-arrays ober gleich mit integern arbeiten. Das ist handlicher.
Z.b.:
#include <stdlib.h>
/*
** Funktion zum "parsen" eines command strings.
** Wenn ein fehler auftritt, gibt die funktion (-1) zurück.
** Ansonsten gibt sie die anzahl der ermittelten Parameter zurück.
**
** ACHTUNG: Funktion arbeitet nur korrekt, wenn
** a) nur EIN zeichen den kommandostring einleitet
** b) der Trenner kein zeichen ist, das atoi konvertiert.
** c) nur EIN Zeichen die Endesequenz darstellt (ACHTUNG: seperator + Endezeichen geht auch nicht!)
*/
#define MAX_PARAMS 4
int params[MAX_PARAMS];
int cmdsplit(char*cmd_string)
{
int cursor = 0;
int param_nr = 0;
// Parameterarray initialisieren
for (int i=0;i<MAX_PARAMS;i++)
params[i] = 0;
/* Prüfen ob der command-string gültig beginnt */
if ((cmd_string == NULL) ||cmd_string[cursor] != '#')
return -1;
while (cmd_string[cursor] != '\0' && cmd_string[cursor] != '*') // prüfen ob das ende erreicht wurde
{
char *tmp;
cursor++;
tmp = (cmd_string + cursor); // tmp wird auf den anfang des für atoi auszulesenden werts "gesetzt"
params[param_nr] = atoi(tmp); // wert auslesen
if (param_nr >= MAX_PARAMS) // sind mehr parameter verfügbar als erwartet? Stimmt was mit dem Commando nicht...
return -1;
param_nr++;
// Überspingen der ausgelesen Zeichen und ermitteln des nächsten "interessanten" Teils im string.
while((cmd_string[cursor] != ':') && (cmd_string[cursor] != '*') && (cmd_string[cursor] != '\0'))
cursor++;
}
return param_nr;
}
Hallo,
ich habe mir deine Funktion "strsplit" mal genauer angesehen. Das Ganze nicht dafür gedacht, dass jemand anderes die Funktion einfach mal so versteht. ;-)
Ein paar Fragen:
1.) "free (parameter);" zu Beginn von "strsplit" soll den vorher allocierten "token speicher wieder freigeben. Beim ersten Aufruf hat aber nie ein Malloc stattgefunden!
2) Du allocierst "str" und kopierst dort deinen Command-String rein. Den Speicherbereich gibst du aber nie wieder frei. Das ist dein MEM Leak...
Leider kannst du den Speicher auch nicht so einfach freigeben, da in diesem deine Token liegen, die du später in deiner case-Anweisung brauchst... Du müsstest irgendwann nach deiner case ein "free parameter[0]" machen... Aber das ist alles andere als geradeaus programmiert.
3.) in der Funktion "task_Command" fängst du zwar ab wenn cmd == NULL ist, aber du machst mit weiter als ob nichts wär. Dabei kann das eigentlich nur sein, wenn malloc die paar Byte die dein Programm zum arbeiten braucht nicht mehr bekommt. Du sollst an diesem Punkt die Arbeit einstellen, da auch andere Funktionen (z.b. Motorsteuerung) keinen Speicher mehr erhalten.
Fabian E.
06.05.2009, 14:25
oh, vielen dank!=)
Also da ich grade in der schule bin,kann ich mich nicht um den code kümmern.
Allerdings muss ich sagen,dass cmd auch dann null ist, wenn der befehl noch nicht ganz übertragen ist.
Fabi
Fabian E.
06.05.2009, 16:14
So, jetzt bin ich wieder zu Hause und kann ein wenig mehr sagen.
Also erst mal der Split-Code ist nicht von mir, den habe ich aus dem Internet.
Verstehen kann ich ihn selbst nicht ;)
Ich werde jetzt wohl erst mal den hier geposteten Code testen.
Zum Auslesen des Speicherverbrauchs... Im Wiki stand, dass das malloc() mit z.B. nicht funktioniert...
Fabi
Allgemein sind malloc und konsorten auf nem 8-Bit Mikrocontroller eher fehl am Platz. Erst recht wenn man nicht weiss was man tut ;)
Fabian E.
10.05.2009, 18:25
So, jetzt klappt alles wunderbar! =) Mit Lurchis Methode klappt es! =)
Vielen Dank euch allen! =)
Fabi
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.