/* Version for EQ3 mount with PLEIADES-2 boards. Using ARDUINO UNO Rev. 3 */ #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 int i2c_address; // actual I2C slave device address as sent from client program by USB or Bluetooth int RA_i2c = 65; // right ascension I2C address (for handpad use) int DE_i2c = 66; // declination I2C address (for handpad use) // initial values for standalone operation (without using a computer or a tablet) ..................................... // values are for the modified EQ3 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 = 139.759; // in counts/sec according to the mount properties ( RA gears, reductors, encoder, microsteps...) float RAmaxSlewSpeed = 11150; // maximum right ascension slew speed for simple handpad potentiometer (20 arcmin/s) float RAacceleration = 11150; // RA acceleration : 0 to max speed in 1 sec float RAfinalApproach = -4000; // final appoach in RA is -4000 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 = 27.024; // in counts/sec according to the mount properties ( DE gears, reductors, encoder, microsteps...) float DEmaxSlewSpeed = 4312; // maximum declination slew speed for simple handpad potentiometer (40 arcmin/s) float DEacceleration = 4312 ; // DE acceleration : 0 to max speed in 1 sec float DEfinalApproach = 1000; // final approach in Decl. is +1000 steps (towards north) int invertRA = 8; // invert direction for RA: 0 = no invert, 8 = invert int invertDE = 8; // invert direction for DE: 0 = no invert, 8 = invert 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 button is pushed String DEslewCommand = "&S"; // the slew command when a button is pushed int sw2, sw3, sw4; // HIGH = 0; values at LOW : 1, 2, 4 int sumTrackSw=0, sumTrackSwPrev=0; // sum of switches values to detect if changed int RAselect = 0; int RAselectPrev = 0; int DEselect = 0; int DEselectPrev = 0; int analogValue = 0; // analog value read from the potentiometer on analog pin 0 // ############################################################################################################################ // SETUP // ############################################################################################################################ void setup() { // USB, BLUETOOTH and I2C initializations *********************************************************************************** Serial.begin(115200); // 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(400000); // set i2c clock to high speed: 400000 kHz // digital pins settings **************************************************************************************************** // input pins must use internal pullup resistors because there are no pullups on the board pinMode(2, INPUT_PULLUP); // tracking = ON if LOW pinMode(3, INPUT_PULLUP); // SIDEREAL track speed if HIGH; SOLAR or LUNAR track speed if LOW pinMode(4, INPUT_PULLUP); // SOLAR track speed if HIGH; LUNAR track speed if LOW; valid when pin 3 is LOW pinMode(5, INPUT_PULLUP); // OTA east side if HIGH; OTA west side if LOW pinMode(6, INPUT_PULLUP); // northern hemisphere if HIGH; southern if LOW pinMode(7, INPUT_PULLUP); // USB if HIGH; Bluetooth if LOW delay(1500); // wait for the Skypikits to stabilize after power-on or reset // INITIAL SETUPS FOR STANDALONE OPERATION (using the simple handpad)******************************************************** // Client program could modify those values if (digitalRead(6) == LOW) { invertRA = 8; } else { invertRA = 0; } // hemisphere: HIGH = north, LOW = south if (digitalRead(5) == LOW) { invertDE = 8 ;} else { invertDE = 0; } // OTA side: HIGH = west, LOW = east int RA_polarityBits = B00000100 + invertRA; // 000, free at stop: NO, invert dir: invertRA, enable votlage: 5V, all limit switches: normally open transmitToDeviceAll("&y" + String(RA_polarityBits), RA_i2c, 60); int DE_polarityBits = B00000100 + invertDE; // 000, free at stop: NO, invert dir: invertDE, enable votlage: 5V, all limit switches: normally open 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 // declination motor is a servo motor and needs correct PI values // USER MUST FIND AND MODIFY THOSE VALUES USING THE SKYPIKIT MOTOR TESTER TUNER APPLICATION transmitToDeviceAll("&p40", DE_i2c, 60); // DE proportional gain = 40 transmitToDeviceAll("&i80", DE_i2c, 60); // DE integral gain = 80 transmitToDeviceAll("&e400", DE_i2c, 60); // DE error limit = 400 transmitToDeviceAll("&j1000", DE_i2c, 60); // DE integral limit = 1000 transmitToDeviceAll("&k1000", DE_i2c, 60); // DE integral max speed = 1000 transmitToDeviceAll("&r" + RAsiderealCommand, RA_i2c, 60); // RA sidereal speed transmitToDeviceAll("&r" + DEsiderealCommand, DE_i2c, 60); // 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() { // ************************************************************************************************************************** // READING CONTROLLER BOX TRACKING BUTTONS // ************************************************************************************************************************** if (digitalRead(2)==LOW) { sw2 = 1;} else {sw2 = 0;} // digital pin 2: LOW = track; HIGH = untrack if (digitalRead(3)==LOW) { sw3 = 2;} else {sw3 = 0;} // digital pin 3: LOW = solar-lunar tracking speed; HIGH = sidereal tracking speed if (digitalRead(4)==LOW) { sw4 = 4;} else {sw4 = 0;} // digital pin 4: LOW = lunar tracking speed; HIGH = solar tracking speed (if pin 3 is set to LOW) sumTrackSw = sw2 + sw3 + sw4; if (sumTrackSw != sumTrackSwPrev) // if tracking switches changed { switch (sumTrackSw) { case 0 : trackCommand = "&N"; break; // untrack case 1 : trackCommand = "&t" + RAoffsetSiderealCommand; break; // track sidereal case 2 : trackCommand = "&N"; break; // untrack case 3 : trackCommand = "&t" + RAoffsetSolarCommand; break; // track solar case 4 : trackCommand = "&N"; break; // untrack case 5 : trackCommand = "&t" + RAoffsetSiderealCommand; break; // track sidereal case 6 : trackCommand = "&N"; break; // untrack case 7 : 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); } // 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) } } sumTrackSwPrev = sumTrackSw; // for rhe next iteration // ************************************************************************************************************************** // READIND SIMPLE HANDPAD BUTTONS // ************************************************************************************************************************** // DETECT AND EXECUTE CHANGES ON SIMPLE HANDPAD 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 HANDPAD 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 // ************************************************************************************************************************** // 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 control } // end of main loop // ############################################################################################################################ // FUNCTIONS // ############################################################################################################################ // **************************************************************************************************************************** // 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 } // **************************************************************************************************************************** // 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]); } // write the command string ( ex. "&G-4000"), skip the first char. of st 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 // 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)); }