Jump to content

  •  

CNers have asked about a donation box for Cloudy Nights over the years, so here you go. Donation is not required by any means, so please enjoy your stay.

Photo

Super Compact Electronic Focuser

  • Please log in to reply
27 replies to this topic

#26 calypsob

calypsob

    Fly Me to the Moon

  • *****
  • Posts: 6,362
  • Joined: 20 Apr 2013
  • Loc: Virginia

Posted 10 November 2017 - 11:50 AM

This is awesome I've been wanting to design one of these for a camera lens.  How small of a motor is practical for this application?  Also how do you determine how fine the movements are? I would like to create a motor plus belt drive system on my lens and leave it rough focused to infinity at all times, since there is not a draw tube connected I assume that I could use a very low torque motor but I also of course need a very low RPM and the Percision to handle F2 apertures.  After recently looking at Arduino nano controllers this topic has certainly spiked my interest . From what I have looked at it seems like a planetary geared stepper might be ideal,  what I would like to know though is how small I can go,  the smaller the better .


Edited by calypsob, 10 November 2017 - 11:52 AM.


#27 Geo.

Geo.

    Vendor - Nexstar Parts

  • *****
  • Vendors
  • Posts: 5,522
  • Joined: 01 Oct 2008
  • Loc: Upstate NY

Posted 12 November 2017 - 10:33 AM

I've taken quite a few dead camera lenses apart and some of the steppers are not much larger than a pencil eraser.

 

https://www.ebay.com...~EAAOSwqXZZvCDl

 

While not geared themselves they usually drive through a large ring gear that surrounds the lens assembly. A belt drive would provide similar torque amplification. A problem may be finding tooth pulleys small enough for you motor. mounting a lever arm on the focusing ring and using a stepper with a lead screw to drive the lever may work as well.


Edited by Geo., 12 November 2017 - 04:23 PM.


#28 cinenosin

cinenosin

    Lift Off

  • *****
  • Posts: 15
  • Joined: 01 Aug 2016
  • Loc: Houston, TX

Posted 25 November 2019 - 11:05 PM

Many thanks to my friend George.  You showed this to me last year.  Well, I finally implemented it.  I wanted to add a temperature sensor, and allow absolute positioning, so I modified the code.  Here is my version:

// Moonlite-compatible stepper controller
// Written by George Carlson June 2014.
// This version uses the Tiny 2.0 a uController based on the Atmel ATMEGA32U4.
// hardware for the remote hand control is not supported.
//
// Many thanks to orly.andico@gmail.com, for the original command parser, others for bits on code picked up here and there on the net
//
// Since the MoonLite focuser only works with positive integers, I center (zero) my system at 32500. The range is from
// 0 to 65535, so 32500 is a good round number for the center.
// If the Current Position, or New Position is set to 0, this code will set the values at 32500. The reason for this
// is the system is designed to be set at center, manually focused, then focused in a +/- fashion by the controller.
//
// Modifications by Robert Brayton teensyfocus@cinenosin.com
// RSB 24NOV2019 Beautified code; Fixed various bugs; Added absolute addressing (EEPROM); Added DS18B20 temp sensor
/********************************************************************/
// First we include the libraries
#include <OneWire.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
/********************************************************************/
// Temperature sensor DS18B20 pinouts
#define ONE_WIRE_BUS 12  // Data wire for temp sensor
// Pin 10 supplies ground
// Pin 11 supplies Vcc
// Note a 4.7k pull up resistor is required between pins 11 and 12
/********************************************************************/
// Setup a oneWire instance to communicate with any OneWire devices 
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
/********************************************************************/
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
/********************************************************************/
#define RUNLED 13 // Amber LED lights whenever motor is active, 11 for the Teensy
#define HOME 32500  // Home position for stepper
#define MAXCOMMAND 32 // Input buffer size
#define SAVED_STEPPER_POSITION 0  // EEPROM address for saved stepper position
// Position counters
bool isRunning = false;
unsigned int Current;
unsigned int Target = HOME;
long DistanceToGo = 0;
// Motor timeout to write current position to EEPROM and shut off motor pins
long millisLastMove = 0;
// Motor speed
int minmotorSpeed = 2;
int maxmotorSpeed = 20;
int motorSpeed = 2;
// Motor connections
int motorPin1 = 2; // Blue - 28BYJ48 pin 1
int motorPin2 = 3; // Pink - 28BYJ48 pin 2
int motorPin3 = 4; // Yellow - 28BYJ48 pin 3
int motorPin4 = 5; // Orange - 28BYJ48 pin 4
// Red - 28BYJ48 pin 5 (VCC)
// lookup table for motor phase control
int StepTable[8] = {0b01001, 0b00001, 0b00011, 0b00010, 0b00110, 0b00100, 0b01100, 0b01000};
unsigned int motorPhase = 0;
void forwardstep()
{
  Current++;
  for ( int x = 0; x < 4; x++ )  // The 28BYJ48 stepper is geared and and I use a 1:10 Feathertouch focuser
                                 // So I need more sub-steps per major step
  {
    motorPhase++;
    motorPhase &= 0x07;
    setOutput(motorPhase);
   
    for (int i = 0; i < motorSpeed >> 1; i++)
      delay(1);
  }
}
void backwardstep()
{
  Current--;
 
  for ( int x = 0; x < 4; x++ )
  {
    motorPhase--;
    motorPhase &= 0x07;
    setOutput(motorPhase);
   
    for (int i = 0; i < motorSpeed >> 1; i++)
      delay(1);
  }
}
void setOutput(int out)
{
  digitalWrite(motorPin1, bitRead(StepTable[out], 0));
  digitalWrite(motorPin2, bitRead(StepTable[out], 1));
  digitalWrite(motorPin3, bitRead(StepTable[out], 2));
  digitalWrite(motorPin4, bitRead(StepTable[out], 3));
}
long hexstr2long(char *line)
{
  long ret = 0;
  ret = strtol(line, NULL, 16);
  return (ret);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
// start of program
void setup()
{
  Serial.begin(9600);
  //setup the motor pins as outputs
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
  // Motor running LED
  pinMode(RUNLED, OUTPUT); /* yellow LED */
  // Temperature sensor pins
  pinMode( 10, OUTPUT );
  pinMode( 11, OUTPUT );
  digitalWrite( 10, 0 );  // Sensor ground
  digitalWrite( 11, 1 );  // Sensor Vcc - sensor uses 1mA, pin is able to source 40mA
  sensors.begin();
  // Fetch current stepper position from EEPROM
  EEPROM.get( SAVED_STEPPER_POSITION, Current );
  if ( Current == (unsigned int) -1 )  // All 1's indicate first use
    Current = HOME;
}
// Forever Loop
void loop()
{
  char inChar;
  char line[MAXCOMMAND] = "";
  bool eoc = false; // End of command detected
  unsigned int idx = 0;  // Index of next character in input buffer
  int currentTemp = 0;
  // Check for a new commend, read the command until the terminating # character
  while ( Serial.available() && !eoc )
  {
    inChar = Serial.read();
    if ( inChar == '#' )
      eoc = true;
    else if ( isAlphaNumeric ( inChar ) )
    {
      line[idx++] = toupper( inChar );
      if (idx >= MAXCOMMAND)  // Input buffer overrun
        idx = MAXCOMMAND - 1;
    }
  }
  // Process command upon end of command received
  if ( eoc )
  {
    // Parse the command and parameter
    char tempString[16];
    unsigned int hashCmd;
   
    char param[MAXCOMMAND];
    unsigned long parameterValue = 0;
    memset(param, 0, MAXCOMMAND);
    int len = strlen(line);
   
    hashCmd = (byte(line[0]) | (byte(line[1]) << 8)); /* combine the two command characters into an unsigned int */
    // Check for parameter following the command
    if ( len > 2 )
    {
      strncpy(param, line + 2, len - 2);
      parameterValue = hexstr2long(param);
    }
    // the stand-alone program sends :C# :GB# on startup
    // :C# is to start a temperature conversion, doesn't require any response (we don't use it)
    switch ( hashCmd )
    {
      // GP command Get current position
      case ('P'<<8 | 'G'):
        sprintf( tempString, "%04X", Current );
        Serial.print( tempString );
        Serial.print( "#" );
        break;
      // GT command Get Temperature
      case ('T'<<8 | 'G'):
        //tempTemp = GetTemp();
        if ( !isRunning )
        {
          sensors.requestTemperatures(); // Send the command to get temperature reading
//          delay( 25 );
          currentTemp = sensors.getTempCByIndex(0);
        }
        sprintf( tempString, "%04X", 0xFFFF & ( currentTemp * 2 ) );
        Serial.print( tempString );
        Serial.print( "#" );
        break;
      // GI command 01 if motor running, 00 if not
      case ('I'<<8 | 'G'):
        Serial.print ( isRunning ? "01#" : "00#" );         
        break;
      case ('B'<<8 | 'G'):
        // GB command Get current backlight value, always 00
        Serial.print( "00#" );
        break;
      case ('V'<<8 | 'G'):
        // GV command Get software version, always 10
        Serial.print( "10#" );
        break;
      case ('N'<<8 | 'G'):
        // GN command Get new (target) position
        sprintf( tempString, "%04X", Target );
        Serial.print( tempString );
        Serial.print( "#" );
        break;
      case ('C'<<8 | 'G'):
        // GC command Get temperature coefficient, always 2
        Serial.print( "02#" );
        break;
      case ('D'<<8 | 'G'):
        // GD command Get motor speed
        sprintf( tempString, "%02X", motorSpeed );
        Serial.print( tempString );
        Serial.print( "#" );
        break;
      case ('H'<<8 | 'G'):
        // GH command Get half step mode, always 00
        Serial.print( "00#" );
        break;
      case ('D'<<8 | 'S'):
        // SD command Set motor speed
        motorSpeed = parameterValue;
        if (motorSpeed < minmotorSpeed)
          motorSpeed = minmotorSpeed;
        if (motorSpeed > maxmotorSpeed)
          motorSpeed = maxmotorSpeed;
        break;
      case ('P'<<8 | 'S'):
        // SP command Set current position
        // RSB - Should this set both Current and Target, thus resetting Current without moving the stepper??
        Current = ( parameterValue == 0 ? HOME : parameterValue );
        isRunning = true;
        digitalWrite( RUNLED, HIGH );
        millisLastMove = millis(); /* reset idle timer */
        break;
      case ('N'<<8 | 'S'):
        // SN command Set (move to) new position
        Target = ( parameterValue == 0 ? HOME : parameterValue );
        isRunning = true;
        digitalWrite( RUNLED, HIGH );
        millisLastMove = millis(); /* reset idle timer */
        break;
      case ('H'<<8 | 'P'):
        // PH command Find motor home
        Current = HOME;
        isRunning = true;
        digitalWrite( RUNLED, HIGH );
        millisLastMove = millis(); /* reset idle timer */
        break;
      case ('G'<<8 | 'F'):
        // FG command Start motor command
        isRunning = true;
        digitalWrite( RUNLED, HIGH );
        millisLastMove = millis(); /* reset idle timer */
        break;
      case ('Q'<<8 | 'F'):
        // FQ command Stop motor command
        isRunning = false;
        digitalWrite( RUNLED, LOW );
        break;
    } // End of command parsing
    // Get ready for the next command
    memset( line, 0, MAXCOMMAND );
    idx = 0;
    eoc = false;
    parameterValue = 0;
  } // end process command
  // Compute remaining distance and move the motor
  DistanceToGo = Target - Current; /* compute remaining distance to go */
  if (DistanceToGo == 0)
  {
    // Motion is complete
    isRunning = false;
    if ( ( millis() - millisLastMove ) > 5000 )  // Turn off motor after 5 second pause
    {
      digitalWrite(motorPin1, 0);
      digitalWrite(motorPin2, 0);
      digitalWrite(motorPin3, 0);
      digitalWrite(motorPin4, 0);
 
      digitalWrite(RUNLED, LOW);
     
      // Store current position in EEPROM
      EEPROM.put( SAVED_STEPPER_POSITION, Current );
    }
  }
  if ( isRunning )
  {
    if ( DistanceToGo > 0 )
      forwardstep();
    if ( DistanceToGo < 0 )
      backwardstep();
  }
} // end forever loop



CNers have asked about a donation box for Cloudy Nights over the years, so here you go. Donation is not required by any means, so please enjoy your stay.


Recent Topics






Cloudy Nights LLC
Cloudy Nights Sponsor: Astronomics