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