Control de movimiento con Arduino motorizar un control deslizante de cámara

La disponibilidad de motores y motores paso a paso baratos en estos días ofrece una gran oportunidad para experimentar fuera de los proyectos de corte e impresión 2D / 3D más costosos y complicados.. 

Para este proyecto, tomaré el control deslizante de la cámara OpenBuilds (refiérase al video de compilación en Creación de un control deslizante de video básico con piezas CNC de código abierto) y lo motorizaré. También crearé un sistema autónomo para controlar el motor..

Este tutorial cubre específicamente la configuración del hardware, pero principalmente la creación de una GUI LCD 16x2 rudimentaria utilizando la biblioteca LiquidCrystal y un sistema de menú simple para que se muestre, seguido del funcionamiento del controlador paso a paso A4988 y cómo controlarlo con Arduino. 

Este proyecto está lleno de bucles y pasos, y aunque en general el proyecto es más intermedio, he tratado de explicarlo de tal manera que los principiantes puedan comenzar a trabajar con relativa rapidez..

Lista de equipo

Componentes

  • Arduino Uno
  • Pantalla de teclado LCD o pantallas y botones separados de 16x2 si sabe cómo hacer que eso funcione
  • Pololu A4988 [Black Edition] controlador paso a paso
  • Pequeño disipador de calor autoadhesivo de aluminio
  • Breadboards, cables de puente macho y hembra, etc.
  • Resistencia de 220-330 ohmios (1 / 4W probablemente funcionará), transistor NPN estándar (utilicé un BC109)
  • Conector estéreo TRS de 3,5 mm
  • Cable adaptador TRS estéreo de 3,5 mm a 2,5 mm
  • Cable de extensión de 3,5 mm según sea necesario para la longitud del deslizador
  • Si desea alejar el Arduino de la alimentación USB de la computadora
  • Fuente de alimentación de 12V 2A para ejecutar el motor paso a paso.
  • Motor paso a paso NEMA 17

Partes

  • GT2 Correa dentada de 5 mm de ancho y 2 mm de ancho: doble la longitud del deslizador más un pie para mayor seguridad (11 pies para mí)
  • Kit de polea loca lisa
  • Muelle de torsión de tensión de la correa si tiene dificultades para mantener la tensión de la correa a largo plazo
  • 2x abrazadera de engarce del cinturón (se puede sustituir por pequeñas cremalleras)
  • GT2 Polea de aluminio de 7 mm de ancho y 20 dientes del mismo calibre que el eje del motor
  • 4x 30mm M3-0.5 tornillos de cabeza de cabeza

Herramientas

  • Computadora con Arduino IDE (yo uso Win7, Arduino 1.0.5 r2)
  • Soldador con punta de cincel pequeño, soldadura, etc.
  • Llave allen de 2,5 mm para tornillos M5
  • Llave allen de 2 mm para tornillos M3
  • Llave Allen de 1,5 mm para tornillos prisioneros en polea GT2
  • Multímetro para solución de problemas y ajuste de corriente.
  • Alicates estrechos para apretar en espacios reducidos.

Descripción funcional

Cubriré agregar el motor y las poleas al control deslizante, ensartar la correa y atarlo todo. Es una modificación simple.. 

Luego hablaré sobre cómo armar un kit Pololu A4988 Black Edition y cómo conectarlo a una placa de pruebas junto con todas las otras placas externas, así como a un simple gabinete de madera contrachapada que pegué en unos minutos para mi potencia de 12 V Suministro (listado arriba) para evitar choques ya que los terminales de cableado están expuestos.

De lado, por lo que sigue siendo lo suficientemente grande como para que los números de pin aún sean visibles!

El menú permite la entrada de distancia para viajar, tiempo para viajar, número de pasos para viajar en y dirección de viaje. Al final de cada paso, el control deslizante se detiene mientras la cámara se activa.

Modificar el control deslizante

Paso 1: Montaje del motor

El montaje de extremo del actuador de ranura en V de OpenBuilds tiene orificios de dimensión NEMA 17, por lo que todo lo que se requiere son cuatro tornillos de cabeza de cabeza M3 de 30 mm para montar el motor en él..

El montaje de extremo del actuador V-Slot de OpenBuilds

Asegúrese de que la polea GT2 de 20 dientes esté dentro del soporte antes de encajar el eje del motor, ya que el soporte no es lo suficientemente ancho como para agregarlo después. Una vez que el motor se atornilla en la parte inferior, apriete los tornillos de fijación con uno de ellos contra la parte plana del eje del motor, asegurándose de que los dientes estén directamente en línea con el centro de toda la unidad de extrusión..

Paso 2: Kit de polea loca

El kit de polea loca va junto como un juego de ruedas, y encaja en el extremo opuesto Actuator Mount:

El kit de polea loca

Paso 3: Belting Up

Alimente la correa a través del centro de la ranura en V en línea con las poleas, asegurándose de que los dientes estén hacia arriba. 

Luego aliméntelo por encima de las dos poleas y vuelva a colocarlo en el centro de la placa de construcción de la plataforma..

Los dientes se agarran unos a otros.

Aquí envuelve un lado a través de la ranura de la correa y la abrazadera, o ciérrelo con una cremallera, luego use eso para apretar toda la correa en todo el sistema antes de conectar el otro lado. No demasiado apretado para que el motor gire, pero no lo suficientemente flojo para saltar los dientes en la polea de transmisión!

Montaje de la electrónica

Paso 1: Ensamble el controlador paso a paso

El controlador de motor paso a paso Pololu A4988 Black Edition (técnicamente la placa de transporte A4988 - el A4988 es el chip en sí mismo) típicamente viene en forma de kit, lo que simplemente significa que los encabezados deben soldarse. Dado que este es un componente de alimentación, aunque no está impulsando la unidad a su capacidad máxima, es una buena idea agregar un disipador de calor para ayudar a mejorar su vida útil..
Rompe la fila del encabezado a la mitad para tener dos filas de ocho. Insértelos en los orificios pasantes enchapados en la placa, y luego insértelos cuidadosamente en la placa de pruebas. Suelde los pasadores en su lugar, mientras que la placa de pruebas contiene todo lo que es agradable y perpendicular..

Juntando el controlador paso a paso

Una vez que se haya completado, corte la esquina de un pequeño disipador de calor autoadhesivo utilizando una sierra para metales o una sierra de desplazamiento (¡con cuidado, en una abrazadera!) Para montar en el A4988 IC.

Cortar la esquina de un pequeño disipador de calor autoadhesivo

Paso 2: Breadboard-Mount los componentes

Ahora todo tiene que ser montado en una placa de pruebas para que pueda ser conectado en un circuito que funcione. Estoy usando tablas separadas para cada parte por claridad en las imágenes, pero si lo desea, no dude en colocarlas en una sola tabla..
El protector del teclado LCD no se puede montar en una placa, gracias a la extraña elección de Arduino de adherirse a un defecto de diseño en lugar de cumplir con los estándares. Esto se mantendrá separado, aunque atornillarlo a un pedazo de madera o algo para proteger los pasadores podría no ser una mala idea..

El circuito de activación de la cámara en su forma más simple consiste en una resistencia, un transistor y un sub-mini TRS de 2,5 mm. He agregado un LED que parpadeará cuando el pin del disparador alcance un nivel alto, y un mini jack TRS de 3.5 mm para permitir la flexibilidad. 

Si está comprando componentes para esta construcción, un zócalo de 3.5 mm diseñado para tablas de paso de 0.1 "sería una buena idea, pero el mío es de la pila barrida, así que le he soldado un conector.
Deshazte de todo, listo para conectarlo todo..

Paso 3: cableando todo junto

Es hora de agarrar todos los cables de puente. Tener suficiente para mantener las cosas codificadas por colores facilitará la vida al resolver problemas. Consulte el diagrama del circuito en la parte superior si la siguiente descripción lo confunde en algún punto.

Cableando todo junto

Primero cablea la pantalla LCD. Agarre 10 puentes hembra y conéctelos a los siguientes pines de protección: pines digitales 4-9, reinicio de pines del bus de alimentación (si desea usar el botón de reinicio de la pantalla LCD), 5 V y uno de los GND. 

Si tiene saltadores de mujer a hombre, puede dejarlo allí. De lo contrario, conecte los puentes macho al otro extremo de las hembras para conectarlos en los zócalos de cabecera Arduino correspondientes. Si tiene un protector de teclado LCD con pasadores hembra pasantes instalados en la parte superior, puede omitir este paso ya que su protector no bloquea nada..
A continuación, el tablero de pololu a4988. Esto requiere ocho puentes en un lado, he usado negro y rojo para la lógica / potencia del motor en el extremo easch, y rojo / verde / azul / amarillo en el centro cuatro para coincidir con los cables servo del motor paso a paso. 

El pin lógico de alimentación va a 3.3V en el arduino, ya que la pantalla LCD anterior está usando el pin de 5V. Los cables de alimentación del motor van a su fuente de alimentación de 12V. En el otro lado, cerca del chip A4988, estoy usando azul y naranja para STP y DIR respectivamente para contrastar con los colores relativamente uniformes en todas partes. Van a los pines Arduino 11 y 12 respectivamente, a menos que modifiques el código. Luego, presione brevemente RST y SLP para mantener el tablero habilitado; He usado el plomo blanco aquí.

Cuando hayas terminado, debería verse algo como esto.

Finalmente, conecte el circuito del interruptor de disparo de la cámara. Aquí los cables negros están conectados a tierra: el cable de la fila A a Arduino, el cable de la fila C al zócalo de 3.5 mm. El amarillo va al pin 13 de Arduino (¡así que hay un indicador LED en la placa, así como en el interruptor!), Y el cable rojo va al otro lado del zócalo de 3.5 mm (o cable de conexión de 2.5 mm si va a esa ruta).
Enchufe el motor paso a paso en los cables de colores según el diagrama de la placa A4988 y la hoja de datos de su paso a paso. Para mí, eso fue así:

Al igual que el lector de botones, el boceto de rotación de prueba se incluye en la parte superior con cremallera.

Precaución: recuerde que los cables que proporcionan energía al motor probablemente tomarán 1-2A a su voltaje elegido, por lo tanto, asegúrese de que los cables utilizados estén clasificados para ello. ¡El chip A4988 y el tablero alrededor pueden calentarse! El potenciómetro incorporado en la placa proporciona una limitación de corriente para proteger tanto el A4988 como el motor, así que asegúrese de configurarlo apropiadamente antes de usarlo con un multímetro.

Configurando el programa

Una vez que los componentes han sido ensamblados, puede pasar a la codificación. Descargue el zip incluido con este tutorial o consulte este repositorio de GitHub si lo prefiere. Describiré cómo lo armé para que pueda comprender el flujo general del programa y cómo funcionan juntos los módulos..

Paso 1: Incluye y definiciones básicas

La única inclusión necesaria para esto fue la biblioteca de escritura LCD LiquidCrystal.h. Esto da acceso a la lcd.xxxx () funciones Hay una pow () en el programa, y ​​encontré que incluyendo la biblioteca de C ++ math.h no es necesario, ya que algunas de sus funciones más útiles se incluyen en el entorno de Arduino, que incluye pow ().


#incluir  LiquidCrystal lcd (8, 9, 4, 5, 6, 7); // establecer pines de salida LCD // definir pines del controlador paso a paso const int stp = 11; // no puedo usar el pin 10 con la pantalla LCD SS ya que es el control de retroiluminación. // si baja, la luz de fondo se apaga! const int dir = 12; // define el pin del disparador const int trig = 13; // BOTONES // define los valores de los botones const int btnUp = 0; const int btnDn = 1; const int btnL = 2; const int btnR = 3; const int btnSel = 4; const int btnNone = 5; // definir variables de lectura de botones int btnVal = 5; int adcIn = 0;

Configuro los pines de salida LCD, los pines de salida del controlador paso a paso y el pin de salida del disparador de la cámara. Una vez que se configuró la interfaz de hardware real, agregué variables para eventos de botón seguidas de la función de lectura de botón, que adapté de la wiki de DFRobot en su protector de teclado LCD idéntico. Tenga en cuenta que SainSmart no proporciona documentación.

Paso 2: Setup () Loop

Esto es super recto. Inicialice la pantalla LCD y los pines de salida relevantes, seguidos de una pantalla de bienvenida básica, luego caiga en la pantalla de inicio: menú opción 1 con valores en cero.

void setup () lcd.begin (16, 2); // inicializar LCD lib pantalla completa lcd.setCursor (0,0); // establecer la posición del cursor pinMode (stp, OUTPUT); // inicializar pasadores de pasos pinMode (dir, OUTPUT); pinMode (trig, SALIDA); // inicializar el pin digitalWrite (trig, LOW); // asegúrese de que el activador esté desactivado lcd.print ("Bienvenido a"); // pantalla de bienvenida lcd.setCursor (0,1); lcd.print ("SliderCam v0.2!"); retraso (1000); lcd.clear (); lcd.print (menuItemsTop [0]); retraso (100); lcd.setCursor (0,1); para (int i = 0; i < 4; i++)  lcd.setCursor(i, 1); lcd.print(currentDistance[i]);  lcd.setCursor(4,1); lcd.print("mm(max 1300)"); 

Paso 3: Botones del monitor

La ventaja aquí en cuanto a codificación es que el equipo no necesita hacer nada en absoluto sin la intervención del usuario. Lo que significa que lo primero puede ser simplemente un bucle eterno de sondeo de botones. Llamando a la readLcdButtons () la función una y otra vez hasta que su valor cambie no afecte negativamente el rendimiento del programa, y ​​no tiene que preocuparse por dejar disponibles los pines de interrupción.

void loop () do btnVal = readLcdButtons (); // lee continuamente los botones ... while (btnVal == 5); // ... hasta que se presione algo
// declarar función de encuesta de botón int readLcdButtons () delay (90); // Retardo de rebote, sintonizado experimentalmente. el retraso está bien ya que el programa no debería hacer nada más // en este punto de todos modos adcIn = analogRead (0); // valor de lectura del pin A0 / * valores de umbral confirmados por la experimentación con el boceto de calibración de botón que devuelve los siguientes valores de lectura de ADC: derecha: 0 arriba: 143 abajo: 328 izquierda: 504 seleccione: 741 * / if (adcIn> 1000) return btnNone ; si (adcIn < 50) return btnR; if (adcIn < 250) return btnUp; if (adcIn < 450) return btnDn; if (adcIn < 650) return btnL; if (adcIn < 850) return btnSel; return btnNone; //if it can't detect anything, return no button pressed 

ReadLcdButtons () Tiene un retraso de 90ms para rebotar los botones. En realidad, esto no es una rebaja, ya que no vuelve a tomar la medición ADC después de un período de tiempo establecido, sino que sondea los botones con poca frecuencia para registrar raramente más de un solo clic.. 

Logra lo mismo desde una vista práctica de UX. Es mas de un botones de encuesta cada 90 ms en lugar de constantemente, Es por eso que el uso de retrasar() por lo general, no se considera una buena práctica para fines de publicación, pero solucionó el problema (solo se podía acceder a cada extremo de los menús).

Paso 4: Actualizar la pantalla

Una vez que la unidad pueda reaccionar a la entrada, debe haber una forma para que muestre esas reacciones. 

Después de probar las actualizaciones sobre la marcha, determiné que la actualización consistente de la pantalla como un sistema operativo real era más fácil de administrar en mis intentos de construir una estructura modular actualizable. Hacer esto es tan simple como limpiar la pantalla y luego reconstruir en función de los parámetros actuales conocidos.
Esto suena complicado, pero en la práctica hace la vida mucho más fácil. Elimina una gran cantidad de comandos LCD de otras partes del programa y crea una zona independiente de tipo variable que se ve afectada mínimamente por las actualizaciones del programa externas a él..
La parte refrescante actual evolucionó para consistir en cuatro pasos distintos:
Restablecer parámetros ...

// IMPRIMIR NUEVOS VALORES DE PANTALLA btnVal = btnNone; lcd.clear ();

... imprimir la línea superior ...

lcd.setCursor (0, 0); lcd.print (menuItemsTop [currentMenuItem]); // imprimir el elemento del menú de nivel superior

... imprime la línea de fondo, que explicaré un poco más adelante ...

 lcd.setCursor (0,1); switch (currentMenuItem) case 0: for (int i = 0; i < 4; i++)  lcd.setCursor(i, 1); lcd.print(currentDistance[i]);  break;  case 1:  for (int i = 0; i < 6; i++)  lcd.setCursor(i, 1); lcd.print(currentDuration[i]);  break;  case 2:  for (int i = 0; i < 4; i++)  lcd.setCursor(i, 1); lcd.print(currentSteps[i]);  break;  case 3:  if (travelDir == 0) lcd.print("From Motor"); else lcd.print("To Motor"); break;  case 4:  lcd.print("Stop!"); break;   //end switch

... y agrega comandos específicos de la pantalla sobre lo que ya está impreso.

if (currentMenuItem == 0) lcd.setCursor (4,1); lcd.print ("mm (max 1300)"); // inserte el recorrido máximo del carro en el control deslizante utilizado if (currentMenuItem == 1) lcd.setCursor (6,1); lcd.print ("s (3600 / hr)");  if (currentMenuLevel == 1) lcd.setCursor (currentCursorPos, 1); lcd.blink ();  else lcd.noBlink ();

Construyendo un menú: encabezados principales

Naturalmente, la sección de actualización de pantalla exacta no se escribe por sí misma, y ​​necesitamos saber el menú que está escribiendo en pantalla antes de que pueda completarse. Los encabezados principales son fáciles, ya que en realidad no cambian dependiendo de la entrada del usuario. Esto significa que puede ser simplemente una matriz de cadenas, técnicamente una matriz de punteros de caracteres o una matriz de matrices:

// GUI DEL MENÚ // definir cadenas de elementos del menú de nivel superior para la navegación numérica char * menuItemsTop [] = "01 Distance>", "< 02 Duration >"< 03 Steps > "< 04 Direction >"< 05 Go!"; int currentMenuLevel = 0; //top menu or submenu int currentMenuItem = 0; //x-axis position of menu selection int currentCursorPos = 0; //current lcd cursor position int currentDistance[4] =  0, 0, 0, 0; int currentDuration[6] =  0, 0, 0, 0, 0, 0; int currentSteps[4] =  0, 0, 0, 1;

Esto significa que este menuItemsTop La matriz se puede navegar simplemente modificando el número dentro de los corchetes en el momento de actualización de la pantalla. Lo que sucede, ya que todo está indexado a cero, para realizar un seguimiento idéntico al entero currentMenuItem

Manipulando currentMenuItem en los eventos de botones nos permite la navegación unidimensional, por lo que cuando ves menuItemsTop [currentMenuItem] Es obvio encabezado del menú actual.

if (currentMenuLevel == 0) switch (btnVal) case btnL: if (currentMenuItem == 0) break; // no puedo ir a la izquierda desde aquí sino currentMenuItem--; descanso;  caso btnR: if (currentMenuItem == 4) break; // no puedo ir directamente desde aquí sino currentMenuItem ++; descanso;  case btnSel: currentMenuLevel ++; if (currentCursorPos> 3 && (currentMenuItem == 0 || currentMenuItem == 2)) currentCursorPos = 3; // no salga del final de los números para los números de 4 dígitos si (currentCursorPos> 0 && (currentMenuItem> 2)) currentCursorPos = 0; // coloca el cursor parpadeante a la izquierda para las opciones basadas en texto si (currentMenuItem == 4) motion = 1; control de movimiento(); descanso;  // fin del interruptor // fin del nivel 0

Así que puedes moverte a la izquierda y a la derecha, e ir dentro Un menú, o en el caso de Ir! entonces se activa el control de movimiento. Que es todo lo que se requiere aquí.

Construyendo un Menú: Submenú

El sistema de submenú tomó un poco más de tiempo, gracias a su complejidad interna. Las tres primeras entradas., Distancia, Duración y Pasos, técnicamente consiste en un submenú, cada uno de los cuales permite la navegación del valor de varios dígitos, así como cada carácter individual.

Esto se cubre haciendo que cada entrada de submenú sea un sistema de encabezado conmutado por derecho propio. Si bien esto fue un largo camino, es un método simple y consistente para permitir tal navegación de bajo nivel. Desde que acabo de descubrir la Distancia submenú y luego lo copió para los otros submenús, he aquí un vistazo a ese.

else // es decir, "else if currentMenuLevel = 1" if (currentMenuItem == 0) // 01 switch DISTANCE (btnVal) case btnUp: currentChar = currentDistance [currentCursorPos]; adjustDigit (currentChar, 1); currentDistance [currentCursorPos] = currentChar; descanso;  caso btnDn: currentChar = currentDistance [currentCursorPos]; adjustDigit (currentChar, 0); currentDistance [currentCursorPos] = currentChar; descanso;  case btnL: if (currentCursorPos == 0) break; // no puedo ir a la izquierda desde aquí sino a currentCursorPos--; descanso;  caso btnR: if (currentCursorPos == 3) break; // no puedo ir a la izquierda desde aquí sino currentCursorPos ++; descanso;  case btnSel: parseArrayDistance (); currentMenuLevel--;  // interruptor final // fin DISTANCIA

La izquierda y la derecha son esencialmente iguales a las del menú de nivel superior, simplemente moviéndose hacia adelante y hacia atrás a lo largo del número de la misma manera, al tener la tecla número en realidad puede ser un conjunto de dígitos en una matriz int y la ubicación actual almacenada en un int llamado currentCursorPos que permite parpadear como se ve en el módulo de actualización de pantalla anterior. 

La impresión de estas matrices a lo largo de la fila inferior del LCD es para lo que se utilizaron los bucles for en la sección de actualización de pantalla; yo desde 0 a 3, Columna LCD desde 0 a 3, currentDistance [] desde 0 a 3.

int adjustDigit (int x, int dir) // función de ajuste de dígitos si (dir == 0 && x> 0) x--; // restar de dígito en btnDn si (dir == 1 && x < 9) x++; // add to digit on btnUp lcd.setCursor(currentCursorPos, 1); lcd.print(x); currentChar = x; return currentChar; //return new digit 

Incrementando y disminuyendo el número se logra almacenando el dígito actual en la variable actualChar, que luego se pasa a la ajustardigito () función junto con un valor booleano que indica la dirección; para aumentar o disminuir el motor. 

Esto simplemente ajusta el dígito de acuerdo con el valor booleano y guarda el resultado, donde el flujo regresa al bucle principal, donde el valor del valor actual se guarda nuevamente en la posición correcta del original. currentDistance [] matriz y el nuevo dígito ajustado se imprime en la actualización de la pantalla.

Análisis de valores de matriz de visualización

Cuando se pulsa Select desde uno de los submenús de matriz numérica, se activa la función de análisis relevante, en este caso parseArrayDistance (). Debe analizar la matriz, utilizada para visualizar y editar convenientemente, en un entero útil para los cálculos de movimiento reales. Elegí hacer esto ahora en lugar de en Ir! para mantener UX sentirse ágil.

int adjustDigit (int x, int dir) // función de ajuste de dígitos si (dir == 0 && x> 0) x--; // restar de dígito en btnDn si (dir == 1 && x < 9) x++; // add to digit on btnUp lcd.setCursor(currentCursorPos, 1); lcd.print(x); currentChar = x; return currentChar; //return new digit 

Se me ocurrió esta función a partir del único comentario de paso útil que encontré después de agotar Google en busca de funciones estándar de arreglo-a-int, quedarme vacío y deshacerme del desorden de las funciones de arreglo-a-char-a-int que fueron Una solución ineficaz. Parece bastante corto y ligero, dado que está literalmente basado en la base de las matemáticas decimales, pero si conoces un método mejor, soy todo oídos..

Control de movimiento y activación de cámara

Se establecen todos los valores y se pulsa. Ir! ¿Qué pasa después? Debe calcular exactamente lo que se supone que deben hacer los números para ejecutar el movimiento final. Esta parte es funcional, pero un trabajo en progreso; Creo que es necesario que haya más opciones para diferentes tipos de movimiento.

int motionControl () totalMotorSteps = currentDistanceInt * 5; // calcular los pasos totales (0.2mm = engranaje de 20 dientes en la correa de paso de 2mm; 40mm por revolución, 200 pasos por revolución, ergo 1 / 5th mm por paso) pulseDelay = (1000L * (currentDurationInt - (currentStepsInt * shutterDuration)) / totalMotorSteps; // cuánto tiempo se debe hacer una pausa en ms entre los pulsos de STP en el controlador de motor intervalDistance = totalMotorSteps / currentStepsInt;

Lo que está sucediendo en esta función es bastante claro a partir de los comentarios, creo. He puesto un shutterDuration De 2 segundos en el software, principalmente para seguir probando bastante rápido. Si está filmando de noche, con ISO más bajos, es posible que tenga que ser más como de 25 a 35 segundos, dependiendo de la velocidad exacta de su obturador..

los pulseDelay se multiplica por 1000 Al final para convertir de segundos a milisegundos, por supuesto. los L convertir el int constante en un largo es más un error en el lado de la precaución que ser estrictamente necesario. Como es un boceto relativamente pequeño, no me preocupa demasiado el uso de memoria variable.

Estos cálculos suponen que el bucle en sí requiere una cantidad de tiempo insignificante para ejecutarse en comparación con pulseDelay El tiempo, que una vez que saqué el botón de sondeo, parece ser cierto..

// una vez por ejecución general if (travelDir == 0) digitalWrite (dir, LOW); else if (travelDir == 1) digitalWrite (dir, HIGH); //Serial.begin(9600); //Serial.println(pulseDelay); // step loop do digitalWrite (stp, HIGH); // retrasar el paso del conductor del motor (pulseDelay); digitalWrite (stp, LOW); // reiniciar el controlador // btnVal = readLcdButtons (); // verifique que no haya paro - esto toma demasiado tiempo y ralentiza significativamente el motor; use reset para detener! currentStep ++; // al final de cada paso si (currentStep% intervalDistance == 0) // si el número actual de pasos del motor es divisible por el número de pasos del motor en un paso de cámara, dispare la cámara digitalWrite (trig, HIGH); // disparar el retardo del obturador de la cámara (80); digitalWrite (trig, LOW); // restablecer el retardo del pin de disparo ((shutterDuration * 1000) -80); // es necesario cambiar el retardo al temporizador para que el botón de parada se pueda sondear while (currentStep < totalMotorSteps);  //end motion control

Por último, tenga en cuenta la currentSteps valor establecido en 1. No he creado una función de comprobación de errores para esto, pero el simple sentido común dice Numero de pie se vuelve infinito si currentStepsInt == 0, así que es mejor mantenerlo en uno si se desea un movimiento continuo. He añadido una entrada de mejora para esto ya.

Ejecutando el producto final

Para algo que se ejecuta en un código escrito más o menos desde cero en dos días y solucionado por dos más, ¡funciona como un sueño! Sin embargo, la prueba está en el pudín. ¿Obtiene realmente el metraje de timelapse que vale la pena, y la unidad de control realmente funciona bien en el campo??

En mis pruebas, la respuesta parece ser un sincero sí. A continuación se muestra un lapso de tiempo de 650 cuadros y dos horas, la primera prueba. El control deslizante también completó una prueba de fotogramas de 720 horas sin problemas, pero desafortunadamente a la batería de la cámara no le fue tan bien después de 2 horas ... lo cual no descubrí hasta la marca de las 8,5 horas, naturalmente.

Si configuro el tiempo y los pasos de manera adecuada, el movimiento puede ser continuo para los movimientos lentos de la muñeca en el video de acción en vivo, aunque los extremos entrecortados necesitan ser eliminados o acelerados.. 

El sonido puede ser un problema a menos que su paso a paso sea muy silencioso, pero para agregar valor de producción a las grabaciones automáticas, es una opción.

Mejoras

Como con cualquier cosa, hay posibles mejoras a realizar. He enumerado estos en la parte superior de la .ino archivo, aunque es cierto que sin especial cuidado sobre la viabilidad ni ordenado por ningún tipo de importancia. 

Algunos de estos consideré arreglarlos antes de lanzar este tutorial con v0.2, pero creo que en sí mismos son una experiencia de aprendizaje a considerar en términos de desmantelamiento mental de la utilidad de un programa..

 MEJORAS Y CONSIDERACIONES HACIA LA V1.0: 1) Eficiencia del código de respuesta del botón del submenú para los tres primeros encabezados del menú 2) El uso del tiempo de obturación de la bombilla como una opción de menú adicional, se pasa al obturador. ) - no se puede sondear el botón de parada! 4) Usar las funciones de la biblioteca EEPROM para ahorrar cantidades, puede simplificar la sección de control de movimiento y usar Restablecer como "parada" 5) Quitar el interruptor del submenú "Ir", reemplazar con una declaración lógica más apropiada 6) ¿Sería mejor cronometrar los pasos de la cámara? en lugar de viajes totales? ¿"Duración" es más como 15 segundos o 2 minutos que 30 minutos o 4 horas? 7) ¿Alguna constricción que sea mejor como #define o ints mejor como boolean? Sin embargo, apenas corre contra los límites del espacio SRAM en 8kB. 8) Interpolación / suavizado para curvas de aceleración, particularmente para uso de video 9) Verificación de error para el tamaño de paso cero, o simplemente agregue uno a la distancia de intervalo si el valor es cero antes de los cálculos - el otro extremo de la distancia es todavía 1 paso 10) Retardo de sub-16 ms () s ser mejor como delayMicroseconds ()? ¿Cuánto interrumpe la interrupción del tiempo? 11) ¿Uso del reposo en A4988 para reducir el consumo de energía en el campo? 12) Comprobación de error para currentDurationInt <= currentStepsInt*shutterDuration, allowing no time for movement or even negative pulseDelay! */

Estas son solo las mejoras en las que he pensado hasta ahora, en un esfuerzo por guiar la base de código desde un v0.2 rudimentario pero funcional hacia un lanzamiento v1.0 más optimizado y más capaz. Puedes notar más. Siéntase libre de dejarlos en los comentarios a continuación o en GitHub.

Terminando

Si ha seguido de principio a fin, incluida la parte de construcción de Tuts + de fotografía, ahora es el orgulloso propietario de un deslizador de cámara motorizado de alta calidad que puede producir