If you are a RemoteQth server user, and own a Prosistel rotor, you will have to deal with the problem of remoting your rotor.
The solution suggested by RemoteQth is to build the K3NG interface that allows you to control any types of rotor. This also means you have to provide an external power supply and leave the
original "D" controller unused.
Actually, looking a bit deeper in the RemoteQth source code, I realized that the RemoteQTH server can natively support the Yaesu rotor protocol. Hence the idea of using an Arduino board to
implement a simple Prosistel to Yaesu rotor protocol converter. As you can see in the picture, Arduino communicates with the RemoteQth server using the main serial port (Serial)
and the Yaesu protocol.
On the other side Arduino communicates with the Prosistel rotor using an additional serial port (Serial3) and the Prosistel protocol. Be sure to use a null-modem cable between
Arduino-RS232-TTL converter and Prosistel control box.
The result is really exciting considering the very cheap component costs, the easy implementation and the simplicity of the source code.
I advise not to use a simple Arduino board, with single serial port, and the SoftwareSerial library to add another serial port. I found, in the long time use, the SoftwareSerial library
could be not so much reliable.
You can use any 5V tolerant RS232-TTL converter
Your values should be different, mostly if you use a clone board with different Vendo/Product/Serial Id.
Be aware of unsupported CCW and CW commands. Unlike Yaesu/K3NG rotators controller, using this converter, you can't use CCW and CW commands. Actually I find these commands a bit dangerous during remote operations.
Of course CLICK AND GO, STOP and REALTIME STATUS are all safe supported features.
Copy the code below and paste it in Arduino IDE.
HardwareSerial *rotSerial = &Serial3;
HardwareSerial *qthSerial = &Serial;
const long interval = 1000;
unsigned long previousMillis = 0;
int beamDir = 0; //Actual rotator direction
int newBeamDir = 0; //New rotator direction
String qthCommand;
String rotCommand;
// ========================= REMOTEQTH PARSER ================================
void parseRemoteQthCmd(String cmd) {
//rotSerial->println(cmd);
String code = cmd.substring(0,1);
code.toUpperCase();
//rotSerial->println(beamDir);
if (code.equals("C")) {
char response[6];
sprintf(response,"+0%03d",beamDir);
qthSerial->print(response); qthSerial->print("\n");
//rotSerial->write(response); rotSerial->write('\n');
} else if (code.equals("A")) { // STOP
qthSerial->print("\n");
//rotSerial->write("\n");
rotorStop();
} else if (code.equals("L")) { // CCW Button
qthSerial->print("\n"); // Actually does nothing - Let
RemoteQth be happy
//rotSerial->write("\n");
} else if (code.equals("R")) { // CW Button
qthSerial->print("\n"); // Actually does nothing - Let
RemoteQth be happy
//rotSerial->write("\n");
} else if (code.equals("M")) { // Move rotator to new beam direction
//rotSerial->print('='); rotSerial->print(cmd.length());
//rotSerial->print('='); rotSerial->print(cmd.substring(1));
if (cmd.length()==4) {
newBeamDir = cmd.substring(1).toInt();
if ((newBeamDir>=0) && (newBeamDir<360)) {
rotorGo(newBeamDir);
}
}
qthSerial->print("\n");
//rotSerial->write("\n");
} else {
qthSerial->print("?>"); qthSerial->print("\n");
//rotSerial->print("?>"); rotSerial->print('\n');
}
}
void readRemoteQthCom()
{
if (qthSerial->available()) {
char c = qthSerial->read();
/*
rotSerial->println("========================================================");
rotSerial->print("In char: >");
rotSerial->print(c);
rotSerial->print("< - ");
rotSerial->print(c,DEC);
rotSerial->print(" - ");
rotSerial->println(c,HEX);
rotSerial->println("========================================================");
*/
if (c == 0x0D)
{
parseRemoteQthCmd(qthCommand);
qthCommand = "";
} else
{
qthCommand += c;
}
}
}
// =======================================================================
// ======================== ROTOR UTILS ==================================
void rotorGo(int newDir) { // Send Go command
char cmd[10];
sprintf(cmd,"%cAG%d%c",2,newDir,13);
rotSerial->write(cmd);
delay(250);
}
void rotorStop() {
rotorGo(977);
// Send Stop command
}
void rotorQueryStatus() {
char cmd[4];
// Send queryStatus command
sprintf(cmd,"%cA?%c",2,13);
rotSerial->write(cmd);
}
// =======================================================================
// ========================= ROTOR PARSER ================================
void readRotorCom()
{
if (rotSerial->available()) {
char c = rotSerial->read();
if (c == 0x0D)
{
//parseRemoteQthCmd(qthCommand);
if (rotCommand.indexOf("A,?,")==1) { // get only status messages
String tmp = rotCommand.substring(5,8);
beamDir = tmp.toInt();
//Serial.print("beamDir:"); Serial.println(beamDir);
}
rotCommand = "";
} else
{
rotCommand += c;
}
}
}
// ======================== SETUP ========================================
void setup() {
// initialize both serial ports:
rotSerial->begin(9600);
delay(500);
rotSerial->flush();
qthSerial->begin(9600);
delay(500);
qthSerial->flush();
//rotSerial->println("=============== Setup ==================");
}
// =======================================================================
// ======================== LOOP =========================================
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // Poll Prosistel rotator status every 1s
//beamDir = (beamDir + 1) % 360; // Comment this line when ready
rotorQueryStatus();
}
readRotorCom();
readRemoteQthCom();
}