AZ-EL ROTATOR

3D baskılı AZ-EL rotator Bu proje fikri pa3axa tarafından 24GHz’de kısa menzilli düzlem dağılımlı qso’lar yapmak için başlatıldı. Hafif bir baskılı çanakla birlikte wavelab 24GHz modüllerinin kullanılabilirliği, uçakları izlemek için hafif bir AZ-EL rotatoruna izin verir. Uçak konumu, çevredeki alandaki uçaklardan adsb verileri alınarak sağlanır. Rotator bir tripoda monte edilecek ve ayrıca uydu takibi için küçük bir combi yagi’yi tutacak kadar güçlüdür. İlk adım, adsb alıcısına veya diğer bilgisayar yazılımlarına arayüz oluşturma olanağı olan bir kontrol panosu ile birlikte düşük maliyetlerle tamamen 3D baskılı küçük bir AZ-EL sistemi tasarlamaktır.

Aşağıdaki basılı parçalar PLA’dan yapılmalı, duvar kalınlığı 1 mm ve dolgu oranı %10 olmalıdır

Alt taban plakası (1 adet)    taban plakası_2.STL
Dikey taban (1 adet)   dikey taban.STL
Büyük dişli yarısı (4 adet)    tandwiel_groot.STL
Motor dişlisi (2 adet)    tandwiel_motor.STL tandwiel_motor.STL
Direnç dişlisi (2 adet)    tandwiel_potmeter.STL
Dişli kelepçesi (2 adet)    lagerklem.STL
Ayırıcı halka (8 adet)    bus.STL

Diğer parçalar:

Bilyalı rulman (4 adet) : 35x44x5mm 6707-2R Aliexpress
Motor (2 adet) : Bringsmart (2 adet) JGY-370 12V 10rpm Aliexpress
10 tur değişken direnç Junkbox

 

Kontrol panosu

Rotor kontrolü, çekirdeğinde bir Arduino nano, bir L298N köprü motor kontrolörü ve I2C 16×2 lcd ekran bulunur. Yazılım Simplesat yayınına dayanmaktadır. Rotor kontrolörü, birçok farklı izleme yazılımı tarafından oluşturulan Yeasu GS-232 komutlarını dinler.

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h> // use software uart library
LiquidCrystal_I2C  lcd(0x27,2,1,0,4,5,6,7); 

const long _azAdZeroOffset   =  0;   // adjust to zero out lcd az reading when control box az = 0
const long _elAdZeroOffset   =  0;   // adjust to zero out lcd el reading when control box el = 0

/*
    10 bit A/D converters in the Arduino have a max value of 1023
    for the azimuth the A/D value of 1023 should correspond to 360 degrees
    for the elevation the A/D value of 1023 should correspond to 360 degrees
    integer math is used so the scale value is multiplied by 100 to maintain accuracy
    the scale factor should be 100 * (1023 / 360) for the azimuth
    the scale factor should be 100 * (1023 / 360) for the elevation
*/

const long _azScaleFactor =  284;  //  adjust as needed
const long _elScaleFactor =  284;  //  adjust as needed

// lcd display control
const byte _backLightOn = 0x11;   // lcd back light on
const byte _cursorOff = 0x16;     // lcd cursor off
const byte _clearScreen = 0x0C;   // lcd clear screen
const byte _line0 = 0x80;         // lcd line 0 - top line
const byte _line1 = 0x94;         // lcd line 1 - bottom line

// pins
const byte _azimuthInputPin = A0;   // azimuth analog signal from potmeter
const byte _elevationInputPin = A1; // elevation analog signal from potmeter
const byte _G5500UpPin = 7;        // elevation rotor up control line
const byte _G5500DownPin = 8;      // elevation rotor down control line
const byte _G5500LeftPin = 9;      // azimuth rotor left control line
const byte _G5500RightPin = 10;     // azimuth rotor right control line
int _ResetPin = 3;
int val=0; 


// take care if you lower this value -  wear or dirt on the pots in your rotors
// or A/D converter jitter may cause hunting if the value is too low.

long _closeEnough = 100;   // tolerance for az-el match in rotor move in degrees * 100

// ------------------------------------------------------------
// ------ values from here down should not need adjusting -----
// ------------------------------------------------------------

// rotor
const long _maxRotorAzimuth = 45000L;  // maximum rotor azimuth in degrees * 100
const long _maxRotorElevation = 18000L; // maximum rotor elevation in degrees * 100

long _rotorAzimuth = 0L;       // current rotor azimuth in degrees * 100
long _rotorElevation = 0L;     // current rotor azimuth in degrees * 100
long _azimuthTemp = 0L;        // used for gs232 azimuth decoding
long _elevationTemp = 0L;      // used for gs232 elevation decoding
long _newAzimuth = 0L;         // new azimuth for rotor move
long _newElevation = 0L;       // new elevation for rotor move
long _previousRotorAzimuth = 0L;       // previous rotor azimuth in degrees * 100
long _previousRotorElevation = 0L;     // previous rotor azimuth in degrees * 100

unsigned long _rtcLastDisplayUpdate = 0UL;      // rtc at start of last loop
unsigned long _rtcLastRotorUpdate = 0UL;        // rtc at start of last loop
unsigned long _displayUpdateInterval = 500UL;   // display update interval in mS
unsigned long _rotorMoveUpdateInterval = 100UL; // rotor move check interval in mS

boolean _gs232WActice = false;  // gs232 W command in process
int _gs232AzElIndex = 0;        // position in gs232 Az El sequence
long _gs232Azimuth = 0;          // gs232 Azimuth value
long _gs232Elevation = 0;        // gs232 Elevation value
boolean _azimuthMove = false;     // azimuth move needed
boolean _elevationMove = false;   // elevation move needed

String azRotorMovement;   // string for az rotor move display
String elRotorMovement;   // string for el rotor move display


//
// run once at reset
//
void setup()
{
 
    // initialize serial ports:
  Serial.begin(9600);  // control
  
  lcd.begin(16, 2); // Inizializzo LCD 16 caratteri per 2 linee
  delay (200);
  lcd.setBacklightPin(3,POSITIVE);
  lcd.setBacklight(HIGH);

  
  // initialize rotor control pins as outputs
  pinMode(_G5500UpPin, OUTPUT);
  pinMode(_G5500DownPin, OUTPUT);
  pinMode(_G5500LeftPin, OUTPUT);
  pinMode(_G5500RightPin, OUTPUT);

  pinMode(_ResetPin, INPUT_PULLUP);

  // set all the rotor control outputs High
  digitalWrite(_G5500UpPin, HIGH);
  digitalWrite(_G5500DownPin, HIGH);
  digitalWrite(_G5500LeftPin, HIGH);
  digitalWrite(_G5500RightPin, HIGH);



  
  lcd.clear();   // clear screen
  delay(100);                         // wait for clear screen

  lcd.println("   PE1CKK V1.8    ");
  delay(2000);
  lcd.clear();   // clear screen


}


//
// main program loop
//
void loop()
{
  // check for serial data
  if (Serial.available() > 0)
  {
    decodeGS232(Serial.read());
  }

  unsigned long rtcCurrent = millis(); // get current rtc value

  // check for rtc overflow - skip this cycle if overflow
  if (rtcCurrent > _rtcLastDisplayUpdate) // overflow if not true    _rotorMoveUpdateInterval
  {
    // update rotor movement if necessary
    if (rtcCurrent - _rtcLastRotorUpdate > _rotorMoveUpdateInterval)
    {
      _rtcLastRotorUpdate = rtcCurrent; // reset rotor move timer base

      // AZIMUTH
      readAzimuth(); // get current azimuth from G-5500
      // see if azimuth move is required
      if ( (abs(_rotorAzimuth - _newAzimuth) > _closeEnough) && _azimuthMove )
      {
        updateAzimuthMove();
      }
      else  // no move required - turn off azimuth rotor
      {
        digitalWrite(_G5500LeftPin, HIGH);
        digitalWrite(_G5500RightPin, HIGH);
        _azimuthMove = false;
        azRotorMovement = "        ";
      }

      // ELEVATION
      readElevation(); // get current elevation from G-5500
      // see if aelevation move is required
      if ( abs(_rotorElevation - _newElevation) > _closeEnough && _elevationMove ) // move required
      {
        updateElevationMove();
      }
      else  // no move required - turn off elevation rotor
      {
        digitalWrite(_G5500UpPin, HIGH);
        digitalWrite(_G5500DownPin, HIGH);
        _elevationMove = false;
        elRotorMovement = "        ";
      }
    } // end of update rotor move


    // update display if necessary
    if (rtcCurrent - _rtcLastDisplayUpdate > _displayUpdateInterval)
    {
      // update rtcLast
      _rtcLastDisplayUpdate = rtcCurrent;  // reset display update counter base
      displayAzEl(_rotorAzimuth, _rotorElevation);
    }
  }
  else // rtc overflow - just in case
  {
    // update rtcLast
    _rtcLastDisplayUpdate = rtcCurrent;
  }
// uitzetten bij bedienen knop
 val = digitalRead(_ResetPin); 

 if (val == LOW)
    {_newElevation = _rotorElevation;
      _newAzimuth = _rotorAzimuth;
    }
 

}


//
// update elevation rotor move
//
void updateElevationMove()
{
  // calculate rotor move
  long rotorMoveEl = _newElevation - _rotorElevation;

  if (rotorMoveEl > 0)
  {
    elRotorMovement = "  U ";
    elRotorMovement = elRotorMovement + String(_newElevation / 100);
    digitalWrite(_G5500DownPin, HIGH);
    digitalWrite(_G5500UpPin, LOW);
  }
  else
  {
    if (rotorMoveEl < 0)
    {
      elRotorMovement = "  D ";
      elRotorMovement = elRotorMovement + String(_newElevation / 100);
      digitalWrite(_G5500UpPin, HIGH);
      digitalWrite(_G5500DownPin, LOW);
    }
  }
}


//
// update azimuth rotor move
//
void updateAzimuthMove()
{
  // calculate rotor move
  long rotorMoveAz = _newAzimuth - _rotorAzimuth;
  // adjust move if necessary
  if (rotorMoveAz > 18000) // adjust move if > 180 degrees
  {
    rotorMoveAz = rotorMoveAz - 180;
  }
  else
  {
    if (rotorMoveAz < -18000) // adjust move if < -180 degrees
    {
      rotorMoveAz = rotorMoveAz + 18000;
    }
  }

  if (rotorMoveAz > 0)
  {
    azRotorMovement = "  R ";
    azRotorMovement = azRotorMovement + String(_newAzimuth / 100);
    digitalWrite(_G5500LeftPin, HIGH);
    digitalWrite(_G5500RightPin, LOW);
  }
  else
  {
    if (rotorMoveAz < 0)
    {
      azRotorMovement = "  L ";
      azRotorMovement = azRotorMovement + String(_newAzimuth / 100);
      digitalWrite(_G5500RightPin, HIGH);
      digitalWrite(_G5500LeftPin, LOW);
    }
  }
}


//
// read azimuth from G5500
//
void readElevation()
{
  long sensorValue = analogRead(_elevationInputPin);
  _rotorElevation = ((sensorValue * 10000) / _elScaleFactor) - _elAdZeroOffset;
}


//
// read azimuth from G5500
//
void readAzimuth()
{
  long sensorValue = analogRead(_azimuthInputPin);
  _rotorAzimuth = ((sensorValue * 10000) / _azScaleFactor) - _azAdZeroOffset;
}


//
// decode gs232 commands
//
void decodeGS232(char character)
{
  switch (character)
  {
    case 'w':  // gs232 W command
    case 'W':
      {
        {
          _gs232WActice = true;
          _gs232AzElIndex = 0;
        }
        break;
      }

    // numeric - azimuth and elevation digits
    case '0':  case '1':   case '2':  case '3':  case '4':
    case '5':  case '6':   case '7':  case '8':  case '9':
      {
        if ( _gs232WActice)
        {
          processAzElNumeric(character);
        }
      }

    default:
      {
        // ignore everything else
      }
  }
}


//
// process az el numeric characters from gs232 W command
//
void processAzElNumeric(char character)
{
  switch (_gs232AzElIndex)
  {
    case 0: // first azimuth character
      {
        _azimuthTemp = (character - 48) * 100;
        _gs232AzElIndex++;
        break;
      }

    case 1:
      {
        _azimuthTemp = _azimuthTemp + (character - 48) * 10;
        _gs232AzElIndex++;
        break;
      }

    case 2: // final azimuth character
      {
        _azimuthTemp = _azimuthTemp + (character - 48);
        _gs232AzElIndex++;

        // check for valid azimuth
        if ((_azimuthTemp * 100) > _maxRotorAzimuth)
        {
          _gs232WActice = false;
          _newAzimuth = 0L;
          _newElevation = 0L;
        }
        break;
      }

    case 3: // first elevation character
      {
        _elevationTemp = (character - 48) * 100;
        _gs232AzElIndex++;
        break;
      }

    case 4:
      {
        _elevationTemp = _elevationTemp + (character - 48) * 10;
        _gs232AzElIndex++;
        break;
      }

    case 5: // last elevation character
      {
        _elevationTemp = _elevationTemp + (character - 48);
        _gs232AzElIndex++;

        // check for valid elevation
        if ((_elevationTemp * 100) > _maxRotorElevation)
        {
          _gs232WActice = false;
          _newAzimuth = 0L;
          _newElevation = 0L;
        }
        else // both azimuth and elevation are ok
        {
          // set up for rotor move
          _newAzimuth = _azimuthTemp * 100;
          _newElevation = _elevationTemp * 100;
          _azimuthMove = true;
          _elevationMove = true;
        }
        break;
      }

    default:
      {
        // should never get here
      }
  }
}


//
// display az el on display
//
void displayAzEl(long az, long el)
{
  // display azimuth - filter A/D noise
  if (abs(_rotorAzimuth - _previousRotorAzimuth) > 50)
  {
    _previousRotorAzimuth = _rotorAzimuth;
    displayAz(az);
  }


  // display elevation - filter A/D noise
  if (abs(_rotorElevation - _previousRotorElevation) > 50)
  {
    _previousRotorElevation = _rotorElevation;
    displayEl(el);
  }

}


//
// display elevation - pad to length 8
//   error message if < 0 or > max
//
void displayEl(long el)
{
  // clear elevation line  lcdSerial
  lcd.setCursor(0, 1); // numero Colonna, Riga
  lcd.print("                ");

  //  adjust value for display
  double elFloat = el;
  elFloat = elFloat / 100.0;

  // position lcd cursor on bottom line
  lcd.setCursor(0, 1); // numero Colonna, Riga

  // display elevation
  lcd.print("EL ");
  // pad with spaces
  if (elFloat < 10.0)
  {
    lcd.print(" ");
  }
  if (elFloat < 100.0)
  {
    lcd.print(" ");
  }
  lcd.print(elFloat, 1);
  lcd.print(elRotorMovement);
}


//
// display azimuth - pad to length 8
//   error message if < 0 or > max
//
void displayAz(long az)
{
  // clear azimuth line
  lcd.setCursor(0, 0);
  lcd.print("                ");

  //  adjust value for display
  double azFloat = az;
  azFloat = azFloat / 100.0;

  // position lcd cursor on top line
  lcd.setCursor(0, 0);

  // display azimuth
  lcd.print("AZ ");
  // pad with spaces
  if (azFloat < 10.0)
  {
    lcd.print(" ");
  }
  if (azFloat < 100.0)
  {
    lcd.print(" ");
  }
  lcd.print(azFloat, 1);
  lcd.print(azRotorMovement);
}

 

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir