Accueil

Balise CW

par Michel Vonlanthen HB9AFO

 

Une petite balise bien utile pour signaler sa présence là où il n'y a pas trop de monde, sur 10 GHz par exemple. Petite, alimentée par piles et suffisamment fûtée pour rendre bien des services!


 

Cela faisait longtemps que j'avais envie de me construire une petite balise pour mes longs essais sur 10 GHz, notamment lors de la journée de trafic contre le Mont Blanc. C'est suite à une discussion sur une liste web que j'ai pris le mors aux dents et une semaine plus tard la balise était opérationnelle. C'est ça le grand avantage de l'Arduino: pouvoir passer très rapidement de l'idée à la réalisation. D'une part parce que l'électronique est très simple et donc compréhensible, et d'autre part parce qu'il y a des millions d'utilisateurs dans le monde qui ont développé des applications pour l'Arduino et les ont mises à disposition de la communauté. On trouve toujours tout ou partie du code sur le Net. Et comme le langage de programmation de la base de développement Arduino est le C, point n'est besoin d'apprendre un nouveau langage lors de chaque changement de processeur comme c'était le cas auparavant avec les PIC.   

 

Ma construction a donc été facilitée par le découverte du code source de Nicola Salsotto IN3GJH prévu pour un microordinateur Arduino. J'ai donc encapsulé la partie "génération de code télégraphique" dans mon propre code et programmé le reste afin d'obtenir exactement ce que je désirais:

  1. Envoi en boucle du texte "balise" transmis en CW, à la vitesse et aux caractéristiques (rapport traits-points-espaces) désirées, suivi d'un long trait continu. Un signal "PTT" (Push To Talk)  est généré pour faire passer en émission un émetteur qui n'aurait pas de Vox.
     

  2. Même chose mais pour lancer appel, un "CQ" cette fois. A la fin de l'appel le transceiver passe à l'écoute de la fréquence. Si quelqu'un répond, l'opérateur prend la main à l'aide d'un manipulateur externe. Si personne ne réponde, la balise continue de lancer appel, avec à chaque fois un temps d'écoute suffisant pour déceler un appelant éventuel.
     

  3. Les textes des messages sont mémorisés dans l'Arduino. Il faut donc reprendre le code source pour les modifier. J'ai prévu une troisième position sur le commutateur de mode afin de pouvoir, si le coeur m'en dit, programmer une façon de mémoriser du texte à envoyer au moyen du manipulateur (en portable par exemple). Avec un manipulateur non automatique ce n'est pas trivial mais j'ai quelques idées à cet égard. Mais pour le moment ce sont les fonctions "balise " et "CQ" qu'il me faut en priorité et la façon de mémoriser le texte par programmation suffit largement à mes besoins.
     

  4. Une fonction intéressante est le "sidetone". Elle permet d'entendre ce qui est transmis, ce qui est surtout utile lorsque l'opérateur reprend la main sur l'électronique. Habituellement, lorsque ce genre de balise lance appel, si quelqu'un répond il faut déconnecter la balise du transceiver et plugger un manipulateur à la place afin de pouvoir répondre à la station appelante. Là ce n'est pas nécessaire puisqu'un commutateur automatique/manuel est monté sur le panneau avant. Après un appel, si quelqu'un répond, il suffit de basculer le commutateur sur "manuel" et d'utiliser le manipulateur branché sur la prise "Key" de la balise.  La particularité c'est que le sidetone reste actif si bien que l'opérateur peut entendre ce qu'il manipule. La balise peut donc n'être utilisée que comme simple oscillateur d'exercice pour tester un nouveau manipulateur par exemple.
     

  5. Le commutateur "PTT" trouve son utilité lorsqu'on désire pouvoir ne transmettre qu'une seule séquence enregistrée. Dans ce cas, il suffit le basculer sur "off" sitôt l'appel démarré. A la fin de la séquence, la balise continuera de tourner mais le TX ne passera pas en émission. A la condition, bien-sûr, que la ligne PTT soit branchée à l'émetteur. Ce n'est pas obligatoire car la plupart des transceivers récents incorporent un VOX  qui fait automatiquement passer l'émetteur en émission dès que le manipulateur est pressé ou que la balise lance une séquence.

 


Fig 1: Balise vue de l'avant.

 

 

Fig 2: balise vue de l'arrière.
A gauche la sortie manipulée vers le TX, à droite 2 jacks en parallèle pour le manipulateur externe (6.35 et 3.5 mm).

 

 

 

 

  

Fig 3: Arduino Pro Mini                       Fig 5: et son programmateur

 

 

 

Dans le logiciel de programmation de l'Arduino, il faut sélectionner "Arduino Pro ou Pro Mini" dans l'option "Type de carte" du menu "Outils".  Dans l'option "Processeur" c'est "ATMega328 (5V, 16MHz)" qui est indiqué. Dans l'option "Programmateur" il faut choisir "AVR ISP".

 

 

 

Fig 6: schéma de la balise

 

 

Il est très simple et se passe de commentaires. Les pins d'entrée et de sortie se retrouvent dans le code source.

A noter que l'Arduino Pro Mini supporte tout à fait bien l'alimentation en 6 Volts au lieu de 5.

 

 

Fig 7: Le boîtier ouvert pour la programmation.

 

Le programmateur se branche sur l'Arduino. Une fois la programmation terminé, on peut le retirer et refermer le boîtier. La balise fonctionne ensuite toute seule. A noter que le programmateur est alimenté par le port USB et alimente à son tour la balise. Lorsque le programmateur est retiré, ce sont les 4 piles de 1,5V qui font fonctionner la balise.

 

Le code source du logiciel est ci-dessous

 

Michel Vonlanthen HB9AFO

Août 2017

 

//****************************************
//*                                      *
//*            BALISE CW                 *
//*                                      *
//*    de Michel Vonlanthern HB9AFO      *
//*    Version 1.5 du 3 août 2017        *
//****************************************
//    
// inspiré du "Arduino Morse Beacon"
// de Nicola Salsotto IN3GJH
#define SPEED  (16)                // vitesse en mots/minute. Defaut: 16
#define DOTLEN  (1200/SPEED)       // defaut: 1200
#define DASHLEN  (3*(1200/SPEED))  // defaut: 3
#define PAUSE 10                    // Temps en secondes, temps entre transmissions (CQ)et longueur porteuse (balise). defaut: 10
int commut1Pin = 2;  // commutateur, contact 1
int commut2Pin = 3;  // commutateur, contact 2 
int commut3Pin = 4;  // commutateur, contact 3
int tonePin =    5;  // tonalité manipulée
int pttPin =     6;  // PTT 
int autoPin =    7;  // inter auto-manuel (avec sidetone)
int keyPin =     8;  // manipulateur
int pttSWPin =   9;  // 
int ledPin =    13;  // manipulation + led
int toneFreq =  700; // Fréquence de la tonalité entre 500 et 900 Hz
int interState =0;   // Etat interrupteur "mode" balise ou CQ
int autoState = 0;   // Etat interrupteur "manip auto" ou "manip manuelle"
int keyState =  1;   // Etat manip externe, LOW = key down
int pttSWState =1;   // Etat inter PTT
void sendMsg(char*);
void dash();
void dot();
void setup()
{
  pinMode(commut1Pin, INPUT);
  pinMode(commut2Pin, INPUT);
  pinMode(commut3Pin, INPUT);
  pinMode(tonePin,    OUTPUT);
  pinMode(pttPin,     OUTPUT);
  pinMode(keyPin,     INPUT); 
  pinMode(autoPin,    INPUT);
  pinMode(pttSWPin,   INPUT);
  pinMode(ledPin,     OUTPUT);
}
void loop()
{
  autoState=digitalRead(autoPin);    // manip auto ou manuelle
  if (autoState == HIGH)             // = manipulation manuelle
  {
    // MANIPULATION MANUELLE
    pttSWState=digitalRead(pttSWPin);    
    if (pttSWState==LOW)               //  PTT ON 
    { digitalWrite(pttPin, HIGH); }  // TX ON
    else { digitalWrite(pttPin, LOW); }  // TX OFF      
    
    keyState=digitalRead(keyPin);    
    if (keyState==LOW)               //  key down 
    {
        digitalWrite(ledPin, HIGH);  
        tone(tonePin, toneFreq);
    } 
    else
    {
      digitalWrite(ledPin, LOW);     // = key up   
      noTone(tonePin); 
    }   
  }
  else // = manipulation auto
  { 
    
    // MANIULATION AUTOMATIQUE
    
    interState=digitalRead(commut1Pin);
    if (interState == HIGH)  // Mode balise
    {  
      
      // MODE BALISE
      
    pttSWState=digitalRead(pttSWPin);    
    if (pttSWState==LOW)               //  PTT ON 
    { digitalWrite(pttPin, HIGH); }  // TX ON
    else { digitalWrite(pttPin, LOW); }  // TX OFF      
      
      sendMsg("HB9AFO HB9AFO HB9AFO JN36GN JN36GN JN36GN");
    
      // Longue porteuse
      delay(1000);
      digitalWrite(ledPin, HIGH);
      tone(tonePin, toneFreq);
      delay(PAUSE*2000);
      digitalWrite(ledPin, LOW);
      noTone(tonePin);
      delay(1000);
    }
    interState=digitalRead(commut2Pin);
    if (interState == HIGH)  // Mode CQ
    { 
      // MODE CQ
      
      pttSWState=digitalRead(pttSWPin);    
      if (pttSWState==LOW)               //  PTT ON 
      { digitalWrite(pttPin, HIGH); }  // TX ON
      else { digitalWrite(pttPin, LOW); }  // TX OFF      
      
      sendMsg("CQ CQ CQ DE HB9AFO HB9AFO HB9AFO PSE K");
      digitalWrite(pttPin, LOW);   // TX OFF 
      delay(PAUSE*1000);           // Période d'écoute
    }
  
    interState=digitalRead(commut3Pin);
    if (interState == HIGH)  // Mode phrase enregistree
    { 
    }
  
  }
}  // fin de loop

// ----------- ROUTINES -------------
//-----------------------------------
void dash()
{
  digitalWrite(ledPin, HIGH);
  tone(tonePin, toneFreq);
  delay(DASHLEN);
  digitalWrite(ledPin, LOW);
  noTone(tonePin);
  tone(tonePin, 21000);
  delay(DOTLEN);
  noTone(tonePin);
}
//-----------------------------------
void dot()
{
  digitalWrite(ledPin, HIGH) ;
  tone(tonePin, toneFreq);
  delay(DOTLEN);
  digitalWrite(ledPin, LOW);
  noTone(tonePin);
  tone(tonePin, 21000);
  delay(DOTLEN);
  noTone(tonePin);
}
//-----------------------------------
void sendMsg(char *str)
{
  int i;
  tone(tonePin, 21000);
  delay(500);
  noTone(tonePin);
  for(i=0;i<strlen(str);i++)
  {
    switch (str[i])
    {
    case 'A':
      dot();dash();break;
    case 'B':
      dash();dot();dot();dot();break;
    case 'C':
      dash();dot();dash();dot();break;
    case 'D':
      dash();dot();dot();break;
    case 'E':
      dot();break;
    case 'F':
      dot();dot();dash();dot();break;
    case 'G':
      dash();dash();dot();break;
    case 'H':
      dot();dot();dot();dot();break;
    case 'I':
      dot();dot();break;
    case 'J':
      dot();dash();dash();dash();break;
    case 'K':
      dash();dot();dash();break;
    case 'L':
      dot();dash();dot();dot();break;
    case 'M':
      dash();dash();break;
    case 'N':
      dash();dot();break;
    case 'O':
      dash();dash();dash();break;
    case 'P':
      dot();dash();dash();dot();break;
    case 'Q':
      dash();dash();dot();dash();break;
    case 'R':
      dot();dash();dot();break;
    case 'S':
      dot();dot();dot();break;
    case 'T':
      dash();break;
    case 'U':
      dot();dot();dash();break;
    case 'V':
      dot();dot();dot();dash();break;
    case 'W':
      dot();dash();dash();break;
    case 'X':
      dash();dot();dot();dash();break;
    case 'Y':
      dash();dot();dash();dash();break;
    case 'Z':
      dash();dash();dot();dot();break;
    case ' ':
      tone(tonePin, 21000);
      delay(DOTLEN*5);
      noTone(tonePin);
      break;
    case '.':
      dot();dash();dot();dash();dot();dash();break;
    case ',':
      dash();dash();dot();dot();dash();dash();break;
    case ':':
      dash();dash();dash();dot();dot();break;
    case '?':
      dot();dot();dash();dash();dot();dot();break;
    case '\'':
      dot();dash();dash();dash();dash();dot();break;
    case '-':
      dash();dot();dot();dot();dot();dash();break;
    case '/':
      dash();dot();dot();dash();dot();break;
    case '(':
    case ')':
      dash();dot();dash();dash();dot();dash();break;
    case '\"':
      dot();dash();dot();dot();dash();dot();break;
    case '@':
      dot();dash();dash();dot();dash();dot();break;
    case '=':
      dash();dot();dot();dot();dash();break;
    case '0':
     dash();dash();dash();dash();dash();break;
    case '1':
     dot();dash();dash();dash();dash();break;
    case '2':
     dot();dot();dash();dash();dash();break;
    case '3':
     dot();dot();dot();dash();dash();break;
    case '4':
     dot();dot();dot();dot();dash();break;
    case '5':
     dot();dot();dot();dot();dot();break;
    case '6':
     dash();dot();dot();dot();dot();break;
    case '7':
     dash();dash();dot();dot();dot();break;
    case '8':
     dash();dash();dash();dot();dot();break;
    case '9':
     dash();dash();dash();dash();dot();break;
    }
    delay(2*DOTLEN);
  }
}