A RemoteQth Cable Adapter for Prosistel Rotator

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.

Logic schematic and wire connections

You can use any 5V tolerant RS232-TTL converter

RemoteQth configuration and control

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.

Arduino Mega source code

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