/* Version for LX200GPS mount with PLEIADES-2 boards, and with LX200GPS motor driver boards inside the mount. No focuser controll for this version. Using ARDUINO UNO Rev. 3 Version 2019-03-22 By Jean Vallieres */ #include // for I2C communication #include // for the strings #include // for Bluetooth // CONSTANTS and GLOBAL VARIABLES ############################################################################################ String inputStringBT = ""; // a string to hold incoming data from the tablet String inputStringUSB = ""; // a string to hold incoming data from the computer boolean stringCompleteBT = false; // whether the input string (from the tablet) is complete boolean stringCompleteUSB = false; // whether the input string (from the computer) is complete int ttBT = 1; // =1 to print characters to Bluetooth serial port int ttUSB = 1; // =1 to print characters to USB serial port int len, i; SoftwareSerial mySerial(10, 11); // (RX, TX), for the tablet handpad by Bluetooth char i2c_address; // real I2C address of the SKYPIKIT controller int RA_i2c = 65; // right ascension I2C address (for handpad use) int DE_i2c = 66; // declination I2C address (for handpad use) int FC_i2c = 67; // focuser I2C address (for handpad use) // initial values for standalone operation (without using a computer or a tablet) ..................................... // values are for the modified LX200GPS mount; // USER MAY CALCULATE THOSE VALUES WITH THE SKYPIKIT MOTOR TESTER TUNER APPLICATION AND MODIFY THOSE VALUES AFTER MAKING TESTS ON THE SKY .... float RAsiderealSpeed = 106.9587; // in counts/sec according to the mount properties ( RA gears, reductors, encoder, microsteps...) float RAmaxSlewSpeed = 25600; // maximum right ascension slew speed for simple handpad potentiometer (1°/s) float RAacceleration = 12800; // RA acceleration : 0 to max speed in 2 sec float RAfinalApproach = -1000; // final appoach in RA is -1000 steps (towards west, same direction as tracking) float RAoffsetSolarTrackSpeed = RAsiderealSpeed * (+0.00273); // offset tracking speed for solar tracking float RAoffsetLunarTrackSpeed = RAsiderealSpeed * (+0.03660); // offset tracking speed for lunar tracking float DEsiderealSpeed = 106.9587; // in counts/sec according to the mount properties ( DE gears, reductors, encoder, microsteps...) float DEmaxSlewSpeed = 25600; // maximum declination slew speed for simple handpad potentiometer (1°/s) float DEacceleration = 12800; // DE acceleration : 0 to max speed in 2 sec float DEfinalApproach = 1000; // final approach in Decl. is +1000 steps (towards north) int invertRA = 0; // invert direction for RA: 0 = no invert, 8 = invert int invertDE = 0; // invert direction for DE: 0 = no invert, 8 = invert int trackingMode = 0; // 0 = not tracking bool trackModeChanged = false; // if trackingMode has been changed with the red button String trackCommand = ""; // the selected track command String RAsiderealCommand = String(round(RAsiderealSpeed * 1000.000)); // 1000 times the sidereal tracking speed String DEsiderealCommand = String(round(DEsiderealSpeed * 1000.000)); // 1000 times the sidereal tracking speed String RAoffsetSiderealCommand = "0"; // 1000 times the offset sidereal speed String RAoffsetSolarCommand = String(round(RAoffsetSolarTrackSpeed * 1000)); // 1000 times the offset solar speed String RAoffsetLunarCommand = String(round(RAoffsetLunarTrackSpeed * 1000)); // 1000 times the offset lunar speed String RAfinalAppCommand = String(round(RAfinalApproach)); // final appoach is -4000 in RA (towards west, same direction as tracking) String DEfinalAppCommand = String(round(DEfinalApproach)); // final approach is +1000 in Decl. (towards north) String RAaccelCommand = String(round(RAacceleration)); String DEaccelCommand = String(round(DEacceleration)); String RAslewCommand = "&S"; // the slew command when a RA button is pushed (default to Stop) String DEslewCommand = "&S"; // the slew command when a DE button is pushed (default to Stop) int analogValue = 0; // analog value read from the simple handbox potentiometer on analog pin 0 int RAselect = 0; int RAselectPrev = 0; int DEselect = 0; int DEselectPrev = 0; // SETUP ##################################################################################################################### void setup() { // USB and I2C initializations *********************************************************************************** Serial.begin(57600); // start serial communication at 57600 bps for the computer connected on USB port while (!Serial) { ; } // wait for serial port to connect. Needed for Leonardo only mySerial.begin(57600); // for the tablet connected with Bluetooth inputStringBT.reserve(64); // reserve 64 bytes for the inputString: inputStringUSB.reserve(64); // reserve 64 bytes for the inputString: Wire.begin(); // join i2c bus (address is optional for master) Wire.setClock(100000); // set Arduino Uno I2C to high speed: 100 kHz // digital pins settings **************************************************************************************************** // input pins must use internal pullup resistors because there are no pullups on the board pinMode(2, INPUT_PULLUP); // Track push button : LOW when pushed pinMode(3, OUTPUT); // LUNAR LED track speed pinMode(4, OUTPUT); // SOLAR LED track speed pinMode(5, OUTPUT); // SIDEREAL LED track speed // initialize tracking at OFF digitalWrite(3, LOW); digitalWrite(4, LOW); digitalWrite(5, LOW); pinMode(7, INPUT_PULLUP); // SW7 : USB if HIGH; Bluetooth if LOW delay(2000); // wait for the SKYPIKITs to stabilize after power-on or reset // INITIAL SETUPS FOR STANDALONE OPERATION (using the simple handbox)******************************************************** // Client program could modify those values invertRA = 8; // hemisphere: 8 = north, 0 = south invertDE = 8; // OTA side: 8 = west, 0 = east (not used because fork mount) int RA_polarityBits = B00000100 + invertRA; // 000, free at stop: NO, invert dir: YES, enable votlage: 5V, all limit switches: normally open or not used transmitToDeviceAll("&y" + String(RA_polarityBits), RA_i2c, 60); int DE_polarityBits = B00000100 + invertDE; // 000, free at stop: NO, invert dir: YES, enable votlage: 5V, all limit switches: normally open or not used transmitToDeviceAll("&y" + String(DE_polarityBits), DE_i2c, 60); transmitToDeviceAll("&a" + RAaccelCommand, RA_i2c, 60); // RA acceleration : 0 to max speed in 1 sec) transmitToDeviceAll("&a" + DEaccelCommand, DE_i2c, 60); // DE acceleration : 0 to max speed in 1 sec) transmitToDeviceAll("&f" + RAfinalAppCommand, RA_i2c, 60); // RA final approach in steps transmitToDeviceAll("&f" + DEfinalAppCommand, DE_i2c, 60); // DE final approach in steps // R.A. and declination motors are servo motors and need correct PI values // USER MUST FIND AND MODIFY THOSE VALUES USING THE SKYPIKIT MOTOR TESTER TUNER APPLICATION transmitToDeviceAll("&p7", RA_i2c, 60); // RA proportional gain = 7 transmitToDeviceAll("&i112", RA_i2c, 60); // RA integral gain = 112 transmitToDeviceAll("&e12800", RA_i2c, 60); // RA error limit = 12800 transmitToDeviceAll("&j12800", RA_i2c, 60); // RA integral limit = 12800 transmitToDeviceAll("&k12800", RA_i2c, 60); // RA integral max speed = 12800 transmitToDeviceAll("&p7", DE_i2c, 60); // DE proportional gain = 7 transmitToDeviceAll("&i112", DE_i2c, 60); // DE integral gain = 112 transmitToDeviceAll("&e12800", DE_i2c, 60); // DE error limit = 12800 transmitToDeviceAll("&j12800", DE_i2c, 60); // DE integral limit = 12800 transmitToDeviceAll("&k12800", DE_i2c, 60); // DE integral max speed = 12800 transmitToDeviceAll("&r" + RAsiderealCommand, RA_i2c, 60); // set RA sidereal speed transmitToDeviceAll("&r" + DEsiderealCommand, DE_i2c, 60); // set DE sidereal speed transmitToDeviceAll("&t0", RA_i2c, 60); // RA offset tracking speed = 0 transmitToDeviceAll("&t0", DE_i2c, 60); // DE offset tracking speed = 0 transmitToDeviceAll("&g" + RAsiderealCommand, RA_i2c, 60); // RA guiding speed = 100% RA sidereal speed transmitToDeviceAll("&g" + DEsiderealCommand, DE_i2c, 60); // DE guiding speed = 100% DE sidereal speed transmitToDeviceAll("&C1", RA_i2c, 60); // connect RA motor in sidereal reference system transmitToDeviceAll("&C0", DE_i2c, 60); // connect DE motor in fixed reference system } // end setup // MAIN LOOP ################################################################################################################# void loop() { // Set trackingMode with the simple handbox red push button ******************************************** if ((digitalRead(2) == LOW) &&(digitalRead(5) == LOW) && (digitalRead(4) == LOW) && (digitalRead(3) == LOW)) { delay(500); digitalWrite(5, HIGH); delay(500); trackingMode = 1; // sidereal trackModeChanged = true; } if ((digitalRead(2) == LOW) && (digitalRead(5) == HIGH)) { delay(500); digitalWrite(5, LOW); digitalWrite(4, HIGH); delay(500); trackingMode = 2; // solar trackModeChanged = true; } if ((digitalRead(2) == LOW) && (digitalRead(4) == HIGH)) { delay(500); digitalWrite(4, LOW); digitalWrite(3, HIGH); delay(500); trackingMode = 3; // lunar trackModeChanged = true; } if ((digitalRead(2) == LOW) && (digitalRead(3) == HIGH)) { delay(500); digitalWrite(5, LOW); digitalWrite(4, LOW); digitalWrite(3, LOW); delay(500); trackingMode = 0; // off trackModeChanged = true; } // SET TRACKING MODE ACCORDING to trackingMode ******************************************* if (trackModeChanged) { trackModeChanged = false; switch (trackingMode) { case 0 : trackCommand = "&N"; break; // untrack case 1 : trackCommand = "&t" + RAoffsetSiderealCommand; break; // track sidereal case 2: trackCommand = "&t" + RAoffsetSolarCommand; break; // track solar case 3 : trackCommand = "&t" + RAoffsetLunarCommand; break; // track lunar default: trackCommand = "&N"; break; // untrack } transmitToDeviceAll(trackCommand, RA_i2c, 60); // track or untrack in right ascension if (trackCommand == "&N") { transmitToDeviceAll("&N", DE_i2c, 60); } // also untrack in declination else { transmitToDeviceAll("&T", RA_i2c, 60); // begin tracking in R.A. transmitToDeviceAll("&t0", DE_i2c, 60); transmitToDeviceAll("&T", DE_i2c, 60); // track at speed zero in declination (to enable guiding) } } // ************************************************************************************************************************** // READIND SIMPLE HANDBOX MOVE BUTTONS // ************************************************************************************************************************** // DETECT AND EXECUTE CHANGES ON SIMPLE HANDBOX RIGHT ASCENSION BUTTONS ***************************************************** analogValue = analogRead(1); // right ascension buttons on analog pin 1 if (analogValue < 256) { RAselect = 0; } // no RA button pressed else if (analogValue < 768) { RAselect = 1; } // west RA button pressed else { RAselect = 2; } // east RA button pressed if (RAselect != RAselectPrev) { switch (RAselect) { case 0 : RAslewCommand = "&S"; break; // stop case 1 : RAslewCommand = "&M-" + handpadSpeedValue(1); break; // slew west case 2 : RAslewCommand = "&M+" + handpadSpeedValue(1); break; // slew east default : RAslewCommand = "&S"; break; // stop } transmitToDeviceAll(RAslewCommand, RA_i2c, 60); // transmits the command to the RA device } RAselectPrev = RAselect; // for rhe next iteration // DETECT AND EXECUTE CHANGES ON SIMPLE HANDBOX DECLINATION BUTTONS ********************************************************* analogValue = analogRead(2); // declination buttons on analog pin 2 if (analogValue < 256) { DEselect = 0; } // no decl. button pressed else if (analogValue < 768) { DEselect = 1; } // south decl. button pressed else { DEselect = 2; } // north decl. button pressed if (DEselect != DEselectPrev) { switch (DEselect) { case 0 : DEslewCommand = "&S"; break; // stop case 1 : DEslewCommand = "&M-" + handpadSpeedValue(2); break; // slew south case 2 : DEslewCommand = "&M+" + handpadSpeedValue(2); break; // slew north default : DEslewCommand = "&S"; break; // stop } transmitToDeviceAll(DEslewCommand, DE_i2c, 60); // transmits the command to the DE device } DEselectPrev = DEselect; // for rhe next iteration /* // DETECT AND EXECUTE CHANGES ON SIMPLE HANDBOX FOCUSER BUTTONS ********************************************************* analogValue = analogRead(3); // focuser buttons on analog pin 3 if (analogValue < 256) { FCselect = 0; } // no focuser button pressed else if (analogValue < 768) { FCselect = 1; } // out button pressed else { FCselect = 2; } // in button pressed if (FCselect != FCselectPrev) { switch (FCselect) { case 0 : FCslewCommand = "&S"; break; // stop case 1 : FCslewCommand = "&M-" + handpadSpeedValue(3); break; // slew out case 2 : FCslewCommand = "&M+" + handpadSpeedValue(3); break; // slew in default : FCslewCommand = "&S"; break; // stop } transmitToDeviceAll(FCslewCommand, FC_i2c, 60); // transmits the command to the FC device } FCselectPrev = FCselect; // for rhe next iteration */ // ************************************************************************************************************************** // READING COMMANDS FROM COMPUTER BY USB OR FROM TABLET BY BLUETOOTH // ************************************************************************************************************************** if (digitalRead(7) == LOW) // CONTROL BY BLUETOOTH *********************************************************************** { while ((mySerial.available())&&(!stringCompleteBT)) { char inChar = (char)mySerial.read(); // get the new byte inputStringBT += inChar; // add it to the inputStringBT if ((inChar == ':')||(inChar == '#')) { stringCompleteBT = true; } // first and last char for SKYPIKIT protocol commands } if (stringCompleteBT) // send the command when stringCompleteBT { i2c_address = inputStringBT[0]; // first char of string gives real SKYPIKIT I2C address (value 64 to 79) if ((inputStringBT[1] == '&')&&(inputStringBT[2] == 'Q')) // this is a request command (&Q0 or &Q1) { transmitToDeviceSkipFirst(inputStringBT, i2c_address, 60); // send the command to the SKYPIKIT at i2c_address, then wait 60ms Wire.requestFrom(i2c_address, 36); // request the status string from the SKYPIKIT I2C slave device, maximum of 36 bytes while (Wire.available()) // as long as there are characters sent from the SKYPIKIT { char c = Wire.read(); // receive one character from the SKYPIKIT if (ttBT == 1) { mySerial.print(c); // and send this character to the computer by BT } if (c == '\0') {ttBT = 0;} // detect the last NULL character and do not send the next characters } } else // this is not a request command { transmitToDeviceSkipFirst(inputStringBT, i2c_address, 0); // send the command to the SKYPIKIT at i2c_address } inputStringBT = ""; // clear the string: stringCompleteBT = false; ttBT = 1; } } // end of BT controll else // CONTROL BY USB (when digital pin 7 is HIGH) ********************************************************************* { while ((Serial.available())&&(!stringCompleteUSB)) { char inChar = (char)Serial.read(); // get the new byte inputStringUSB += inChar; // add it to the inputStringUSB if ((inChar == ':')||(inChar == '#')) { stringCompleteUSB = true; } // first and last char for SKYPIKIT protocol commands } if (stringCompleteUSB) // send the command when stringCompleteUSB { i2c_address = inputStringUSB[0]; // first char of string gives real SKYPIKIT I2C address (value 64 to 79) if (inputStringUSB[0] == '@') // this is a test command (@&Q0) at address 64+0 { Serial.print('O'); Serial.print('K'); Serial.print('#'); // print "OK" } else if ((inputStringUSB[1] == '&')&&(inputStringUSB[2] == 'Q')) // this is a request command (&Q0 or &Q1) { transmitToDeviceSkipFirst(inputStringUSB, i2c_address, 60); // *** send the command to the SKYPIKIT at i2c_address, then wait 60ms Wire.requestFrom(i2c_address, 36); // request the status string from the SKYPIKIT I2C slave device, maximum of 36 bytes while (Wire.available()) // as long as there are characters sent from the SKYPIKIT { char c = Wire.read(); // receive one character from the SKYPIKIT if (ttUSB == 1) { if (c == '\0') { Serial.print('#'); } // for compatibilty with Skypikit ASCOM driver end of line char else { Serial.print(c); } // and send this character to the computer on USB port } if (c == '\0') {ttUSB = 0;} // detect the last NULL character and do not send the next characters } } else // this is not a request command { transmitToDeviceSkipFirst(inputStringUSB, i2c_address, 0); // send the command to the SKYPIKIT at i2c_address } inputStringUSB = ""; // clear the string: stringCompleteUSB = false; ttUSB = 1; } } // end of USB controll } // end of main loop // FUNCTIONS ################################################################################################################# // **************************************************************************************************************************** // Transmit the command string st to the SKYPIKIT device at address i2c_addr, end with a delay in ms // Skip the first Character (normally the address char) // **************************************************************************************************************************** void transmitToDeviceSkipFirst(String st, int i2c_addr, int afterdelay) { Wire.beginTransmission(i2c_addr); // default i2c_addr = 65 for RA, 66 for DE, 67 for focuser len = st.length(); for (i = 1; i < len; i++) { Wire.write(st[i]); } // skip the first char. of st and write the command string ( ex. "&G-4000") Wire.write(0); // write a NULL char at the end of the string Wire.endTransmission(); // stop transmitting delay(afterdelay); // delay in ms } // **************************************************************************************************************************** // Transmit all the command string st to the Skypikit device at address i2c_addr, end with a delay in ms // **************************************************************************************************************************** void transmitToDeviceAll(String st, int i2c_addr, int afterdelay) { Wire.beginTransmission(i2c_addr); // default i2c_addr = 65 for RA, 66 for DE, 67 for focuser len = st.length(); for (i = 0; i < len; i++) { Wire.write(st[i]); } // write the command string ( ex. "&G-4000", len = 7) Wire.write(0); // write a NUL char at the end of the string Wire.endTransmission(); // stop transmitting delay(afterdelay); // delay in ms } // **************************************************************************************************************************** // Return a string giving the speed value according to the device (1=RA, 2=DE, 3=focuser) // and to the potentiometer value on analog pin 0 . // **************************************************************************************************************************** String handpadSpeedValue(int device) { float potValue = analogRead(0); // simple handpad potentiometer value on pin A0 : 0 to 1023 (10 bits) float slewSpeedValue; // slew speed value to send to the Skypikit float maxSlowSpeed; // 4 times the sidereal speed float maxMidSpeed; // 16 times the sidereal speed float maxFastSpeed; // the max slew speed (for example: 1°/s) if (device == 1) { maxSlowSpeed = RAsiderealSpeed * 4; maxMidSpeed = RAsiderealSpeed * 16; maxFastSpeed = RAmaxSlewSpeed; } // RA motor else if (device == 2) { maxSlowSpeed = DEsiderealSpeed * 4; maxMidSpeed = DEsiderealSpeed * 16; maxFastSpeed = DEmaxSlewSpeed; } // DE motor //else if (device == 3) { maxSlowSpeed = FCsiderealSpeed * 4; maxMidSpeed = FCsiderealSpeed * 16; maxFastSpeed = FCmaxSlewSpeed; } // focuser motor // slow mode if pot is below the 340 position : speed = 0 to 4 times the sidereal speed if (potValue < 340) { slewSpeedValue = maxSlowSpeed * potValue/340; } // mid mode if pot is between 340 and 680 position : speed = 4 to 16 times the sidereal speed else if (potValue < 680) { slewSpeedValue = maxSlowSpeed + ((maxMidSpeed - maxSlowSpeed) * (potValue-340)/340); } // fast mode if pot is above the 680 position : speed = 16*siderealSpeed to maxFastSpeed else { slewSpeedValue = maxMidSpeed + ((maxFastSpeed - maxMidSpeed) * (potValue-680)/344); } return String(round(slewSpeedValue)); }