Morsecodegenerator, der 2.

Hi

Nach meinen kläglichen ersten Versuchen einen Morsecodegenerator zu bauen, habe ich von einem anderen Arduino Nutzer den Programmcode eines funktionierenden Morsecodegenerators zugeschickt bekommen. Einige Sachen an dem Programm verstehe ich allerdings noch nicht.

//
// Simple Arduino Morse Beacon
// Written by Mark VandeWettering K6HX
// Email: k6hx@arrl.net
//
// This code is so trivial that I'm releasing it completely without
// restrictions.  If you find it useful, it would be nice if you dropped
// me an email, maybe plugged my blog @ http://brainwagon.org or included
// a brief acknowledgement in whatever derivative you create, but that's
// just a courtesy.  Feel free to do whatever.
//

struct t_mtab { char c, pat; } ;

struct t_mtab morsetab[] = {
  1. Warum werden 2 Strukturen erzeugt?
  2. Was bedeutet dieses struct t_mtab morsetab[]?
  	{'.', 106},
	{',', 115},
	{'?', 76},
	{'/', 41},
	{'A', 6},
	{'B', 17},
	{'C', 21},
	{'D', 9},
	{'E', 2},
	{'F', 20},
	{'G', 11},
	{'H', 16},
	{'I', 4},
	{'J', 30},
	{'K', 13},
	{'L', 18},
	{'M', 7},
	{'N', 5},
	{'O', 15},
	{'P', 22},
	{'Q', 27},
	{'R', 10},
	{'S', 8},
	{'T', 3},
	{'U', 12},
	{'V', 24},
	{'W', 14},
	{'X', 25},
	{'Y', 29},
	{'Z', 19},
	{'1', 62},
	{'2', 60},
	{'3', 56},
	{'4', 48},
	{'5', 32},
	{'6', 33},
	{'7', 35},
	{'8', 39},
	{'9', 47},
	{'0', 63}
} ;

#define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0]))

#define SPEED  (12)
#define DOTLEN  (1200/SPEED)
#define DASHLEN  (3*(1200/SPEED))

int LEDpin = 13 ;

void
dash()
{
  digitalWrite(LEDpin, HIGH) ;
  delay(DASHLEN);
  digitalWrite(LEDpin, LOW) ;
  delay(DOTLEN) ;
}

void
dit()
{
  digitalWrite(LEDpin, HIGH) ;
  delay(DOTLEN);
  digitalWrite(LEDpin, LOW) ;
  delay(DOTLEN);
}

void
send(char c)
{
  int i ;
  if (c == ' ') {
    Serial.print(c) ;
    delay(7*DOTLEN) ;
    return ;
  }
  for (i=0; i<N_MORSE; i++) {
    if (morsetab[i].c == c) {
      unsigned char p = morsetab[i].pat ;
      Serial.print(morsetab[i].c) ;
  1. Was hat es mit morsetab[i].c und morsetab[i].pat auf sich?
      while (p != 1) {
          if (p & 1)
  1. Was bewirkt der & Operator?
            dash() ;
          else
            dit() ;
          p = p / 2 ;
      }
      delay(2*DOTLEN) ;
      return ;
    }
  }
  /* if we drop off the end, then we send a space */
  Serial.print("?") ;
}

void
sendmsg(char *str)
{
  while (*str)
    send(*str++) ;
  Serial.println("");
}

void setup() {
  pinMode(LEDpin, OUTPUT) ;
  Serial.begin(9600) ;
  Serial.println("Simple Arduino Morse Beacon v0.0") ;
  Serial.println("Written by Mark VandeWettering <k6hx@arrl.net>") ;
  Serial.println("Check out my blog @ http://brainwagon.org") ;
  Serial.println("") ;
}

void loop() {
  sendmsg("K6HX/B CM87") ;
  delay(3000) ;
}

Vielen Dank!

Gruß
Atalanttore

morsen.pde (2.24 KB)

struct t_mtab { char c, pat; } ;

Das definiert wie die eigentliche Struktur aussieht, damit das System weiß wie es an die Daten kommt und wie sie gespeichert werden.

struct t_mtab morsetab[] = {

Das beinhaltet dann die tatsächlichen Daten, welcher Buchstabe welches Morsezeichen ist. Wenn ich das richtig verstanden hab steht in “c” der Buchstabe und in “pat” das entsprechende Morsezeichen, wobei ein “1”-Bit für einen Strich und ein “0”-Bit für einen Punkt steht, von rechts ausgehend, mit dem letzten (also am weitesten links stehenden) “1”-Bit als Stopp-Zeichen.

Das “&” sorgt dafür dass nur das am weitesten rechts liegende Bit betrachtet wird, ob es ein “1”- oder ein “0”-Bit ist.

Joghurt: struct t_mtab morsetab[] = {

Das beinhaltet dann die tatsächlichen Daten, welcher Buchstabe welches Morsezeichen ist. Wenn ich das richtig verstanden hab steht in "c" der Buchstabe und in "pat" das entsprechende Morsezeichen, wobei ein "1"-Bit für einen Strich und ein "0"-Bit für einen Punkt steht, von rechts ausgehend, mit dem letzten (also am weitesten links stehenden) "1"-Bit als Stopp-Zeichen.

So hab ich das jetzt verstanden:

struct ? Struktur wird erzeugt t_mtab ? nach dieser Form morsetab[] = ? in dieser Tabelle

Joghurt: Das "&" sorgt dafür dass nur das am weitesten rechts liegende Bit betrachtet wird, ob es ein "1"- oder ein "0"-Bit ist.

Vielen Dank, aber ich verstehe immer noch nicht so ganz wie folgende Schleife arbeitet:

while (p != 1) {               // Während p UNGLEICH 1 ...
          if (p & 1)           // Wenn p ...?
        dash() ;           // Funktion dash() aufrufen
          else                 // Sonst
            dit() ;            // Funktion dit() aufrufen
          p = p / 2 ;          // p wird halbiert
      }                        // Schleifenkörper Ende

Habe meine Gedankengänge als Kommentare dahinter geschrieben.

Gruß Atalanttore

Hallo Atalanttore,

Deine Gedankengänge sind schon ganz richtig. Der & ist der binary UND-Operator (http://www.arduino.cc/en/Reference/BitwiseAnd). Der vergleicht zwei Zahlen bit für bit.

Wenn wir z.B. die Schleife als Beispiel mit einem "A" durchgehen, dann ist p ja zuerst 6 (110).

6 ist ungleich 1, also läuft die schleife los. Das if schaut dann ob 6 & 1 wahr ist (also ungleich 0). 6 & 1 ist aber 0 (110 & 001 = 000), also wird dit() ausgeführt. p=p/2 ist dann gleichbedeutend damit, dass Du in der binären Darstellung der Zahl alle Stellen um eins nach rechts verschiebst (aus 6 wird 3, binär: aus 110 wird 11).

Jetzt kommt also der zweite Schleifendurchgang mit p=3 (binär 11). Das ist immer noch ungleich 1, also wird die Schleife weiter ausgeführt. 2 & 1 ist diesmal aber wahr (weil 11 & 01 == 01), es wird nun also ein dash() ausgegeben. Jetzt werden wieder alle Stellen um eins nach rechts verschoben, was aus der 3 die 1 macht (binär wieder 11 -> 1).

Diesmal ist p also gleich 1 und damit wird die Schleife beendet und hat das "A" in den Morsecode kurz-lang umgewandelt.

Ich hoffe das war verständlich;-) Les einfach mal den Artikel über die binären Operatoren, ich bin mir sicher, dann verstehst Du den Code:-)

Viele Grüße

Harald

Vielen Dank für die Erklärung, aber die Verwendung des ASCII-Code als Morsecode mit Stoppbit und dessen Auswertung mit binären Operatoren ist ziemlich verwirrend für mich.

Da mir noch vieles unklar ist, habe ich mal den gesamten Programmcode mit meinen Überlegungen kommentiert.

//
// Simple Arduino Morse Beacon
// Written by Mark VandeWettering K6HX
// Email: k6hx@arrl.net
//
// This code is so trivial that I'm releasing it completely without
// restrictions.  If you find it useful, it would be nice if you dropped
// me an email, maybe plugged my blog @ http://brainwagon.org or included
// a brief acknowledgement in whatever derivative you create, but that's
// just a courtesy.  Feel free to do whatever.
//
// > Ja nee, is klar! Voll einfach.

struct t_mtab { char c, pat; } ; // Struktur nach der Form t_mtab erzeugen mit der
                                 // Variable c vom Datentyp char in der linken Spalte und
                                 // pat ohne Datentyp (?) auf der rechten Spalte.

struct t_mtab morsetab[] = {     // Struktur morsetab[] mit der Form t_mtab erzeugen.
  	{'.', 106},
	{',', 115},
	{'?', 76},
	{'/', 41},
	{'A', 6},
	{'B', 17},
	{'C', 21},
	{'D', 9},
	{'E', 2},
	{'F', 20},
	{'G', 11},
	{'H', 16},
	{'I', 4},
	{'J', 30},
	{'K', 13},
	{'L', 18},
	{'M', 7},
	{'N', 5},
	{'O', 15},
	{'P', 22},
	{'Q', 27},
	{'R', 10},
	{'S', 8},
	{'T', 3},
	{'U', 12},
	{'V', 24},
	{'W', 14},
	{'X', 25},
	{'Y', 29},
	{'Z', 19},
	{'1', 62},
	{'2', 60},
	{'3', 56},
	{'4', 48},
	{'5', 32},
	{'6', 33},
	{'7', 35},
	{'8', 39},
	{'9', 47},
	{'0', 63}
} ;

#define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0])) // Länge der Variable morsetab geteilt durch die Länge der Variable morsetab[0]

#define SPEED  (12)                                     // int SPEED = 12
#define DOTLEN  (1200/SPEED)                            // int DOTLEN = 1200/SPEED
#define DASHLEN  (3*(1200/SPEED))                       // int DASHLEN = 3*(1200/SPEED)

int LEDpin = 13 ;

void
dash()
{
  digitalWrite(LEDpin, HIGH) ;
  delay(DASHLEN);
  digitalWrite(LEDpin, LOW) ;
  delay(DOTLEN) ;
}

void
dit()
{
  digitalWrite(LEDpin, HIGH) ;
  delay(DOTLEN);
  digitalWrite(LEDpin, LOW) ;
  delay(DOTLEN);
}

void
send(char c)                   // Funktion send() definieren, welche ein Argument benötigt,
                               // dass in der Variable c vom Datentyp char gespeichert wird.
{
  int i ;                      // Varible i ist vom Datentyp Integer.
  if (c == ' ') {              // Wenn c gleich einem Leerzeichen ...
    Serial.print(c) ;          // c in der seriellen Konsole ausgeben.
    delay(7*DOTLEN) ;          // Warten, 7 mal so lang wie der Wert der Variable DOTLEN.
    return ;                   // Zurückgeben
  }
  for (i=0; i<N_MORSE; i++) {  // i ist 0; i kleiner als N_MORSE; i um den Wert 1 erhöhen.
    if (morsetab[i].c == c) {  // Wenn morsetab[i].c gleich einem Leerzeichen ...
      unsigned char p = morsetab[i].pat ;  // Variable p ist vom Datentyp unsigned char und
                                           // ist gleich dem Wert aus der pat-Spalte der Struktur.
      Serial.print(morsetab[i].c) ;        // Wert von morsetab[i].c auf der seriellen Konsole ausgeben.

      while (p != 1) {         // Solange der Wert von p UNGLEICH 1 ist bzw. die letzte 1 (Stoppbit) nicht kommt ...
          if (p & 1)           // Wenn der binäre Wert von p gleich dem binären Wert von 1 ist ...
	    dash() ;           // Funktion dash() aufrufen.
          else                 // Sonst
            dit() ;            // Funktion dit() aufrufen.
          p = p / 2 ;          // p halbieren.
      }                        // Schleifenkörper Ende.
      delay(2*DOTLEN) ;        // Warten, 2 mal so lang wie der Wert der Variable DOTLEN
      return ;                 // Zurückgeben
    }
  }
  /* if we drop off the end, then we send a space */
  Serial.print("?") ;          // ? auf der seriellen Konsole ausgeben
}

void
sendmsg(char *str)            // Funktion sendmsg() definieren, welche ein Argument benötigt,
                              // dass in der Variable *str vom Datentyp char gespeichert wird.
{
  while (*str)                // Solange *str ???
    send(*str++) ;            // Mit *str um 1 erhöht die Funktion send() aufrufen.
  Serial.println("");         // ??? auf der seriellen Konsole ausgeben.
}

void setup() {
  pinMode(LEDpin, OUTPUT) ;   // LEDpin als Ausgang festlegen.
  Serial.begin(9600) ;        // Serielle Schnittstelle initialisieren.
  Serial.println("Simple Arduino Morse Beacon v0.0") ;                // Programmname auf der seriellen Konsole ausgeben.
  Serial.println("Written by Mark VandeWettering <k6hx@arrl.net>") ;  // Programmierername auf der seriellen Konsole ausgeben.
  Serial.println("Check out my blog @ http://brainwagon.org") ;       // URL auf der seriellen Konsole ausgeben.
  Serial.println("") ;
}

void loop() {
  sendmsg("K6HX/B CM87") ;   // Die Funktion sendmsg() mit dem Argument "K6HX/B CM87" aufrufen.
  delay(3000) ;              // 3 Sekunden warten.
}

Gruß
Atalanttore

Ich hätte da noch ein Programm das auch den Morse Code erzeugt.

Die Zeichenkette kann man über den Seriellen Monitor senden. Da dieses Programm aus meinen Anfängen mit dem Arduino stammt, ist kein Wert auf Speicheroptimierung gelegt worden.

Code:

/* ********************************************** */
/* Autor:       Nils F.                           */
/* Datum:       19.05.2009                        */
/* Zeitaufwand: 5 Stunden                         */
/* ********************************************** */

//# INIT
    int signal = 13;
    int value = LOW;

//# Zeiten
    int t_char  = 400; // Charakter --
    int t_num   = 200;  // Nummer    -
    int t_short = 100;  // Short     .
    int t_break = 500;  // Pause nach einem Vector

//# Arrays
    char array_char[26] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};

//# Matritzen
    int matrix_int[10][5] = {
        {2,2,2,2,2}, // 0
        {1,2,2,2,2}, // 1
        {1,1,2,2,2}, // 2
        {1,1,1,2,2}, // 3
        {1,1,1,1,2}, // 4
        {1,1,1,1,1}, // 5
        {2,1,1,1,1}, // 6
        {2,2,1,1,1}, // 7
        {2,2,2,1,1}, // 8
        {2,2,2,2,1}  // 9
    };

    int matrix_char[26][4] ={
        {3,1,0,0},   // A
        {3,1,1,1},   // B
        {3,1,3,1},   // C
        {3,1,1,0},   // D
        {1,0,0,0},   // E
        {1,1,3,1},   // F
        {3,3,1,0},   // G
        {1,1,1,1},   // H
        {1,1,0,0},   // I
        {1,3,3,3},   // J
        {3,1,3,0},   // K
        {3,3,3,1},   // L
        {3,3,0,0},   // M
        {3,1,0,0},   // N
        {3,3,3,0},   // O
        {1,3,3,1},   // P
        {3,3,1,3},   // Q
        {1,3,1,0},   // R
        {1,1,1,0},   // S
        {3,0,0,0},   // T
        {1,1,3,0},   // U
        {1,1,1,3},   // V
        {1,3,3,0},   // W
        {3,1,1,3},   // X
        {3,1,3,3},   // Y
        {3,3,1,1}    // Z
    };

//# Setup
    void setup() 
    { 
      //## Interfaces
      Serial.begin(115200); 
      
      //## Outputs
      pinMode(signal, OUTPUT); 
    } 


//# Morse Serial (R\u00fcckgabe Wert was gerade gemacht wird)
    void morse_ser(int s)
    {
    //## init     
        int i, ii;
      
    //## serial
        Serial.print(s);
        Serial.print(" = ");  
        digitalWrite(signal, LOW);
      
    //## Ausgabe Zahlen
      if(s >= 48 && s <= 57) {
          Serial.print(s-48);
      }

    //## Ausgabe Gro\u00dfe Buchstaben      
      if( s >= 65 && s <= 90) {
          Serial.print(array_char[(s-65)]);
      }
      
    //## Ausgabe Gro\u00dfe Buchstaben
      if( s >= 97 && s <= 122) {
          Serial.print(array_char[(s-97)]);
      }
      
    //## Newline
      Serial.print("\n");  
    }

//# Send Signal to LED (numeric)
    void send_signal_num(int v[5]) 
    {
    //## init
        int i;
        
    //## Ausgabe Signal
        for(i=0;i<5;i++) {
            if(v[i] == 0) {
            // Schleife Beenden
                break;
            } else if(v[i] == 1) {
            // Kurzes Signal
                digitalWrite(signal, HIGH);
                delay(t_short);      
            } else if(v[i] == 2) {
            // Langes Signal Zahl
                digitalWrite(signal, HIGH);
                delay(t_num);
            }
            // Pause Signal
            digitalWrite(signal, LOW);    
            delay(t_num);    
        }
        delay(t_break);
    }

//# Send Signal to LED (char)
    void send_signal_char(int v[4]) 
    {
    //## init
        int i;  
    
    //## Ausgabe Signal
        for(i=0;i<4;i++) {
            if(v[i] == 0) {
            // Schleife Beenden
                break;
            } else if(v[i] == 1) {
            // Kurzes Signal
                digitalWrite(signal, HIGH);
                delay(t_short);     
            } else if(v[i] == 3) {
            // Langes Signal Character
                digitalWrite(signal, HIGH);
                delay(t_char);
            }
            // Pause Signal
            digitalWrite(signal, LOW);      
            delay(t_char);   
        }
        delay(t_break);
    }

//# Morse LED (einteilung: numeric oder char)
    void morse_led(int s)
    {
    //## init
        int i = 0;
        int ii = 0;

    //## numeric  
        if(s >= 48 && s <= 57) {
            send_signal_num(matrix_int[(s-48)]) ;    
        }
    
    //## char
        if( s >= 65 && s <= 90) {
            send_signal_char(matrix_char[(s-65)]);    
        }  
        if( s >= 97 && s <= 122) {
            send_signal_char(matrix_char[(s-97)]);
        }   
    }
  
//# Morse
    void morse(int vec[20])
    {
    //## init
        int i;

    //## Maximal 20 Signale 
        for(i=0; i<20; i++) {
            if(vec[i] == 33) {
                break;
            }
        //### Serielle Ausgabe
            morse_ser(vec[i]); 
  
        //### Led Ausgabe
            morse_led(vec[i]);   
        } 
    }

//# Loop 
    void loop() 
    {
    //## init  
        int vec[20];
        int i;
    
        for(i=0;i<20;i++) {
            vec[i] = 33;     //33 entspricht dem "!", ich verwende es in diesem fall als end signal
        }
        
    //## einlesen in einen vector 
    i = 0;  
    while(Serial.available()) {        
        vec[i] = Serial.read();    
        i++;    
    }

    //## Ausgabe, das Werte empfangen wurden  
    if(i > 0) {
        //### ausgabe
        Serial.println("Eine neu Eingabe wurde erfasst:\n");
        
        //### serielle und led ausgabe aufrufen  
        morse(vec);
    }
}

Alles ist Kommentiert.
Gruß
Jomelo

struct t_mtab { char c, pat; } ; // Hier wird definiert wie die Struktur "t_mtab" aussieht. Sie besteht aus jeweils zwei "char"s mit den Bezeichnungen "c" und "pat".

struct t_mtab morsetab[] = {     // Hier wird hinterlegt welches Zeichen welchem Morsecode entspricht. Der Morsecode ist binär hinterlegt, von der rechten Seite ausgehend, das beim shiften letzte HIGH-Bit dient als Stopp-Bit.
  	{'.', 106},
	{',', 115},
	{'?', 76},
	{'/', 41},
	{'A', 6},
	{'B', 17},
	{'C', 21},
	{'D', 9},
	{'E', 2},
	{'F', 20},
	{'G', 11},
	{'H', 16},
	{'I', 4},
	{'J', 30},
	{'K', 13},
	{'L', 18},
	{'M', 7},
	{'N', 5},
	{'O', 15},
	{'P', 22},
	{'Q', 27},
	{'R', 10},
	{'S', 8},
	{'T', 3},
	{'U', 12},
	{'V', 24},
	{'W', 14},
	{'X', 25},
	{'Y', 29},
	{'Z', 19},
	{'1', 62},
	{'2', 60},
	{'3', 56},
	{'4', 48},
	{'5', 32},
	{'6', 33},
	{'7', 35},
	{'8', 39},
	{'9', 47},
	{'0', 63}
} ;

#define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0])) // Hier wird die Anzahl der Morsezeichen in der Struktur "morsetab" bestimmt indem ihre Gesamtgröße durch die Größe des ersten Eintrags dividiert wird. Da alle Einträge zwei chars lang sind ist das repräsentativ.

#define SPEED  (12)                                     // Jede Stelle im Code, an der "SPEED" vorkommt, wird vor dem compilieren durch "(12)" ersetzt. Das steht dann fix da drin und es wird kein Variablenspeicher verschwendet.
#define DOTLEN  (1200/SPEED)                            // Analog mit DOTLEN...
#define DASHLEN  (3*(1200/SPEED))                       // Analog mit DASHLEN...

int LEDpin = 13 ;                 // Das ist der einzige Pin am Arduino, der von Haus aus eine LED angeschlossen hat.

void
dash()
{
  digitalWrite(LEDpin, HIGH) ;                // LED einschalten
  delay(DASHLEN);                                 // 3*1200/12 Millisekunden warten
  digitalWrite(LEDpin, LOW) ;                // LED ausschalten
  delay(DOTLEN) ;                                 // 1200/12 Millisekunden warten, das ist die Zeit zwischen den einzelnen Morsezeichen innerhalb eines Buchstaben
}

void
dit()
{
  digitalWrite(LEDpin, HIGH) ;
  delay(DOTLEN);
  digitalWrite(LEDpin, LOW) ;
  delay(DOTLEN);
}

void
send(char c)                   // Diese Funktion morst einen Buchstaben, welcher in "c" übergeben wird
{
  int i ;
  if (c == ' ') {              // Wenn das übergebene Zeichen ein Leerzeichen ist, dann warte 700 Millisekunden. Das ist die Zeit zwischen zwei Morsezeichen.
    Serial.print(c) ;          // c zum debuggen in der seriellen Konsole ausgeben.
    delay(7*DOTLEN) ;
    return ;                   // Beim Leerzeichen muss nichts gemorst werden, daher kann die Funktion an dieser Stelle verlassen werden.
  }
  for (i=0; i<N_MORSE; i++) {  // Die komplette "morsetab"-Liste durchgehen
    if (morsetab[i].c == c) {  // Wenn das übergebene Zeichen in der Liste gefunden wurde...
      unsigned char p = morsetab[i].pat ;  // ...dann das zugehörige Morsezeichen in der Variable "p" merken.
      Serial.print(morsetab[i].c) ;        // Das gefundene Zeichen zwecks debugging in die Konsole ausgeben.

      while (p != 1) {         // Solange die Variable "p" noch nicht nur das Stoppbit enthält
          if (p & 1)           // Wenn das am weitesten rechts liegende Bit von "p" HIGH (also "1") ist ...
	    dash() ;           // dann morse einen Strich.
          else                 // Sonst
            dit() ;            // morse einen Punkt
          p = p / 2 ;          // Alle Bits in "p" um eine Stelle nach rechts verschieben. Das aktuelle rechte Bit, welches ja gerade schon betrachtet wurde, fällt dabei weg. Das bisherige zweite Bit von rechts wird das neue rechte Bit etc.
      }                        // Bis hierhin wird alles so lange wiederholt bis "p" nur noch das Stoppbit enthält
      delay(2*DOTLEN) ;        // 200ms warten, das ist die Zeit zwischen den einzelnen Dits und Dahs. Prinzipiell ist entweder diese Zeile oder die jeweils letzten Zeilen in dit() und dash() unnötig, weil sie das gleiche machen.
      return ;                 // Das ist ein bisschen tricky: Wenn das "c"-Zeichen gefunden und alles gemorst wurde muss die for-Schleife nicht weiter durchlaufen werden, weils ja keine weiteren Treffer mehr geben kann. Daher kann die Schleife an dieser Stelle verlassen werden.
    }
  }
  /* if we drop off the end, then we send a space */
  Serial.print("?") ;          // Falls ein unbekanntes Zeichen gemorst werden soll (also eines welches nicht in der Struktur vorkommt), dann wird hier ein "?" auf der seriellen Konsole ausgeben
}

void
sendmsg(char *str)            // Diese Funktion teilt eine Kette von Zeichen (die in "str" übergeben werden) in ihre Bestandteile und morst jeden einzelnen dieser Teile einzeln.
{
  while (*str)                // Eine Zeichenkette wie z.B. "abc" besteht im Speicher aus "a","b" und "c" sowie einem 0-Byte am Ende, damit das System weiß dass da Schluß ist. Eine while-Schleife wird so lange durchlaufen wie der Wert in der Klammer ungleich 0 ist. Somit würde sie drei Mal durchlaufen und "send" die Werte "a", "b" und "c" übergeben und dan wegen der abschließenden 0 nicht weiter durchlaufen.
    send(*str++) ;            // Hier gehts um Pointerarithmetik. Im Speicher steht irgendwo "abc0", "str" zeigt am Anfang auf "a". Wenn "str" eines hochgezählt wird zeigt es auf "b" und so weiter.
  Serial.println("");         // Einen Zeilenumbruch auf der seriellen Konsole ausgeben. Ein Return, quasi.
}

void setup() {
  pinMode(LEDpin, OUTPUT) ;   // LEDpin als Ausgang festlegen.
  Serial.begin(9600) ;        // Serielle Schnittstelle initialisieren.
  Serial.println("Simple Arduino Morse Beacon v0.0") ;                // Programmname auf der seriellen Konsole ausgeben.
  Serial.println("Written by Mark VandeWettering <k6hx@arrl.net>") ;  // Programmierername auf der seriellen Konsole ausgeben.
  Serial.println("Check out my blog @ http://brainwagon.org") ;       // URL auf der seriellen Konsole ausgeben.
  Serial.println("") ;
}

void loop() {
  sendmsg("K6HX/B CM87") ;   // Diese Nachricht soll gemorst werden
  delay(3000) ;              // 3 Sekunden warten.
}

Joghurt:

#define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0])) // Hier wird die Anzahl der Morsezeichen in der Struktur "morsetab" bestimmt indem ihre Gesamtgröße durch die Größe des ersten Eintrags dividiert wird. Da alle Einträge zwei chars lang sind ist das repräsentativ.

Welche Einträge sind zwei chars lang und was ist daran repräsentativ?

Joghurt:

#define SPEED  (12)                                     // Jede Stelle im Code, an der "SPEED" vorkommt, wird vor dem compilieren durch "(12)" ersetzt. Das steht dann fix da drin und es wird kein Variablenspeicher verschwendet.

#define DOTLEN  (1200/SPEED)                            // Analog mit DOTLEN…
#define DASHLEN  (3*(1200/SPEED))                       // Analog mit DASHLEN…

Also werden die Zahlenwerte/Rechnungen ohne Variablenzuweisung direkt ins Programm miteinkompiliert. Ist das etwa ressourcenschonender?

Joghurt:

  for (i=0; i<N_MORSE; i++) {  // Die komplette "morsetab"-Liste durchgehen

if (morsetab[i].c == c) {  // Wenn das übergebene Zeichen in der Liste gefunden wurde…
     unsigned char p = morsetab[i].pat ;  // …dann das zugehörige Morsezeichen in der Variable “p” merken.
     Serial.print(morsetab[i].c) ;        // Das gefundene Zeichen zwecks debugging in die Konsole ausgeben.

Das [i] steht dann wohl für die Position des gerade ausgewählten Buchstabens, oder?

Joghurt:

      while (p != 1) {         // Solange die Variable "p" noch nicht nur das Stoppbit enthält

if (p & 1)           // Wenn das am weitesten rechts liegende Bit von “p” HIGH (also “1”) ist …
   dash() ;           // dann morse einen Strich.
         else                 // Sonst
           dit() ;            // morse einen Punkt
         p = p / 2 ;          // Alle Bits in “p” um eine Stelle nach rechts verschieben. Das aktuelle rechte Bit, welches ja gerade schon betrachtet wurde, fällt dabei weg. Das bisherige zweite Bit von rechts wird das neue rechte Bit etc.
     }                        // Bis hierhin wird alles so lange wiederholt bis “p” nur noch das Stoppbit enthält

Also “p” ist ja binär. Wie wird “p” um eine Stelle nach rechts verschoben, wenn man es durch 2 teilt? Verstehe das Prinzip dahinter nicht.

Joghurt:

void

sendmsg(char *str)            // Diese Funktion teilt eine Kette von Zeichen (die in “str” übergeben werden) in ihre Bestandteile und morst jeden einzelnen dieser Teile einzeln.
{
 while (*str)                // Eine Zeichenkette wie z.B. “abc” besteht im Speicher aus “a”,“b” und “c” sowie einem 0-Byte am Ende, damit das System weiß dass da Schluß ist. Eine while-Schleife wird so lange durchlaufen wie der Wert in der Klammer ungleich 0 ist. Somit würde sie drei Mal durchlaufen und “send” die Werte “a”, “b” und “c” übergeben und dan wegen der abschließenden 0 nicht weiter durchlaufen.
   send(*str++) ;            // Hier gehts um Pointerarithmetik. Im Speicher steht irgendwo “abc0”, “str” zeigt am Anfang auf “a”. Wenn “str” eines hochgezählt wird zeigt es auf “b” und so weiter.
}

Was bedeutet das Sternchen vor dem “str”?

Vielen Dank für deine Erklärungen.


@Jomelo: Vielen Dank für dein Programm. Sieht etwas einfacher aus als das Programm das ich bekommen habe.

Gruß
Atalanttore

Also werden die Zahlenwerte/Rechnungen ohne Variablenzuweisung direkt ins Programm miteinkompiliert. Ist das etwa ressourcenschonender?

Jap, das stimmt. Es ist nicht Ressourcenshonender, es hat nur den Vorteil das Werte an einer Stelle verändert werden können.

Das steht dann wohl für die Position des gerade ausgewählten Buchstabens, oder? [/quote] Jap, das stimmt auch. Die genaue Erklärung lässt sich mit der nächsten Antwort zusammen fassen > Was bedeutet das Sternchen vor dem "str"? Es handelt sich hier um Zeiger. Zeiger klingen erstmal schlimm, da sie für nicht wissende auch schlimm sind ;-) Ein Beispiel: ``` char *str = "Das hier ist ein String"; ``` Bei dieser Zuweisung wird eine Kette von char Zeichen also ein String erzeugt. Jeder einzelne Buchstabe kann nun mit ``` str[i]; ``` Ausgewählt werden, wobei i für die Position des Zeichens steht. Mann kann auch mit ``` *str ``` auf die Variable zugreifen. Hier bekommt man immer den ersten Buchstaben ausgeben. Wenn man nun * *``` str++ ```

ausführt. Wird der Zeiger um eine Position weiter geschoben und du kannst den nächsten Buchstaben abfragen. *Arbeiten mit Zeigern braucht Erfahrung und diese ist als Unwissender immer schwer zu erlangen. *

Danke, nun verstehe ich wieder etwas mehr und vor Zeigern hab ich jetzt auch keine Angst mehr. :D

Ein paar Fragen hab ich aber noch:

Joghurt: ```

define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0])) // Hier wird die Anzahl der Morsezeichen in der Struktur "morsetab" bestimmt indem ihre Gesamtgröße durch die Größe des ersten Eintrags dividiert wird. Da alle Einträge zwei chars lang sind ist das repräsentativ.

Welche Einträge sind zwei chars lang? Was ist mit repräsentativ gemeint?

Joghurt:       while (p != 1) {        // Solange die Variable "p" noch nicht nur das Stoppbit enthält           if (p & 1)          // Wenn das am weitesten rechts liegende Bit von "p" HIGH (also "1") ist ...     dash() ;          // dann morse einen Strich.           else                // Sonst             dit() ;            // morse einen Punkt           p = p / 2 ;          // Alle Bits in "p" um eine Stelle nach rechts verschieben. Das aktuelle rechte Bit, welches ja gerade schon betrachtet wurde, fällt dabei weg. Das bisherige zweite Bit von rechts wird das neue rechte Bit etc.       }                        // Bis hierhin wird alles so lange wiederholt bis "p" nur noch das Stoppbit enthält

"p" ist ja binär. Wie wird "p" um eine Stelle nach rechts verschoben, wenn man es durch 2 teilt? Kann mir einfach keinen Reim drauf machen.

Gruß Atalanttore

"p" ist ja binär. Wie wird "p" um eine Stelle nach rechts verschoben, wenn man es durch 2 teilt?
Kann mir einfach keinen Reim drauf machen.

Was passiert, wenn du im Dezimalsystem z.B. 1000 durch 10 teilst? Du bekommst 100. Binär ist Basis zwei, also das gleiche Spiel, nur durch zwei Teilen.

Alternative Veranschaulichung mittels simplem Rechnen:

p = 123 = 0b01111011 p / 2 = 61 = 0b00111101

Ah, danke, aber eigentlich ist 123 / 2 = 61,5 oder doch wieder nicht???

Gruß Atalanttore

Nicht, wenn wir mit zwei Integer-Werten arbeiten, da fällt der Rest nämlich weg.

Auch wenn du es schon verstanden hast, ich hab mir gerad die Mühe gemacht, es schriftlich zu dividieren, also füg ich das auch mal noch mit ein:

      00111101 remainder 1
      ---------
10 )  01111011
     -0|||||||   
     ---------    
      01||||||    
     -00||||||     
     ---------    
       11|||||     
      -10|||||
      --------
        11||||
       -10||||     
       -------       
         11|||       
        -10|||      
        ------      
          10||       
         -10||      
         -----  
           01|   
          -00|
          ----
            11
           -10
           ---   
             1

Wenn du dir schon die Mühe gemacht hast, kann ich dich auch gleich noch fragen was diese | Striche zu bedeuten haben? ;)

Gruß Atalanttore

Die sollten eigentlich dazu dienen, das ganze übersichtlicher zu machen, damit man sieht, von wo man sich die neuen Ziffern runter holt (schriftliche Division). In der Schule wurden da auch gerne mal Pfeile gezeichnet.

Um noch die Frage mit N_MORSE zu beantworten: Wir wissen, dass jedes Element in morsetab die gleiche Größe hat, weil in C alle Elemente eines Arrays den gleichen Typ haben. Wenn wir also wissen, dass das gesammte Array X Bytes groß ist, und irgendein beliebiges Element in dem Array Y Bytes groß ist, dann ist X/Y die Anzahl der Elemente, weil X = n * Y, wo n die Anzahl der Elemente ist.

morsetab[0] dient hier einfach als "irgendein beliebiges Element im Array".

Zwei chars ist es lang, weil der struct genau zwei Elemente hat, beide vom Typ char¹.

¹: Es gibt bei structs noch sogenanntes Padding, damit alle Elemente an bestimmten Grenzen im Speicher anfangen (z.B. an 4-byte Boundaries). Daher kann man nicht immer sagen, dass die Größe eines Structs einfach die Summe aller Elemente ist. Dies spielt für die eigentliche Rechnung X/Y aber keine Rolle. Wenn Y größer wird, wird auch X linear größer.

dominikh: Um noch die Frage mit N_MORSE zu beantworten: Wir wissen, dass jedes Element in morsetab die gleiche Größe hat, weil in C alle Elemente eines Arrays den gleichen Typ haben. Wenn wir also wissen, dass das gesammte Array X Bytes groß ist, und irgendein beliebiges Element in dem Array Y Bytes groß ist, dann ist X/Y die Anzahl der Elemente, weil X = n * Y, wo n die Anzahl der Elemente ist.

was mich zu der Frage verleitet, wieso man dem Arduino C nicht so etwas wie ein "foreach" beibringen könnte. :%

Diese Frage musst du nicht uns stellen, sondern den Entwicklern der Arduino Umgebung. Wir können hier viel darüber diskutieren aber etwas Sinnvolles wird nie herauskommen. ;-)

Oh, die Antwort ist einfach: Weil es eben immer noch C ist :) Arrays werden als Pointer auf das erste Element übergeben, und somit ist die Länge des Arrays nicht bekannt. Bei Strings (also char Arrays) ist es einfach lösbar, in dem man festlegt, dass das letzte Element die Null ist. Das geht bei anderen Arrays aber eher schlecht, weil die Null da ein valides Element sein kann.

Heißt selbst wenn der Arduino Umgebung ein extra Parser verpasst werden würde, der eine Ergänzung wie "foreach" implementiert, wäre es nicht möglich, dies sauber in C zu übersetzen.

Für jedes Array implizit so eine Konstante zu erzeugen ist natürlich auch keine Option zwecks Namenskonflikten und fällen, wo es einfach nicht möglich ist (zur Laufzeit bestimmte Länge, z.b. [malloc]).

void loop() {
  sendmsg("K6HX/B CM87") ;
  delay(3000) ;
}

Da mir die Ausgabe eines vorbestimmten Textes zu langweilig war, habe ich versucht den Code um eine Eingabe per serieller Konsole umzuschreiben.

Am Anfang des Programms eine neue Variable namens incomingByte definiert:

int N_MORSE = sizeof(morsetab)/sizeof(morsetab[0]);
int SPEED = 12;
int DOTLEN = 1200/SPEED;
int DASHLEN = 3*(1200/SPEED);
int LEDpin = 13 ;
char incomingByte = 0;

Und die loop-Schleife um eine Abfrage der seriellen Konsole erweitert.

void loop() {

  if (Serial.available() > 0) {    
    incomingByte = Serial.read();
    Serial.print("Du hast ");
    Serial.print(incomingByte);
    Serial.print(" eingegeben.\n");
    }
  sendmsg(incomingByte) ;

  delay(3000) ;
}

Fast wie erwartet funktioniert es nicht. Es erscheinen folgende Fehlermeldungen:

Morsecodegenerator.cpp: In function »void loop()«: Morsecodegenerator.cpp:142: Fehler: ungültige Umwandlung von »char« in »char*« Morsecodegenerator.cpp:142: Fehler: Argument 1 von »void sendmsg(char*)« wird initialisiert

Ist das ein Problem mit den Zeigern auf char*?

Gruß Atalanttore