Création d'une librairie concernant le morse avec buzzer

Bonjour,
Avant propos : je me suis inspiré d'une librairie dont je ne retrouve pas l'auteur. J'ai supprimé la fonction led, je l'ai complètement revisitée en la simplifiant au niveau du code, en y ajoutant une classe... J'ai gardé l'algorithme qui permet le calcul de la vitesse de lecture : (60 * 1000L) / (50 * vitesse)
MorseCode.h (9,4 Ko)
Bizarrement un fichier .h sans .cpp suffit à la mettre en œuvre !

Depuis mon dernier fil de discussion :
https://forum.arduino.cc/t/poo-creation-librairie-creation-dune-classe/1021804
J'ai décidé de stopper mes lectures (livres sur le C++) afin de mettre à profit ce que vous m'avez appris et d'essayer de créer une librairie sans plus de connaissances. J'ai donc fait une librairie pour lire le morse à partir du moniteur série et le traduire au niveau sonore avec la fonction tone() d'arduino et un Buzzer piézoélectrique. j'aurai pu la zipper selon la structure adéquate (keywords, exemple) mais je me contente ici de vous la présenter. Elle souffre peut-être d'imperfections ...
Le fichier ino a pour unique fonction de créer un tableau de char nommé message et de le transmettre à la classe Morse avec en paramètres en plus du tableau :

  • la pin utilisée ;
  • la vitesse de transcription sonore ;
  • et la fréquence la plus adaptée au code Morse.
    Dans l'ordre : pin, message, vitesse, fréquence.
#include "Morse.h"

const size_t taille = 250;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  if ( Serial.available() > 0 )
  {
    static char message[taille];
    static uint8_t i;

    char c = Serial.read();

    if ( c != '\n' && i < taille - 1 )
      message[ i++ ] = c;
    else
    {
      message[ i ] = '\0';
      i = 0;

      Serial.print( "transcription en morse de : " );
      Serial.println( message );
      Morse Code(12,message,17, NOTE_C4); // configuration la plus adaptée au Morse 
      Code.joue();
    }
  }

}

Le fichier h est on ne peut plus simple :

#include <arduino.h> // pour fonction tone()

#define NOTE_C4  262 // fréquence la plus adaptée pour le morse

#ifndef Morse_h
#define Morse_h

class Morse {
  public:
    // Constructeur
    Morse(int pin, char *s, int vitesse, int frequence);
    void joue(void);

    // Destructeur
    ~Morse();
    
  private:
    int _pin;
    char *_s;
    int _vitesse;
    int _frequence;
};

#endif

Quant au fichier cpp il se contente dans la méthode joue de la classe Morse:

  • d'analyser message lettre par lettre ;
  • de transcrire les lettres et chiffres en points et en tirets (tableaux codeMorse et codeMorseChiffre) ;
  • de transmettre les lettres transformées en points et tirets à la fonction lettres

Cette dernière fonction transmettra chaque point et tiret aux fonctions void point(int pin, int duree, int frequence) et void tiret(int pin, int duree, int frequence) aux fins de transcription sonore avec la fonction tone() d'arduino.

#include "Morse.h"
const char *codeMorse[27] = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."};
const char *codeMorseChiffre[11] = {"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----."};

// joue un point avec la fonction tone() d'arduino
void point(int pin, int duree, int frequence)
{
  tone(pin, frequence, duree);
  delay(duree);
}

// joue un tiret avec la fonction tone() d'arduino
void tiret(int pin, int duree, int frequence)
{
  duree *= 3; // un tiret = trois points
  tone(pin, frequence, duree);
  delay(duree);
}

// chaque point et tiret constitutif d'une lettre est transmit à la procédure point() ou tiret() pour transcription sonore
void lettres(int pin, int vitesse, int frequence, char *a)
{

  while ( *a ) {

    switch ( *a ) {
      case '.':
        point(pin, vitesse, frequence);
        delay(vitesse); // entre chaque point constitutif d'une lettre un espacement de temps court
        break;

      case '-':
        tiret(pin, vitesse, frequence);
        delay(vitesse); // entre chaque tiret constitutif d'une lettre un espacement de temps court
        break;
    }

    a++;
  }
  delay(3 * vitesse); // entre chaque lettre un espacement de temps un peu plus long (X3)
}

// Initialisation
Morse::Morse(int pin, char *s, int vitesse, int frequence) 
  : _pin(pin),
    _s(s),
    _vitesse((60 * 1000L) / (50 * vitesse)), // _vitesse (vitesse des points) = 1 minute divisée par vitesse * 50
    _frequence(frequence)
{}

void Morse::joue() {

  /*On extrait chaque lettre ou chiffre du tableau de char transmit à la classe Morse - on transmet l'équivalent en morse
    (points et tirets) à la procédure lettres (tableaux de char *codeMorse ou *codeMorseChiffre)
  */
  while ( *_s ) {
    Serial.print(*_s);
    for (byte i = 0; i < 26; i++) {
      if ((*_s == 'a' + i) || (*_s == 'A' + i)) { //on teste d'abord les minuscules puis les majuscules
        Serial.print("\t"); Serial.println((char*)codeMorse[i]);
        lettres(_pin, _vitesse, _frequence, (char*)codeMorse[i]);
      }
    }
    for (byte j = 0; j < 10; j++) {
      if (*_s == '0' + j) { //on teste les chiffres
        Serial.print("\t"); Serial.println((char*)codeMorseChiffre[j]);
        lettres(_pin, _vitesse, _frequence, (char*)codeMorseChiffre[j]);
      }
    }
    if (*_s == 32) { // en cas d'espace un délais plus long est appliqué
      delay(8 * _vitesse);
      Serial.print("\n");
    }
    _s++;
  }

}

// Destruction
Morse::~Morse() {}

Voilà.
Bonne journée.

Dans ton .h tu définis une constante.
Pas de problème, sauf si ce .h est inclus dans un programme qui définit lui-même cette constante NOTE_C4 (avec une autre valeur bien sûr)
Pour éviter ce risque, tu peux définir ta constante à l'intérieur de la classe:

class Morse {
public:
   .../...
  const int NOTE_C4 = 262;
  .../...
};

Pour utiliser la constante depuis l'exterieur de la classe, il faudra l'appeler Morse:: NOTE_C4
Morse Code ( 12, message, 17, Morse::NOTE_C4 );

Ceci est un principe général, dans ton cas particulier cette constante devrait plutôt être privée.

Merci @biggil pour ces informations, du coup pour rendre ma constante privée j'ai fait ça :
.ino :

#include "Morse.h"

const size_t taille = 250;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  if ( Serial.available() > 0 )
  {
    static char message[taille];
    static uint8_t i;

    char c = Serial.read();

    if ( c != '\n' && i < taille - 1 )
      message[ i++ ] = c;
    else
    {
      message[ i ] = '\0';
      i = 0;

      Serial.print( "transcription en morse de : " );
      Serial.println( message );
      Morse Code(12,message,17); 
      Code.joue();
    }
  }

}

.h :

#include <arduino.h> // pour fonction tone()

#ifndef Morse_h
#define Morse_h

class Morse {
  public:
    // Constructeur
    Morse(int pin, char *s, int vitesse);
    void joue(void);

    // Destructeur
    ~Morse();
    
  private:
    int _pin;
    char *_s;
    int _vitesse;
    const int NOTE_C4 = 262;
};

#endif

.cpp :

#include "Morse.h"
const char *codeMorse[27] = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."};
const char *codeMorseChiffre[11] = {"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----."};

// joue un point avec la fonction tone() d'arduino
void point(int pin, int duree, int frequence)
{
  tone(pin, frequence, duree);
  delay(duree);
}

// joue un tiret avec la fonction tone() d'arduino
void tiret(int pin, int duree, int frequence)
{
  duree *= 3; // un tiret = trois points
  tone(pin, frequence, duree);
  delay(duree);
}

// chaque point et tiret constitutif d'une lettre est transmit à la procédure point() ou tiret() pour transcription sonore
void lettres(int pin, int vitesse, int frequence, char *a)
{

  while ( *a ) {

    switch ( *a ) {
      case '.':
        point(pin, vitesse, frequence);
        delay(vitesse); // entre chaque point constitutif d'une lettre un espacement de temps court
        break;

      case '-':
        tiret(pin, vitesse, frequence);
        delay(vitesse); // entre chaque tiret constitutif d'une lettre un espacement de temps court
        break;
    }

    a++;
  }
  delay(3 * vitesse); // entre chaque lettre un espacement de temps un peu plus long (X3)
}

// Initialisation classe Morse
//Morse::Morse(int pin, char *s, int vitesse, int frequence)
Morse::Morse(int pin, char *s, int vitesse)
  : _pin(pin),
    _s(s),
    _vitesse((60 * 1000L) / (50 * vitesse)) // _vitesse (vitesse des points) = 1 minute divisée par vitesse * 50
{}

void Morse::joue() {

  /*On extrait chaque lettre ou chiffre du tableau de char transmit à la classe Morse - on transmet l'équivalent en morse
    (points et tirets) à la procédure lettres (tableaux de char *codeMorse ou *codeMorseChiffre)
  */
  while ( *_s ) {
    Serial.print(*_s);
    for (byte i = 0; i < 26; i++) {
      if ((*_s == 'a' + i) || (*_s == 'A' + i)) { //on teste d'abord les minuscules puis les majuscules
        Serial.print("\t"); Serial.println((char*)codeMorse[i]);
        lettres(_pin, _vitesse, NOTE_C4, (char*)codeMorse[i]);
      }
    }
    for (byte j = 0; j < 10; j++) {
      if (*_s == '0' + j) { //on teste les chiffres
        Serial.print("\t"); Serial.println((char*)codeMorseChiffre[j]);
        lettres(_pin, _vitesse, NOTE_C4, (char*)codeMorseChiffre[j]);
      }
    }
    if (*_s == 32) { // en cas d'espace un délais plus long est appliqué
      delay(8 * _vitesse);
      Serial.print("\n");
    }
    _s++;
  }

}

// Destruction
Morse::~Morse() {}

Merci et bonne journée.

vous pourriez mettre les codes morses en mémoire flash histoire de laisser plus de RAM dispo

la fonction lettres devrait prendre un const char * comme paramètre, ça éviterait aussi le cast lors de l'appel

au lieu de faire deux boucles pour trouver le caractère dans le tableau (mettez au moins un test, pas la peine de chercher un chiffre si vous avez déjà trouvé une lettre)

➜ si ce n'est pas un un espace (https://cplusplus.com/reference/cctype/isspace/), vous passez le caractère reçu en minuscule (https://cplusplus.com/reference/cctype/tolower/) puis vous testez si c'est entre 'a' et 'z' ou '0' et '9' et suivant le cas vous soustrayez 'a' ou '0' pour avoir l'index dans le bon tableau

void Morse::joue() {
  while ( *_s ) {
    Serial.print(*_s);
    if (isspace(*_s)) {
      delay(8 * _vitesse);
      Serial.print("\n");
    } else {
      char l = tolower(*_s);
      if ((l >= 'a') && (l <= 'z')) {
        byte index = l - 'a';
        Serial.print("\t"); Serial.println(codeMorse[index]);
        lettres(_pin, _vitesse, NOTE_C4, codeMorse[index]);
      } 
      else if ((l >= '0') && (l <= '9')) {
        byte index = l - '0';
        Serial.print("\t"); Serial.println(codeMorseChiffre[index]);
        lettres(_pin, _vitesse, NOTE_C4, codeMorseChiffre[index]);
      } /* else erreur */
    }
    _s++;
  }
}

modifier _s n'est pas forcément une bonne idée, tout dépend de ce que vous voulez faire.

Bonsoir JML,

Vos passages dans les fils de discussion sont toujours aussi pertinents et riches en informations ! Votre version de void Morse::joue() ne me pose aucun problème de compréhension. C'est une meilleure approche bien plus subtile que la mienne ...
Par contre là où je bloque un peu :

Vous voulez que j'utilise PROGMEM ?
Ne me donnez pas la réponse complète, juste oui ou non, il faut que je trouve par moi-même.
Merci.

OUI c'est ça :slight_smile:

Du coup, c'est un truc dans ce genre ou il y a plus simple :

const char a[] PROGMEM = ". - ";
const char b[] PROGMEM = " - ...";
const char c[] PROGMEM = " - . - .";
const char d[] PROGMEM = "-..";
const char e[] PROGMEM = ".";
const char f[] PROGMEM = "..-.";
const char g[] PROGMEM = "--.";
const char h[] PROGMEM = "....";
const char i[] PROGMEM = "..";
const char j[] PROGMEM = ".---";
const char k[] PROGMEM = "-.-";
const char l[] PROGMEM = ".-..";
const char m[] PROGMEM = "--";
const char n[] PROGMEM = "-.";
const char o[] PROGMEM = "---";
const char p[] PROGMEM = ".--.";
const char q[] PROGMEM = "--.-";
const char r[] PROGMEM = ".-.";
const char s[] PROGMEM = "...";
const char t[] PROGMEM = "-";
const char u[] PROGMEM = "..-";
const char v[] PROGMEM = "...- ";
const char w[] PROGMEM = ".--";
const char x[] PROGMEM = "-..-";
const char y[] PROGMEM = " -.--";
const char z[] PROGMEM = "--..";

const char *const codeMorse[] PROGMEM = {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};

char morseBuffer[30];  // make sure this is large enough for the largest string it must hold

void setup() {
  Serial.begin(115200);
  for (int i = 0; i < 26; i++) {
  strcpy_P(morseBuffer, (char *)pgm_read_word(&(codeMorse[i])));
  Serial.println(morseBuffer);
}
}

void loop() {
    
}

Oui ou non bien sûr. Merci.

Plus les chiffres bien sûr

Cette représentation un peu pénible à taper et celle qui occupera le moins d'octets en mémoire puisque votre tableau contient des pointeurs vers des chaînes de tailles différentes.

Au prix de la perte d'un peu de mémoire flash, vous pourriez avoir un tableau de caractère de taille fixe pour chaque code morse (ajusté au plus long donc + 1 pour le caractère nul) alors c'est plus simple à taper et imprimer

ça ne me dérange pas du tout que ce soit fastidieux à taper du moment que j'optimise l'utilisation de la mémoire de mon petit µcontrôleur.
Non je posais la question de savoir si il y avait plus simple juste parce que je découvre, je n'ai jamais vraiment programmé avec PROGMEM. Vous savez, j'ai encore énormément de choses à apprendre ...
C'est le même principe qu'avec les c-strings.
Donc je vais le faire, d'ailleurs je n'ai plus qu'à faire du copier coller.
Je le ferai demain soir ou après demain car il faut que je prépare le brame du cerf. Je dois être sur le terrain demain matin avant le levé du jour.
Bonne soirée à vous et merci encore.

puisque vous êtes curieux, voici une autre représentation avec taille fixe (et la correspondance dans la structure entre code et caractère pour faire la recherche dans les 2 sens)

const byte MAXCODE = 6 + 1; // +1 pour le caractère nul

struct t_code {
  char representation[MAXCODE];
  char lettre;
};

static const t_code codeMorse[] PROGMEM = {
  {"-----",   '0'},
  {".----",   '1'},
  {"..---",   '2'},
  {"...--",   '3'},
  {"....-",   '4'},
  {".....",   '5'},
  {"-....",   '6'},
  {"--...",   '7'},
  {"---..",   '8'},
  {"----.",   '9'},
  {"---...",  ':'},
  {"-.-.-.",  ';'},
  {"",        '<'},       // n'existe pas en morse
  {"-...-",   '='},
  {"",        '>'},       // n'existe pas en morse
  {"..--..",  '?'},
  {".--._.",  '@'},
  {".-",      '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'},
};
const byte nbMorseSymboles = sizeof codeMorse / sizeof * codeMorse;

void setup() {
  Serial.begin(115200); Serial.println();
  for (byte index = 0; index < nbMorseSymboles; index++) {
    Serial.write(pgm_read_byte(&codeMorse[index].lettre));
    Serial.write('\t');
    Serial.println((__FlashStringHelper*) codeMorse[index].representation);
  }
}

void loop() {}

le code morse contient quelques caractères en plus non représentables, pour alignement avec la table ASCII si ça peut donner des idées

Notez aussi qu'on peut imprimer sans buffer intermédiaire, simplement en précisant à la fonction print qu'il faut aller chercher le contenu en mémoire flash et pas en RAM (en changeant le type du pointeur vers __FlashStringHelper

Bonjour @J-M-L ,
Malgré la fatigue, je viens de passer une heure à étudier PROGMEM

Il semble que cela ne fonctionne qu'avec l'impression. Je crois qu'il est impossible de récupérer directement une chaine de caractères dans la mémoire flash sans passer par un buffer (c'est peut-être du à l'architecture de type Harvard du processeur de l'UNO, en fait je ne sais pas trop...)

Du coup je vous propose ces deux versions du fichier cpp de la librairie morse :
1/ sur le premier modèle :

#include "Morse.h"
const char a[] PROGMEM = ". - ";
const char b[] PROGMEM = " - ...";
const char c[] PROGMEM = " - . - .";
const char d[] PROGMEM = "-..";
const char e[] PROGMEM = ".";
const char f[] PROGMEM = "..-.";
const char g[] PROGMEM = "--.";
const char h[] PROGMEM = "....";
const char i[] PROGMEM = "..";
const char j[] PROGMEM = ".---";
const char k[] PROGMEM = "-.-";
const char l[] PROGMEM = ".-..";
const char m[] PROGMEM = "--";
const char n[] PROGMEM = "-.";
const char o[] PROGMEM = "---";
const char p[] PROGMEM = ".--.";
const char q[] PROGMEM = "--.-";
const char r[] PROGMEM = ".-.";
const char s[] PROGMEM = "...";
const char t[] PROGMEM = "-";
const char u[] PROGMEM = "..-";
const char v[] PROGMEM = "...- ";
const char w[] PROGMEM = ".--";
const char x[] PROGMEM = "-..-";
const char y[] PROGMEM = " -.--";
const char z[] PROGMEM = "--..";
const char* const codeMorse[] PROGMEM = {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};

static const char _0[] PROGMEM = "-----";
static const char _1[] PROGMEM = ".----";
static const char _2[] PROGMEM = "..---";
static const char _3[] PROGMEM = "...--";
static const char _4[] PROGMEM = "....-";
static const char _5[] PROGMEM = ".....";
static const char _6[] PROGMEM = "-....";
static const char _7[] PROGMEM = "--...";
static const char _8[] PROGMEM = "---..";
static const char _9[] PROGMEM = "----.";
const char *const codeMorseChiffre[] PROGMEM = { _0, _1, _2, _3, _4, _5, _6, _7, _8, _9};
char buffer[30];

// joue un point avec la fonction tone() d'arduino
void point(int pin, int duree, int frequence)
{
  tone(pin, frequence, duree);
  delay(duree);
}

// joue un tiret avec la fonction tone() d'arduino
void tiret(int pin, int duree, int frequence)
{
  duree *= 3; // un tiret = trois points
  tone(pin, frequence, duree);
  delay(duree);
}

// chaque point et tiret constitutif d'une lettre est transmit à la procédure point() ou tiret() pour transcription sonore
void lettres(int pin, int vitesse, int frequence,  const char *a)
{
  // const char * string_ptr = (char *) pgm_read_word(a);

  while ( *a ) {
    //Serial.print(*a);
    switch ( *a ) {
      case '.':
        point(pin, vitesse, frequence);
        delay(vitesse); // entre chaque point constitutif d'une lettre un espacement de temps court
        break;

      case '-':
        tiret(pin, vitesse, frequence);
        delay(vitesse); // entre chaque tiret constitutif d'une lettre un espacement de temps court
        break;
    }

    a++;
  }
  delay(3 * vitesse); // entre chaque lettre un espacement de temps un peu plus long (X3)
}

// Initialisation classe Morse
Morse::Morse(int pin, char *s, int vitesse)
  : _pin(pin),
    _s(s),
    _vitesse((60 * 1000L) / (50 * vitesse)) // _vitesse (vitesse des points) = 1 minute divisée par vitesse * 50
{}


/*On extrait chaque lettre ou chiffre du tableau de char transmit à la classe Morse - on transmet l'équivalent en morse
  (points et tirets) à la procédure lettres (tableaux de char *codeMorse ou *codeMorseChiffre)
*/
void Morse::joue() {
  while ( *_s ) {
    Serial.print(*_s);
    if (isspace(*_s)) {
      delay(8 * _vitesse);
      Serial.print("\n");
    } else {
      char l = tolower(*_s);
      if ((l >= 'a') && (l <= 'z')) {
        byte index = l - 'a';
        Serial.print("\t"); Serial.println((__FlashStringHelper*) pgm_read_word(&codeMorse[index]));
        strcpy_P(buffer, pgm_read_word(&(codeMorse[index])));
        lettres(_pin, _vitesse, NOTE_C4,  buffer);
      }
      else if ((l >= '0') && (l <= '9')) {
        byte index = l - '0';
        Serial.print("\t"); Serial.println((__FlashStringHelper*) pgm_read_word(&codeMorseChiffre[index]));
        strcpy_P(buffer, (char *)pgm_read_word(&(codeMorseChiffre[index])));
        lettres(_pin, _vitesse, NOTE_C4,  buffer);
      } /* else erreur */
    }
    _s++;
  }
}

// Destruction
Morse::~Morse() {}

2/ suivant votre deuxième modèle :

#include "Morse.h"
const byte MAXCODE = 6 + 1; // +1 pour le caractère nul

struct t_code {
  char representation[MAXCODE];
  char lettre;
};

static const t_code codeMorse[] PROGMEM = {
  {"--..--",   ','},
  {"-....-",   '-'},
  {".-.-.-",   '.'},
  {"-..-.",   '/'},
  {"-----",   '0'},
  {".----",   '1'},
  {"..---",   '2'},
  {"...--",   '3'},
  {"....-",   '4'},
  {".....",   '5'},
  {"-....",   '6'},
  {"--...",   '7'},
  {"---..",   '8'},
  {"----.",   '9'},
  {"---...",  ':'},
  {"-.-.-.",  ';'},
  {"",        '<'},       // n'existe pas en morse
  {"-...-",   '='},
  {"",        '>'},       // n'existe pas en morse
  {"..--..",  '?'},
  {".--._.",  '@'},
  {".-",      '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'},
};
const byte nbMorseSymboles = sizeof codeMorse / sizeof * codeMorse;
char buffer[30];

// joue un point avec la fonction tone() d'arduino
void point(int pin, int duree, int frequence)
{
  tone(pin, frequence, duree);
  delay(duree);
}

// joue un tiret avec la fonction tone() d'arduino
void tiret(int pin, int duree, int frequence)
{
  duree *= 3; // un tiret = trois points
  tone(pin, frequence, duree);
  delay(duree);
}

// chaque point et tiret constitutif d'une lettre est transmit à la procédure point() ou tiret() pour transcription sonore
void lettres(int pin, int vitesse, int frequence,  const char *a)
{
  // const char * string_ptr = (char *) pgm_read_word(a);

  while ( *a ) {
    //Serial.print(*a);
    switch ( *a ) {
      case '.':
        point(pin, vitesse, frequence);
        delay(vitesse); // entre chaque point constitutif d'une lettre un espacement de temps court
        break;

      case '-':
        tiret(pin, vitesse, frequence);
        delay(vitesse); // entre chaque tiret constitutif d'une lettre un espacement de temps court
        break;
    }

    a++;
  }
  delay(3 * vitesse); // entre chaque lettre un espacement de temps un peu plus long (X3)
}

// Initialisation classe Morse
Morse::Morse(int pin, char *s, int vitesse)
  : _pin(pin),
    _s(s),
    _vitesse((60 * 1000L) / (50 * vitesse)) // _vitesse (vitesse des points) = 1 minute divisée par vitesse * 50
{}


/*On extrait chaque lettre ou chiffre du tableau de char transmit à la classe Morse - on transmet l'équivalent en morse
  (points et tirets) à la procédure lettres (tableaux de char *codeMorse ou *codeMorseChiffre)
*/
void Morse::joue() {
  while ( *_s ) {
    Serial.print(*_s);
    if (isspace(*_s)) {
      delay(8 * _vitesse);
      Serial.print("\n");
    } else {
      char l = toupper(*_s);
      byte index = l - ',';
      //Serial.write(pgm_read_byte(&codeMorse[index].lettre));
      Serial.print("\t");
      Serial.println((__FlashStringHelper*) codeMorse[index].representation);
      strcpy_P(buffer, codeMorse[index].representation);
      lettres(_pin, _vitesse, NOTE_C4, buffer);

    }
    _s++;
  }
}

// Destruction
Morse::~Morse() {}

Notez que j'ai rajouté des caractères...
Il me reste à gérer les erreurs.

Merci

il faut effectivement y aller octet par octet, c'est ce que fait print - mais ça ne change pas grand chose par rapport à la SRAM. par exemple pour copier un bloc mémoire en SRAM vous utiliser memcpy(), et si c'est en flash vous utilisez memcpy_P()

Une question qui ne fait pas vraiment avancer le schmilblick, mais il n'y a aucune logique sur les suites de codes morses entre deux lettres ou chiffres successifs?

Merci @J-M-L,
Tous vos fils de discussion sont une référence pour moi. Celui-ci en sera une autre ...
Voilà qui est encore mieux optimisé. Je ne trouve pas tout ça dans mes livres !

void Morse::joue() {
  while ( *_s ) {
    Serial.print(*_s);
    if (isspace(*_s)) {
      delay(8 * _vitesse);
      Serial.print("\n");
    } else {
      char l = toupper(*_s);
      byte index = l - ',';
      //Serial.write(pgm_read_byte(&codeMorse[index].lettre));
      Serial.print("\t");
      Serial.println((__FlashStringHelper*) codeMorse[index].representation);
      memcpy_P(buffer, codeMorse[index].representation, sizeof( codeMorse[index].representation));
      lettres(_pin, _vitesse, NOTE_C4, buffer);

    }
    _s++;
  }
}

Je garde votre dernière version. Elle est plus lisible à mon sens même si elle prend un peu plus de mémoire en Flash.
Merci encore.

non à l'origine le choix de la longueur des codages était inversement proportionnel à la fréquence de la lettre en anglais pour minimiser la transmission.

Par exemple le 'E' qui est le plus utilisé est représenté par un point.

la fonction lettre() pourrait avoir une version qui prendrait un paramètre en mémoire flash, comme cela pas la peine d'utiliser un buffer intermédiaire

oupsssssssss
j'y regarde ...

J'ai la mauvaise habitude de chercher à économiser les octets depuis que je travaille avec les microcontrôleurs qui ne sont pas pourvus de mémoire "infinie" et cela m'arrive parfois d'avoir des programmes trop gros ou trop lents.
Si j'avais à programmer le code mors, je me poserai la question de l'économie. Représenter une lettre par 6 octets me semble bien encombrant.

Dans le cas présent, comme le code a la particularité de n'avoir que deux symboles, le "." et le "-", on peut représenter un symbole par un bit et pas par un octet. Mettons que l'on représente le "." par 0 et le "-" par 1. Il faut alors 6 bits pour le codage des "." et des "-". Il faut 3 bits pour coder la longueur (1 à 6). On a donc besoin en tout de 9 bis, ce qui ne tient pas dans un seul octet. En utilisant un premier octet pour donner la longueur (l'envoi se fera par une boucle for) et un deuxième pour la définition, et un troisième pour la représentation ascii cela va donner quelque chose comme;

static const t_code codeMorse[] PROGMEM = {
  {6, 0b110011,   ','}, // --..--
  {6, 0b100001,   '-'}, // -....-
...
  {5, 0b11111,   '0'}, // -----
...
  {2, 0b01,      'A'}, // .-
  {4, 0b1000,    'B'}, // -000
...
  {1, 0b0,       'E'}, //.
...

Il serait peut être plus rapide en fonction de la réalisation de mettre les bits dans le sens inverse si pour extraire les différents symboles, on fait des décalages vers la droite (>>1) et un test

si (valeur & 1) == 0 c'est un point
sinon c'est un trait

On peut d'ailleurs gagner un octet avec un codage plus complexe car mon troisième octet ascii n'utilise que 7 bits. On peut donc par exemple stocker les 3 bits de longueur, un dans le bit 7 de l'ascii et deux dans les bits de poids forts du premier octet.


Pour économiser encore plus les octets, il faut voir si il est utile de stocker la valeur ascii. Si on stocke que les codes 32 à 95 (64 codes) et que seuls 48 sont utilisés (d'après ce que j'ai compté), en disant que le code ascii c'est l'indice du tableau+32, on économise 48 octets de code ascii, mais il y a 16 codes à rajouter (les 16 codes inutilisés), soit 32 octets car le codage se fait alors sur 2 octets. Pas forcément intéressant sauf si on utilisait quasiment tous les codes (il faut au moins se poser la question.

Oui si la place en mémoire flash vient à manquer on peut aller plus loin.