Arduino, and Mechanical Movement of Objects - a Newbie Question

Retroplayer:
If I am understanding him right, the stops have the < and > groove to them to redirect the follower into the other track.

Yes, but it's not the ends I'm asking about - it's the crossing points in the middle. When the grooves cross what's to stop the follower from changing direction? At the end of the track there's only one option and the follower has to change direction. But at the intermediate crossing points, there are two options and nothing to prevent the follower from taking either of them.

PeterH:

Retroplayer:
If I am understanding him right, the stops have the < and > groove to them to redirect the follower into the other track.

Yes, but it's not the ends I'm asking about - it's the crossing points in the middle. When the grooves cross what's to stop the follower from changing direction? At the end of the track there's only one option and the follower has to change direction. But at the intermediate crossing points, there are two options and nothing to prevent the follower from taking either of them.

oh... the follower is long enough that it touches both sides even at the crossing points so it stays on the track it is on. And the tapers on the end of the follower are designed to allow it to flip angle at the ends. The shape of the follower is actually the most important part of the mechanism.

Retroplayer:
oh... the follower is long enough that it touches both sides even at the crossing points so it stays on the track it is on. And the tapers on the end of the follower are designed to allow it to flip angle at the ends. The shape of the follower is actually the most important part of the mechanism.

I imagine something like that would be required, but I'm struggling to follow how that would enable you to adjust the end points i.e. have a spool that has a crossing point that can optionally be 'blocked off' to make the follower reverse at that point. If the shape of the groove and follower make it possible for the follower to reverse at that point, what prevents it from reversing even when the crossing is not 'blocked off' iyswim? It seems to me that for this to work, the turning points at the ends need to be a different shape to the crossing points in the middle. Maybe there's a devious way round that, but I can't figure it.

I think I see what you are asking...

Because of the length and shape of the follower, it cannot follow the cross tracks. It requires the leading edge of the follower to bump against an edge to toggle it in the other direction. The end stops provide this edge that bumps it and toggles it. Notice the taper on the edges. They touch nothing at all while following the track until they hit the end points.

Or am I still not understanding your concern?

I just pulled my baitcasting reel apart and the cam follower looks like this (crap pic):

You folks are awesome-fantastic-brilliant-and-generous! Thanks for all these ideas. The one I think is most elegant is also probably the lease accessible to me, that being the rod with spiraling, machined grooves. In addition to finding someone to do the machining at a price I could afford, that one poses another thing to figure out. Splitting even one ball of yarn creates a couple of grams of lint. Lint, and grease don't play well together; they make a sticky mess.

I'm leaning toward some variation of the threaded rod. Parts are cheap, and ubiquitous, If I mess up, or make some revision, I'll still have more to have at it again. The threaded rod presents the same grease/lint issue, and I haven't resolved that.

I don't know if you noticed, but in the video, there's a dangling gadget that looks like a toilet paper holder holding a ball of yarn, and it's rotating. It rotates to remove some varying amount of twist in the yarn, but I've been stopping to spin it manually every 15 or 20 seconds. There's now a little 12v. DC motor up there. One problem is that I need a 4" or larger pulley above the yarn ball holder, but I'm wondering if there's a way to edit the sketch so that I can adjust it as needed more easily than I can now. It seems to reach maximum RPMs well before the potentiometer reaches it's extreme. That sketch follows.

#include <EEPROM.h>

// Maurice Ribble 
// 2-4-2010
// http://www.glacialwanderer.com/hobbyrobotics
// Open Source, licensed under a Creative Commons Attribution 3.0 License (http://creativecommons.org/licenses/by-sa/3.0/)
// Compiled with Arduino Software 0017 (http://arduino.cc)

// This program reads an input potentiometer and uses that value to set a motor controller's direction and speed.

// REVISIONS:
// Initial Version

// Frequency for updating motor
#define UPDATE_HZ   100

// Define digital/analog pins
#define BUTTON_PIN        2
#define MOTOR_ENABLE_PIN  3
#define MOTOR_IN1_PIN     4
#define MOTOR_IN2_PIN     5

#define DIAL_APIN         0

// Positions that different data is stored in eeprom
#define EEPROM_DIAL_LOW  0
#define EEPROM_DIAL_MID  1
#define EEPROM_DIAL_HIGH 2

enum { BUTTON_PRESSED=0, BUTTON_NOT_PRESSED=1 };

// Globals
unsigned long g_dialLow;
unsigned long g_dialMid;
unsigned long g_dialHigh;

void setup()
{
  int button;

  //Serial.begin(9600); // open hw serial for debugging

  pinMode(BUTTON_PIN, INPUT);
  pinMode(MOTOR_ENABLE_PIN, OUTPUT);
  pinMode(MOTOR_IN1_PIN, OUTPUT);
  pinMode(MOTOR_IN2_PIN, OUTPUT);

  // Default values  
  digitalWrite(MOTOR_ENABLE_PIN, LOW);
  digitalWrite(MOTOR_IN1_PIN, LOW);
  digitalWrite(MOTOR_IN2_PIN, LOW);

  button = digitalRead(BUTTON_PIN);
 
  // If the button is pressed during startup enter a special mode to set the min, max and mid dial readings
  // Should only need to do this once (unless you change the the potentiometer is changed)
  if (button == BUTTON_PRESSED)
  {
    waitTillAllButtonsReleased();                   // debounce
    g_dialLow = analogRead(DIAL_APIN);              // save low speed position
    
    button = digitalRead(BUTTON_PIN);               // wait for button to be pressed again
    while(button == BUTTON_NOT_PRESSED)
    {
       button = digitalRead(BUTTON_PIN);
    }
    
    waitTillAllButtonsReleased();                   // debounce
    g_dialHigh = analogRead(DIAL_APIN);             // save high speed position
    
    button = digitalRead(BUTTON_PIN);               // wait for button to be pressed again
    while(button == BUTTON_NOT_PRESSED)
    {
       button = digitalRead(BUTTON_PIN);
    }
    
    waitTillAllButtonsReleased();                   // debounce
    g_dialMid = analogRead(DIAL_APIN);              // save middle speed position

    eepromWriteInt(EEPROM_DIAL_LOW, g_dialLow);    // save dial position references to eeprom
    eepromWriteInt(EEPROM_DIAL_MID, g_dialMid);
    eepromWriteInt(EEPROM_DIAL_HIGH, g_dialHigh);
  }
  else
  {
    g_dialLow  = eepromReadInt(EEPROM_DIAL_LOW, 0, 1023);  // read dial position references from eeprom
    g_dialMid  = eepromReadInt(EEPROM_DIAL_MID, 0, 1023);
    g_dialHigh = eepromReadInt(EEPROM_DIAL_HIGH, 0, 1023);
  }
}

void loop()
{
  unsigned long dialVal = analogRead(DIAL_APIN);
  long percentOn;
  unsigned long usTotal = 0;
  unsigned long usOn = 0;
  unsigned long usOff = 0;
  int forward;

  if (g_dialHigh > g_dialLow)
  {
    if (dialVal >= g_dialMid)
    {
      forward = 0;
      percentOn = 100*(dialVal-g_dialMid)/(g_dialHigh-g_dialMid);
    }
    else
    {
      forward = 1;
      percentOn = 100*(g_dialMid-dialVal)/(g_dialMid-g_dialLow);
    }
  }
  else // g_dialHigh < g_dialLow
  {
    if (dialVal <= g_dialMid)
    {
      forward = 0;
      percentOn = 100*(g_dialMid-dialVal)/(g_dialMid-g_dialHigh);
    }
    else
    {
      forward = 1;
      percentOn = 100*(dialVal-g_dialMid)/(g_dialLow-g_dialMid);
    }
  }
  
  if (percentOn <= 5)  // Turn motors off when they are close to off
  {
    percentOn = 0;
  }
  else if (percentOn >= 95)  // Turn motors full on when they are close to full on
  {
    percentOn = 100;
  }
 
  usTotal = 1000000/UPDATE_HZ;
  usOn = usTotal*percentOn/100;
  usOff = usTotal - usOn;

  if (forward)
  {
    digitalWrite(MOTOR_IN1_PIN, HIGH);
    digitalWrite(MOTOR_IN2_PIN, LOW);
  }
  else
  {
    digitalWrite(MOTOR_IN1_PIN, LOW);
    digitalWrite(MOTOR_IN2_PIN, HIGH);
  }

  if (usOn)
  {
    unsigned int msOn = usOn/1000;
    usOn %= 1000;
    digitalWrite(MOTOR_ENABLE_PIN, HIGH);
    if (msOn)
      delay(msOn);
    if (usOn)
      delayMicroseconds(usOn);
  }

  if (usOff)
  {
    unsigned int msOff = usOff/1000;
    usOff %=1000;
    digitalWrite(MOTOR_ENABLE_PIN, LOW);
    if (msOff)
      delay(msOff);
    if (usOff)
      delayMicroseconds(usOff);
  }
}

////////////////////////////////////////////////////////////////////////////////
// Helper functions
////////////////////////////////////////////////////////////////////////////////

// Writes an integer to eeprom
void eepromWriteInt(int addr, int val)
{
  addr *= 2;  // int is 2 bytes
  EEPROM.write(addr+1, val&0xFF);
  val /= 256;
  EEPROM.write(addr+0, val&0xFF);
}

// Reads an integer from eeprom
int eepromReadInt(int addr, int minVal, int maxVal)
{
  int val;

  addr *= 2;  // int is 2 bytes
  val = EEPROM.read(addr+0);
  val *= 256;
  val |= EEPROM.read(addr+1);
  val = constrain(val, minVal, maxVal);
  return val;
}

// Wait for all the current button presses to end (handles debouncing buttons)
void waitTillAllButtonsReleased()
{
  while(1)
  {
    int i;
    int button;

    // Need to sample many times to makes sure the button isn't currently bouncing
    for(i=0; i<100; ++i)
    {
      button  = digitalRead(BUTTON_PIN);
      delayMicroseconds(10);
    }

    if (button == BUTTON_NOT_PRESSED)
    {
      break;
    }
  }
}

I've harvested a lot of plastic gears, several stepper motors, and other cool toys. There has been discussion about a lever toggling the direction of rotation of the shaft that will move the "hooks". Right now, I'm imagining something that will be better illustrated than described with words. I'll post that sometime this afternoon.

You all gave me so much food for thought. I am very grateful.