Another ARDUINO based antenna rotor controller (AZ & EL)

Project presentation

Being interested in EME I was looking for a possibility to control in azimuth and elevation my 2x 11 element YAGI antenna's  by rotor (YAESU G-600 type + G-500 on top, or a Yaesu G-5500, with AC motor and no brake) automatically, in particular from WSJT software.

However WSJT does not offer a possibility to control directly  antenna rotors, but 'PstRotator' software does - see    http://www.pstrotator.com    by monitoring a 'status file' generated by WSJT during QSO's.  This PstRotator software can as well track the moon on it's own

This software can be considered as the 'Swiss army knife' to control almost any existing rotor and interface to almost any software ... it was developed by  Codrut YO3DMU and is offered at a very reasonable cost..

As I absolutely wanted to keep the existing control boxes, an 'add-on' interface between PstRotator and the control box had to be considered. I still owned a rotor interface which I used for AO-10 / AO-40 satellite tracking, this was based on a project by AMSAT-DL  (IF-100 card) dated of 1986 about...

As PstRotataor did not support this IF-100, which is to be interfaced with a PC through the printer (parallel) port, I decided to 'upgrade' the hardware with ARDUINO  being controlled by YAESU RS-232 protocol, which is supported by most software and offers bi-directional communication (see protocol specs here).

In the original azimuth and elevation control boxes, the switches were bridged by relays, which are energized by ARDUINO nano.

The DC voltage reflecting the antenna position must be taken from the box. This voltage must be varying - for a good readout accuracy - 'as much as possible' in the range of 0 ... +5V between azimuth 000° and 359° and elevation 0° to 90°, but by no means go into negative polarity ... or additional circuits would be necessary.

Now, regarding to the ARDUINO sketch, there are several possibilities published on the web. The most complete solution is certainly proposed by K3NG, but for my purpose probably far to complicated to configure and put into operation, the sketch being as well too elaborated to modify for my simple requirements ...

I found another solution proposed by Viorel YO3RAK - see information  here . This sketch was more 'straightforward' and well documented to dig into it and consider as a good starting point ... not wanting to reinvent the wheel.

 

Specific functional requirements

The sketch written by Viorel is for  'EasyComm2' protocol,  I changed communication protocol to YAESU GS23, GS-232A and GS-232B which supports bi-directional data exchange, and is widely spread.  Only the basic commands were implemented, as per PstRotator functional features (this is : 'go to azimuth XXX elevation XX  read and return actual azimuth/elevation, and STOP rotation)

The sketch already re-engineered for Azimuth only was extended to include  'elevation' functions,  and read inputs from voltage instead as from encoders.

Further refinements were added :

Hardware

The project was realized with an ARDUINO Nano board, offering the advantage of being flashable by USB port. On this same USB port, you can connect your PC where the control software (PstRotator or other ...)  is running. The Arduino Nano board and 2x20 character LCD display were easily integrated in my existing project box. With a joystick, antenna can be manually positioned - in this case, AUTO TRACKING is automatically disabled. If software commands are issued while in manual control, the buzzer will emit a warning tone.

Instead of applying power to Arduino NANO via the 'VIN pin', better provide a µA7805 voltage regulator which will as well power the LCD display. As the backlight consumes about 50 mA, it is required to mount the voltage regulator on the project box casing to provide some cooling.

IMPORTANT :  it is absolutely necessary to protect the analog input of Arduino against a negative voltage and/or a level higher than +5 V  !  Even if your potentiometer does 'normally' produce a voltage within range, what happens when the control box is not connected to rotor .... or a potmeter becomes defective ??? See the 2 diodes provided for the protection.

YAESU G5400B Rotor Controller

At a local flea market, I could purchase a G5400B dual rotor controller with elevation and azimuth motors at 35,-EUR ... a real bargain !! This set is ideally suited to be remote controlled, as there are relay inputs for controlling both motors and their position is available on the voltage outputs (which acn be adjusted by trimmers). However, azimuth scale is indicated as South - North - South (so the stop is in the South)  and elevation is scaled from 0 to 180 degrees. For EME , it would be more convenient to scale azimuth with a stop in the North (so from 0 to 360 degrees) and elevation from 0 to 90 degrees (luckily, the calibration pot allows full scale indication when rotor at 90 degrees position).

Consequently, new scales were designed accordingly - not so fancy, but very functional. Simply print them,  laminate them under plastic foil, and carefully them cut out - using the originals as template.  You can download the azimuth and elevation scales as pdf documents by clicking on the links.  Finally, the classic light bulbs were replaced by 3 pcs 5mm LEDS on top of each scale.

 

Arduino sketch

The original sketch was modified while not looking into crunching program code lines, as there is plenty room available in the Arduino.... 

Below is the sketch  or download it here.   It was compiled with IDE version 1.8.13 - IMPORTANT : Should you get errors when compiling, use the same version of IDE !  You still can download previous versions from ARDUINO website .  

IMPORTANT: Please note that the software & device concept is presented 'as is' with no guarantees whatsoever and can be used free of license cost by individual  HAMs, but not  used for any commercial purpose, as the intellectual property remains entirely by the author. 


 

 





/*  AZ/EL Antenna Rotator controller for Arduino - DC motors
 *  ========================================================
 *  Uses EasyComm protocol for computer - Tracking Software
 *  Manual command by means of two rotary encoders AZ - EL
 *  
 *  Viorel Racoviteannu
 *  https://www.youtube.com/channel/UCiRLZX0bV9rS04BGAyUf-fA
 *  https://racov.ro
 *  [email protected]
 *  
 * I cannot take any responsibility for missuse of this code
 * or any kind of damage it may occur from using this code.
 * 
 * dec 2020 v2 - improved serial comm stability
 * january 2021 - improved near target dead-zone, for which antenna won't move
 * apr 2021 - improved serial comm stability
 * jun 2021 - error proportional power for tracking movement
 *   
 */




 ////////////// on FS laptop : UPPER USB = COM 9 CH340  //////////////////////


/// VERSION HISTORY :
 

// V4 : implement interpolation between 8 ref positions
// V5 : limit motor running time to 1min15 = 75000 ms = OK
// V6 : stall detect , AZ not changing
// V7 : manual / software control mode + OK !
// V8 : LCD parallel 
//      STOP command software
// V9 : finetuning
// V10 : CW / CCW cast difference
// V11 : ERR display

// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

// V12 : implement ELEVATION
// V13 : Decoding GS232 protocol for AZ and EL OK, Azimuth working OK
// V14 : Added autotrack input detect by switch
// V15 : added AZ tolerance in function of elev
// V16 : debug sporadic ERR
// V17 : move azium & elev detect with RAW data
// V18 : Still ERR on Azim Stall , tested with satPC32 = OK.
//         No more reset AzimChangeTime at new position command, but on activation of CW or CCW relay
//         Tested when Az motor interrupted = ERR detected.
// V19 : ERR troubles solved ! Tested with SatPC32 & moon/sun track of PstRotator
//       implemented different max runtimes AZ & Elev   and 500ms delay if motor rotation direction sense required

// V20 : Added buzzer for error signalling
// V21 : Added predictive rotation Azimuth !
// V22 : Added predictive rotation Elevation !
// V23 : finetuning P rotation
// V24 : Added INHIBIT on PTT input. Inhibit pin now D2, AUTORACK  pin moved from D2 to A3
// V25 : Avoid negative AZ and EL possible with predictive tracking routines

/*   // FOR I2C LCD
#include <Wire.h> // Library for I2C communication
#include <LiquidCrystal_I2C.h> // https://www.arduinolibraries.info/libraries/liquid-crystal-i2-c (Library for LCD)
// Wiring: SDA pin is connected to A4 and SCL pin to A5.
// Connect to LCD via I2C, default address 0x27 (A0-A2 not jumpered)
LiquidCrystal_I2C lcd(0x27, 8, 2); // address, chars, rows.
*/ 
 
//  FOR PARALLEL LCD

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

/* *****  for LCD DISPLAY  2x 20 ******
 *
 *
 * LCD RS pin  to digital pin 7
 * LCD R/W pin  - put to GND
 * LCD Enable pin to digital pin 8
 * LCD D4 pin to digital pin 9
 * LCD D5 pin to digital pin 10
 * LCD D6 pin to digital pin 11
 * LCD D7 pin to digital pin 12
 * 
 * 
 *
 * 
 */

#include <SoftwareSerial.h>



/***********************************THIS IS WHERE YOU REALY TWEAK THE ANTENNA MOVEMENT***************/

/* // ANTENNA potentiometers CALIBRATION  (counts) for NON LINEAR AZIMUTH

#define  Az000 213                //begining of the potentiometer  (000°)
#define  Az045 321
#define  Az090 430
#define  Az135 525
#define  Az180 614
#define  Az225 697
#define  Az270 770
#define  Az315 840
#define  Az360 902                //end of the potentiometer 

*/

// ANTENNA potentiometers CALIBRATION  (counts) for LINEAR AZIMUTH

#define  Az000 026                //end of the potentiometer 
#define  Az360 1023               //end of the potentiometer 

// ANTENNA potentiometers CALIBRATION  (counts) for LINEAR ELEVATION

#define  El00 000                //end of the potentiometer, horizontal position 
#define  El90 533                //90° up, vertical






// ************    ROTOR PARAMETERS   ************************************************************

// Allowed error for which antennna won't move. Minimum 1 degree
  
    int AzErr = 2;          

    #define  AzErr00 2        // Azimuth error at higher elevation (gain controlled steering)
    #define  AzErr50 3
    #define  AzErr60 4
    #define  AzErr65 5
    #define  AzErr70 6
    #define  AzErr80 10 

    int ElErr = 2;


// Angle difference where soft stop begins (for casting motor)
  int Amax_CW = 1;            // clockwise / right
  int Amax_CCW = 1;           // counter clockwise / left 

  int Amax_UP = 0;            // going UP
  int Amax_DWN = 0;           // going DOWN

// max running time of AZ rotor motor 1min15s
  long unsigned AzMotorRunTimeLimit = 75000 ;  

// max running time of EL rotor motor 45s
  long unsigned ElMotorRunTimeLimit = 45000 ; 
  

// max time motor can run if no azimuth or elevation change detected (system stall / rotation blocked)
  int StallTimer = 5000 ;

     

// ***********************************************************************************************
 
  
// other variables
  #define AzPotPin  A0        // select the input pin for the azim. potentiometer
  #define ElPotPin  A1        // select the input pin for the elev. potentiometer
  
  #define AutoTrackPin A3      // select the input pin for signalling autotracking : When HIGH = autotrack required (connected to switch)

  #define AzRotPin_CCW 3      // select the out pin for rotation direction
  #define AzRotPin_CW  4      // select the out pin for rotation direction 

  #define ElRotPin_UP  5      // select the out pin for rotation upwards
  #define ElRotPin_DWN  6      // select the out pin for rotation downwards   

  #define RS232pin    13      // RS-232 activity blinker 

// Inhibit parameters
  #define InhibitPin   2      // Inhibit pin (PTT detect) - when low, inhibit is active. On Arduino nano, pin 2 or 3 are interrupt capable  
  bool InhibitStatus = false;
  long unsigned InhibitTime ; //  Timestamp start inhibition
  long unsigned InhibitDelay = 16000; //  Delay of inhibition, in ms
     
  
  #define BuzzerPin    A4      // Buzzer pin  

  bool SoftControlAz = false;   // if AZ controlled by software commands = true
  bool SoftControlEl = false;   // if EL controlled by software commands = true  
  

  int TruAzim = 0;            // calculated real azimuth value
  int ComAzim = 0;            // commanded azimuth value
  int RComAzim = 0;           // Raw commanded azimuth value (uncorrected for predicted rotation)
  int ROld_ComAzim = 0;       // older Raw commanded azimuth value
 
  
  int RepAzim = 0;            // reported Azimuth to software
  int RawAzim = 0;            // for interpolation
  int OldRawAzim = 0;

  int Pred_rotation = 0;      // counter predictive rotation, + rotation CW, - rotation CCW
  bool Pred_rotation_active = false ;     // if active = true
  int Pred_rotation_moves = 3;// minimum # moves in same direction before predictive rotation is active

  int TruElev = 0;            // calculated real elevation value
  int ComElev = 0;            // commanded elevation value
  int RComElev = 0;          // Raw commanded elevation value (uncorrected for predicted move)
  int ROld_ComElev = 0;       // older Raw commanded Elev value

  
  int RepElev = 0;            // reported elevation to software
  int RawElev = 0;            // for interpolation
  int OldRawElev = 0;

  int Pred_elev = 0;          // counter predictive elev, + upwards, - downwards
  bool Pred_elev_active = false ;     // if active = true
  int Pred_elev_moves = 3;    // minimum # moves in same direction before predictive elev is active

  int OldTruAzim = 0;         // to store previous azimuth value
  int OldComAzim = 0;
  bool ComAzDisplClr = true;  // Clear Com Azim display indication 

  int OldTruElev = 0;         // to store previous elevation value
  int OldComElev = 0; 
  bool ComElDisplClr = true;  // Clear Com Elev display indication
  
  char AzDir;                 // symbol for azim rot display
  char ElDir;                 // symbol for elev rot display            
  

// flags for AZ tolerances

  bool AzRotate = false;          // azimuth 'rotate command' active
  bool rotate_CW = true;          // flag rotate CW

// flags for EL tolerances

  bool ElRotate = false;          // Elevation 'rotate command' active
  bool rotate_UP = true;          // flag rotate UP
  
  
//averaging loop
  const int numReadings = 5;      // averages the reading
  int readIndexAz = 0;            // the index of the current reading AZ
  int readIndexEl = 0;            // the index of the current reading EL   
  int azimuth[numReadings];       // the readings from the analog input
  int elevation[numReadings];     // the readings from the analog input

  int totalAz = 0;                // the running total
  int totalEl = 0;                // the running total
  
  long unsigned LastDispUpdate;   // display update frequency 

  long unsigned LastBuzzer;       // Last buzzer sounding

  long unsigned AzimChangeTime;   // change of true azim detected 
  long unsigned AzMotorStartTime; // start of motor
  long unsigned AzMotorStopTime;  // stop of motor 

  long unsigned ElevChangeTime;   // change of true azim detected 
  long unsigned ElMotorStartTime; // start of motor
  long unsigned ElMotorStopTime;  // stop of motor 

  long unsigned StopCmdTime;      // stop of motor 
 
  bool MotorErrAz = false;
  bool MotorErrEl = false; 

  bool StandBy = true;            // in StandBy mode no tracking by software allowed 
   

// variables for serial comm
  String Azimuth = "";
  String Elevation = "";
  String ComputerRead;
  String ComputerWrite;
  
  long unsigned LastSerExch;

  bool printLeft = true;

  // LCD  character 8 x 5 dots

  // build LCD specific characters 'degree'
  byte degree [8] = {
  B00100,
  B01010,
  B00100,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  };

 // build LCD specific characters 'right'
  byte right [8] = {
  0b01000,
  0b01100,
  0b01110,
  0b11111,
  0b11111,
  0b01110,
  0b01100,
  0b01000
  };


 // build LCD specific characters 'left'
  byte left [8] = {
  0b00010,
  0b00110,
  0b01110,
  0b11111,
  0b11111,
  0b01110,
  0b00110,
  0b00010
  };


 // build LCD specific characters 'up'
  byte up [8] = {
  0b00100,
  0b01110,
  0b11111,
  0b00100,
  0b00100,
  0b00100,
  0b00100,
  0b00000
  };


 // build LCD specific characters 'down'
  byte down [8] = {
  0b00000,
  0b00100,
  0b00100,
  0b00100,
  0b00100,
  0b11111,
  0b01110,
  0b00100
  };

 
 // build LCD specific characters 'P+'
  byte Pplus [8] = {
  0b01100,
  0b01010,
  0b01100,
  0b01000,
  0b01000,
  0b00100,
  0b01110,
  0b00100
  };

 
 // build LCD specific characters 'P+'
  byte Pmin [8] = {
  0b01100,
  0b01010,
  0b01100,
  0b01000,
  0b01000,
  0b00000,
  0b01110,
  0b00000
  }; 




////////////////////////////////////////////////////
////////////////////  S E T U P  ///////////////////
////////////////////////////////////////////////////

 
  
void setup() {
  
  Serial.begin(9600);

  Serial.setTimeout(30);                         // sets the maximum milliseconds to wait for serial data. It defaults to 1000 milliseconds
                                                 // is too long for PST rotator with 0,5s updates ...
 // FOR PARALLEL LCD 

 //
 //  Initiate the LCD:   20 char x 2 rows
   lcd.begin(20,2);
 //

 /* Initiate the I2C LCD:

  lcd.init();
  lcd.backlight();

 */
  
  lcd.createChar(1, degree);
  lcd.createChar(2, right);  
  lcd.createChar(3, left); 
  lcd.createChar(4, up); 
  lcd.createChar(5, down); 
  lcd.createChar(6, Pplus); 
  lcd.createChar(7, Pmin);  
   
  // pin declaration

  digitalWrite(AzRotPin_CCW, LOW);                  // deactivate rotation pin left
  digitalWrite(AzRotPin_CW,  LOW);                  // deactivate rotation pin right

  digitalWrite(ElRotPin_UP, LOW);                   // deactivate rotation pin up
  digitalWrite(ElRotPin_DWN,  LOW);                 // deactivate rotation pin down


  digitalWrite(RS232pin,  LOW);                     // RS232 activity pin 
  
  pinMode(AzRotPin_CCW, OUTPUT);                    //declaring  azim. rotation direction Pin as OUTPUT
  pinMode(AzRotPin_CW,  OUTPUT);                    //declaring  azim. rotation direction Pin as OUTPUT

  pinMode(ElRotPin_UP, OUTPUT);                     //declaring  elev. rotation direction Pin as OUTPUT
  pinMode(ElRotPin_DWN, OUTPUT);                    //declaring  elev. rotation direction Pin as OUTPUT

  pinMode(RS232pin, OUTPUT);
  
  pinMode(AzPotPin, INPUT);
  pinMode(ElPotPin, INPUT);

  pinMode (AutoTrackPin,INPUT);

  pinMode(InhibitPin, INPUT_PULLUP);                       // for PTT detect
  attachInterrupt(digitalPinToInterrupt(InhibitPin), Inhibit, FALLING); 
  
  // write on display name and version
  lcd.clear();
  lcd.setCursor(3, 0);          
  lcd.print("Rotor  Control");  
  lcd.setCursor(0, 1);         
  lcd.print("V25 11-23");
  lcd.setCursor(11, 1);          
  lcd.print("by ON7EQ"); 

  pinMode(BuzzerPin, OUTPUT);
  tone(BuzzerPin,2200);  
  delay(150);
  tone(BuzzerPin,2400); 
  delay(150);  
  noTone (BuzzerPin);
        
  delay(2500);                  
   
  lcd.clear();
  lcd.setCursor(0, 0);              
  lcd.print(" YAESU Protocol 9k6");   
  lcd.setCursor(0, 1);
  lcd.print(" Predictive Tracking");
  delay(1500); 
  lcd.setCursor(0, 1);
  lcd.print("Accuracy Az");
  lcd.print(" "); 
  lcd.print(AzErr);             
  
  lcd.setCursor(13, 1);   
  lcd.write(1);
  lcd.print(" ");

  lcd.print("El ");
  
  lcd.print(ElErr);  
  lcd.setCursor(19, 1);   
  lcd.write(1);
            
  delay(3500);                  
    
  lcd.clear();

  
  // display Azim.  value
  lcd.setCursor(0, 0);
  
  lcd.print("Az  --- ");  
  lcd.setCursor(2, 0);
  lcd.write(1);
  lcd.setCursor(0, 1); 
  lcd.print("Ctl  -  "); 

  lcd.setCursor(13, 0);  
  lcd.print("El  -- ");  
  lcd.setCursor(15, 0);
  lcd.write(1);
  lcd.setCursor(13, 1); 
  lcd.print("Ctl  - "); 
    
 


  
  // this is to set azim-command the same value as real, not to jerk the antenna at start-up
 
  /////    AZIMUTH READING 


 
  RawAzim = analogRead(AzPotPin);
  OldRawAzim = RawAzim;

   /*         // For NON linear pot Azimuth
  
  if (                        (RawAzim <= Az045))  TruAzim = (map(RawAzim, Az000, Az045,   0,  45));
  if ((RawAzim  > Az045)  and (RawAzim <= Az090))  TruAzim = (map(RawAzim, Az045, Az090,  45,  90));
  if ((RawAzim  > Az090)  and (RawAzim <= Az135))  TruAzim = (map(RawAzim, Az090, Az135,  90, 135));  
  if ((RawAzim  > Az135)  and (RawAzim <= Az180))  TruAzim = (map(RawAzim, Az135, Az180, 135, 180));
  if ((RawAzim  > Az180)  and (RawAzim <= Az225))  TruAzim = (map(RawAzim, Az180, Az225, 180, 225));
  if ((RawAzim  > Az225)  and (RawAzim <= Az270))  TruAzim = (map(RawAzim, Az225, Az270, 225, 270));
  if ((RawAzim  > Az270)  and (RawAzim <= Az315))  TruAzim = (map(RawAzim, Az270, Az315, 270, 315));
  if ((RawAzim  > Az315)                        )  TruAzim = (map(RawAzim, Az315, Az360, 315, 359));
   
  */

  //  FOR LINEAR POTMETER
  TruAzim = (map(analogRead(AzPotPin), Az000, Az360, 0, 359));                // azimuth value 0-359 , for linear pot

  if (TruAzim<0) {TruAzim=0;}
  if (TruAzim>359) {TruAzim=359;}                                             // keep values between limits


// initialize all the readings
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    azimuth[thisReading] = 0;                                           
  }
  
  ComAzim = TruAzim;

  OldTruAzim = TruAzim;
  OldComAzim = ComAzim;

  DisplTruAzim();
  DisplComAzim();

  RepAzim = TruAzim; 

  AzimChangeTime = millis ();

  /////    ELEVATION READING 
 
  RawElev = analogRead(ElPotPin); 
  
  TruElev = (map(analogRead(ElPotPin), El00, El90, 0, 90));                 // Elevation value 0-90 , for linear pot
  
  if (TruElev<0) {TruElev=0;}
  if (TruElev>99) {TruElev=99;}                                             // keep values between limits


// initialize all the readings
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    elevation[thisReading] = 0;                                       
  }
  
  ComElev = TruElev;

  OldTruElev = TruElev;
  OldComElev = ComElev;

  DisplTruElev();
  DisplComElev();

  RepElev = TruElev; 

  ElevChangeTime = millis ();

  LastBuzzer = millis();


}

////////////////////////////////////////////////////
////////////////////// L O O P /////////////////////
////////////////////////////////////////////////////
  
void loop() {


////  DETECT IF INHIBIT is active

 if (digitalRead (InhibitPin) == LOW)  {
        InhibitStatus = true ;          // just to be sure, if problem with interrupt
        InhibitTime = millis();         // reset timer as long as PTT low
        }

 else if (millis()-InhibitTime > InhibitDelay) InhibitStatus = false;  // recover from inhibit after delay
 
 if (InhibitStatus == true) {
        ComAzim = TruAzim;                    // Fool the sketch as RS-232 position commands are disabled in the procedure
        ComElev = TruElev;
        lcd.setCursor(8, 1); 
        lcd.print("INHI");
 
      }


////  DETECT IF AUTOTRACK is required, as per switch active 

    if (digitalRead (AutoTrackPin) == HIGH) {
              StandBy = false ;                  // logic high = standby
              }
       else {
              StandBy = true;
              Pred_rotation_active = false;      // disable predictive tracking
              Pred_rotation = 0;                 // reset counter   
              lcd.setCursor(8, 0);
              lcd.print(" ");
              Pred_elev_active = false;          // disable predictive tracking
              Pred_elev = 0;                     // reset counter   
              lcd.setCursor(11, 0);
              lcd.print(" ");
               }


///   SOUND BUZZER IF MOTOR ERROR
       
    if ((millis() - LastBuzzer > 500) and ((MotorErrEl == true) or (MotorErrAz == true)))  {
      tone(BuzzerPin,2400,150);
      //delay (150);
      //noTone (BuzzerPin);   
      LastBuzzer = millis();
     }



//// Clear Ctl AZIM display

/*
    if ( (millis() - AzimChangeTime > 1500) and (ComAzDisplClr == true)) {

              ComAzDisplClr = false ;   
              lcd.setCursor(4, 1); 
              lcd.print(" - ");        
    }

// other possibility ...

*/
    if ( (millis() - AzimChangeTime > 1500) and (MotorErrAz == false) and (digitalRead(AzRotPin_CCW) == LOW) and (digitalRead(AzRotPin_CW) == LOW)) { //   motor is stopped, no motor error, so clear control value                                                                                                                             ) {

              // ComAzDisplClr = false ;   
              lcd.setCursor(4, 1); 
              lcd.print(" - ");        
    }

  

//// Clear Ctl ELEV display

/*
    if ( (millis() - ElevChangeTime > 1500) and (ComElDisplClr == true)) {

              ComElDisplClr = false ;   
              lcd.setCursor(17, 1); 
              lcd.print(" - ");        
  
    }

// other possibility ...

*/

    if ( (millis() - ElevChangeTime > 1500) and (MotorErrEl == false) and (digitalRead(ElRotPin_DWN) == LOW) and (digitalRead(ElRotPin_UP) == LOW)) { //   motor is stopped, no motor error, so clear control value                                                                                                                             ) {

              // ComElDisplClr = false ;   
              lcd.setCursor(17, 1); 
              lcd.print(" - ");        
    }

   
  

/////   AZIMUTH AVERAGING LOOP

if (InhibitStatus == false) {

    totalAz = totalAz - azimuth[readIndexAz];
  
    // read from the sensor:



     RawAzim = analogRead(AzPotPin);    

  
    if (abs (RawAzim - OldRawAzim)> 1) {              // azim change detect !!!!  1 = for anti jitter

                OldRawAzim = RawAzim ;
                AzimChangeTime = millis();    // reset azim change time
             }
  
  /*         // For NON linear pot Azimuth
  
  if (                        (RawAzim <= Az045))  azimuth[readIndex] = (map(RawAzim, Az000, Az045,   0,  45));
  if ((RawAzim  > Az045)  and (RawAzim <= Az090))  azimuth[readIndex] = (map(RawAzim, Az045, Az090,  45,  90));
  if ((RawAzim  > Az090)  and (RawAzim <= Az135))  azimuth[readIndex] = (map(RawAzim, Az090, Az135,  90, 135));  
  if ((RawAzim  > Az135)  and (RawAzim <= Az180))  azimuth[readIndex] = (map(RawAzim, Az135, Az180, 135, 180));
  if ((RawAzim  > Az180)  and (RawAzim <= Az225))  azimuth[readIndex] = (map(RawAzim, Az180, Az225, 180, 225));
  if ((RawAzim  > Az225)  and (RawAzim <= Az270))  azimuth[readIndex] = (map(RawAzim, Az225, Az270, 225, 270));
  if ((RawAzim  > Az270)  and (RawAzim <= Az315))  azimuth[readIndex] = (map(RawAzim, Az270, Az315, 270, 315));
  if ((RawAzim  > Az315)                        )  azimuth[readIndex] = (map(RawAzim, Az315, Az360, 315, 359));

  */

  //  FOR LINEAR POTMETER
  
      azimuth[readIndexAz] = (map(analogRead(AzPotPin), Az000, Az360, 0, 359));   // For linear pot
  
      // add the reading to the total:
      totalAz = totalAz + azimuth[readIndexAz];
      // advance to the next position in the array:
      readIndexAz = readIndexAz + 1;               
      // if we're at the end of the array, wrap around to the beginning:
      if (readIndexAz >= numReadings) {readIndexAz = 0;}   
      // calculate the average:
      TruAzim = totalAz / numReadings;  

  
      if (TruAzim<0) {TruAzim=0;}
      if (TruAzim>359) {TruAzim=359;}                                           // keep values between limits


/////   ELEVATION AVERAGING LOOP

      totalEl = totalEl - elevation[readIndexEl];

      // read from the sensor:

       RawElev = analogRead(ElPotPin);

       if (abs(RawElev - OldRawElev) > 1) {              // elev change detect !!!!  1 = anti jitter
                OldRawElev = RawElev ;
                ElevChangeTime = millis();    // reset elev change time
             }
 
  
      elevation[readIndexEl] = (map(analogRead(ElPotPin), El00, El90, 0, 90));  // Elevation value 0-90 , for linear pot
  
      // add the reading to the total:
      totalEl = totalEl + elevation[readIndexEl];
      // advance to the next position in the array:
       readIndexEl = readIndexEl + 1;
      // if we're at the end of the array, wrap around to the beginning:
      if (readIndexEl >= numReadings) {readIndexEl = 0;}
      // calculate the average:
      TruElev = totalEl / numReadings;    
   
      if (TruElev<0) {TruElev=0;}
      if (TruElev>99){
         {TruElev=99;}  // keep values between limits
          digitalWrite(ElRotPin_UP, LOW);                                   // deactivate rotation pin up
          MotorErrEl = true;  
          }

      ///   Define permitted AZI error in function of ELEV  (gain related pointing)
 
     AzErr = AzErr00;                                                           // default value 
 
 /*
 if (TruElev >= 50) AzErr = AzErr50;                                        // at higher elev value = 1 / cos (elev)
 if (TruElev >= 60) AzErr = AzErr60;
 if (TruElev >= 65) AzErr = AzErr65; 
 if (TruElev >= 70) AzErr = AzErr70;
 */
     if (TruElev >= 80) AzErr = AzErr80;

}  /////////////// end of inhibit on position readings  //////////////

  

// update antenna true positions displays

    if ((millis()- LastDispUpdate) > 500){                                  //not to flicker the display
        
        LastDispUpdate = millis();

                             if (RawAzim < (Az000 - 5) ) {                  // Probably controller is OFF ?
                                            lcd.setCursor(4, 0);
                                            lcd.print ("OFF");            
                                          }
                    
                              if (RawElev < El00 ) {
                                            lcd.setCursor(17, 0);
                                            lcd.print ("?? ");            
                                          }
                                        
        
            if (OldTruAzim != TruAzim)  {                             // set to (abs difference > 1) in order to  eliminate last digit jitter in display and PC software
            AzimChangeTime = millis();                                // reset azimuth change timer
            DisplTruAzim();
            RepAzim = TruAzim;                                                                                           
             }

            if (OldTruElev != TruElev )  {                            // set to (abs difference > 1) in order to  eliminate last digit jitter in display and PC software
            ElevChangeTime = millis();                                // reset elev change timer
            DisplTruElev();
            RepElev = TruElev;                                                                                           
             }
             
     }

// if ( (millis() - AzimChangeTime > StallTimer) and (AzRotate == true)) {                                                                // Check for motor stall / rotation is blocked ?

if ( (millis() - AzimChangeTime > StallTimer) and ((digitalRead(AzRotPin_CCW) == HIGH) or  (digitalRead(AzRotPin_CW) == HIGH))) {         // Check for motor stall / rotation is blocked ?

  
            MotorErrAz = true;                                                  // No AZ change during > stalltimer while rotate command active --> ERROR !
            digitalWrite(AzRotPin_CCW, LOW);                                    // deactivate rotation pin left
            digitalWrite(AzRotPin_CW,  LOW);                                    // deactivate rotation pin right 
            AzRotate = false;
            lcd.setCursor(3, 0); 
            lcd.print(" ");
            lcd.setCursor(7, 0);         
            lcd.print(" ");
            lcd.setCursor(4, 1); 
            lcd.print("ERR");
            }

// if ( (millis() - ElevChangeTime > StallTimer) and (ElRotate == true)) {                                                                    // Check for motor stall / rotation is blocked ?

if ( (millis() - ElevChangeTime > StallTimer) and ((digitalRead(ElRotPin_DWN) == HIGH) or  (digitalRead(ElRotPin_UP) == HIGH)) )    {         // Check for motor stall / rotation is blocked ?
  
            MotorErrEl = true;                                                  // No EL change during > stalltimer while rotate command active --> ERROR !
            digitalWrite(ElRotPin_DWN, LOW);                                    // deactivate rotation pin down
            digitalWrite(ElRotPin_UP,  LOW);                                    // deactivate rotation pin up
            ElRotate = false;
            lcd.setCursor(16, 0); 
            lcd.print(" ");
            lcd.setCursor(19, 0);         
            lcd.print(" ");
            lcd.setCursor(17, 1); 
            lcd.print("ERR");
            }



// every 0,1 seconds looking for serial communication


if ((millis()- LastSerExch) > 100){  

    digitalWrite(RS232pin,  LOW);                   // clear serial activity LED

    if ((millis()- LastSerExch) > 2500) {           // no more serial exchange ... clear dots
         lcd.setCursor(9, 0);
         lcd.print("  "); 

          Pred_rotation_active = false;             // disable predictive tracking, as no more software control
          Pred_rotation = 0;                        // reset counter   
          lcd.setCursor(8, 0);
          lcd.print(" ");
          Pred_elev_active = false;                 // disable predictive tracking
          Pred_elev = 0;                            // reset counter   
          lcd.setCursor(11, 0);
          lcd.print(" ");
        
         }


    
    if (Serial.available() > 0) {

        LastSerExch = millis();        
        digitalWrite(RS232pin,  HIGH);              // blink RS232 LED

        lcd.setCursor(9, 0);                        // RS232 activity dots
         if (printLeft == true ) {
              lcd.print(". "); 
              printLeft = false;
               }
          else {
              lcd.print(" ."); 
              printLeft = true;            
          }
     
          SerComm();
          }
  }

// update command target position display
  if ((ComAzim != OldComAzim) )  {

    AzimChangeTime = millis () ;                // reset AZ change timer, keep before rotating antenna
    DisplComAzim();
    }

  if ((ComElev != OldComElev) ) {

    ElevChangeTime = millis () ;                // reset EL change timer, keep before rotating antenna
    DisplComElev();
    
    }
    

/////////////////////////////////////////////////////////////
/////////   This is to rotate in azimuth   //////////////////
/////////////////////////////////////////////////////////////


  if (TruAzim == ComAzim) {                                       // if equal, stop moving

              digitalWrite(AzRotPin_CCW, LOW);                    // deactivate rotation pin left
              digitalWrite(AzRotPin_CW,  LOW);                    // deactivate rotation pin right 
              AzRotate = false;                                   // AzMotor stopped
              SoftControlAz = false;                              // back to manual mode         
              lcd.setCursor(3, 0);
              lcd.print(" ");
              lcd.setCursor(7, 0);
              lcd.print(" ");
              lcd.setCursor(4, 1); 
                if (MotorErrAz == false) {
                ComAzDisplClr = true ;
                //lcd.print(" - ");           
                  }               
              
              }
    
    else if ((abs(ComAzim-TruAzim) <= Amax_CW) and (rotate_CW == true))  {    // casting motor, STOP !
              digitalWrite(AzRotPin_CCW, LOW);                                // deactivate rotation pin left
              digitalWrite(AzRotPin_CW,  LOW);                                // deactivate rotation pin right 
              AzRotate = false;                                               // AzMotor stopped
              SoftControlAz = false ;                                         // back to manual mode             
              lcd.setCursor(3, 0);
              lcd.print(" ");
              lcd.setCursor(7, 0);
              lcd.print(" ");
              lcd.setCursor(4, 1); 
                if (MotorErrAz == false) {
                ComAzDisplClr = true ;                
                //lcd.print(" - ");                  
                  }
               }
   
    else if ((abs(ComAzim-TruAzim) <= Amax_CCW) and (rotate_CW == false))  {   // casting motor, STOP !
              digitalWrite(AzRotPin_CCW, LOW);                                 // deactivate rotation pin left
              digitalWrite(AzRotPin_CW,  LOW);                                 // deactivate rotation pin right 
              AzRotate = false;                                                // AzMotor stopped
              SoftControlAz = false ;                                          // back to manual mode             
              lcd.setCursor(3, 0);
              lcd.print(" ");
              lcd.setCursor(7, 0);
              lcd.print(" "); 
              lcd.setCursor(4, 1); 
                    if (MotorErrAz == false) {
                    ComAzDisplClr = true ;                      
                    //lcd.print(" - ");                
                  }   
               }

        
              
    else if ((abs(TruAzim - ComAzim)<=AzErr) && (AzRotate == true) && (SoftControlAz == true)) {  // if in tolerance, but it wasn't an equal, rotate
              if (MotorErrAz == false) {
                AzimRotate();
                }
              else {                                              // MotorErrAz = true !
                digitalWrite(AzRotPin_CCW, LOW);                  // we have motor error
                digitalWrite(AzRotPin_CW,  LOW);                  // 
                   }
         
               }
               
    else if ((abs(TruAzim - ComAzim)>AzErr) && (SoftControlAz == true)){       // if target is off tolerance
 
              if (MotorErrAz == false) {

                        if (abs(TruAzim - ComAzim)> 2 * AzErr)  {             // we have a large offset, so assume not tracking object, but manual moving

                                Pred_rotation_active = false;                 // so disable predictive tracking
                                Pred_rotation = 0;                            // reset counter   
                                lcd.setCursor(8, 0);
                                lcd.print(" ");
                                Pred_elev_active = false;                     // disable predictive tracking
                                Pred_elev = 0;                                // reset counter   
                                lcd.setCursor(11, 0);
                                lcd.print(" ");
                          
                              }
                
                  AzimRotate();                                    // ROTATE PROCEDURE
                  }
              else {                                               // MotorErr = true !
                digitalWrite(AzRotPin_CCW, LOW);                   // we have motor error
                digitalWrite(AzRotPin_CW,  LOW);                   //  
                   }
              }


/////////////////////////////////////////////////////////////
/////////   this is to rotate in elevation   ////////////////
/////////////////////////////////////////////////////////////


  if (TruElev == ComElev) {                                     // if equal, stop moving

              digitalWrite(ElRotPin_DWN, LOW);                  // deactivate rotation pin down
              digitalWrite(ElRotPin_UP,  LOW);                  // deactivate rotation pin up 
              ElRotate = false;                                 // ElMotor stopped
              SoftControlEl = false;                            // back to manual mode         
              lcd.setCursor(16, 0);
              lcd.print(" ");
              lcd.setCursor(19, 0);
              lcd.print(" ");
              lcd.setCursor(17, 1); 
                if (MotorErrEl == false) {
                   ComElDisplClr = true;
                   // lcd.print(" - ");         
                  }               
              
              }

    // NOT EQUAL

    // But within casting tolerance
    
    else if ((abs(ComElev-TruElev) <= Amax_UP) and (rotate_UP == true))  {   // casting motor upwards, STOP !
              digitalWrite(ElRotPin_DWN, LOW);                               // deactivate rotation pin Down
              digitalWrite(ElRotPin_UP,  LOW);                               // deactivate rotation pin Up
              ElRotate = false;                                              // ElMotor stopped
              SoftControlEl = false ;                                        // back to manual mode             
              lcd.setCursor(16, 0);
              lcd.print(" ");
              lcd.setCursor(19, 0);
              lcd.print(" ");
              lcd.setCursor(17, 1); 
                if (MotorErrEl == false) {
                    ComElDisplClr = true;
                    // lcd.print(" - ");         
                  } 
               }
   
    else if ((abs(ComElev-TruElev) <= Amax_DWN) and (rotate_UP == false))  {   // casting motor downwards, STOP !
              digitalWrite(ElRotPin_DWN, LOW);                                 // deactivate rotation pin Down
              digitalWrite(ElRotPin_UP,  LOW);                                 // deactivate rotation pin Up 
              ElRotate = false;                                                // ElMotor stopped
              SoftControlEl = false ;                                          // back to manual mode             
              lcd.setCursor(16, 0);
              lcd.print(" ");
              lcd.setCursor(19, 0);
              lcd.print(" ");
              lcd.setCursor(17, 1); 
                if (MotorErrEl == false) {
                    ComElDisplClr = true;
                    // lcd.print(" - ");         
                  } 
               }

        
              
    else if ((abs(TruElev - ComElev)<=ElErr)&&(ElRotate == true) && (SoftControlEl == true)) {  // if in tolerance, but it wasn't an equal, rotate
              if (MotorErrEl == false) {
                ElevRotate();                                     // ROTATE
                }
              else {                                              // MotorErr = true !
                digitalWrite(ElRotPin_DWN, LOW);                  // we have motor error
                digitalWrite(ElRotPin_UP,  LOW);                  // 
                   }
         
               }
               
    else if ((abs(TruElev - ComElev)>ElErr) && (SoftControlEl == true)){       // if target is off tolerance
 
              if (MotorErrEl == false) {

                        if (abs(TruElev - ComElev)> 2 * ElErr)  {             // we have a large offset, so assume not tracking object, but manual moving

                                Pred_rotation_active = false;                 // so disable predictive tracking
                                Pred_rotation = 0;                            // reset counter   
                                lcd.setCursor(8, 0);
                                lcd.print(" ");
                                Pred_elev_active = false;                     // disable predictive tracking
                                Pred_elev = 0;                                // reset counter   
                                lcd.setCursor(11, 0);
                                lcd.print(" ");
                          
                              }
                
                  ElevRotate();                                    // ROTATE
                  }
              else {                                              // MotorErr = true !
                digitalWrite(ElRotPin_DWN, LOW);                  // we have motor error
                digitalWrite(ElRotPin_UP,  LOW);                  //  
                   }
              }




//   RESET STOP Indicator, display if STBY or AUTO mode

    if ((millis() - StopCmdTime) > 3000 and (InhibitStatus == false)) {
                  lcd.setCursor(8, 1); 

                  if (StandBy == true) lcd.print("stby"); 
                      else lcd.print("auto"); 
               }




//   delay(50);                      // pause the program for x ms, give time to hardware to move

}

/////////////////////////////////////////////////////
////////////// procedures definitions  //////////////
/////////////////////////////////////////////////////



//// This is the procedure that interrupt calls to check PTT line for inhibit ////

 void Inhibit ()   { 

    if ((millis) - InhibitDelay > 0) {                                        // to avoid INHI at startup of sketch
          InhibitStatus = true;              
          InhibitTime = millis();                                             // reset timer 
          digitalWrite(AzRotPin_CCW, LOW);                                    // deactivate rotation Az
          digitalWrite(AzRotPin_CW,  LOW);
          digitalWrite(ElRotPin_DWN, LOW);                                    // deactivate rotation Elev
          digitalWrite(ElRotPin_UP,  LOW);   
           }
  
     } 


//////// Display actual rotor azimuth   ///////////

void DisplTruAzim() {
  lcd.setCursor(4, 0);
  if (TruAzim<100) lcd.print("0");
  if (TruAzim<10)  lcd.print("0");
  lcd.print(TruAzim);
   
    OldTruAzim = TruAzim;
  
// ************** FOR CALIBRATION PURPOSES **************

        /*
          lcd.setCursor(0, 1);
          lcd.print (analogRead(AzPotPin));
          lcd.print ("  ") ;
        */

      }

/////// Display command azimuth  //////////////

void DisplComAzim(){

      if (abs (ComAzim - TruAzim) > AzErr) {
           lcd.setCursor(4, 1);
           if (ComAzim<100)  lcd.print("0");
           if (ComAzim<10)   lcd.print("0");
             lcd.print(ComAzim);
           }    

     OldComAzim = ComAzim;
      }

//////// Display actual rotor elevation   ///////////

void DisplTruElev() {

  lcd.setCursor(17, 0);
  if (TruElev<10)  lcd.print("0");
      lcd.print(TruElev);
   
    OldTruElev = TruElev;
  
// ************** FOR CALIBRATION PURPOSES **************

        /*
          lcd.setCursor(13, 1);
          lcd.print (analogRead(ElPotPin));
          lcd.print ("  ") ;
        */
      }



/////// Display command elevation  //////////////

void DisplComElev(){


      if (abs (ComElev - TruElev) > ElErr) {
            lcd.setCursor(17, 1);
            if (ComElev<10)   lcd.print("0");
            lcd.print(ComElev);
            }
         OldComElev = ComElev;
   }


////////// Rotate antenna in Azimuth //////////////////////

void AzimRotate() {
    
    if (AzRotate == false ) AzMotorStartTime = millis();                      // reset Az motor Start time if not rotating
    
    if ((ComAzim-TruAzim) > (TruAzim-ComAzim))   {                            // this to determine direction of rotation
        
     // Rotate RIGHT / CW
     
        if ((AzRotate == false ) and (MotorErrAz == false)) AzMotorStartTime = millis();
        
        digitalWrite(AzRotPin_CCW, LOW);                                      // deactivate rotation pin left
        if ((rotate_CW == false) and (AzRotate == true)) delay(750);          // allow some time to reverse motor, if rotation sense  required
        
        if (millis() - AzMotorStartTime < AzMotorRunTimeLimit) {
                if (MotorErrAz == false ) digitalWrite(AzRotPin_CW, HIGH);    //  rotate right
                if (AzRotate == false) {
                          AzRotate = true;
                          AzimChangeTime = millis();                          // reset azim change time
                          delay (50);                                         // time for relay to energize and produce rotation
                          }
                
                rotate_CW = true;
                AzDir = char(126);
                  }                               
               
                
                else {
                  digitalWrite(AzRotPin_CW, LOW);                             // stop if running too long
                  AzRotate = false;
                  lcd.setCursor(3, 0); 
                  lcd.print(" ");
                  lcd.setCursor(7, 0);         
                  lcd.print(" ");
                  lcd.setCursor(4, 1); 
                  lcd.print("ERR");
                  MotorErrAz = true;
                }

    }
     
      else {

     // Rotate LEFT / CCW
        
        if ((AzRotate == false ) and (MotorErrAz == false)) AzMotorStartTime = millis();
        
        digitalWrite(AzRotPin_CW, LOW);                                         // deactivate rotation pin right
        if ((rotate_CW == true) and (AzRotate == true)) delay(750);          // allow some time to reverse motor, if rotation sense  required


        
              if (millis() - AzMotorStartTime < AzMotorRunTimeLimit) {
                if (MotorErrAz == false ) digitalWrite(AzRotPin_CCW, HIGH);     //  rotate left
                if (AzRotate == false) {
                          AzimChangeTime = millis();                            // reset azim change time
                          AzRotate = true;
                          delay (50);                                           // time for relay to energize and produce rotation
                          }
             
                rotate_CW = false;
                AzDir = char(127);
                }                              
            
          
          else {
            digitalWrite(AzRotPin_CCW, LOW);                                    // stop if running too long
            AzRotate = false;
            lcd.setCursor(3, 0); 
            lcd.print(" ");
            lcd.setCursor(7, 0);         
            lcd.print(" ");
            lcd.setCursor(4, 1); 
            lcd.print("ERR");
            MotorErrAz = true;
          }

      }


// Print direction arrows

  if (AzDir == char(126)) {               //CW or turning right
        lcd.setCursor(3, 0); 
        lcd.print(" ");
        lcd.setCursor(7, 0);         
        //lcd.print(String(AzDir));
        lcd.write(2);         
             }
 
  if (AzDir == char(127)) {               //CCW or turning left
        lcd.setCursor(7, 0); 
        lcd.print(" ");
        lcd.setCursor(3, 0);         
        //lcd.print(String(AzDir));
        lcd.write(3);         
             }
      
 }        // End AziRotate

////////// Elevate antenna  //////////////////////

void ElevRotate() {
    
    if (ElRotate == false ) ElMotorStartTime = millis();                      // Reset ElMotorStartTime  if not rotating
    
    if ((ComElev-TruElev) > (TruElev-ComElev))   {                            // this to determine direction of rotation
    
    // UPWARDS
        
        if ((ElRotate == false ) and (MotorErrEl == false)) ElMotorStartTime = millis();
        
        digitalWrite(ElRotPin_DWN, LOW);                                      // deactivate elevation  pin Down, just to be sure
        if ((rotate_UP == false) and (ElRotate == true)) delay(750);          // allow some time to reverse motor, if rotation sense  required

        
        if (millis() - ElMotorStartTime < ElMotorRunTimeLimit) {
                if (MotorErrEl == false ) digitalWrite(ElRotPin_UP, HIGH);    //  rotate UPWARDS
                if (ElRotate == false) {
                  ElevChangeTime = millis();                                  // reset elev change time
                  ElRotate = true;
                  delay (50);                                                 // time for relay to energize and produce rotation
                  }
              
                ElRotate = true;
                rotate_UP = true;
                ElDir = char(126);
                  }                               
               
                
                else {
                  digitalWrite(ElRotPin_UP, LOW);                             // stop if running too long
                  ElRotate = false;
                  lcd.setCursor(16, 0); 
                  lcd.print(" ");
                  lcd.setCursor(19, 0);         
                  lcd.print(" ");
                  lcd.setCursor(17, 1); 
                  lcd.print("ERR");
                  MotorErrEl = true;
                }

    }
     
      else {

    // DOWNWARDS
        
        if ((ElRotate == false ) and (MotorErrEl == false)) ElMotorStartTime = millis();
        
        digitalWrite(ElRotPin_UP, LOW);                                                 // deactivate rotation pin UP, just to be sure
        if ((rotate_UP == true) and (ElRotate == true)) delay(750);                     // allow some time to reverse motor, if rotation sense  required
        
        if (millis() - ElMotorStartTime < ElMotorRunTimeLimit) {
                if (MotorErrEl == false ) digitalWrite(ElRotPin_DWN, HIGH);             //  rotate DOWNWARDS
                if (ElRotate == false) {
                  ElevChangeTime = millis();                                            // reset elev change time                  
                  ElRotate = true;
                  delay (50);                                                           // time for relay to energize and produce rotation
                  }

                rotate_UP = false;
                ElDir = char(127);
                }                              
            
          
          else {
            digitalWrite(ElRotPin_DWN, LOW);                                            // stop if running too long
                  ElRotate = false;
                  lcd.setCursor(16, 0); 
                  lcd.print(" ");
                  lcd.setCursor(19, 0);         
                  lcd.print(" ");
                  lcd.setCursor(17, 1); 
                  lcd.print("ERR");
                  MotorErrEl = true;
          }

      }


// Print direction arrows

  if (ElDir == char(126)) {               // UPWARDS
        lcd.setCursor(16, 0); 
        lcd.print(" ");
        lcd.setCursor(19, 0);         
        //lcd.print(String(AzDir));
        lcd.write(4);         
             }
 
  if (ElDir == char(127)) {               // DOWNWARDS
        lcd.setCursor(19, 0); 
        lcd.print(" ");
        lcd.setCursor(16, 0);         
        //lcd.print(String(AzDir));
        lcd.write(5);         
             }
      
 }        // End ElevRotate

 

///////// Handle serial communication  /////////////

void SerComm() {
  // initialize readings
  ComputerRead = "";
  Azimuth = "";
  Elevation = "";


  while(Serial.available()) {
 
    ComputerRead= Serial.readString();                  // read the incoming data as string (in this case, default timeout = 1 sec unless declared)
    
    //    Serial.println(ComputerRead);                 // echo the reception for testing purposes
   }
  
// looking for position command : YAESU style : Wazi 0el  = Move to azimuth 'azi' and elevation 'el'

        for (int i = 0; i <= ComputerRead.length(); i++) {
          
         if ((ComputerRead.charAt(i) == 'W')and (MotorErrEl == false)  and (MotorErrAz == false) and (StandBy == false) and (InhibitStatus == false) )  {           // if read AZIMUTH & ELEV command
    /*
                 //  DEBUG print received string
                 lcd.setCursor(0, 1); 
                 lcd.print(ComputerRead);       
      
    
                // debug : check length = 9 !
                lcd.setCursor(9, 0); 
                lcd.print(ComputerRead.length());
    
    */
                 SoftControlAz = true;                        // we receive commands !
                 SoftControlEl = true;                        // we receive commands !                 
                 // lcd.setCursor(8, 1); 
                 // lcd.print("    ");                        // Command received, so softcontrol active ! Wipe out STOP  (now wiped after timer elapsed)
                                               
           Azimuth = Azimuth + ComputerRead.charAt(i+1);
           Azimuth = Azimuth + ComputerRead.charAt(i+2);
           Azimuth = Azimuth + ComputerRead.charAt(i+3);
              //  i+4 is space
              //  i+5 is 0                  
           Elevation = Elevation + ComputerRead.charAt(i+6);
           Elevation = Elevation + ComputerRead.charAt(i+7);

         
            
         }  

         if ((ComputerRead.charAt(i) == 'W')and (MotorErrEl == false)  and (MotorErrAz == false) and (StandBy == true) and (InhibitStatus == false) )  {  // Signal command received but rotor interface in STBY mode
 
                tone(BuzzerPin,2200,30); 
                delay (80);
                tone(BuzzerPin,2200,30); 
                delay (80);                
                tone(BuzzerPin,2200,30); 
           
               }

/// End looking for W

        

// looking for STOP command : YAESU style : S  (this will as well reset any errors like stall, ..)

        if (ComputerRead.charAt(i) == 'S'){                                       // if read STOP command
                  SoftControlAz = false;                                          // halt software control
                  SoftControlEl = false;                                          // halt software control
                
                  AzRotate = false;                                               // interrupts rotate command
                  ElRotate = false;
                  digitalWrite(AzRotPin_CCW, LOW);                                // deactivate rotation pin left
                  digitalWrite(AzRotPin_CW,  LOW);                                // deactivate rotation pin right
                  digitalWrite(ElRotPin_DWN, LOW);                                // deactivate rotation pin down
                  digitalWrite(ElRotPin_UP,  LOW);                                // deactivate rotation pin up
             
                  MotorErrEl = false;                                             // force reset of motor error condition
                  MotorErrAz = false;

                  Pred_rotation_active = false;                                   // disable predictive tracking
                  Pred_rotation = 0;                                              // reset counter   
                  lcd.setCursor(8, 0);
                  lcd.print(" ");
                  Pred_elev_active = false;                                       // disable predictive tracking
                  Pred_elev = 0;                                                  // reset counter   
                  lcd.setCursor(11, 0);
                  lcd.print(" ");
                   
                  lcd.setCursor(3, 0);                                            // clear arrows
                  lcd.print(" ");
                  lcd.setCursor(7, 0);         
                  lcd.print(" ");
                  lcd.setCursor(16, 0); 
                  lcd.print(" ");
                  lcd.setCursor(19, 0);         
                  lcd.print(" ");

                  lcd.setCursor(4, 1);                                            // clear Ctl values
                  lcd.print(" - ");
                  lcd.setCursor(17, 1); 
                  lcd.print(" - ");
                  
                  lcd.setCursor(8, 1); 
                  lcd.print("STOP"); 

                  StopCmdTime = millis();

                  tone(BuzzerPin,2400,50);
                    
                   }
    
    }

  
// if AZIMUTH command received, be sure from 0 - 360

    if (Azimuth != ""){
      RComAzim = Azimuth.toInt();
      RComAzim = (RComAzim+360)%360;     // keeping values between limits 
      if ( RComAzim > 358 ) RComAzim = 359 ; 

      // Check predictive rotation  (sorry no handling of passing through North !)

      if (RComAzim != ROld_ComAzim) {                // we have received a new azimuth command  
           if ((RComAzim > ROld_ComAzim)  and (Pred_rotation >= 0)) {
                          Pred_rotation = Pred_rotation + 1;   // counter change, again going CW
                          if (Pred_rotation > 10) Pred_rotation = 10;                                   // limit integer value
                          }
           if ((RComAzim < ROld_ComAzim)  and (Pred_rotation <= 0)) {
                          Pred_rotation = Pred_rotation - 1;                                            // counter change, again going CCW
                          if (Pred_rotation < -10) Pred_rotation = -10;   
                           }
            
           if ((RComAzim > ROld_ComAzim)  and (Pred_rotation < 0)) Pred_rotation = 0;                    // reset counter, going from CCW to CW
           if ((RComAzim < ROld_ComAzim)  and (Pred_rotation > 0)) Pred_rotation = 0;                    // reset counter, going from CW to CCW    
           
           if (Pred_rotation == 0) {
                      Pred_rotation_active = false;                                                    //reset flag
                      lcd.setCursor(8, 0);
                      lcd.print(" ");
                      }

           
           if (abs(Pred_rotation) >= Pred_rotation_moves) {
             Pred_rotation_active = true; 
                    }
 
           ROld_ComAzim = RComAzim; 

      // Apply predictive rotation if required
      
      if (Pred_rotation_active == true) {                          // Predictive rotation is active
                  if (Pred_rotation > 0)   {                       // detect CW
                            ComAzim = RComAzim + AzErr / 2 ;
                            lcd.setCursor(8, 0);
                            lcd.write(6); 

                             }
                   else  {                                         // if not CW then CCW
 
                            if (RComAzim - AzErr / 2 >= 0) ComAzim = RComAzim - AzErr / 2 ;    // no negative values possible
                            else ComAzim = 0;  
                                                      
                            lcd.setCursor(8, 0);
                            lcd.write(7);
                             }
                       
                   }

                 else   ComAzim = RComAzim ;                      // Predictive rotation is not active

              ComAzim = (ComAzim + 360 )%360;                     // keeping values between limits 
              if ( ComAzim > 358 ) ComAzim = 359 ;       
       
            }
         
      }

// if ELEVATION command received, be sure from 0 - 90

    if (Elevation != ""){
      RComElev = Elevation.toInt();
      RComElev = (RComElev+90)%90;     // keeping values between limits
      if ( RComElev > 88 ) RComElev = 89 ;


      // Check predictive elevation  
      if (RComElev != ROld_ComElev) {                // we have received a new elevation command  
           if ((RComElev > ROld_ComElev)  and (Pred_elev >= 0)) {
                          Pred_elev = Pred_elev + 1;   // counter change, again going UP
                          if (Pred_elev > 10) Pred_elev = 10;                                           // limit integer value
                          }
           if ((RComElev < ROld_ComElev)  and (Pred_elev <= 0)) {
                          Pred_elev = Pred_elev - 1;                                            // counter change, again going CCW
                          if (Pred_elev < -10) Pred_elev = -10;   
                           }
            
           if ((RComElev > ROld_ComElev)  and (Pred_elev < 0)) Pred_elev = 0;                    // reset counter, going from UP from DOWN
           if ((RComElev < ROld_ComElev)  and (Pred_elev > 0)) Pred_elev = 0;                    // reset counter, going from DOWN to UP    
           
           if (Pred_elev == 0) {
                      Pred_elev_active = false;                                                    //reset flag
                      lcd.setCursor(11, 0);
                      lcd.print(" ");
                      }

           
           if (abs(Pred_elev) >= Pred_elev_moves) {
             Pred_elev_active = true; 
                    }
 
           ROld_ComElev = RComElev; 

      // Apply predictive rotation if required
      
      if (Pred_elev_active == true) {                          // Predictive rotation is active
                  if (Pred_elev > 0)   {                       // detect UP
                            ComElev = RComElev + ElErr / 2 ;
                            lcd.setCursor(11, 0);
                            lcd.write(6); 

                             }
                   else  {                                         // if not UP then DOWN
                            
                            if ( RComElev - ElErr / 2 >= 0) ComElev = RComElev - ElErr / 2 ;   // no negative values possible
                            else ComElev = 0;
                                
                            lcd.setCursor(11, 0);
                            lcd.write(7);
                                  
                            
                             }
                       
                   }

                 else   ComElev = RComElev ;                      // Predictive elevation is not active

              ComElev = (ComElev + 90 )%90;                       // keeping values between limits 
              if ( ComElev > 88 ) ComElev = 89 ;       
       
            }
           
      }

      


// looking for <AZ> and <EL> interogation for antenna position   / YAESU protocol = 'C2'   reply is +0azi+0ele     (azimuth / elev)
 
  for (int i = 0; i <= (ComputerRead.length()); i++) {
    if ((ComputerRead.charAt(i) == 'C') and (ComputerRead.charAt(i+1) == '2') and (millis() > 10000)  )     {    /// give some time to average position readings @ startup
      
    // send back the antenna position <+0xxx>
      ComputerWrite = "+0";
      if ((RepAzim) < 100 ) ComputerWrite = ComputerWrite +"0";
      if ((RepAzim) < 10 )  ComputerWrite = ComputerWrite +"0";      
      ComputerWrite = ComputerWrite + String(RepAzim)+"+0";
      if (RepElev < 100) ComputerWrite = ComputerWrite + "0";
      if (RepElev < 10)  ComputerWrite = ComputerWrite + "0";      
      ComputerWrite = ComputerWrite + String(RepElev);
      
      Serial.println(ComputerWrite);
    }
  }

  
}