Jump to content
 

luce001

Members
  • Posts

    1
  • Joined

  • Last visited

Recent Profile Visitors

50 profile views

luce001's Achievements

0

Reputation

  1. Firstly I would like to thank Ray for his hard work in starting off this excellent post! also for Eric and friends for the various coding updates.. I have made a few changes to the code and managed to get a working turntable on my layout, including an OLED display to show the position and head/tail indication. I only have 4 positions on my turntable and I didn't use the servo brake or manual pot but left these functions in. The display I used was a 1.3" I2C IIC Serial 128X64 OLED LCD LED Display Module Digital for Arduino W which can be obtained easily on eBay... the driver for this is U8glib.h https://code.google.com/p/u8glib/ and the other components as mentioned by others I ordered from Proto-pic. The revised coding is as follows, pictures to follow... Jonathan //////////////////////////////////////////////////////////////////////////////// // // DCC Turntable Rev 17 9 Jan 2015 // based on code from rmweb.co.uk forum - edited by J.Luce // Program functions: // "Real World" interface // 2- N.O. Push buttons to enable move to "Head" or "Tail" // 1- 10k Continuous turn POT to select track. // 1- N.O. Reset button on fascia // Use digital inputs on 6 & 7 to stepforward and backward and // read out in the serial monitor so that you can program easier. // Release & Servo Brake function works with brake and release function. // Route positioning not taking shortest route to next location- (i.e. Tk.1 to Tk. 10 would be through 0) // Always approaches a track from the same direction to account for gear/mechanical lash/slop. // if the table was going CW to pos. 1 it approaches value B and stops // if the table was going CCW to Pos 1 it should go past B a (value B-X) and then go CW to B. // Added a (commented out)set up tool in the "loop" section of the code to set up the POT on the fascia panel. // this let's you align the POT to the markings on the panel. Uncomment to use, then re-comment to hide. // Set up tool in the "loop" section of the code to set up the Brake Servo using the POT on the fascia panel. // this let's you find the optimal Release and Brake position for the servo brake. Uncomment to use, then re-comment to hide. // Added u8g display to show track position and heading, initialise at startup // Move to track 0 "Head" at startup //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // DCC Turntable Control Routines #include <DCC_Decoder.h> #include <AccelStepper.h> #include <Wire.h> #include <Adafruit_MotorShield.h> #include "utility/Adafruit_PWMServoDriver.h" #include <Servo.h> //servo library reference #include "U8glib.h" // setup u8g object, please remove comment from one of the following constructor calls // IMPORTANT NOTE: The following list is incomplete. The complete list of supported // devices with all constructor calls is here: http://code.google.com/p/u8glib/wiki/device U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST); // Dev 0, Fast I2C / TWI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Defines and structures // #define kDCC_INTERRUPT 0 typedef struct { int address; // Address to respond to } DCCAccessoryAddress; DCCAccessoryAddress gAddresses[5]; // Allows 4 dcc addresses: [XX] = number of addresses you need (including 0). const unsigned long releaseTimeout_ms = 2000; //reduced to 2 seconds for now const long MOTOR_STEP_COUNT = 200 * 16; //number of steps for a full rotation //new stuff boolean tableTargetHead = false; //the new variable for Head/Tail Position either from DCC or Analog int tableTargetPosition = 0; //the new variable for Table Postition either from DCC or Analog boolean newTargetLocation = false; boolean buttonPushedHead = false; boolean buttonPushedTail = false; boolean inMotionToNewTarget = false; boolean isReleased = false; // isReleased tries to make sure the motor is not continuously released unsigned long stepperLastMoveTime = 0; const long MOTOR_OVERSHOOT = 10; // the amount of overshoot/ lash correction when approaching from CCW int overshootDestination = -1; //Servo Stuff Servo brakeservo; // create servo object to control a servo const int servoBrake = 9; //value for brake position const int servoRelease = 2; //value for release position //Programming button variables boolean programmingMode = false; boolean programmingModeMoveForward = true; //If in programming mode, are we moving forward? unsigned long programmingLastMoveMillis = 0; //When was the last programming move done? const int programRotateDelay = 100; //Delay between steps in ms while holding button //location variables const int A = 144; //TT Track 0- Head const int B = 328; //TT Track 1- Head const int C = 482; //TT Track 2- Head const int D = 632; //TT Track 3- Head const int N = 1745; //TT Track 0- Tail const int O = 1928; //TT Track 1- Tail const int P = 2080; //TT Track 2- Tail const int Q = 2230; //TT Track 3- Tail const int PositionTrackHead[] = { A, B, C, D }; const int PositionTrackTail[] = { N, O, P, Q }; //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // Adafruit Setup Adafruit_MotorShield AFMStop(0x60); // Default address, no jumpers // Connect stepper with 200 steps per revolution (1.8 degree) // to the M3, M4 terminals (blue,yellow,green,red) Adafruit_StepperMotor *myStepper2 = AFMStop.getStepper(200, 2); // you can change these to SINGLE, DOUBLE, INTERLEAVE or MICROSTEP! // wrapper for the motor! (3200 Microsteps/revolution) void forwardstep2() { myStepper2->onestep(BACKWARD, MICROSTEP); } void backwardstep2() { myStepper2->onestep(FORWARD, MICROSTEP); } void release2() { myStepper2->release(); } // Now we'll wrap the stepper in an AccelStepper object AccelStepper stepper2 = AccelStepper(forwardstep2, backwardstep2); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Decoder Initiation // void ConfigureDecoder() { //Put all the decoder #'s you need here. Remember to change //DCCAccessoryAddress gAddresses[XX];(above) where XX = number of addresses you need. gAddresses[0].address = 200; gAddresses[1].address = 201; gAddresses[2].address = 202; gAddresses[3].address = 203; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Basic accessory packet handler // void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data) { // Convert NMRA packet address format to human address address -= 1; address *= 4; address += 1; address += (data & 0x06) >> 1; boolean enable = (data & 0x01) ? 1 : 0; for(int i=0; i<(int)(sizeof(gAddresses)/sizeof(gAddresses[0])); i++) { if( address == gAddresses[i].address ) { Serial.println(""); Serial.print("DCC addr: "); Serial.print(address,DEC); Serial.print(" Head/Tail = (1/0) : "); Serial.println(enable,DEC); //new stuff tableTargetHead = enable; tableTargetPosition = i; //picture loop u8g.firstPage(); do { draw(tableTargetPosition, tableTargetHead); } while( u8g.nextPage() ); //New packet and we have a new target location, set the flag newTargetLocation = true; doStepperMove(); // old location for if/else statements } } } ///////////////////////////////////////////////////////////////////////////// // // draw ug8 display // void draw(int address, boolean enable) { // graphic commands to redraw the complete screen should be placed here u8g.setFont(u8g_font_unifont); u8g.setPrintPos(20, 25); u8g.print("Position "); u8g.print(address,DEC); u8g.setPrintPos(20, 45); if (enable) { u8g.print("Head"); } else { u8g.print("Tail"); } u8g.drawRFrame(0,0,128,64,8); } ///////////////////////////////////////////////////////////////////////////// // // readAnalogLocation() Reads the pot input on analog pin 1, displays some // information out the serial, and sets the target position variable // int readAnalogLocation() { int potDisp = analogRead(1); int potOut = potDisp / 79; //1023 / 13 Serial.println(""); Serial.print("Analog Location: "); Serial.print("Track # "); Serial.print(potOut, DEC); Serial.print(" Head/Tail = (1/0) : "); Serial.println(tableTargetHead, DEC); //tableTargetPosition = potOut; return potOut; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Subroutine: doStepperMove() // Moves the stepper based on location inputs, calls up "SetStepperTargetLocation()" // /////////////////////////////////////// void doStepperMove() { // Run the Stepper Motor // stepper2.run(); boolean isInMotion = (abs(stepper2.distanceToGo()) > 0); boolean newTargetSet = false; // If there is a new target location, set the target if (newTargetLocation) { Serial.println("Moving to New Target Location..."); SetStepperTargetLocation(); newTargetSet = true; } if (inMotionToNewTarget) { if ((!isInMotion) && (!newTargetSet)) { Serial.print("Not Moving! DtG: "); Serial.print(stepper2.distanceToGo()); Serial.print(" TP: "); Serial.print(stepper2.targetPosition()); Serial.print(" CP: "); Serial.print(stepper2.currentPosition()); Serial.print(" S: "); Serial.print(stepper2.speed()); Serial.println(); } //release the brake brakeservo.write(servoRelease); delay (5); inMotionToNewTarget = isInMotion; } else { if (programmingMode) { //If we are programming, do that move //release the brake brakeservo.write(servoRelease); delay (5); if ((millis() - programmingLastMoveMillis) >= programRotateDelay) { programmingLastMoveMillis = millis(); stepper2.move(programmingModeMoveForward ? 1 : -1); Serial.println(""); Serial.print("Programming mode, Current location: "); Serial.println(stepper2.currentPosition()); int potDisp = analogRead(1); int potOut = potDisp / 79; //1023 / 13 Serial.print("Analog Location: "); Serial.print("Track # "); Serial.println(potOut, DEC); delay(45); } } if ((stepper2.currentPosition() % MOTOR_STEP_COUNT) == 0) { //setCurrentPosition seems to always reset the position to 0, ignoring the parameter Serial.print("Current location: "); Serial.print(stepper2.currentPosition()); Serial.println(" % STEPCOUNT. Why here?"); } } //stepper timer subroutine came from here. } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Subroutine: SetStepperTargetLocation() // Takes the global variables: tableTargetHeadOrTail, and tableTargetPosition, and sets the stepper2 // object moveTo() target position in steps- inserts values back into "doStepperMove()" // /////////////////////////////////////// void SetStepperTargetLocation() { int newTargetLoc = -1; if (tableTargetHead) { //use head location variable { newTargetLoc = PositionTrackHead[tableTargetPosition]; inMotionToNewTarget = true; } } else { //use tail location variable { newTargetLoc = PositionTrackTail[tableTargetPosition]; inMotionToNewTarget = true; } } if (newTargetLoc > 0) { int currentLoc = stepper2.currentPosition(); int mainDiff = newTargetLoc - currentLoc; if (mainDiff > (MOTOR_STEP_COUNT / 2)) { mainDiff = mainDiff - MOTOR_STEP_COUNT; } else if (mainDiff < (-MOTOR_STEP_COUNT / 2)) { mainDiff = mainDiff + MOTOR_STEP_COUNT; } if (mainDiff < 0) { mainDiff -= MOTOR_OVERSHOOT; overshootDestination = MOTOR_OVERSHOOT; } stepper2.move(mainDiff); } programmingMode = false; newTargetLocation = false; } ////////////////////////////////////////////////////////////////////////////////////////////////// // // Stepper Timer sub routine this runs from the main loop. It also supports the release function. // ////////////////////////////////////////////////////////////////////////////////////////////////// void stepperTimer() { // Run the Stepper Motor // stepper2.run(); boolean isInMotion = (abs(stepper2.distanceToGo()) > 0); //Check if we have any distance to move for release() timeout. Can check the // buffered var isInMotion because we also check the other variables. if (isInMotion || programmingMode ) { //We still have some distance to move, so reset the release timeout stepperLastMoveTime = millis(); isReleased = false; } else { if (!isReleased) { if (overshootDestination > 0) { stepper2.move(overshootDestination); overshootDestination = -1; } if (((millis() - stepperLastMoveTime) >= releaseTimeout_ms)) { //If isReleased, don't release again. isReleased = true; Serial.print ("Relative Current Position: "); Serial.print(stepper2.currentPosition()); //shows position the table thinks it is at (how it got here) int currentLoc = stepper2.currentPosition(); // Resets the positon to the actual positive number it should be currentLoc = currentLoc % MOTOR_STEP_COUNT; if (currentLoc < 0) { currentLoc += MOTOR_STEP_COUNT; } stepper2.setCurrentPosition(currentLoc); stepper2.moveTo(currentLoc); Serial.print (" Actual Current Position: "); Serial.println(stepper2.currentPosition()); // shows the position value corrected. //Set the servo brake brakeservo.write(servoBrake); delay(750); //release the motor release2(); Serial.println(" Brake Set & Motor Released "); } } } } /////////////////////////////////////////////////////// // // Check Programming Buttons- look for either of the programming buttons being pushed // void checkProgrammingButtons() { programmingMode = false; bool buttonPushedProgUp = (digitalRead(6) == LOW); bool buttonPushedProgDown = (digitalRead(7) == LOW); //If one button is pushed, but not both if ((buttonPushedProgDown || buttonPushedProgUp) && !(buttonPushedProgDown && buttonPushedProgUp)) { programmingMode = true; programmingModeMoveForward = buttonPushedProgUp; doStepperMove(); } } /////////////////////////////////////////////////////////////// // Manual move to using the pushbuttons and POT // void checkManualButtons() { //Read the Head button input if (digitalRead(4) == LOW) { buttonPushedHead = true; } else if (buttonPushedHead) { //Button was pushed, but is not being pushed now //Clear pushed variable buttonPushedHead = false; //Set the target head variable tableTargetHead = true; //Read the analog location tableTargetPosition = readAnalogLocation(); //Then set the new target flag newTargetLocation = true; doStepperMove(); } //Read the Tail button input if (digitalRead(5) == LOW) { buttonPushedTail = true; } else if (buttonPushedTail) { //Button was pushed, but is not being pushed now //Clear pushed variable buttonPushedTail = false; //Set the target head variable tableTargetHead = false; //Read the analog location tableTargetPosition = readAnalogLocation(); //Then set the new target flag newTargetLocation = true; doStepperMove(); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // // Setup // void setup() { Serial.begin(9600); AFMStop.begin(); // Start the shield //servo brake release before stepp moves brakeservo.attach(10); // attaches the servo on pin 10 to the servo object brakeservo.write(servoRelease); delay (30); //initial display tableTargetHead = true; tableTargetPosition = 0; u8g.firstPage(); do { draw(tableTargetPosition, tableTargetHead); } while( u8g.nextPage() ); //configure pin3 as an input and enable the internal pull-up resistor pinMode(3, INPUT_PULLUP); //Hall Effect sensor: to reset position on startup //configure pin4 as an input and enable the internal pull-up resistor pinMode(4, INPUT_PULLUP); //Head button //configure pin5 as an input and enable the internal pull-up resistor pinMode(5, INPUT_PULLUP); //Tail button //configure pin6 as an input and enable the internal pull-up resistor pinMode(6, INPUT_PULLUP); //Programming: Move forward single step //configure pin7 as an input and enable the internal pull-up resistor pinMode(7, INPUT_PULLUP); //Programming: Move reverse single step //read the sensoron Dig I/O #3 (open collector type- Hall Effect sensor) value into a variable int sensorVal = digitalRead(3); //set stepper motor speed and acceleration stepper2.setMaxSpeed(80.0); stepper2.setAcceleration(10.0); // if near reference point move away while (sensorVal == LOW) { sensorVal = digitalRead(3); forwardstep2(); delay(25); Serial.println("Stepper Home"); } // step backward to sensor index point while (sensorVal == HIGH) { sensorVal = digitalRead(3); backwardstep2(); delay(50); } //when home- sets brake delay (500); brakeservo.write(servoBrake); //initial track position 0 head newTargetLocation = true; doStepperMove(); DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true); ConfigureDecoder(); DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT ); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Main loop // void loop() { static int addr = 0; //////////////////////////////////////////////////////////////// // Loop DCC library DCC.loop(); //////////////////////////////////////////////////////////////// // Bump to next address to test if( ++addr >= (int)(sizeof(gAddresses)/sizeof(gAddresses[0])) ) { addr = 0; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // To configure the POT (uncomment this text) //configure alalog pin 1 as an input // int potDisp = analogRead(1); // // int potOut = potDisp / 79; //1023 / 13 // delay(1000); // Serial.print("Analog Location: "); // Serial.println(potOut); // end of POT configuration- re-comment this section to hide during normal use //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // To configure the Servo Brake (uncomment this text) //configure alalog pin 1 as an input // int potDisp = analogRead(1); // reads the value of the potentiometer (value between 0 and 1023) // int potOut = map(potDisp, 0, 1023, 0, 50); // scale it to use it with the servo (value between 0 and 180) // brakeservo.write(potOut); // sets the servo position according to the scaled value // delay(1000); // Serial.print("Servo Position Value: "); // Serial.println(potOut); // end of POT configuration- re-comment this section to hide during normal use //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //Tests to see if the manual move buttons are pressed, if so set the needed flags checkManualButtons(); //Checks the programming buttons, if depressed then set the right flags checkProgrammingButtons(); //StepperAccel.Run() in this function stepperTimer(); }
×
×
  • Create New...