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