www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AT Kommandos / Befehlsliste verarbeiten


Autor: Axel R. (axelr) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Liebe Mitleser ;)

als Hobby C-Progger bin ich gerade dabei, mir einen
Kommandozeilen-Interpreter zu bauen.
Man findet tatsächlich nur schwer etwas zu diesem Thema.

In der Codesammlung habe ich einen Beitrag gefunden
Beitrag "Einfacher Interpreter für Komandozeilen/Befehlszeilen"
und bei RN im wiki gibt es einen Anhaltspunkt:
http://www.rn-wissen.de/index.php/Avr-gcc (Section 31 Sprungtabelle)

Bei AT Befehlen, wie man sie bei Modems zB. eingibt,
lässt sich ja generell mit:

1) AT+xyz=? der erlaubte Bereich des Parameters abfragen
2) AT+xyz? der aktuelle Wert von "xyz" abfragen
3) AT+xyz=100 den Wert von "xyz" mit 100 festlegen
4) AT+xyz= 1,2,"0000",5 der Funktione, die hinter "xyz" steht,
   mit Werten und Parametern füttern.
Hinzu kommt dann noch "AT*" irgentwas, "AT^", "ATA", usw. also Befehle, 
bei denen das "PLUS" Zeichen als Befehlstrenner fehlt.

Was soll ich nun in die Sprungtabelle eintragen, wenn ich das Beispiel 
ausm RN als Grundlagen nehmen würde?

Eigentlich müsste ich in diese Sprungtabelle

1) AT+
2) AT*
3) ATA
4) ATH

usw. eintragen und das, was danach kommt, also
1) xyz
2) test1
3) test2

usw. in eine zweite Sprungtabelle?
Was mache ich mit den unterschiedlichen Parametern, welche ja als Zahl 
oder als String vorliegen können un dauch noch von der Anzahl her 
unterschiedlich sein können?

Dem Beispiel in der Codesammlung konnte ich entnehmen, das es dieses 
Problem wohl schon mal gegeben hat.
Beitrag "Einfacher Interpreter für Komandozeilen/Befehlszeilen"
Leider sind hier verschiedene Punkte für mich unverständlich, da hier 
ganz viel Pointerrechnerei betrieben wurde und viele vorhandene 
String-Funktionen selbst geschrieben wurde und ich bei denen leider mit 
meinen bescheidenen C-Kenntnissen nicht wirklich durchsteige.

Ein ganz simples Beispiel aus dem Thread:
int8_t execute_cmd_interface(char *string){
  /* local variables */
  register uint8_t  ui8_cnt    =0;
  register char    act_char  =1;
  register int8_t    cmd_ret_val  =0;
  register uint8_t  cmd_count  =0;
  int8_t         ret_val    =0;
  /* start reading string */
  act_char=string[ui8_cnt++];  /* Read next sign */


Speziell /* Read next sign */

um welches Vorzeichen handelt es sich? Ich dachte char ist deshalb 
Vorzeichen behaftet, weil die Druckbaren Zeich im 7Bit Bereich liegen, 
also bis 127 gehen.

So wird auch viel hin-und herkopiert -k.A. ;)

Ich werde nun versuchen, mit Hilfe der Standardbefehle vom WINAvr, mir 
das nochmal aufzudröseln. Auwei - ob ich das hinbekomme?

Aber mit:
if (buffer[i] =='+' $$ buffer[i-1]=='T' && buffer[i-2] =='A')
wollte ich eben diesmal nicht machen, hmm.

Hat evtl. jemand solch eine Kommandoschnittstelle schon einmal 
implementiert?

BTW. Muss ja nicht mit "AT+ oder "AT*" laufen. Das sollte nur als 
Beispiel dienen. Auch andere Kommadostrukturen wären sicher denkbar.

Danke fürs Lesen

Axelr.

Frohe Pfingsten

Autor: G. B. (garyb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Axel...

ohne zu wissen was du wie mit den AT Befehehlen machen möchtest - hier 
mein Vorschlag wie du mit meinem "Pointern"-Beispiel ;O) es hinbekommen 
könntest.
    irgendwo in main
  /* add command-functions to command-interface */
  cmd_interface_add_cmd("ATA",  &command_0);

damit wäre der Befehl schon mal bekannt.. die Anzahl der Argumente 
interessieren den Code erst mal nicht, du musst aber den Funktionszeiger
&command_0 mit Leben füllen, indem du eine eine Funktion dazu 
schreibst...

Beispiel - machen das Leben leichter ;O)
uint8_t command_0(int8_t argc, char *argval[]){ /* ATA-Befehl */
  uint8_t ui8_cnt;
        /*Sende Befehl*/
  USART_SENDEFUNKTION("ATA");
        /*Sende Parameter so wie sie eingegeben wurden ohne Trennzeichen */
  for (ui8_cnt=0; argc>ui8_cnt; ui8_cnt++){
    USART_SENDEFUNKTION(argval[ui8_cnt]);
  }
  return 0;
}

uint8_t command_0(int8_t argc, char *argval[])
die Parameterliste muss so bleiben, das bestimmt mein Code so.
argc    - hier bekommst du die Anzahl der Parameter
argval  - hier bekommst du die pointer auf die verschidenen Strings
          deiner Argumente/Parameter

Beispiel: ATA P1,p2 - keine Ahnung ob das Sinn macht
argval[0] = "P1"
argval[1] = "p2"

Hilft dir das?

Gruss Gary

Autor: Axel R. (axelr) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Gary,

>ohne zu wissen was du wie mit den AT Befehehlen machen möchtest

Ich will ein SIEMENS TC35 emulieren.

Mir geht es in erster Linie um die Herangehensweise. Deinen BeispielCode 
habe ich ans Laufen gebracht. Habs an meinen Mega128 angepasst.
Der Befehl "ATA" nimmt gerade bei der Abarbeitung eine Sonderstellung 
ein, so wie alle Befehle, die halt einen Buchstaben mehr haben, als eben 
nur AT.

Ich werde nun doch eine Statemachine programmieren, in der ich
AT, AT*, AT^, AT+, AT& ...
usw erkenne.

Wenn keines davon dabei war, nehm ich mir die restlichen bleibenden
ATA, ATH, ATE, usw. vor.

Dann werde ich nachsehen, ob Kommas drinn sind und wieviele Zeichen 
zwischen den Kommas.
Hier hatte ich schon einst einen NMEA GPS String Auswerter (APRS nach 
N4TXI) gebastelt. Den leihe ich mir dafür aus.
Dann wären noch das "=" interessant und das "?".
Hieran werde ich entscheiden, ob die dahinterstehende Funktion ein GET, 
ein SET oder ein Report bringen muss.

Im Anschluss daran die Funktionszeigertabelle, die dann die 
entsprechende Funktion anspringt.

Nunja - diese Verbindung zwischen Statmachine und Funktionszeigertabelle 
bereitete mir etwas Kopfzerbrechen.

Vielen Dank und viele Grüße

Axelr.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Axel Rühl schrieb:
> Nunja - diese Verbindung zwischen Statmachine und Funktionszeigertabelle
> bereitete mir etwas Kopfzerbrechen.

Die Sache mit der Tabelle würde ich mir etwa so vorstellen:
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

// Z.B. Typ einer Funktion, die ein Kommando bekommt (erster Parameter
// ist das Kommando, zweiter der Rest der Kommandozeile).
// Rückgabe: eine int als Fehlerkennung
// Könnte auch anders sein, aber alle Funktionen müssen
// gleiche Signatur haben:

typedef int (*cmd_f_t)( const char *p_cmd, char *p_rest );


// Die einzelnen Funktionen (eine zum Kopieren, eine zum Löschen, ...)
// Alle müssen zu der obigen Signatur passen (cmd_f_t):

int copy_f( const char *p_cmd, char *p_rest )
{
  // Kopiere irgendwas anhand p_rest...
  printf( "Kopierbefehl: <%s> <%s>\n", p_cmd, p_rest );

  return 0;
}

int delete_f( const char *p_cmd, char *p_rest )
{
  // Lösche irgendwas anhand p_rest...
  printf( "Löschbefehl: <%s> <%s>\n", p_cmd, p_rest );

  return 0;
}

int rename_f( const char *p_cmd, char *p_rest )
{
  // Benenne irgendwas anhand p_rest um...
  printf( "Schiebebefehl: <%s> <%s>\n", p_cmd, p_rest );

  return 0;
}

// ggf. weitere Funktionen...


// Tabelle mit allen Kommandos und den zugehörigen Funktionen:

typedef struct
{
  const char    *p_cmd;   // Kommando
  cmd_f_t        p_f_t;   // Zeiger auf zugehörige Funktion
} cmd_map_entry_t;

cmd_map_entry_t     cmd_map[] =
{
  { "copy", &copy_f },
  { "delete", &delete_f },
  { "rename", &rename_f },
  // ... ggf. weitere
};

// Anzahl Elemente in der Map:
const int   l_cmd_map = sizeof(cmd_map)/sizeof(cmd_map[0]);


// Vergleichsfunktion für cmd_map_entry_t-Elemente (zum Sortieren mit
// qsort() und zum Suchen mit bsearch() geeignet):
int compare_cmd_map_entry( const void *p1, const void *p2 )
{
  cmd_map_entry_t  *p_cmd_map_entry_1 = (cmd_map_entry_t*)p1;
  cmd_map_entry_t  *p_cmd_map_entry_2 = (cmd_map_entry_t*)p2;

  return strcmp( p_cmd_map_entry_1->p_cmd, p_cmd_map_entry_2->p_cmd );

}


int main( int nargs, char **args )
{
  // Map sortieren (kann entfallen, wenn die Kommandos bereits
  // alphabetisch sortiert in cmd_map[] stehen):
  qsort( cmd_map, l_cmd_map, sizeof(cmd_map[0]), compare_cmd_map_entry );

  // Anhand eines Kommandos die Funktion suchen und aufrufen:
  const char        *p_cmd = "copy";
  char              *p_restliches_Kommando = "a.txt b.bak";
  cmd_map_entry_t   *p_map_entry_gefunden;
  cmd_map_entry_t    schluessel = { "copy", NULL }; // 2. Parameter
                                                    // egal, da
                                                    // compare_cmd_map_entry
                                                    // nur den ersten anschaut

  if( (p_map_entry_gefunden=bsearch( &schluessel,
                                     cmd_map,
                                     l_cmd_map,
                                     sizeof(cmd_map[0]),
                                     compare_cmd_map_entry
                                     )
       )
      )
  {
    // p_map_entry_gefunden ungleich NULL, also Eintrag in Map
    // gefunden!
    // Funktion aufrufen:
    int err = p_map_entry_gefunden->p_f_t( p_cmd, p_restliches_Kommando );
    printf( "Kommando liefert %d als Fehler\n", err );
  }
  else
  {
    // Kommando nicht gefunden!
    printf( "Kommando <%s> unbekannt\n", p_cmd );
  }

  return 0;
}

Nach diesem Strickmuster kann man leicht neue Kommandos hinzufügen.
Mit wenig Änderung (Feld verlängerbar) könnte man sogar zur
Laufzeit neue Kommandos dazubauen. Nach dem Verlängern des Feldes
muß man wieder qsort() aufrufen, damit das Feld sortiert ist und
bsearch() wieder die Kommandos findet.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.