//------------------------------------------------------------------ // Focusser // Dave Wells // Thanks for code snippets & inspiration: // Gina (Stargazers Lounge) // //------------------------------------------------------------------ const boolean debug = false; // Turn debug on & off - should be off if using ASCOM const String programName = "Arduino Focuser"; const String programVersion = "0.01"; const int motorPins[4] = {7,8,9,10}; // Declare pins to drive motor control board const int switch5Pins[3] = {2,3,4}; // Declare pins to read switch output on const int motorSpeedLo = 16000; // Motor step delay for Lo speed (uS) const int motorSpeedHi = 4000; // Motor step delay for Hi speed (uS) const int motorSpeedDefault = 65535; // Default motor step speed (uS)(failsafe operation) const int speedupThreshold = 2000; // Hi speed increases if stepCount exceeds this value const int speedupFactor = 3; // Hi speed increased by this factor (speedup) // lookup table to drive motor control board const int stepPattern[8] = {B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001}; int switch5Values[3]; // array to store switch input int switch5ValuesPrev[3]; // array to store previous switch input int motorSpeed; // current delay for motor step speed (uS) int stepCount; // count steps for auto-speedup // booleans to store switch states boolean switchCentre; // switch is in the center (true/false) boolean switchClockwise; // switch says go clockwise (true/false) boolean switchAntiClockwise; // switch says go anticlockwise (true/false) boolean switchHi; // switch says go fast (true/false) boolean switchLo; // switch says go slow (true/false) // variables for rotary encoder const int encoderPin1 = 5; // encoder Pin1 const int encoderPin2 = 6; // encoder Pin2 const int dirAntiClockwise = 0x10; // result for AntiClockwise motion const int dirClockwise = 0x20; // result for Clockwise motion volatile unsigned char state = 0; // stores the state of the rotary encoder const int steps_per_click = 4; // steps to move stepper for each click of the rotary encoder // state table for rotary encoder const unsigned char ttable[6][4] = { {0x3 , 0x2, 0x1, 0x0}, {0x23, 0x0, 0x1, 0x0}, {0x13, 0x2, 0x0, 0x0}, {0x3 , 0x5, 0x4, 0x0}, {0x3 , 0x3, 0x4, 0x10}, {0x3 , 0x5, 0x3, 0x20} }; // For ASCOM connection String inputString = ""; // a string to hold incoming data boolean stringComplete = false; // whether the string is complete //------------------------------------------------------------------ // Read 5-position switch pins //------------------------------------------------------------------ void readSwitch() { //read the switch values for (int i=0; i<3; i++) { switch5Values[i] = digitalRead(switch5Pins[i]); } } //------------------------------------------------------------------ //------------------------------------------------------------------ // Calculate 5-position switch position //------------------------------------------------------------------ void decodeSwitch() { if (switchMoved()) { if (debug) { Serial.print(" Moved "); printSwitch(); } stepCount = 0; if ((switch5Values[0] == 0) && (switch5Values[2] == 1)) { switchCentre = false; switchAntiClockwise = false; switchClockwise = true; } if ((switch5Values[0] == 1) && (switch5Values[2] == 0)) { switchCentre = false; switchAntiClockwise = true; switchClockwise = false; } if ((switch5Values[0] == 1) && (switch5Values[2] == 1)) { switchCentre = true; switchAntiClockwise = false; switchClockwise = false; } if (switch5Values[1] == 0) switchLo = true; else switchLo = false; if (switch5Values[1] == 1) switchHi = true; else switchHi = false; } } //------------------------------------------------------------------ //------------------------------------------------------------------ // Return true if switch has been moved //------------------------------------------------------------------ boolean switchMoved() { for (int i=0; i<3; i++) { if (switch5Values[i] != switch5ValuesPrev[i]) { for (int j=0; j<3; j++) { switch5ValuesPrev[j] = switch5Values[j]; } return true; } } return false; } //------------------------------------------------------------------ //------------------------------------------------------------------ // read rotary encoder and determine state change //------------------------------------------------------------------ unsigned char rotary_process() { unsigned char pinstate = (digitalRead(encoderPin2) << 1) | digitalRead(encoderPin1); state = ttable[state & 0xf][pinstate]; return (state & 0x30); } //------------------------------------------------------------------ //------------------------------------------------------------------ // Move the motor according to values set by switch position //------------------------------------------------------------------ void motorAction() { if (switchCentre) { // Switch is centred so de-energise the motor outputs clearOutput(); // if rotary encoder is moved then nudge motor in appropriate direction unsigned char result = rotary_process(); if (result) { if (debug) { Serial.println(state); Serial.println(result == dirAntiClockwise ? "ANTICLOCKWISE" : "CLOCKWISE"); } for (int i = 0; i < steps_per_click; i++) { if (result == dirAntiClockwise) { anticlockwise(); } else { clockwise(); } } } // Check for any actions from ASCOM ascom_action(); } else { // Set motor movement values according to switch position if (switchLo) { motorSpeed = motorSpeedLo; } else if (switchHi) { motorSpeed = motorSpeedHi; } else { motorSpeed = motorSpeedDefault; } // Adjust the Hi speed upwards if the threshold is crossed if (switchHi && (stepCount > speedupThreshold)) { motorSpeed /= speedupFactor; } // move 1 step in the correct direction at the correct speed if (switchClockwise) { clockwise(); } else { anticlockwise(); } } } //------------------------------------------------------------------ //------------------------------------------------------------------ // Output 5-position switch values to serial monitor for debugging //------------------------------------------------------------------ void printSwitch () { //print out the value of the pushbutton for (int i=0; i<3; i++) { Serial.print(switch5Values[i]); } if (switchCentre) { Serial.print(" Centre "); } else { if (switchClockwise) Serial.print(" Clockwise "); if (switchAntiClockwise) Serial.print(" Anticlockwise "); if (switchLo) Serial.print(" Lo "); if (switchHi) Serial.print(" Hi "); } Serial.print(stepCount); Serial.print(" "); Serial.print(motorSpeed); Serial.println(); } //------------------------------------------------------------------ //------------------------------------------------------------------ // Move stepper anticlockwise //------------------------------------------------------------------ void anticlockwise() { for(int i = 0; i < 8; i++) { setOutput(i); delayMicroseconds(motorSpeed); stepCount++; } } //------------------------------------------------------------------ //------------------------------------------------------------------ // Move stepper clockwise //------------------------------------------------------------------ void clockwise() { for(int i = 7; i >= 0; i--) { setOutput(i); delayMicroseconds(motorSpeed); stepCount++; } } //------------------------------------------------------------------ //------------------------------------------------------------------ // Set output pins for stepper //------------------------------------------------------------------ void setOutput(int out) { for (int i=0; i<4; i++) { digitalWrite(motorPins[i], bitRead(stepPattern[out], i)); } } //------------------------------------------------------------------ //------------------------------------------------------------------ // Clear output pins for stepper // To ensure they are not left on when switch is in centre //------------------------------------------------------------------ void clearOutput() { for (int i=0; i<4; i++) { digitalWrite(motorPins[i], 0); } } //------------------------------------------------------------------ //------------------------------------------------------------------ // ASCOM action - only executed when switch is in centre postion //------------------------------------------------------------------ void ascom_action() { // process the command string when a hash arrives: if (stringComplete) { serialCommand(inputString); // clear the command string: inputString = ""; stringComplete = false; } } //------------------------------------------------------------------ //------------------------------------------------------------------ // ASCOM Serial Commands //------------------------------------------------------------------ void serialCommand(String command) { //String param = command.substring(2); switch(command.charAt(0)) { case '#': // Confirm Connection Serial.print("OK!#"); break; case 'A': // Step Anti-clockwise { int hashpos = command.indexOf('#'); // position of hash in string String stepsS = command.substring(1,hashpos); int stepsI = stepsS.toInt(); Serial.print("A" + stepsS + ":OK#"); for (int i = 0; i < stepsI; i++) { anticlockwise(); } break; } case 'C': // Step Clockwise { int hashpos = command.indexOf('#'); // position of hash in string String stepsS = command.substring(1,hashpos); int stepsI = stepsS.toInt(); Serial.print("C" + stepsS + ":OK#"); for (int i = 0; i < stepsI; i++) { clockwise(); } break; } case 'V': // Get Version Serial.print( programName + " V" + programVersion + "#"); break; default: Serial.print("ERR#"); break; } //Serial.print("#"); } //------------------------------------------------------------------ //------------------------------------------------------------------ // Setup //------------------------------------------------------------------ void setup() { // Declare the stepper motor pins as outputs for (int i=0; i<4; i++) { pinMode(motorPins[i], OUTPUT); } // Configure 5-way switch pins as inputs // Enable the internal pull-up resistors for (int i=0; i<3; i++) { pinMode(switch5Pins[i], INPUT_PULLUP); } // Declare rotary encoder pins as inputs // Enable the internal pull-up resistors pinMode(encoderPin1, INPUT_PULLUP); pinMode(encoderPin2, INPUT_PULLUP); stepCount = 0; clearOutput(); //if (debug) // Serial.begin(9600); // initialize serial for ASCOM Serial.begin(9600); // reserve 200 bytes for the ASCOM driver inputString: inputString.reserve(200); } //------------------------------------------------------------------ //------------------------------------------------------------------ // Main Loop //------------------------------------------------------------------ void loop() { readSwitch(); // Read the 5-way switch inputs decodeSwitch(); // Decode switch inputs to booleans motorAction(); // Take actions with motor based on booleans } //------------------------------------------------------------------ //------------------------------------------------------------------ // SerialEvent occurs whenever new data comes in the serial RX. //------------------------------------------------------------------ void serialEvent() { while (Serial.available()) { // get the new byte: char inChar = (char)Serial.read(); // add it to the inputString: inputString += inChar; // if incoming character is hash, set flag so main loop can action it if (inChar == '#') { stringComplete = true; } } } //------------------------------------------------------------------