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