From b46f187a5f5c99277573a2c8fbcd23d589eb0555 Mon Sep 17 00:00:00 2001
From: Thomas Carpenter <strange_orange_fish@hotmail.com>
Date: Wed, 29 Feb 2012 17:43:13 +0000
Subject: [PATCH] Initial commit of project. Currently tracking is at incorrect
 speed, I'm working on it :S

---
 AstroEQ/AstroEQ.ino  | 417 +++++++++++++++++++++++++++++++++++++++++++
 AstroEQ/commands.cpp | 181 +++++++++++++++++++
 AstroEQ/commands.h   |  64 +++++++
 AstroEQ/synta.cpp    | 149 ++++++++++++++++
 AstroEQ/synta.h      |  44 +++++
 README               |  17 ++
 6 files changed, 872 insertions(+)
 create mode 100644 AstroEQ/AstroEQ.ino
 create mode 100644 AstroEQ/commands.cpp
 create mode 100644 AstroEQ/commands.h
 create mode 100644 AstroEQ/synta.cpp
 create mode 100644 AstroEQ/synta.h

diff --git a/AstroEQ/AstroEQ.ino b/AstroEQ/AstroEQ.ino
new file mode 100644
index 0000000..5e6e56d
--- /dev/null
+++ b/AstroEQ/AstroEQ.ino
@@ -0,0 +1,417 @@
+/*
+  Code written by Thomas Carpenter 2012
+  
+  With thanks Chris over at the EQMOD Yahoo group for explaining the Skywatcher protocol
+  
+  
+  Equatorial mount tracking system for integration with EQMOD using the Skywatcher/Syntia
+  communication protocol.
+ 
+  Works with EQ5, HEQ5, and EQ6 mounts (Not EQ3-2, they have a different gear ratio)
+ 
+  Current Verison: 3.5 
+*/
+
+#include "synta.h"
+
+//Create an instance of the mount
+Synta synta(1281,4505600,32368,16,31289);
+
+//Synta synta(e,a,b,g,s);
+//
+// e = version number (this doesnt need chaning)
+//
+// a = number of steps per complete axis revolution. 
+//
+// b = sidereal rate. This again depends on number of microsteps per step.
+//
+// g = high speed multiplyer. This is the EQMOD recommended default. In highspeed mode, the step rate will be 16 times larger.
+//
+// s = steps per worm revolution
+//
+// mount specifics can be found here: http://tech.groups.yahoo.com/group/EQMOD/database?method=reportRows&tbl=5&sortBy=5
+//
+// The motor portion of the code I have is written is for EQ5 Mounts.
+// It may work for EQ6 mounts if you change the numbers above to:
+// (1281,4512000,32466,16,50133)
+// And possibly for HEQ5 mounts if you change the numbers above to:
+// (1281,4512000,32466,16,66844)
+//
+
+//Define the two axes
+#define RA 0
+#define DC 1
+
+unsigned long gotoPosn[2] = {0,0}; //where to slew to
+unsigned int timerOVF[2];
+
+//Pins
+const char resetPin[2] = {A1,A0};
+const char errorPin[2] = {2,6};
+const char dirPin[2] = {3,7};
+const char enablePin[2] = {4,8};
+const char stepPin[2] = {5,9};
+//Used for direct port register write
+#define STEP1PORT PORTE
+#define _STEP1_HIGH 0b00001000
+#define _STEP1_LOW 0b11110111
+#define STEP2PORT PORTH
+#define _STEP2_HIGH 0b01000000
+#define _STEP2_LOW 0b10111111
+
+
+void setup()
+{
+  for(byte i = 0;i < 2;i++){ //for each motor...
+    pinMode(errorPin[i],INPUT); //enable the Error pin
+    pinMode(enablePin[i],OUTPUT); //enable the Enable pin
+    digitalWrite(enablePin[i],HIGH); //IC disabled
+    pinMode(stepPin[i],OUTPUT); //enable the step pin
+    digitalWrite(stepPin[i],LOW); //default is low
+    pinMode(dirPin[i],OUTPUT); //enable the direction pin
+    digitalWrite(dirPin[i],LOW); //default is low
+    pinMode(resetPin[i],OUTPUT); //enable the reset pin
+    digitalWrite(resetPin[i],LOW); //active low reset
+    delay(1); //allow ic to reset
+    digitalWrite(resetPin[i],HIGH); //complete reset
+  }
+    
+  // start Serial port:
+  Serial.begin(9600); //SyncScan runs at 9600Baud, use a serial port of your choice (Serial, Serial, Serial2, or Serial3)
+  while(Serial.available()){
+    Serial.read(); //Clear the buffer
+  }
+
+  Serial1.begin(115200);
+  configureTimer(); //setup the motor pulse timers.
+}
+
+
+void loop(){
+  char decodedPacket[11]; //temporary store for completed command ready to be processed
+  if (Serial.available()) { //is there a byte in buffer
+    char recievedChar = Serial.read(); //get the next character in buffer
+    Serial1.print(recievedChar);
+    char decoded = synta.recieveCommand(decodedPacket,recievedChar); //once full command packet recieved, decodedPacket returns either error packet, or valid packet
+    if (decoded == 1){ //Valid Packet
+      decodeCommand(synta.command(),decodedPacket); //decode the valid packet
+    } else if (decoded == -1){ //Error
+      Serial.print(decodedPacket); //send the error packet
+    } //otherwise command not yet fully recieved, so wait for next byte
+  }
+}
+
+void decodeCommand(char command, char* packetIn){ //each command is axis specific. The axis being modified can be retrieved by calling synta.axis()
+  char response[11]; //generated response string
+  unsigned long responseData; //data for response
+  
+  switch(command){
+    case 'e': //readonly, return the eVal (version number)
+        responseData = synta.cmd.eVal(synta.axis()); //response to the e command is stored in the eVal function for that axis.
+        break;
+    case 'a': //readonly, return the aVal (steps per axis)
+        responseData = synta.cmd.aVal(synta.axis()); //response to the a command is stored in the aVal function for that axis.
+        break;
+    case 'b': //readonly, return the bVal (sidereal rate)
+        responseData = synta.cmd.bVal(synta.axis()); //response to the b command is stored in the bVal function for that axis.
+        break;
+    case 'g': //readonly, return the gVal (high speed multiplier)
+        responseData = synta.cmd.gVal(synta.axis()); //response to the g command is stored in the gVal function for that axis.
+        break;
+    case 's': //readonly, return the sVal (steps per worm rotation)
+        responseData = synta.cmd.sVal(synta.axis()); //response to the s command is stored in the sVal function for that axis.
+        break;
+    case 'f': //readonly, return the fVal (axis status)
+        responseData = synta.cmd.fVal(synta.axis()); //response to the f command is stored in the fVal function for that axis.
+        break;
+    case 'j': //readonly, return the jVal (current position)
+        responseData = synta.cmd.jVal(synta.axis()); //response to the j command is stored in the jVal function for that axis.
+        break;
+    case 'K': //stop the motor, return empty response
+    
+    
+        motorStop(0); //No specific response, just stop the motor (Feel free to customise motorStop function to suit your needs
+      
+      
+        responseData = 0;
+        break;
+    case 'G': //set mode and direction, return empty response
+        synta.cmd.GVal(synta.axis(), (packetIn[0] - 48)); //Store the current mode for the axis
+        synta.cmd.dir(synta.axis(),(packetIn[1] - 48)); //Store the current direction for that axis
+        responseData = 0;
+        break;
+    case 'H': //set goto position, return empty response (this sets the number of steps to move from cuurent position if in goto mode)
+        synta.cmd.HVal(synta.axis(), synta.hexToLong(packetIn)); //set the goto position container (convert string to long first)
+        responseData = 0;
+        break;
+    case 'I': //set slew speed, return empty response (this sets the speed to move at if in slew mode)
+        synta.cmd.IVal(synta.axis(), synta.hexToLong(packetIn)); //set the speed container (convert string to long first)
+  
+        //This will be different if you use a different motor code
+        calculateRate(); //Used to convert speed into the number of 0.5usecs per step. Result is stored in TimerOVF
+  
+  
+        responseData = 0;
+        break;
+    case 'E': //set the current position, return empty response
+        synta.cmd.jVal(synta.axis(), synta.hexToLong(packetIn)); //set the current position (used to sync to what EQMOD thinks is the current position at startup
+        responseData = 0;
+        break;
+    case 'F': //Enable the motor driver, return empty response
+
+
+        motorEnable(); //This enables the motors - gives the motor driver board power
+
+
+        responseData = 0;
+        break;
+    default: //Return empty response (deals with commands that don't do anything before the response sent (i.e 'J'), or do nothing at all (e.g. 'M', 'L') )
+        responseData = 0;
+        break;
+  }
+  
+  synta.assembleResponse(response, command, responseData); //generate correct response (this is required as is)
+  Serial.print(response); //send response to the serial port
+  
+  if(command == 'J'){ //J tells
+    if(synta.cmd.GVal(synta.axis()) & 1){
+      
+      //This is the funtion that enables a slew type move.
+      slewMode(); //Slew type
+      
+      
+    } else {
+      
+      //This is the function for goto mode. You may need to customise it for a different motor driver
+      gotoMode(); //Goto Mode
+      
+      
+    }
+  }
+}
+
+//
+//Everything below this point needs to be customised for your motor driver. As yet code below here is untested
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+
+//Calculates the rate based on the recieved I value 
+void calculateRate(){
+  
+  //steps per second = IVal / BVal; for lowspeed move
+  //or steps per second = IVal / (BVal * gVal); for highspeed move 
+  //
+  //whether to use highspeed move or not is determined by the GVal;
+  //
+  
+  unsigned long rate;
+  if((synta.cmd.GVal(synta.axis()) == 2)||(synta.cmd.GVal(synta.axis()) == 1)){ //Normal Speed
+    rate = 2000000L * synta.cmd.IVal(synta.axis());
+    rate /= synta.cmd.bVal(synta.axis());
+  } else if ((synta.cmd.GVal(synta.axis()) == 0)||(synta.cmd.GVal(synta.axis()) == 3)){ //High Speed
+    rate = 2000000L * synta.cmd.IVal(synta.axis());
+    rate /= (synta.cmd.bVal(synta.axis()) * synta.cmd.gVal(synta.axis()));
+  }
+  
+  //Now truncate to an unsigned int with a sensible max value (the int is to avoid register issues with the 16 bit timer)
+  if(rate < 539){ //We do 65535 - result as timer counts up from this number to 65536 then interrupts
+    timerOVF[synta.axis()] = 64997L;
+  } else {
+    timerOVF[synta.axis()] = 65535L - rate;
+  }
+}
+
+void slewMode(){
+  digitalWrite(dirPin[synta.axis()],synta.cmd.dir(synta.axis())); //set the direction
+  if(timerOVF[synta.axis()] > 38000L){
+    accellerate(64); //accellerate to calculated rate in 64 steps
+  } else {
+    accellerate(32); //accellerate to calculated rate in 32 steps (faster to avoid missing serial messages)
+  }
+  if(synta.axis()){
+    TCNT4 = timerOVF[synta.axis()];
+    TIMSK4 |= (1<<TOIE4); //Enable timer interrupt
+  } else {
+    TCNT3 = timerOVF[synta.axis()];
+    TIMSK3 |= (1<<TOIE3); //Enable timer interrupt
+  }
+  synta.cmd.stopped(synta.axis(), 0);
+}
+
+void gotoMode(){
+  digitalWrite(dirPin[synta.axis()],synta.cmd.dir(synta.axis())); //set the direction
+  synta.cmd.IVal(synta.axis(), 140); //Set GOTO speed
+  calculateRate();
+  unsigned int temp = 0;
+  byte accellerateSteps;
+  if (synta.cmd.HVal(synta.axis()) < 32){
+    synta.cmd.HVal(synta.axis(), 32); //This will cause a small error on VERY TINY Goto's though in reality should never be an issue.
+  }
+  if (synta.cmd.HVal(synta.axis()) < 64){
+    synta.cmd.HVal(synta.axis(),(synta.cmd.HVal(synta.axis()) - 16));
+    accellerateSteps = 16;
+  } else if(synta.cmd.HVal(synta.axis()) < 96){
+    synta.cmd.HVal(synta.axis(),(synta.cmd.HVal(synta.axis()) - 32));
+    accellerateSteps = 32;
+  } else if(synta.cmd.HVal(synta.axis()) < 128){
+    synta.cmd.HVal(synta.axis(),(synta.cmd.HVal(synta.axis()) - 48));
+    accellerateSteps = 48;
+  } else {
+    synta.cmd.HVal(synta.axis(),(synta.cmd.HVal(synta.axis()) - 64));
+    accellerateSteps = 64;
+  }
+  gotoPosn[synta.axis()] = synta.cmd.jVal(synta.axis()) + (synta.cmd.stepDir(synta.axis()) * synta.cmd.HVal(synta.axis())); //current position
+  if(synta.cmd.HVal(synta.axis()) < 124){ //there are 496 steps of decelleration, so we need to find the correct part of the decelleration curve
+    //find point in decelleration curve (timerOVF[synta.axis()] -= 76; for each step)
+    temp = 124 - synta.cmd.HVal(synta.axis()); //number of decelleration steps into the curve
+    temp *= 304;
+    timerOVF[synta.axis()] -= temp;
+  }
+  accellerate(accellerateSteps); //accellerate to calculated rate in 64 steps
+  synta.cmd.gotoEn(synta.axis(),1);
+  synta.cmd.stopped(synta.axis(),0);
+  if(synta.axis()){
+    TCNT4 = timerOVF[synta.axis()];
+    TIMSK4 |= (1<<TOIE4); //Enable timer interrupt
+  } else {
+    TCNT3 = timerOVF[synta.axis()];
+    TIMSK3 |= (1<<TOIE3); //Enable timer interrupt
+  }
+}
+
+void motorStop(boolean caller){
+  if(!synta.cmd.stopped(synta.axis())){ //only has an effect if not already stopped
+    if(!caller && !synta.cmd.gotoEn(synta.axis())){ //If not in goto mode, then decellerate
+      synta.cmd.HVal(synta.axis(), (64997 - timerOVF[synta.axis()])); //work out where in decelleration curve we are to know how many steps to run
+      synta.cmd.HVal(synta.axis(), (synta.cmd.HVal(synta.axis()) / 76)); //19 0.5us's per step
+      synta.cmd.HVal(synta.axis(), (496 - synta.cmd.HVal(synta.axis()))); //find steps
+      gotoPosn[synta.axis()] = synta.cmd.jVal(synta.axis()) + (synta.cmd.stepDir(synta.axis()) * synta.cmd.HVal(synta.axis())); //current position
+      synta.cmd.gotoEn(synta.axis(),1); //Enter short goto mode stint
+    } else if (caller){ //otherwise, if decelleration complete, stop.
+      if(synta.axis()){ //switch off correct axis
+        TIMSK4 &= ~(1<<TOIE4); //disable timer
+      } else {
+        TIMSK3 &= ~(1<<TOIE3); //disable timer
+      }
+      synta.cmd.gotoEn(synta.axis(),0); //Cancel goto mode
+      synta.cmd.stopped(synta.axis(),1); //mark as stopped
+    } else if (synta.cmd.gotoEn(synta.axis())){ //otherwise, abort GOTO and decellerate.
+      synta.cmd.HVal(synta.axis(), (64997 - timerOVF[synta.axis()])); //work out where in decelleration curve we are to know how many steps to run
+      synta.cmd.HVal(synta.axis(), (synta.cmd.HVal(synta.axis()) / 76)); //76 0.5us's per step
+      synta.cmd.HVal(synta.axis(), (496 - synta.cmd.HVal(synta.axis()))); //find steps
+      gotoPosn[synta.axis()] =  synta.cmd.jVal(synta.axis()) + (synta.cmd.stepDir(synta.axis()) * synta.cmd.HVal(synta.axis())); //current position
+    }
+  }
+}
+
+void motorEnable(){
+  digitalWrite(enablePin[synta.axis()],LOW);
+  synta.cmd.FVal(synta.axis(),1);
+}
+
+void accellerate(byte accellerateSteps){
+  //x steps to speed up to timerOVF[synta.axis()]
+  unsigned int speedPerStep = (timerOVF[synta.axis()] - 10036L) / accellerateSteps; //Initial Speed is 10036
+  unsigned int microDelay = 27750L; //number of uS to wait for initial speed
+  for(int i = 0;i < accellerateSteps;i++){
+    for(byte j = 0;j < 2;j++){
+      delayMicroseconds(microDelay - 2);
+      if(synta.axis()){ //Step
+        writeSTEP2(HIGH);
+        delayMicroseconds(2);
+        writeSTEP2(LOW);
+      } else {
+        writeSTEP1(HIGH);
+        delayMicroseconds(2);
+        writeSTEP1(LOW);
+      }
+    }
+    microDelay -= speedPerStep;
+  }
+  //Now at speed, so return, and timers will be used from now on.
+}
+
+//Timer Interrupt-----------------------------------------------------------------------------
+void configureTimer(){
+  TIMSK3 &= ~(1<<TOIE3); //disable timer so it can be configured
+  TIMSK4 &= ~(1<<TOIE4); //disable timer so it can be configured
+  //set to normal counting mode
+  TCCR3A &= ~((1<<WGM31) | (1<<WGM30));
+  TCCR3B &= ~((1<<WGM33) | (1<<WGM32));
+  TCCR4A &= ~((1<<WGM41) | (1<<WGM40));
+  TCCR4B &= ~((1<<WGM43) | (1<<WGM42));
+  //Disable compare interrupt (only interested in overflow)
+  TIMSK3 &= ~((1<<OCIE3A) | (1<<OCIE3B) | (1<<OCIE3C));
+  TIMSK4 &= ~((1<<OCIE4A) | (1<<OCIE4B) | (1<<OCIE4C));
+  //Set prescaler to F_CPU/1
+  TCCR3B &= ~(1<<CS32); //0
+  TCCR3B |= (1<<CS30);//1
+  TCCR3B &= ~(1<<CS31);//0
+  TCCR4B &= ~(1<<CS42); //0
+  TCCR4B |= (1<<CS40);//1
+  TCCR4B &= ~(1<<CS41);//0
+}
+
+/*Timer Interrupt Vector*/
+ISR(TIMER3_OVF_vect) {
+  TCNT3 = timerOVF[0]; //reset timer straight away to avoid compounding errors
+  motorStep(0);
+}
+
+/*Timer Interrupt Vector*/
+ISR(TIMER4_OVF_vect) {
+  TCNT4 = timerOVF[1]; //reset timer straight away to avoid compounding errors
+  motorStep(1);
+}
+
+void motorStep(byte motor){
+  static byte divider = 0;
+  if(motor){
+    writeSTEP2(HIGH);
+    delayMicroseconds(2);
+    writeSTEP2(LOW);
+  } else {
+    writeSTEP1(HIGH);
+    delayMicroseconds(2);
+    writeSTEP1(LOW);
+  }
+  divider++;
+  if((divider & 7) == 0){
+    synta.cmd.jVal(motor, (synta.cmd.jVal(motor) + synta.cmd.stepDir(motor)));
+    if(synta.cmd.gotoEn(motor)){
+      long stepsLeft = gotoPosn[motor] - synta.cmd.jVal(motor);
+      stepsLeft *= synta.cmd.stepDir(motor);
+      if(stepsLeft <= 0){
+        motorStop(1);
+      } else if (stepsLeft < 124){
+        timerOVF[motor] -= 304; //decelleration region
+      }
+    }
+  }
+}
+
+void writeSTEP1(boolean bitValue){
+  if (bitValue){
+    STEP1PORT |= _STEP1_HIGH;
+  } else {
+    STEP1PORT &= _STEP1_LOW;
+  }
+}
+
+void writeSTEP2(boolean bitValue){
+  if (bitValue){
+    STEP2PORT |= _STEP2_HIGH;
+  } else {
+    STEP2PORT &= _STEP2_LOW;
+  }
+}
+//--------------------------------------------------------------------------------------------
+
diff --git a/AstroEQ/commands.cpp b/AstroEQ/commands.cpp
new file mode 100644
index 0000000..607cc8c
--- /dev/null
+++ b/AstroEQ/commands.cpp
@@ -0,0 +1,181 @@
+//command Structures ---------------------------------------------------------
+//
+// Definition of the commands used by the Synta protocol, and variables in which responses
+// are stored
+//
+// Data structure of flag Flag:
+//   flag = xxxx00ds000g000f where bits:
+//   x = dont care
+//   d = dir
+//   s = stopped
+//   g = goto
+//   f = energised
+//
+// Only dir can be used to set the direction, but stepDir method can be used
+// to returns it in a more useful format
+//
+//----------------------------------------------------------------------------
+
+#if ARDUINO >= 100
+  #include "Arduino.h"
+#else
+  #include "WProgram.h"
+#endif
+
+#include "commands.h"
+
+void Commands::init(unsigned long eVal,unsigned long aVal,unsigned long bVal,byte gVal,unsigned long sVal){ 
+  for(byte i = 0;i < 2;i++){
+    _flag[i] = 0x100;
+    _jVal[i] = 0x800000; //Current position, 0x800000 is the centre
+    _IVal[i] = 0; //Recieved Speed
+    _GVal[i] = 0; //Mode recieved from :G command
+    _HVal[i] = 0; //Value recieved from :H command
+    _eVal[i] = eVal; //version number
+    _aVal[i] = aVal; //steps/axis
+    _bVal[i] = bVal; //sidereal rate
+    _gVal[i] = gVal; //High speed scalar
+    _sVal[i] = sVal; //steps/worm rotation
+  }
+}
+
+const char Commands::command[numberOfCommands][3] = { {'e', 0, 6},
+                                                      {'a', 0, 6},
+                                                      {'b', 0, 6},
+                                                      {'g', 0, 2},
+                                                      {'s', 0, 6},
+                                                      {'K', 0, 0},
+                                                      {'E', 6, 0},
+                                                      {'j', 0, 6},
+                                                      {'f', 0, 3},
+                                                      {'G', 2, 0},
+                                                      {'H', 6, 0},
+                                                      {'M', 6, 0},
+                                                      {'I', 6, 0},
+                                                      {'J', 0, 0},
+                                                      {'P', 1, 0},
+                                                      {'F', 0, 0},
+                                                      {'L', 0, 0} };
+
+char Commands::getLength(char cmd, boolean sendRecieve){
+  for(byte i = 0;i < numberOfCommands;i++){
+    if(command[i][0] == cmd){
+      if(sendRecieve){
+        return command[i][1];
+      } else {
+        return command[i][2];
+      }
+    }
+  }
+  return -1;
+}
+
+unsigned int Commands::fVal(byte target){
+  return _flag[target];
+}
+
+char Commands::stepDir(byte target){ //Get Method
+  char stepDir = 1 - ((_flag[target] >> 8) & 0x02); //get step direction
+  return stepDir;
+}
+
+byte Commands::dir(byte target, char dir){ //Set Method
+  if(dir == 1){
+    //set direction
+    _flag[target] |= (1 << 9); //set bit
+  } else if (dir == 0){
+    _flag[target] &= ~(1 << 9); //unset bit
+  } else {
+    dir = (_flag[target] >> 9) & 0x01; //get direction
+  }
+  return dir;
+}
+
+byte Commands::stopped(byte target, byte stopped){ //Set Method
+  if(stopped == 1){
+    _flag[target] |= (1 << 8); //set bit
+  } else if (stopped == 0){
+    _flag[target] &= ~(1 << 8); //unset bit
+  } else {
+    stopped = (_flag[target] >> 8) & 0x01;
+  }
+  return stopped;
+}
+
+byte Commands::gotoEn(byte target, byte gotoEn){ //Set Method
+  if(gotoEn == 1){
+    _flag[target] |= (1 << 4); //set bit
+  } else if (gotoEn == 0){
+    _flag[target] &= ~(1 << 4); //unset bit
+  } else {
+    gotoEn = (_flag[target] >> 4) & 0x01;
+  }
+  return gotoEn;
+}
+
+byte Commands::FVal(byte target, byte FVal){ //Set Method
+  if(FVal == 1){
+    _flag[target] |= (1 << 0); //set bit
+  } else if (FVal == 0){
+    _flag[target] &= ~(1 << 0); //unset bit
+  } else {
+    FVal = (_flag[target] >> 0) & 0x01;
+  }
+  return FVal;
+}
+
+unsigned long Commands::jVal(byte target, unsigned long jVal){ //Set Method
+  if(jVal < 0x01000000){
+    _jVal[target] = jVal;
+  } else {
+    jVal = _jVal[target];
+  }
+  return jVal;
+}
+
+unsigned long Commands::IVal(byte target, unsigned long IVal){ //Set Method
+  if(IVal < 0x01000000){
+    _IVal[target] = IVal;
+  } else {
+    IVal = _IVal[target];
+  }
+  return IVal;
+}
+
+byte Commands::GVal(byte target, byte GVal){ //Set Method
+  if(GVal < 4){
+    _GVal[target] = GVal;
+  } else {
+    GVal = _GVal[target];
+  }
+  return GVal;
+}
+
+unsigned long Commands::HVal(byte target, unsigned long HVal){ //Set Method
+  if(HVal < 0x01000000){
+    _HVal[target] = HVal;
+  } else {
+    HVal = _HVal[target];
+  }
+  return HVal;
+}
+
+unsigned long Commands::eVal(byte target){
+  return _eVal[target];
+}
+
+unsigned long Commands::aVal(byte target){
+  return _aVal[target];
+}
+
+unsigned long Commands::bVal(byte target){
+  return _bVal[target];
+}
+
+byte Commands::gVal(byte target){
+  return _gVal[target];
+}
+
+unsigned long Commands::sVal(byte target){
+  return _sVal[target];
+}
diff --git a/AstroEQ/commands.h b/AstroEQ/commands.h
new file mode 100644
index 0000000..bd78e96
--- /dev/null
+++ b/AstroEQ/commands.h
@@ -0,0 +1,64 @@
+//_fVal Flag get/set callers -------------------------------------------------
+//
+//Data structure of _fVal Flag:
+//  _fVal = xxxx00ds000g000f where bits:
+//  x = dont care
+//  d = dir
+//  s = stopped
+//  g = goto
+//  f = energised
+//
+//Only dir can be used to set the direction, but stepDir method can be used
+//to returns it in a more useful format
+//
+//----------------------------------------------------------------------------
+#ifndef commands_h
+#define commands_h
+  #if ARDUINO >= 100
+    #include "Arduino.h"
+  #else
+    #include "WProgram.h"
+  #endif
+  
+  #define numberOfCommands 17
+  
+  class Commands{
+    public:
+      void init(unsigned long eVal,unsigned long aVal,unsigned long bVal,byte gVal,unsigned long sVal);
+      
+      //Command definitions
+      static const char command[numberOfCommands][3];
+      
+      //Methods for accessing class variables
+      char stepDir(byte target); //Get Method
+      byte dir(byte target, char dir = 2); //Get Method
+      byte stopped(byte target, byte stopped = 2); //Get Method
+      byte gotoEn(byte target, byte gotoEn = 2); //Get Method
+      byte FVal(byte target, byte FVal = 2); //Get Method
+      unsigned int fVal(byte target); //return the flag to main program
+      unsigned long jVal(byte target, unsigned long jVal = 0x01000000); //Get Method
+      unsigned long IVal(byte target, unsigned long IVal = 0x01000000); //Get Method
+      byte GVal(byte target, byte GVal = 4); //Get Method
+      unsigned long HVal(byte target, unsigned long HVal = 0x01000000); //Get Method
+      unsigned long eVal(byte target); //Get Method
+      unsigned long aVal(byte target); //Get Method
+      unsigned long bVal(byte target); //Get Method
+      byte gVal(byte target); //Get Method
+      unsigned long sVal(byte target); //Get Method
+      
+      char getLength(char cmd, boolean sendRecieve);
+      
+    private:
+      //class variables
+      unsigned long _jVal[2]; //_jVal: Current position
+      unsigned long _IVal[2]; //_IVal: speed to move if in slew mode
+      byte _GVal[2]; //_GVal: slew/goto mode
+      unsigned long _HVal[2]; //_HVal: steps to move if in goto mode
+      unsigned int _flag[2]; //_fVal: 00ds000g000f; d = dir, s = stopped, g = goto, f = energised
+      unsigned long _eVal[2]; //_eVal: Version number
+      unsigned long _aVal[2]; //_aVal: Steps per axis revolution
+      unsigned long _bVal[2]; //_bVal: Sidereal Rate of axis
+      byte _gVal[2]; //_gVal: Speed scalar for highspeed slew
+      unsigned long _sVal[2]; //_sVal: Steps per worm gear revolution
+  };
+#endif
diff --git a/AstroEQ/synta.cpp b/AstroEQ/synta.cpp
new file mode 100644
index 0000000..133ce43
--- /dev/null
+++ b/AstroEQ/synta.cpp
@@ -0,0 +1,149 @@
+
+#include "synta.h"
+
+Synta::Synta(unsigned long eVal,unsigned long aVal,unsigned long bVal,byte gVal,unsigned long sVal){
+  validPacket = 0;
+  _axis = 0;
+  commandIndex = 0;
+  clearBuffer(commandString,sizeof(commandString));
+  cmd.init(eVal, aVal, bVal, gVal, sVal);
+}
+
+const char Synta::startInChar = ':';
+const char Synta::startOutChar = '=';
+const char Synta::errorChar = '!';
+const char Synta::endChar = '\r';
+
+void Synta::error(char* buf){
+  buf[0] = errorChar;
+  buf[1] = endChar;
+  buf[2] = 0;
+}
+
+void Synta::clearBuffer(char* buf, byte len){
+  strncpy(buf,"",len);
+}
+
+void Synta::success(char* buf, char data[], byte dataLen){
+  strcpy(buf + 1,data);
+  buf[0] = startOutChar;
+  buf[dataLen] = endChar;
+  buf[dataLen + 1] = 0;
+}
+
+void Synta::assembleResponse(char* dataPacket, char commandOrError, unsigned long responseData){
+  char replyLength = cmd.getLength(commandOrError,0); //get the number of data bytes for response
+  
+  char tempStr[11];
+  
+  switch (replyLength){
+    case -1:
+        error(dataPacket); //In otherwords, invalid command, so send error
+        return;
+    case 0:
+        clearBuffer(tempStr,sizeof(tempStr)); //empty temporary string
+        break;
+    case 2:
+        byteToHex(tempStr,responseData);
+        break;
+    case 3:
+        intToHex(tempStr,responseData);
+        break;
+    case 6:
+        longToHex(tempStr,responseData);
+        break;
+  }        
+  success(dataPacket,tempStr,replyLength + 1); //compile response
+  return;
+}
+
+boolean Synta::validateCommand(byte len){
+  _command = commandString[0]; //first byte is command
+  _axis = commandString[1] - 49; //second byte is axis
+  if(_axis > 1){
+    return 0; //incorrect axis
+  }
+  char requiredLength = cmd.getLength(_command,1); //get the required length of this command
+  len -= 3; //Remove the command and axis bytes, aswell as the end char;
+  if(requiredLength != len){ //If invalid command, or not required length
+    return 0;
+  }
+  
+  byte i;
+  for(i = 0;i < len;i++){
+    commandString[i] = commandString[i + 2];
+  }
+  commandString[i] = 0; //Null
+  return 1;
+}
+
+char Synta::recieveCommand(char* dataPacket, char character){
+  if(validPacket){
+    if (character == startInChar){
+      goto error; //new command without old finishing! (dataPacket contains error message)
+    }
+    
+    commandString[commandIndex++] = character; //Add character to current string build
+    
+    if(character == endChar){
+      if(validateCommand(commandIndex)){
+        strcpy(dataPacket,commandString); //Return decoded packet
+        validPacket = 0;
+        return 1; //Successful decode (dataPacket contains decoded packet)
+      } else {
+        goto error; //Decode Failed (dataPacket contains error message)
+      }
+    } else if (commandIndex == sizeof(commandString)){
+      goto error; //Message too long! (dataPacket contains error message)
+    }
+  } else if (character == startInChar){
+    //Begin new command
+    commandIndex = 0;
+    validPacket = 1;
+    clearBuffer(commandString,sizeof(commandString));
+  }
+  return 0; //Decode not finished (dataPacket unchanged)
+error:
+  error(dataPacket);
+  validPacket = 0;
+  return -1;
+}
+
+unsigned long Synta::hexToLong(char* hex){
+  char *boo; //waste point for strtol
+  char str[7]; //Destination of rearranged hex
+  strncpy(str,&hex[4],2); //Lower Byte
+  strncpy(str+2,&hex[2],2); //Middle Byte
+  strncpy(str+4,hex,2); //Upper Byte
+  str[6] = 0;
+  return strtol(str,&boo,16); //convert hex to long integer
+}
+
+void Synta::longToHex(char* hex, unsigned long data){
+  byte bytes[3];
+  bytes[0] = (data >> 16) & 0xFF;
+  bytes[1] = (data >> 8) & 0xFF;
+  bytes[2] = (data) & 0xFF;
+  sprintf(hex,"%02X%02X%02X",bytes[2],bytes[1],bytes[0]);
+}
+
+void Synta::intToHex(char* hex, unsigned int data){
+  data &= 0xFFF;
+  sprintf(hex,"%03X",data);
+}
+
+void Synta::byteToHex(char* hex, byte data){
+  data &= 0xFF;
+  sprintf(hex,"%02X",data);
+}
+
+char Synta::command(){
+  return _command;
+}
+
+byte Synta::axis(byte axis){
+  if(axis < 2){
+    _axis = axis;
+  }
+  return _axis;
+}
diff --git a/AstroEQ/synta.h b/AstroEQ/synta.h
new file mode 100644
index 0000000..e3fabb7
--- /dev/null
+++ b/AstroEQ/synta.h
@@ -0,0 +1,44 @@
+
+#ifndef synta_h
+#define synta_h
+  #if ARDUINO >= 100
+    #include "Arduino.h"
+  #else
+    #include "WProgram.h"
+  #endif
+  
+  #include "commands.h"
+  
+  class Synta{
+    public:
+      Synta(unsigned long eVal,unsigned long aVal,unsigned long bVal,byte gVal,unsigned long sVal);
+      Commands cmd;
+      void assembleResponse(char* dataPacket, char commandOrError, unsigned long responseData);
+      char recieveCommand(char* dataPacket, char character);
+      byte axis(byte axis = 2); //make target readonly to outside world.
+      char command(); //make current command readonly to outside world.
+      
+      unsigned long hexToLong(char* hex);
+      void longToHex(char* hex, unsigned long data);
+      void intToHex(char* hex, unsigned int data);
+      void byteToHex(char* hex, byte data);
+    
+    private:
+      void clearBuffer(char* buf, byte len);
+      void success(char* buf, char data[], byte dataLen);
+      void error(char* buf);
+      boolean validateCommand(byte len);
+      
+      boolean validPacket;
+      char commandString[11];
+      char commandIndex;
+      
+      byte _axis;
+      char _command;
+      
+      static const char startInChar;
+      static const char startOutChar;
+      static const char errorChar;
+      static const char endChar;
+  };
+#endif
diff --git a/README b/README
index e69de29..c0edb1d 100644
--- a/README
+++ b/README
@@ -0,0 +1,17 @@
+AstroEQ Arduino code. Hardware schematics to follow
+
+Currently it doesn't track at the correct speed, I am working on it. Will add a ZIP download when it is properly functional.
+
+/*
+  Code written by Thomas Carpenter 2012
+  
+  With thanks Chris over at the EQMOD Yahoo group for explaining the Skywatcher protocol
+  
+  
+  Equatorial mount tracking system for integration with EQMOD using the Skywatcher/Synta
+  communication protocol.
+ 
+  Works with EQ5, HEQ5, and EQ6 mounts
+ 
+  Current Verison: 3.5 
+*/
\ No newline at end of file
-- 
GitLab