Being interested in sporadic-E (Es) propagation on 4 and 6m bands, I was looking for a possibility to control in azimuth my small YAGI antenna rotor (KENPRO / YAESU G400 type, with AC motor and no brake, stop in the NORTH) automatically, in particular from WSJT & N1MM software. (Note : if you are looking for AZ & EL control, see my other controller here). For controller with stop in SOUTH, I have as well a sketch which needs to be tested - please contact me for details !
However WSJT does not offer a possibility to control directly an antenna rotors, but 'PstRotator' software does - see http://www.pstrotator.com by monitoring a 'status file' generated by WSJT during QSO's.

A simple display is more than enough for my basic needs
...
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 box, an 'add-on' interface between PstRotator and the control box had to be considered.

The controller interface on top of the original control
box
First of all, 2 relays were provided in cascade to the 'LEFT' and 'RIGHT' switches of front panel, in such manner that the automatic control can give opposite commands to the manual switches, but by no means it is possible to have the motor rotating left & right simultaneously ;o). There is plenty room left in the control box cabinet for adding the relays.
See schematic of mods below.

Then some 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' between azimuth 000° and 359°, but by no means go into negative polarity ... or additional circuits would be necessary. Ideally, it should vary between approx +1V and + 4V against control circuit 'ground' (in my case : the center tap of mains transformer).
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, however PstRotator software (maybe in version 'Azimuth only' ?) can apparently only handle 'EasyComm1' protocol, which does not support interrogation of actual antenna position and display on screen - it can only give commands ... a pity :o(.
Therefore, 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, read and return actual azimuth, and STOP rotation)
The sketch was streamlined to eliminate all 'elevation' matters, as only azimuth is considered, 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 my simple rotator, there are no limit switches... so I have provided 2 'failsafe' conditions :
my rotator turns the complete 360° in 1 minute and 10 seconds.... if the rotor motor is running continuously during 1minute and 15seconds, 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 ...
if a rotation command is issued, but Arduino does subsequentially not detect any azimuth 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 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 position did not seem to be completely linear ... therefore, interpolation was 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.
In case the rotor is running, and the rotation sense is to be reversed, there is 'dead time' of 500ms provided to allow the motor to effectively stop before being reversed.
A LED is indicating by short flashes the serial data activity
A buzzer will signal commands received and errors (e.g. motor stall, controller not powered, ...)
In PstRotator software, a 'PARK' command can be issued. This will park the rotor in a pre-defined azimuth. The disadvantage in this case is that the rotor pinions will start to wear and tear at same position, due to effect of wind 'playing' with antenna.... The sketch has a feature where the 'PARK' command is detected and rotor is sent to a position + or - some random degrees around the preset value in PstRotator. It will acknowledge the command showing 'PRK' then after 2 seconds the azimuth to which rotor will be parked. The 'parking band' can be adjusted as a parameter, here iit is defined as + and - 10° around the nominal park value set in in PstRotator.
The Arduino Nano board and 2x8 character LCD display are easily integrated in a small project box.
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.
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.

Note : a buzzer was added at a later stage. it is a piezo buzzer, powered from the +5V line, a BC547 (small signal NPN) transistor acts as driver, it's base connected by a 4k7 resistor to Arduino digital pin 6 - this is not drawn on above schematic diagram.
The original sketch was modified while not looking into crunching program code lines, as there is plenty room available in the Arduino.... The sketch was developed for controllers with stop in the NORTH - as usual for satellite tracking, EME etc. For controller with stop in SOUTH, I have as well a sketch in Beta version - where stop in N or S is defined as a parameter - and still needs to be tested - please contact me for details !
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
*
*/
////////////// UPPER USB = COM 16 CH340 //////////////////////
////////////// DELL PC LEFT USB COM 12 //////////////////////
/// 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
// V12 : 27-02-2022: 500ms delay if motor direction is reversed while running
// V13 : Buzzer for error alert
// V14 : Random parking to avoid wear of pinions
/* // 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 EA DIPS082 DISPLAY 2x 8 ******
*
*
* LCD RS pin 4 to digital pin 7
* LCD R/W pin 5 - put to GND
* LCD Enable pin 6 to digital pin 8
* LCD D4 pin 11 to digital pin 9
* LCD D5 pin 12 to digital pin 10
* LCD D6 pin 13 to digital pin 11
* LCD D7 pin 14 to digital pin 12
*
* LCD pin 1 = GND
* LCD pin 2 = + 5v 80 mA
* LCD pin 3 = contrast adjust), or adjust between 0 ... 5v by trimmer
*
*
*/
#include <SoftwareSerial.h>
/***********************************THIS IS WHERE YOU REALY TWEAK THE ANTENNA MOVEMENT***************/
// ANTENNA potentiometers CALIBRATION (counts)
#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
// ************ ROTOR PARAMETERS ************************************************************
// Allowed error for which antennna won't move. Minimum 1 degree
int AzErr = 15;
// Angle difference where soft stop begins (for casting motor)
int Amax_CW = 2; // clockwise / right
int Amax_CCW = 0; // counter clockwise / right
// max running time of rotor motor 1min15s
long unsigned MotorRunTimeLimit = 75000 ;
// max time motor can run if nu azimuth change detected (system stall)
int StallTimer = 5000 ;
// For PstRotator : Random parking band definition - e.g. '20' means + and - 10° around park set point in PstRotator
int ParkBand = 20 ; // Set to zero to disable this function. Must be < than 2x AzErr !
// ***********************************************************************************************
// other variables
#define AzPotPin A0 // select the input pin for the azim. potentiometer
#define AzRotPin_CCW 3 // select the out pin for rotation direction
#define AzRotPin_CW 4 // select the out pin for rotation direction
#define RS232pin 13 // RS-232 activity blinker
#define BuzzerPin (6) // pin for buzzer
bool SoftControl = false; // if controlled by software commands = true
int TruAzim = 0; // calculated real azimuth value
int ComAzim = 0; // commanded azimuth value
int RepAzim = 0; // reported Azimuth to software
int RawAzim = 0; // for interpolation
int OldTruAzim = 0; // to store previous azimuth value
int OldComAzim = 0;
char AzDir; // symbol for azim rot display
// flags for AZ tolerances
bool AzStop = false; // not used !!!!
bool rotate = false; // 'rotate command' active
bool rotate_CW = true; // flag rotate CW
//averaging loop
const int numReadings = 5; // averages the reading
int readIndex = 0; // the index of the current reading
int azimuth[numReadings]; // the readings from the analog input
int totalAz = 0; // the running total
long unsigned LastDispUpdate; // display update frequency
long unsigned LastBuzzer; // Buzzer sounding
long unsigned AzimChangeTime; // change of true azim detected
long unsigned MotorStartTime; // start of motor
long unsigned MotorStopTime; // stop of motor
bool MotorErr = false;
bool OFF = false; // when rotor command box is OFF = true
// variables for serial comm
String Azimuth = "";
String Elevation = "";
String ComputerRead;
String ComputerWrite;
long unsigned LastSerExch;
long unsigned StopCmdTime;
bool StopPrinted = false;
// 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
};
////////////////////////////////////////////////////
//////////////////// S E T U P ///////////////////
////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
Serial.setTimeout(50); // sets the maximum milliseconds to wait for serial data. It defaults to 1000 milliseconds
// too long for PST rotator updates ...
// FOR PARALLEL LCD
//
// Initiate the LCD: 8 char x 2 rows
lcd.begin(8,2);
//
/* Initiate the I2C LCD:
lcd.init();
lcd.backlight();
*/
lcd.createChar(1, degree);
lcd.createChar(2, right);
lcd.createChar(3, left);
// pin declaration
digitalWrite(AzRotPin_CCW, LOW); // deactivate rotation pin left
digitalWrite(AzRotPin_CW, LOW); // deactivate rotation pin right
digitalWrite(RS232pin, LOW); // RS232pin
pinMode(AzRotPin_CCW, OUTPUT); //declaring azim. rotation direction Pin as OUTPUT
pinMode(AzRotPin_CW, OUTPUT); //declaring azim. rotation direction Pin as OUTPUT
pinMode(RS232pin, OUTPUT);
pinMode(AzPotPin, INPUT);
pinMode(BuzzerPin, OUTPUT);
// write on display name and version
lcd.clear();
lcd.setCursor(0, 0); // Set the cursor on the first column first row.(counting starts at 0!)
lcd.print("RotorCTL");
lcd.setCursor(0, 1); // Set the cursor on the first column the second row
lcd.print("V4 04-24");
/*
tone(BuzzerPin,2100);
delay (250);
tone(BuzzerPin,2300);
delay (250);
*/
tone(BuzzerPin,2400,250); // Frequency, duration
//delay (250);
//noTone (BuzzerPin);
delay(1500); // keep for 1.5 seconds
lcd.clear();
lcd.setCursor(0, 0); // Set the cursor on the first column first row.(counting starts at 0!)
lcd.print(" by ");
lcd.setCursor(0, 1); // Set the cursor on the first column the second row
lcd.print(" ON7EQ");
delay(1500); // keep for 1.5 seconds
lcd.clear();
lcd.setCursor(0, 0); // Set the cursor on the first column first row.(counting starts at 0!)
lcd.print(" YAESU");
lcd.setCursor(0, 1); // Set the cursor on the first column the second row
lcd.print(" 9k6");
delay(1500); // keep for 1.5 seconds
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 --- ");
// this is to set azim-command the same value as real, not to jerk the antenna at start-up
RawAzim = analogRead(AzPotPin);
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));
//TruAzim = (map(analogRead(AzPotPin), AzMin, AzMax, 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();
AzimChangeTime = millis ();
LastBuzzer = millis();
randomSeed(analogRead(AzPotPin)); // generate random for parking
if (ParkBand > (2 * AzErr)) ParkBand = 2 * AzErr; // ParkBand can't be > than 2x AzErr !
}
////////////////////////////////////////////////////
////////////////////// L O O P /////////////////////
////////////////////////////////////////////////////
void loop() {
// Sound buzzer for motor error
if ((millis() - LastBuzzer > 2000) and (MotorErr == true)) {
tone(BuzzerPin,2400,500); // Frequency, duration
//delay (500);
//noTone (BuzzerPin);
LastBuzzer = millis();
}
// AZIMUTH AVERAGING LOOP
totalAz = totalAz - azimuth[readIndex];
// read from the sensor:
RawAzim = analogRead(AzPotPin);
if (RawAzim < 150 ) { // detect if CONTROLLER is powered
OFF = true;
}
else OFF = false;
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));
//azimuth[readIndex] = (map(analogRead(AzPotPin), Az000, Az360, 0, 359)); /// For linear pot
// add the reading to the total:
totalAz = totalAz + azimuth[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;
// if we're at the end of the array, wrap around to the beginning:
if (readIndex >= numReadings) {readIndex = 0;}
// calculate the average:
TruAzim = totalAz / numReadings;
if (TruAzim<0) {TruAzim=0;}
if (TruAzim>359) {TruAzim=359;} // keep values between limits
// update antenna true position display
if ((millis()- LastDispUpdate) > 100){ //not to flicker the display
LastDispUpdate = millis();
/*
if (RawAzim < 150 ) {
lcd.setCursor(4, 0);
lcd.print ("OFF");
OFF = true;
}
*/
if (OFF == true) {
lcd.setCursor(4, 0);
lcd.print ("OFF");
}
else if (abs(OldTruAzim - TruAzim)>1 ) { // eliminate last digit jitter in display and PC software
AzimChangeTime = millis(); //reset azimuth change timer
DisplTruAzim();
RepAzim = TruAzim;
}
}
if ( (millis() - AzimChangeTime > StallTimer) and (rotate == true)) { // Check for motor stall
MotorErr = 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
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(7, 0);
lcd.print(" ");
lcd.setCursor(4, 1);
lcd.print("ERR");
}
// every 0,1 seconds looking for serial communication
if ((millis()- LastSerExch) > 100){
digitalWrite(RS232pin, LOW);
LastSerExch = millis();
if (Serial.available() > 0) {
digitalWrite(RS232pin, HIGH); // blink LED
SerComm();
}
}
// update command target position display
if (ComAzim != OldComAzim) {
SoftControl = true; // we have received a command from software
AzimChangeTime = millis () ; // reset AZ change timer, keep before rotating antenna
DisplComAzim();
}
// 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
rotate = false; // AzMotor stopped
SoftControl = false; // back to manual mode
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(7, 0);
lcd.print(" ");
lcd.setCursor(4, 1);
if (MotorErr == false) {
lcd.print(" - ");
}
}
/*
else if ((abs(ComAzim-TruAzim) <= Amax)) { // uitloop motor, STOP !
digitalWrite(AzRotPin_CCW, LOW); // deactivate rotation pin left
digitalWrite(AzRotPin_CW, LOW); // deactivate rotation pin right
rotate = false; // AzMotor stopped
SoftControl = false ; // back to manual mode
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(7, 0);
lcd.print(" ");
}
*/
else if ((abs(ComAzim-TruAzim) <= Amax_CW) and (rotate_CW == true)) { // uitloop motor, STOP !
digitalWrite(AzRotPin_CCW, LOW); // deactivate rotation pin left
digitalWrite(AzRotPin_CW, LOW); // deactivate rotation pin right
rotate = false; // AzMotor stopped
SoftControl = false ; // back to manual mode
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(7, 0);
lcd.print(" ");
lcd.setCursor(4, 1);
if (MotorErr == false) {
lcd.print(" - ");
}
}
else if ((abs(ComAzim-TruAzim) <= Amax_CCW) and (rotate_CW == false)) { // uitloop motor, STOP !
digitalWrite(AzRotPin_CCW, LOW); // deactivate rotation pin left
digitalWrite(AzRotPin_CW, LOW); // deactivate rotation pin right
rotate = false; // AzMotor stopped
SoftControl = false ; // back to manual mode
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(7, 0);
lcd.print(" ");
lcd.setCursor(4, 1);
if (MotorErr == false) {
lcd.print(" - ");
}
}
else if ((abs(TruAzim - ComAzim)<=AzErr)&&(rotate == true) && (SoftControl == true)) { // if in tolerance, but it wasn't an equal, rotate
if (MotorErr == false) {
AzimRotate();
}
else { // MotorErr = true !
digitalWrite(AzRotPin_CCW, LOW); // we have motor error
digitalWrite(AzRotPin_CW, LOW); //
}
}
else if ((abs(TruAzim - ComAzim)>AzErr) && (SoftControl == true)){ // if target is off tolerance
if (MotorErr == false) {
AzimRotate(); // rotate
}
else { // MotorErr = true !
digitalWrite(AzRotPin_CCW, LOW); // we have motor error
digitalWrite(AzRotPin_CW, LOW); //
}
}
//// Clear STOP in display
if ((millis()- StopCmdTime > 3000) and (StopPrinted == true)) {
lcd.setCursor(4, 1);
lcd.print(" - ");
StopPrinted = false;
}
// delay(20); //pause the program for x ms
}
/////////////////////////////////////////////////////
////////////// procedures definitions //////////////
/////////////////////////////////////////////////////
//////// Display actual rotor azimuth ///////////
void DisplTruAzim() {
lcd.setCursor(4, 0);
if (TruAzim<10) {
lcd.print("00");
lcd.print(TruAzim);}
else if (TruAzim<100) {
lcd.print("0");
lcd.print(TruAzim);}
else {lcd.print(TruAzim);}
OldTruAzim = TruAzim;
// ************** FOR CALIBRATION PURPOSES **************
/*
lcd.setCursor(0, 1);
lcd.print (analogRead(AzPotPin));
lcd.print (" ") ;
*/
}
/////// Display command azimuth //////////////
void DisplComAzim(){
lcd.setCursor(4, 1);
if (ComAzim<10) {
lcd.print("00");
lcd.print(ComAzim);}
else if (ComAzim<100) {
lcd.print("0");
lcd.print(ComAzim);}
else {lcd.print(ComAzim);}
//lcd.print (String(char(223))); // degrees
OldComAzim = ComAzim;
//
}
////////// Rotate antenna //////////////////////
void AzimRotate() {
if (rotate == false ) MotorStartTime = millis();
if ((ComAzim-TruAzim) > (TruAzim-ComAzim)) { // this to determine direction of rotation
// ROTATE RIGHT - CW
if ((rotate == false ) and (MotorErr == false)) MotorStartTime = millis();
digitalWrite(AzRotPin_CCW, LOW); // deactivate rotation pin left
if ((rotate_CW == false) and (rotate == true)) delay (500); // allow time to treverse motor, if rotation inversed
if (millis() - MotorStartTime < MotorRunTimeLimit) {
if (MotorErr == false ) digitalWrite(AzRotPin_CW, HIGH); // rotate right
rotate = true;
rotate_CW = true;
AzimChangeTime = millis () ; // reset AZ change timer at start of rotation
AzDir = char(126);
} // "->"
else {
digitalWrite(AzRotPin_CW, LOW); // stop if running too long
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(7, 0);
lcd.print(" ");
lcd.setCursor(4, 1);
lcd.print("ERR");
MotorErr = true;
}
}
else {
// ROTATE LEFT - CCW
if ((rotate == false ) and (MotorErr == false)) MotorStartTime = millis();
digitalWrite(AzRotPin_CW, LOW); // deactivate rotation pin right
if ((rotate_CW == true) and (rotate == true)) delay (500); // allow time to treverse motor, if rotation inversed
if (millis() - MotorStartTime < MotorRunTimeLimit) {
if (MotorErr == false ) digitalWrite(AzRotPin_CCW, HIGH); // rotate left
rotate = true;
rotate_CW = false;
AzimChangeTime = millis () ; // reset AZ change timer at start of rotation
AzDir = char(127);
} // "<-"
else {
digitalWrite(AzRotPin_CCW, LOW); // stop if running too long
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(7, 0);
lcd.print(" ");
lcd.setCursor(4, 1);
lcd.print("ERR");
MotorErr = 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);
}
}
///////// Handle serial communication /////////////
void SerComm() {
// initialize readings
ComputerRead = "";
Azimuth = "";
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 command : YAESU style : Mxxx = Move to azimuth XXX
for (int i = 0; i <= ComputerRead.length(); i++) {
if ((ComputerRead.charAt(i) == 'M') and (MotorErr == 0)){ // if read AZIMUTH command
for (int j = i+1; j <= ComputerRead.length(); j++) {
if (isDigit(ComputerRead.charAt(j))) { // if the character is number
Azimuth = Azimuth + ComputerRead.charAt(j);
}
else {break;}
}
}
if (ComputerRead.charAt(i) == 'S'){ // if read STOP command
SoftControl = false; // halt software control
rotate = false; // interrupts rotate command
digitalWrite(AzRotPin_CCW, LOW); // deactivate rotation pin left
digitalWrite(AzRotPin_CW, LOW); // deactivate rotation pin right
MotorErr = false; // manual reset of motor error condition
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(7, 0);
lcd.print(" ");
lcd.setCursor(4, 1);
lcd.print("STP");
StopPrinted = true;
StopCmdTime = millis();
tone(BuzzerPin,2400,150); // Frequency, duration
}
}
// if <AZxx> received
if (Azimuth != ""){
ComAzim = Azimuth.toInt();
// if PARK command issued from PSTrotator : First STOP, immediately followed by MOVE
if (millis() - StopCmdTime < 500) {
ComAzim = ComAzim - ParkBand/2 ; // park position within Park Band
ComAzim = ComAzim + random(0,ParkBand+1);
}
ComAzim = (ComAzim+360)%360; // keeping values between limits
if (OFF == false) {
tone(BuzzerPin,2400,30); // Frequency, duration
}
else {
tone(BuzzerPin,2400,30); // Frequency, duration
delay (80);
tone(BuzzerPin,2400,30); // Frequency, duration
delay (80);
tone(BuzzerPin,2400,30); // Frequency, duration
}
}
// looking for <AZ> interogation for antenna position / YAESU protocol = 'C' reply is +0xxx xxx = azimuth
for (int i = 0; i <= (ComputerRead.length()); i++) {
if ((ComputerRead.charAt(i) == 'C') and (millis() > 6000) ){ /// give some time to average position, so PstRotataor indicates set AZ as actual AZ
// send back the antenna position <+0xxx>
ComputerWrite = "+0"+String(RepAzim);
Serial.println(ComputerWrite);
}
}
}