vendredi, juin 19, 2026
Activités RadioAntennesArduinoRéalisationsSatellites

Arduino à la poursuite des satellites (nouvelle version du programme)

Par Gilles F1EFW

Une étape franchie : 1 000 contacts par satellites radioamateurs !

Pour célébrer ce palier, j’ai fait évoluer mon système de pilotage de rotors afin de le rendre compatible avec les nouveaux logiciels de poursuite satellite, de plus en plus nombreux sur le marché.

Les points forts de cette solution :

  • Très économique : Un coût de revient de quelques dizaines d’euros seulement.
  • Performante : Elle rivalise avec des équipements professionnels.
  • Polyvalente : Elle permet de piloter des rotors très anciens ou des modèles de fabrication maison.

Ma réalisation se substitue aux boîtiers de commande d’origine des deux rotors. Si vous possédez encore les boîtiers d’origine, rien ne vous empêche de les conserver (personnellement, je n’en avais pas, ce qui a motivé cette conception).

Voir le précédent article https://f6kmf.fr/index.php/2023/09/14/arduino-et-la-radio-a-la-poursuite-des-satellites-version-site-azimut/

Évolution du contrôleur de rotor : vers une compatibilité universelle

La précédente version de mon programme était limitée à WispDDE, nécessitant l’utilisation d’applications comme Orbitron ou SDR-Console pour assurer l’interfaçage.

La nouvelle mouture franchit une étape importante en adoptant le protocole GS-232, devenu le standard du suivi satellite. Elle est désormais compatible avec la majorité des logiciels de poursuite modernes, tels que SatTrack, OscarWatch, ou PstRotator.

Fonctionnement technique

Le programme orchestre le suivi de manière autonome grâce au processus suivant :

  • Analyse du flux : Le logiciel intercepte les données provenant du logiciel de poursuite satellite, extrait les chaînes commençant par « W » ou « w » et isole les valeurs d’azimut et de site (ex: W235 025 pour 235° d’azimut et 25° de site). Les données non pertinentes, comme les commandes de parcage, sont ignorées.
  • Boucle d’asservissement : Le programme compare en temps réel la position du satellite avec celle des antennes. La lecture analogique des potentiomètres des moteurs est convertie en degrés à partir des tensions relevées.
  • Correction : Des ajustements sont appliqués automatiquement pour aligner précisément les moteurs sur la trajectoire du satellite.


Personnalisation et calibration

L’adaptation à votre matériel est simplifiée : il suffit d’étalonner les tensions de vos potentiomètres pour déterminer le coefficient de transformation spécifique à vos moteurs. Une fois ce rapport tension/degré défini, le programme assure une précision optimale.

///////////////////////////////
// Pilotage rotor azimut V4  //
//  Gilles PLATRET F1EFW     //
///////////////////////////////

// La version de départ était seulement compatible avec les informations en provenance de WISP DDE
// Cette version extrait la chaine de caractères WXXX XXX de n'importe quel logiciel de suivi satellites
// Des test concluants ont été effectués avec SatTrack, OscarWatch, SdrConsole, PstRotator etc ..... 

// On ajoute une librairie qui va nous aider pour l'affichage

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// On déclare quelques variables A_ pour affichage 

String Lecture_Port_USB, A_AZI, A_ELE, A_Potar_Azi, A_Potar_Ele ;
int AZI, ELE, Potar_Azi, Potar_Ele;
int pin0 = A0,pin1 = A1, marge = 4;
   
      // on peut changer marge pour éviter les A/R  (ex: 5, 6 ou 8).
    

// On initialise le programme 

void setup() {
  
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
   lcd.print("F1EFW VERSION 4");
   delay(3000);
   lcd.setCursor(0, 0);
   lcd.print("               ");
  Serial.begin(9600); // vitesse avec le port de communication
  
// Les relais Azimut
  pinMode(5, OUTPUT); // relais 1
  pinMode(6, OUTPUT); // relais 2


// Les relais Elevation
  pinMode(3, OUTPUT); // relais 1
  pinMode(4, OUTPUT); // relais 2

  
  AZI=0,ELE=0;
}

// On commence à bosser 

void loop() 
{
  
// Lecture du port de communication.
// au format GS-232 -> ex W127 052
// on extrait les caractères Azumuth et Elevation
// et on les convertis en nombres (ToInt)
// On dispose de l'information sous format numérique pour les calculs
// et au format chaine de caractères pour l'affichage

  while(Serial.available()){
    Lecture_Port_USB = Serial.readString();

     for (int i = 0; i <= Lecture_Port_USB.length(); i++) {
         
      if ((Lecture_Port_USB.charAt(i) == 'W') || (Lecture_Port_USB.charAt(i) == 'w')) {
                                               
           A_AZI = Lecture_Port_USB.charAt(i+1);
           A_AZI = A_AZI + Lecture_Port_USB.charAt(i+2);
           A_AZI = A_AZI + Lecture_Port_USB.charAt(i+3);
           
           A_ELE = Lecture_Port_USB.charAt(i+5);
           A_ELE = A_ELE + Lecture_Port_USB.charAt(i+6);
           A_ELE = A_ELE + Lecture_Port_USB.charAt(i+7);


      AZI = (A_AZI.toInt());
      ELE = (A_ELE.toInt());
      
    lcd.setCursor(0, 0);
    lcd.print("SAT A:");
    lcd.print(A_AZI);
    lcd.print(" E:");     
    lcd.print(A_ELE);
    
   }
   } 
  }
  
// fin de lecture du port USB

// Lecture du potar_Azumuth
// Potar_Azi varie de 0 a 1024 (0 quand il est à la masse et 1024 quand il reçoit 5V)
// pour convertir en degres on fera theoriquement 1024/360 = 2.84
// Mais pour moi le potar AZIMUT va de 74 à 1020 donc (1020-74)/360 = 2.62
// Le potar ELEVATION va de 610 a 810 pour faire 90° donc (810-610)/90 = 2.22

Potar_Azi =  ((analogRead(pin0)-74)/2.62);
Potar_Ele =  ((analogRead(pin1)-530)/2.25);

   lcd.setCursor(0, 1);
   lcd.print("ANT A:");
       
       // pour afficher le nombre sous la forme de 3 caractères
       
       A_Potar_Azi = String(Potar_Azi);
         if (A_Potar_Azi.length() < 2) {
             A_Potar_Azi = ("00"+ A_Potar_Azi);
              }
              else {
               if (A_Potar_Azi.length() < 3) {
                A_Potar_Azi = ("0"+ A_Potar_Azi);
               }
               }
              lcd.print(A_Potar_Azi);

        A_Potar_Ele = String(Potar_Ele);
         if (A_Potar_Ele.length() < 2) {
             A_Potar_Ele = ("00"+ A_Potar_Ele);
              }
              else {
               if (A_Potar_Ele.length() < 3) {
                A_Potar_Ele = ("0"+ A_Potar_Ele);
               }
               }
              lcd.print(" E:");
              lcd.print(A_Potar_Ele);
           // lcd.print(analogRead(pin1));

              
    lcd.setCursor(0, 0);
    lcd.print("SAT A:");
    lcd.print(A_AZI);
    lcd.print(" E:");     
    lcd.print(A_ELE);

                 
   // on ne corrige que si le satellite est visible (ELE>0) et qu'on quitte la marge
   // donc si différence des valeurs > marge on tourne dans un sens
   // si < marge on tourne dans l'autre sens et si presque égale on stoppe 
   ////////////////////////  AZIMUT //////////////////////////////////////////

    if (ELE > 0) { // Si le satellite est visible
     if ((Potar_Azi > AZI) && (abs(Potar_Azi - AZI) > marge)) {
       // Tourne dans un sens
       digitalWrite (5, HIGH);
       digitalWrite (6, LOW);
     } 
     else if ((Potar_Azi < AZI) && (abs(Potar_Azi - AZI) > marge)) {
       // Tourne dans l'autre sens
       digitalWrite (5, LOW);
       digitalWrite (6, HIGH);
     } 
     else {
       // Si on n'est ni trop haut, ni trop bas (donc dans la marge) : on STOPPE
       digitalWrite (5, HIGH);
       digitalWrite (6, HIGH);
     }
   } else {
     // Si ELE < 1, on sécurise et on stoppe le moteur
     digitalWrite (5, HIGH);
     digitalWrite (6, HIGH);
   }


    ////////////////////////  ELEVATION //////////////////////////////////////////

     if (ELE > 0) {
     if ((Potar_Ele > ELE) && (abs(Potar_Ele - ELE) > marge)) {
       // Tourne dans un sens
       digitalWrite (3, HIGH);
       digitalWrite (4, LOW);
     } 
     else if ((Potar_Ele < ELE) && (abs(Potar_Ele - ELE) > marge)) {
       // Tourne dans l'autre sens
       digitalWrite (3, LOW);
       digitalWrite (4, HIGH);
     } 
     else {
       // Dans la marge : on STOPPE
       digitalWrite (3, HIGH);
       digitalWrite (4, HIGH);
     }
   } else {
     // Si ELE < 1, on sécurise et on stoppe le moteur
     digitalWrite (3, HIGH);
     digitalWrite (4, HIGH);
   }
}

Simplification du contrôle manuel

N’utilisant pas les boîtiers de commande d’origine, j’ai opté pour une approche directe : l’envoi de commandes depuis le port série (COM1) de mon ordinateur vers l’Arduino.

Pour réaliser ces tests de manière rapide et ergonomique, j’utilise Termite. C’est, à mon sens, l’outil idéal pour envoyer des chaînes de caractères en toute simplicité. Son principal atout est d’être portable, ce qui évite toute installation fastidieuse sur le système.

A votre disposition pour plus d’information https://f6kmf.fr/index.php/nous-contacter/