/* 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 * YO3RAK@gmail.com * * 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 /* // FOR I2C LCD #include // Library for I2C communication #include // 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 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 /***********************************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("V24 01-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 > 5000) 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 ComAzim = RComAzim - AzErr / 2 ; 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 ComElev = RComElev - ElErr / 2 ; 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 ) ComAzim = 89 ; } } // looking for and 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); } } }