NexStar™ Direct Telescope Mount Control - Revisited

Since publishing my first article on developing an interface for the NexStar™ GT-series telescope mount, I've improved on the interface and software, which this page documents.  The first improvement is to use a tri-state buffer to replace the 1N4148 diode and 50K pull up resistor used to combine the TX and RX lines from the Arduino into the single, bidirectional "Serial" line.  I also used a 2nd tri-state buffer to implement the pull down of the Select line rather than rely on switching the Arduino pin between input and output.  And, finally, I added a 5 volt regulator so the interface can draw power from the NexStar mount's 12 volt output.  This, in turn, let me switch to an Arduino Pro Mini and use an HC-05 Bluetooth module to make the entire interface wireless.  However, you should be able to use the new design with almost any Arduino.  The main differences is that I relocated the pins used from D0-D3 to D10-D12 and A0 and now use SoftSerial to communicate to the NexStar.  Here's the schematic for the new interface:

Click for larger view

And, here's the new interface built on a solder less breadboard and connected to an Arduino Pro Mini (shown using a Fritzing diagram):

Click for larger view

The Fritzing diagram shows the Arduino Pro Mini connected to an FTDI Serial Adapter for programming and command input, but this can be replaced with the Bluetooth interface, if desired.  However, if you're powering the interface from the 12 volts provided by the NexStar mount you should remove the VCC connection from the FTDI Adapter.  Also, as the new interface uses 4 signals lines, the code needed to be revised, as follows:

  // This version is for new design with tri-state buffers and an Arduino Pro Mini

  //  using SoftwareSerial

#include <SoftwareSerial.h>

#define DEBUG   0

#define RX      10

#define TX      11

#define SEL_OUT 12

#define SEL_IN  A0

#define AZM 0x10

#define ALT 0x11

uint8_t   maxSpd = 0x09;

uint8_t   stopSpd = 0x00;

uint8_t   posDir = 0;

uint8_t   negDir = 1;

uint8_t   rsp[10];

uint32_t  position[2];

uint8_t   selAxis = ALT;

SoftwareSerial mount(RX, TX);

void setup() {

  Serial.begin(57600);

  mount.begin(19200);

  pinMode(SEL_OUT, OUTPUT);

  digitalWrite(SEL_OUT, HIGH);

  pinMode(SEL_IN, INPUT_PULLUP);

  Serial.println("Ready.");

#if DEBUG

  // Test SEL_OUT and SEL_IN

  delay(10);

  if (digitalRead(SEL_IN) == LOW) {

    Serial.println("SEL_IN not HIGH");

  }

  digitalWrite(SEL_OUT, LOW);

  delay(10);

  if (digitalRead(SEL_IN) == HIGH) {

    Serial.println("SEL_IN not LOW");

  }

  digitalWrite(SEL_OUT, HIGH);

#endif

}

void receive () {

  while (mount.available()) {

    unsigned char cc = mount.read();

#if DEBUG

    if (cc < 0x10)

      Serial.print('0');

    Serial.print(cc, 16);

    Serial.print(' ');

#endif

  }

}

void sendCmd (uint8_t dst, uint8_t id, uint8_t* data, uint8_t len) {

#if DEBUG

    Serial.println("sendCmd(start)");

#endif

    digitalWrite(SEL_OUT, LOW);                     // Select SEL_OUT LOW (send)

    delayMicroseconds(50);

    mount.write(0x3B);

    receive();

    mount.write(0x03 + len);

    receive();

    mount.write(0x0D);

    receive();

    mount.write(dst);

    receive();

    mount.write(id);

    receive();

    uint8_t crc = (0x03 + 0x0D) + len + dst + id;

    for (uint8_t ii = 0; ii < len; ii++) {

      mount.write(data[ii]);

      receive();

      crc += data[ii];

    }

    mount.write(((~crc) + 1) & 0xFF);

    mount.flush();

    digitalWrite(SEL_OUT, HIGH);                  // Select SEL_OUT HIGH (receive)

#if DEBUG

    Serial.println();

    Serial.println("1");

#endif

#if 1

    while (digitalRead(SEL_IN) == LOW) {          // Wait for SEL_IN to go HIGH

      receive();

    }

#endif

#if DEBUG

    Serial.println("2");

#endif

    for (int ii = 0; ii < 1000; ii++) {           // Wait for SEL_IN to go LOW

      if (digitalRead(SEL_IN) == LOW)

        break;

      delayMicroseconds(50);

    }

#if DEBUG

   Serial.println("3");

#endif

    int idx = 0;

    while (digitalRead(SEL_IN) == LOW) {           // Read response while SEL_IN is LOW

      delayMicroseconds(50);

      if (mount.available()) {

        unsigned char cc = mount.read();

        rsp[idx++] = cc;

      }

    }

#if DEBUG

    for (int ii = 0; ii < idx; ii++) {

      unsigned char cc =rsp[ii];

      if (cc < 0x10)

        Serial.print('0');

      Serial.print(cc, 16);

      Serial.print(' ');

    }

    Serial.println();

#endif

#if DEBUG

    Serial.println("sendCmd(end)");

#endif

}

// Move to position at maximum motor speed (9)

void gotoFast (uint8_t dst, uint32_t pos) {

  uint8_t tmp[] = {(pos >> 16) & 0xFF, (pos >> 8) & 0xFF, pos & 0xFF};

  sendCmd(dst, 0x02, (uint8_t*) &tmp, 3);

}

// Move to position at slow motor speed (?)

void gotoSlow (uint8_t dst, uint32_t pos) {

  uint8_t tmp[] = {(pos >> 16) & 0xFF, (pos >> 8) & 0xFF, pos & 0xFF};

  sendCmd(dst, 0x17, (uint8_t*) &tmp, 3);

}

// Set position of axis to 'pos' (does not move axis)

void setPosition (uint8_t dst, uint32_t pos) {

  uint8_t tmp[] = {(pos >> 16) & 0xFF, (pos >> 8) & 0xFF, pos & 0xFF};

  sendCmd(dst, 0x04, (uint8_t*) &tmp, 3);

}

uint32_t getPosition (uint8_t dst) {

  sendCmd(ALT, 0x01, 0, 0);

  return (rsp[5] << 16) + (rsp[6] << 8) + rsp[7];

}

void loop() {

  receive();

  if (Serial.available()) {

    unsigned char cc = Serial.read();

#if DEBUG

    Serial.println();

    Serial.println((char) cc);

#endif

    switch (cc) {

      case '[':

        selAxis = ALT;

        break;

      case ']':

        selAxis = AZM;

        break;

      case '0':

        // Stop Slew of Selected Axis

        sendCmd(selAxis, 0x24, &stopSpd, 1);

        break;

      case '1':

        // Slew Selected Axis in negative direction

        sendCmd(selAxis, 0x25, &maxSpd, 1);

        break;

      case '2':

        // Slew Selected Axis in positive direction

        sendCmd(selAxis, 0x24, &maxSpd, 1);

        break;

      case '-':

        // Rotate in negative direction by 0x100000

        gotoFast(selAxis, position[selAxis & 1] = (position[selAxis & 1] - 0x100000) & 0xFFFFFF);

        break;

      case '+':

        // Rotate in positive direction by 0x100000

        gotoFast(selAxis, position[selAxis & 1] = (position[selAxis & 1] + 0x100000) & 0xFFFFFF);

        break;

      case 'x':

         // Move Selected Axis to position 0x000000

        gotoFast(selAxis, 0x000000);

        break;

      case 'g':

        // Get Selected Axis current position

        Serial.println(getPosition(selAxis) & 0xFFFFFF, 16);

        break;

      case 's':

        // Set Selected Axis current position to 0x000000

        setPosition(selAxis, 0x000000);

        position[selAxis & 1] = 0x000000;

        break;

      case 'v':

        // Get AZM Version number

        sendCmd(AZM, 0xFE, 0, 0); 

        Serial.print(rsp[5]);

        Serial.print('.');

        Serial.println(rsp[6]);

        break;

      case '?':

        Serial.println("OK");

        break;

    }

    // Delay before processing next command to avoid overrun

    delay(500);

  }

}