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.
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 :
Viorel's sketch relies on Serial.readString() to capture data from the serial line (in fact, by the on board Arduino UART). Nice and easy, but by default this command has a timeout of 1 second, waiting for more incoming data ... what means that incoming data (like antenna position commands or antenna position requests) can only be read and processed at most once per second. It is desirable to have a more regular data flow, this is possible by declaring an appropriate timeout. I used 'Serial.setTimeout(50)' which will provide 50ms timeout.... and now PstRotator can handle data exchange (and update it's display) every 0,5s ! For other software, it might be required to increase the timeout somewhat ...
In the elevation rotator, there are no limit switches... so I have provided 2 'failsafe' conditions :
my rotator elevates the complete 90° in about 45 seconds.... if the rotor motor is running continuously longer, there must be something really wrong ! In this case, motor is powered off, and an ERROR is generated and the automatic tracking is completely disabled ! Investigate problem and reset with a power switch cycle or by commanding a STOP.
if a rotation/elevation command is issued, but Arduino does sub sequentially not detect the respective antenna movement change, there must be something wrong - maybe the antenna or motor are stuck or stalled ? In this case, motor is powered OFF and an ERROR is generated, and automatic tracking is disabled !
In case the movement of a rotor is to be reversed while running, there will be 500 ms delay before reversing direction, allowing the motor and antenna to stop rotating.
While I monitor EME activity, I sometimes call DX stations on HF as well with QRO ... and have on some bands interaction on the control boxes of motors and/or ARDUINO voltage measurement input, driving the system erratically. Despite RF decoupling, inserting toroids in lines between motors and controllers, problem is latent . So I decided to provide an 'INHIBIT' function for the controller : when this input (connected to the 'KEY' alias 'PTT' line of HF system) goes to zero volts, all motor rotations are stopped, serial rotation commands from PC are not considered, and position potentiometers are not read. The input on Arduino board is monitored by an interrupt, therefore reaction is immediate. In this state, INHI is shown in LCD display, and after end of transmission on HF plus a delay of 16 seconds, the system returns to normal operation.
Tracking accuracy can be set in the sketch for azimuth and elevation - as I use high gain antenna's with a quite narrow beam width, I have set this to 2°. So, only if a position command with more than 2° difference to actual position is received, antenna will move
The sketch was further refined in August 2022 to include 'PREDICTIVE TRACKING', a very useful feature for antenna's with narrow beam width. Without predictive tracking, the antenna direction is only correct immediately after executing a rotation command, thereafter always lagging for up to the allowed tracking accuracy. In fact the antenna is always chasing the celestial object ... With the predictive tracking, the direction of movement is detected (both for AZIMUTH and ELEVATION), and when we are certain the movements are consistent in a certain direction (= parameter in the sketch, by default the 3rd move in same direction will be corrected ...), a correction (half of the tracking accuracy) is applied at each movement. The result is that the tracking accuracy will be 2x better than before, and the celestial object will be longer in the antenna's beam maximum. If the movement direction is changed, or a large movement (= more than 2x tracking accuracy) is executed, or a STOP command issues, predictive tracking will be disabled. When predictive tracking is active, this is indicated in the display (P+ for going CW and/or UP ward, P- for CCW and/or DOWN ward).
For azimuth, there is a possibility to track with consideration of elevation (so called 'gain related' tracking). This because at high elevations, azimuth tracking can be less accurate (in fact at 90°, it is not relevant ...)
In automatic tracking mode, once the antenna has reached the requested target position, it can be manually further adjusted with control box switches. Once the target position is reached, you are back into 'manual' mode, so Arduino will not attempt to keep / hold the target position...
The DC output voltage from the control box corresponding to antenna azimuth position is completely linear ... Should this not be the case, interpolation will be required between 8 cardinal directions, more than enough to have a good match between Arduino LCD / PstRotator readouts and the moving pointer on the control box.
Two flashing dots in middle of top row are indicating the serial data activity
On my interface box, there was an 'AUTO TRACK' push button, allowing remote control. The status of this button is as well indicated in the LCD display on the second row.

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.
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.
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);
}
}
}