domingo, 24 de noviembre de 2013

Slide dolly - electrónica y arduino

Buenas tardes! O tarde de perros en la calle por lo que aprovecho para postear un poco.

Ahora toca la parte de electrónica y el programa de Arduino. La electrónica es muy similar a la utilizada en el robot panorámico, sólo que esta vez utilizo un Arduino Uno en vez del ChipKit, y que ahora sólo hay un motor que mover, por lo que sólo hay un driver L298N. Esta vez no incluyo ningún sensor de temperatura LM35 y todo y que no aparece en el siguiente esquema, hay un ventilador pequeñito de 12v que funciona constantemente para refrigerar el driver del motor NEMA-17. Otra pequeña diferencia es el tele, esta vez alimentado a 5 v y finalmente el tema LCD. En la anterior aplicación usé una shield LCD con pulsadores incorporados , mientras que esta vez usé un LCD I2C con el cual se gana en "disponibilidad de salidas" del Arduino. El I2C es un protocolo de comunicaciones que usa 4 hilos (2 para alimentar el dispositivos y 2 de comunicación que van a los pins SDA y SCL). Si tubiéramos otro dispositivo I2C lo deberíamos conectar en paralelo a los 4 cables sin "gastar" más señales de Arduino. Cada dispositivo tiene una "dirección" que deberemos usar en nuestro programa para definir y que todo funcione. Si no sabemos que dirección deberemos usar programas tipo I2C-Scanner  . Finalmente use 3 pulsadores para seleccionar en los diferentes menús.  Todo ello alimentado por una bateria de 12v y un regulador tipo StepDown de 12 a 5 v.



Id siguiendo los colores, que aunque parezca un lio es muy fácil !

Y ahora el programa de Arduino:

/*
 Dolly lapse
 by XavierGP

 */

Incluimos las librerías Wire, Stepper y LiquidCrystal_I2C

// define the pins used
#include <Wire.h>  // Comes with Arduino IDE
#include <Stepper.h>        // incluimos la Stepper library
#include <LiquidCrystal_I2C.h>

Definimos el numero de pasos del motor, en mi caso es de 200 pasos cada vuelta.

#define motorStepsX 200     // Pasos motor X 360º/1.8º


Definimos los pins digitales de entradas del Arduino, en mi caso utilizo los 5, 6 y 7 que corresponden a cada uno de los 3 pulsadores

left=7;
int right=5;
int enter=6;

Definimos las variables:
* Espacio = recorrido inicial de la dolly (luego en el programa ejecutable la podemos modificar)
* minutos_secuencia = el tiempo que va a durar el time-lapse 
* minimo_minutos_secuencia = es el tiempo mínimo que puede durar el time-lapse, está calculado en base al tiempo necesario en realizar las fotos y todos los movimientos.
* segundos_secuencia = es el tiempo de espera que hay desde que acaba de hacer un movimiento hasta que realiza una toma.(inicialmente está definida como una simple multiplicación en base a los minutos)
* paso = número de pasos a realizar el motor entre diferentes tomas.
* foto = es el pin de salida que nos da señal para hacer una foto (de aqui al relé y luego del rele lo que queráis)
* shot = número de fotos del time-lapse (luego en el programa ejecutable la podemos modificar)
* a = es un bit que utilizo como memoria en los bucles internos del programa.
* porcentaje = es el % del proyecto realizado.

int espacio=500;        // recorrido dolly 
int minutos_secuencia;   // Variable pre definida
int minimo_minutos_secuencia;
float segundos_secuencia=minutos_secuencia*60;
int paso;
float posicion_actual;
int foto=12;  // salida remoto camara
int shot=100;    //variable pre definida
float tiempo_reloj;
int a=0; // bucles
float porcentaje;

Definimos los pins de salida del arduino hacia el driver L298N, en este caso serán el 8,9,10 y 11

Stepper myStepperX(motorStepsX, 8,9,10,11); // libreria motor X   3,13,12,11

Definimos la dirección I2C del la LCD

 LiquidCrystal_I2C lcd(0x20, 16,2);  // Set the LCD I2C address


Realizamos el set-up para que entienda el Arduino como vamos a utilizar los pins, en este caso left (pin 7) , right (pin 5) , enter (pin 6) son entradas y foto (12) es una salida. 

void setup() {
pinMode(left, INPUT);
pinMode(right, INPUT);
pinMode(enter, INPUT);
pinMode(foto, OUTPUT);


Inicializamos en LCD

lcd.init();

Definimos la velocidad del motor 

myStepperX.setSpeed(12); // definimos velocidad impulsos motor

Encendemos iluminación de la pantalla LCD

lcd.backlight();

Mensaje de Bienvenida !

lcd.setCursor(0,0); 
lcd.print("Hello, XavierGP!");
delay (1000);
lcd.clear();

Insertamos la primera parte del "gif casero" (cámara fotos)

logo0();
logo1();

Y continuamos con el resto de "presentación de bienvenida":

lcd.setCursor(2,0);
lcd.print("Slide");
delay(700);
logo2();
lcd.setCursor(0,1);
lcd.print("dolly v2.0");
delay(700);
logo1();
delay (2000);
lcd.clear();

Llamamos a la subrutina define_espacio (dónde definiremos la longitud del movimiento de nuestra dolly)

define_espacio();

Llamamos a la subrutina define_fotos (dónde definiremos el número de fotos que queremos realizar)

define_fotos();

En base al numero de fotos y distancia a recorrer se gestiona el tiempo mínimo necesario para realizar las tomas. 

minutos_secuencia=(espacio/125)+1+(0.02*shot);  // original minutos_secuencia=(espacio/410)+1+(0.02*shot)
minimo_minutos_secuencia=minutos_secuencia;
segundos_secuencia=minutos_secuencia*60;

Una vez tenemos definido el tiempo mínimo, lo podremos incrementar en la subrutina define_tiempo

define_tiempo();


Ahora es el tiempo de establecer el tiempo entre fotos (desde que acabamos un movimiento y hasta que realizamos una nueva foto) y también establecemos el número de pasos a realizar por el motor entre las diferentes capturas

lcd.clear();
segundos_secuencia=(minutos_secuencia*60)/(shot+1);
paso=(12800/2000)*(espacio)/(shot);  //originalment /2100

segundos_secuencia=(segundos_secuencia-1-(paso*0.075)); //original *0.024

Nos preparamos para empezar. Sólo hay que pulsar sobre ENTER para que empieze la secuencia

lcd.print("[ENTER] = Start");
lcd.setCursor(0,1);

Hacemos un barrido de puntos (mariconadas...jeje)

for (int k=0 ; k<16; k++){
  lcd.print(".");
  delay(100);
}

Provocamos una pausa mientras a=0 (hasta que pulsemos ENTER), momento en que a será 1 y saldremos del bucle.

a=0; // provoquem pausa
while(a==0){
if(digitalRead(enter)==HIGH){
a=1;
}
}

Empezamos la sequencia!

lcd.clear();

Definimos el numero de veces a realizar el bucle (numero de fotos menos 1)

for(int i = 1; i< shot; i++){
    
Mostramos un gif de camara de fotos, y mostramos la información en la pantalla

    logo0();
    logo1();

Comprobamos cuantas fotos hay tomadas , si es inferior a 100 ponemos un espacio en blanco, y si es inferior a 10, ponemos dos espacios en blanco, asi queda todo siempre ordenado!
    
    lcd.setCursor(1,0);
    if(i<=99){
    lcd.print(" ");
    }
    if(i<=9){
    lcd.print(" ");
    }
    
    lcd.print(i);
    lcd.print("s");
    
    Mostramos el porcentaje de ejecución del proyecto
    
    lcd.setCursor(7,0);
    lcd.print(i*100/shot);
    lcd.print("%");
  
    En el momento de realizar una captura nos aparece la palabra FOTO! en el LCD, cambia el gif de la cámara como si cerrara el diafragma y activamos el relé durante 0.3 segundos. Acto seguido, desactivamos el relé del comando remoto y cambiamos el gif.

 lcd.setCursor(0,1);
 lcd.print("FOTO!"); 
  digitalWrite(foto, HIGH);
  logo2();
  delay(300);
  digitalWrite(foto, LOW);
  delay(300);
  logo1();

 Ahora es hora de mover la dolly, en la LCD nos muestra la palabra MOVE! y se desplaza el carro. Una vez llegado a destino y parado, desaparece la palabra MOVE! y modificamos la variable  del valor de la posición del carro.

 lcd.setCursor(0,1);
 lcd.print("MOVE!");
 myStepperX.step(paso);
 lcd.setCursor(0,1);
 lcd.print("     ");
  posicion_actual=(posicion_actual+(paso*0.165));
  
Mostramos en la pantalla el tiempo restante hasta la siguiente foto en unidades de décima de segundo, junto a los valores de posiciones

 for (int j=0; j<(segundos_secuencia*10); j++){
    
    delay (100);
    lcd.setCursor(0,1);
    tiempo_reloj=j*0.1;
    if ((segundos_secuencia-tiempo_reloj)<10){
      lcd.print(" ");
    }
    lcd.print(segundos_secuencia-tiempo_reloj);
    lcd.setCursor(4,1);
    lcd.print(char(34));
    lcd.print(" ");
    lcd.setCursor(6,1);

Insertamos espacios en el valor de posicionamiento del carro (al igual que hicimos anteriormente con el valor de numero de fotos)

    if(posicion_actual<1000){
      lcd.print(" ");
    }
    if(posicion_actual<100){
      lcd.print(" ");
    }
    lcd.print(posicion_actual,0);
    lcd.setCursor(10,1);
    lcd.print("mm");
    
  }
}

Ahora es la hora de realizar la última toma y de mostrarnos el mensaje de MotionLapse Completo!

logo0();
logo1();
lcd.setCursor(0,1);
lcd.print("     ");
digitalWrite(foto, HIGH);
  logo2();
  delay(500);
  digitalWrite(foto, LOW);
  delay(100);
  logo1();
  lcd.setCursor(0,0);
  lcd.print("MotionLapse");
  lcd.setCursor(0,1);
  lcd.print(" completo!  ");
}

Aqui es dónde debería estar el programa para que fuera cíclico, pero como que no interesa, lo dejo en vacío, así una vez terminado todo el ciclo se queda todo "bloqueado" hasta que no quitemos tensión evitando ningún movimiento sorpresa.

void loop(){             // ********************* loop ***********
     
}

Subrutina dónde definimos el espacio a recorrer, con los pulsadores LEFT,RIGHT desplazaremos el punto final del carro (el inicial es dónde lo tenemos aparcado cuando damos tensión inicial al sistema). Finalmente validaremos con ENTER. Está "capado" a 2000 mm ! Al igual que en otras partes del programa se insertan espacios en blanco cada vez que pasamos de valores de miles a centenares o a décimas del valor.

void define_espacio(){
a=0;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Define  espacio"); 
lcd.setCursor(3,1);
if(espacio<1000){
  lcd.print(" ");
}
lcd.print(espacio);
lcd.setCursor(9,1);
lcd.print("mm.");
delay(200);
while(a==0){
if(digitalRead(enter)==HIGH){
a=1;
}
delay(150);
if(digitalRead(right)==HIGH){
if (espacio<2000){       // recorrido máximo, modificar esta variable si es necesario.
espacio=espacio+100;
lcd.setCursor(3,1);
if(espacio<1000){
  lcd.print(" ");
}
lcd.print(espacio);
lcd.setCursor(7,1);
lcd.print("  mm.  ");
}
}

if(digitalRead(left)==HIGH){
if (espacio>100){
espacio=espacio-100;
lcd.setCursor(3,1);
if(espacio<1000){
  lcd.print(" ");
}
lcd.print(espacio);
lcd.setCursor(7,1);
lcd.print("  mm.  ");
}
}
}

Subrutina de definición del tiempo de la secuencia total. Aqui al igual que en la subrutina anterior, podemos incrementar (existe el valor mínimo explicado anteriormente) o decrementar el tiempo necesario. Validaremos también con ENTER.

void define_tiempo(){
a=0;
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Define  tiempo"); 
lcd.setCursor(5,1);
lcd.print(minutos_secuencia);
lcd.setCursor(8,1);
lcd.print("min.");
delay(200);
while(a==0){

if(digitalRead(enter)==HIGH){
a=1;
}
delay(150);
if(digitalRead(right)==HIGH){
if (minutos_secuencia<250){
minutos_secuencia=minutos_secuencia+1;
lcd.setCursor(4,1);
if(minutos_secuencia<100){
  lcd.print(" ");
}
lcd.print(minutos_secuencia);
lcd.print(" min.  ");
}
}

if(digitalRead(left)==HIGH){
if (minutos_secuencia>minimo_minutos_secuencia){
minutos_secuencia=minutos_secuencia-1;
lcd.setCursor(4,1);
if(minutos_secuencia<100){
  lcd.print(" ");
}
lcd.print(minutos_secuencia);
lcd.print(" min.  ");
}
}
}

Subrutina dónde definimos el número de tomas a realizar, más de lo mismo, la lógica utilizada es la misma que en las anteriores subrutinas.

void define_fotos(){
a=0;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Define num fotos"); 
lcd.setCursor(4,1);
lcd.print(shot);
lcd.setCursor(8,1);
lcd.print("fotos");
delay(200);
while(a==0){

if(digitalRead(enter)==HIGH){
a=1;
}
delay(150);
if(digitalRead(right)==HIGH){
if (shot<250){
shot=shot+1;
lcd.setCursor(4,1);
if (shot<100){
  lcd.print(" ");
}
lcd.print(shot);
lcd.setCursor(8,1);
lcd.print("fotos");
}
}

if(digitalRead(left)==HIGH){
if (shot>10){
shot=shot-1;
lcd.setCursor(4,1);
if (shot<100){
  lcd.print(" ");
}
lcd.print(shot);
lcd.setCursor(8,1);
lcd.print("fotos");
}
}
}

En las siguientes sub-rutinas void logoX() es dónde defino los caracteres especiales correspondiente al "gif casero" en que aparece una cámara fotográfica realizando foto. No tiene mucho misterio , cada una de las 7 lineas que hay en cada caracter está compuesta de 5 columnas. Si queremos que aparezca un pixel marcado deberemos poner un 1 de lo contrario un 0.

void logo0(){
byte logo1a[8] = {B01111, B00110, B01111, B11111, B11111, B11111, B11110, B11110};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
byte logo1e[8] = {B11110, B11110, B11110, B11111, B11111, B11111, B11111, B01111};
byte logo1h[8] = {B01111, B01111, B01111, B11111, B11111, B11111, B11111, B11110};
lcd.createChar(1, logo1a);
lcd.createChar(4, logo1d);
lcd.createChar(5, logo1e);
lcd.createChar(8, logo1h);
}
  
void logo1(){
byte logo1b[8] = {B00000, B00111, B11100, B10001, B00110, B01100, B01000, B10000};
byte logo1c[8] = {B00000, B11100, B00111, B10001, B01100, B00110, B00010, B00001};
byte logo1f[8] = {B10000, B10000, B01000, B01100, B00110, B10001, B11100, B11111};
byte logo1g[8] = {B00001, B00001, B00011, B00110, B01100, B10001, B00111, B11111};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
lcd.createChar(2, logo1b);
lcd.createChar(3, logo1c);
lcd.createChar(6, logo1f);
lcd.createChar(7, logo1g);
lcd.createChar(4, logo1d);
lcd.setCursor(12,0);
lcd.write(1);
lcd.setCursor(13,0);
lcd.write(2);
lcd.setCursor(14,0);
lcd.write(3);
lcd.setCursor(15,0);
lcd.write(4);
lcd.setCursor(12,1);
lcd.write(5);
lcd.setCursor(13,1);
lcd.write(6);
lcd.setCursor(14,1);
lcd.write(7);
lcd.setCursor(15,1);
lcd.write(8);
}

void logo2(){
byte logo1b[8] = {B00000, B00111, B11100, B10001, B00111, B01111, B01111, B11111};
byte logo1c[8] = {B00000, B11100, B00111, B10001, B11100, B11110, B11110, B11111};
byte logo1f[8] = {B11110, B11111, B01111, B01111, B00111, B10001, B11100, B11111};
byte logo1g[8] = {B01111, B11111, B11111, B11110, B11100, B10001, B00111, B11111};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
lcd.createChar(2, logo1b);
lcd.createChar(3, logo1c);
lcd.createChar(6, logo1f);
lcd.createChar(7, logo1g);
lcd.createChar(4, logo1d);
lcd.setCursor(12,0);
lcd.write(1);
lcd.setCursor(13,0);
lcd.write(2);
lcd.setCursor(14,0);
lcd.write(3);
lcd.setCursor(15,0);
lcd.write(4);
lcd.setCursor(12,1);
lcd.write(5);
lcd.setCursor(13,1);
lcd.write(6);
lcd.setCursor(14,1);
lcd.write(7);
lcd.setCursor(15,1);
lcd.write(8);
}

Un pequeño video de como funciona el programa:



Sólo os faltan buscar las librerías que no tengáis por internet ! Y el programa entero (mejor para hacer un copiar/pegar no ? ):

/*
 Dolly lapse
 by XavierGP

 */

// define the pins used
#include <Wire.h>  // Comes with Arduino IDE
#include <Stepper.h>        // incluimos la Stepper library
#include <LiquidCrystal_I2C.h>
#define motorStepsX 200     // Pasos motor X 360º/1.8º
int left=7;
int right=5;
int enter=6;
int espacio=500;        // recorrido dolly 
int minutos_secuencia;   // Variable pre definida
int minimo_minutos_secuencia;
float segundos_secuencia=minutos_secuencia*60;
int paso;
float posicion_actual;
int foto=12;  // salida remoto camara
int shot=100;    //variable pre definida
float tiempo_reloj;
int a=0; // bucles
float porcentaje;

Stepper myStepperX(motorStepsX, 8,9,10,11); // libreria motor X   3,13,12,11
LiquidCrystal_I2C lcd(0x20, 16,2);  // Set the LCD I2C address
void setup() {
pinMode(left, INPUT);
pinMode(right, INPUT);
pinMode(enter, INPUT);
pinMode(foto, OUTPUT);

lcd.init();
myStepperX.setSpeed(12); // definimos velocidad impulsos motor
lcd.backlight();
lcd.setCursor(0,0); 
lcd.print("Hello, XavierGP!");
delay (1000);
lcd.clear();
logo0();
logo1();
lcd.setCursor(2,0);
lcd.print("Slide");
delay(700);
logo2();
lcd.setCursor(0,1);
lcd.print("dolly v2.0");
delay(700);
logo1();
delay (2000);
lcd.clear();


define_espacio();
define_fotos();
minutos_secuencia=(espacio/125)+1+(0.02*shot);  // original minutos_secuencia=(espacio/410)+1+(0.02*shot)
minimo_minutos_secuencia=minutos_secuencia;
segundos_secuencia=minutos_secuencia*60;

define_tiempo();

lcd.clear();
segundos_secuencia=(minutos_secuencia*60)/(shot+1);
paso=(12800/2000)*(espacio)/(shot);  //originalment /2100

segundos_secuencia=(segundos_secuencia-1-(paso*0.075)); //original *0.024

lcd.print("[ENTER] = Start");
lcd.setCursor(0,1);
for (int k=0 ; k<16; k++){
  lcd.print(".");
  delay(100);
}

a=0; // provoquem pausa
while(a==0){
if(digitalRead(enter)==HIGH){
a=1;
}
}
lcd.clear();
 for(int i = 1; i< shot; i++){
    
    logo0();
    logo1();
    lcd.setCursor(1,0);
    if(i<=99){
    lcd.print(" ");
    }
    if(i<=9){
    lcd.print(" ");
    }
    
    lcd.print(i);
    lcd.print("s");
    lcd.setCursor(7,0);
    lcd.print(i*100/shot);
    lcd.print("%");
  
 lcd.setCursor(0,1);
 lcd.print("FOTO!"); 
  digitalWrite(foto, HIGH);
  logo2();
  delay(300);
  digitalWrite(foto, LOW);
  delay(300);
  logo1();
 lcd.setCursor(0,1);
 lcd.print("MOVE!");
 myStepperX.step(paso);
 lcd.setCursor(0,1);
 lcd.print("     ");
  posicion_actual=(posicion_actual+(paso*0.165));
  
  for (int j=0; j<(segundos_secuencia*10); j++){
    
    delay (100);
    lcd.setCursor(0,1);
    tiempo_reloj=j*0.1;
    if ((segundos_secuencia-tiempo_reloj)<10){
      lcd.print(" ");
    }
    lcd.print(segundos_secuencia-tiempo_reloj);
    lcd.setCursor(4,1);
    lcd.print(char(34));
    lcd.print(" ");
    lcd.setCursor(6,1);
    if(posicion_actual<1000){
      lcd.print(" ");
    }
    if(posicion_actual<100){
      lcd.print(" ");
    }
    lcd.print(posicion_actual,0);
    lcd.setCursor(10,1);
    lcd.print("mm");
    
  }
  
}
logo0();
logo1();
lcd.setCursor(0,1);
lcd.print("     ");
digitalWrite(foto, HIGH);
  logo2();
  delay(500);
  digitalWrite(foto, LOW);
  delay(100);
  logo1();
  lcd.setCursor(0,0);
  lcd.print("MotionLapse");
  lcd.setCursor(0,1);
  lcd.print(" completo!  ");
}

void loop(){             // ********************* loop ***********
     
}

void define_espacio(){
a=0;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Define  espacio"); 
lcd.setCursor(3,1);
if(espacio<1000){
  lcd.print(" ");
}
lcd.print(espacio);
lcd.setCursor(9,1);
lcd.print("mm.");
delay(200);
while(a==0){
if(digitalRead(enter)==HIGH){
a=1;
}
delay(150);
if(digitalRead(right)==HIGH){
if (espacio<2000){
espacio=espacio+100;
lcd.setCursor(3,1);
if(espacio<1000){
  lcd.print(" ");
}
lcd.print(espacio);
lcd.setCursor(7,1);
lcd.print("  mm.  ");
}
}

if(digitalRead(left)==HIGH){
if (espacio>100){
espacio=espacio-100;
lcd.setCursor(3,1);
if(espacio<1000){
  lcd.print(" ");
}
lcd.print(espacio);
lcd.setCursor(7,1);
lcd.print("  mm.  ");
}
}
}

void define_tiempo(){
a=0;
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Define  tiempo"); 
lcd.setCursor(5,1);
lcd.print(minutos_secuencia);
lcd.setCursor(8,1);
lcd.print("min.");
delay(200);
while(a==0){

if(digitalRead(enter)==HIGH){
a=1;
}
delay(150);
if(digitalRead(right)==HIGH){
if (minutos_secuencia<250){
minutos_secuencia=minutos_secuencia+1;
lcd.setCursor(4,1);
if(minutos_secuencia<100){
  lcd.print(" ");
}
lcd.print(minutos_secuencia);
lcd.print(" min.  ");
}
}

if(digitalRead(left)==HIGH){
if (minutos_secuencia>minimo_minutos_secuencia){
minutos_secuencia=minutos_secuencia-1;
lcd.setCursor(4,1);
if(minutos_secuencia<100){
  lcd.print(" ");
}
lcd.print(minutos_secuencia);
lcd.print(" min.  ");
}
}
}

void define_fotos(){
a=0;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Define num fotos"); 
lcd.setCursor(4,1);
lcd.print(shot);
lcd.setCursor(8,1);
lcd.print("fotos");
delay(200);
while(a==0){

if(digitalRead(enter)==HIGH){
a=1;
}
delay(150);
if(digitalRead(right)==HIGH){
if (shot<250){
shot=shot+1;
lcd.setCursor(4,1);
if (shot<100){
  lcd.print(" ");
}
lcd.print(shot);
lcd.setCursor(8,1);
lcd.print("fotos");
}
}

if(digitalRead(left)==HIGH){
if (shot>10){
shot=shot-1;
lcd.setCursor(4,1);
if (shot<100){
  lcd.print(" ");
}
lcd.print(shot);
lcd.setCursor(8,1);
lcd.print("fotos");
}
}
}

void logo0(){
byte logo1a[8] = {B01111, B00110, B01111, B11111, B11111, B11111, B11110, B11110};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
byte logo1e[8] = {B11110, B11110, B11110, B11111, B11111, B11111, B11111, B01111};
byte logo1h[8] = {B01111, B01111, B01111, B11111, B11111, B11111, B11111, B11110};
lcd.createChar(1, logo1a);
lcd.createChar(4, logo1d);
lcd.createChar(5, logo1e);
lcd.createChar(8, logo1h);
}
  
void logo1(){
byte logo1b[8] = {B00000, B00111, B11100, B10001, B00110, B01100, B01000, B10000};
byte logo1c[8] = {B00000, B11100, B00111, B10001, B01100, B00110, B00010, B00001};
byte logo1f[8] = {B10000, B10000, B01000, B01100, B00110, B10001, B11100, B11111};
byte logo1g[8] = {B00001, B00001, B00011, B00110, B01100, B10001, B00111, B11111};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
lcd.createChar(2, logo1b);
lcd.createChar(3, logo1c);
lcd.createChar(6, logo1f);
lcd.createChar(7, logo1g);
lcd.createChar(4, logo1d);
lcd.setCursor(12,0);
lcd.write(1);
lcd.setCursor(13,0);
lcd.write(2);
lcd.setCursor(14,0);
lcd.write(3);
lcd.setCursor(15,0);
lcd.write(4);
lcd.setCursor(12,1);
lcd.write(5);
lcd.setCursor(13,1);
lcd.write(6);
lcd.setCursor(14,1);
lcd.write(7);
lcd.setCursor(15,1);
lcd.write(8);
}

void logo2(){
byte logo1b[8] = {B00000, B00111, B11100, B10001, B00111, B01111, B01111, B11111};
byte logo1c[8] = {B00000, B11100, B00111, B10001, B11100, B11110, B11110, B11111};
byte logo1f[8] = {B11110, B11111, B01111, B01111, B00111, B10001, B11100, B11111};
byte logo1g[8] = {B01111, B11111, B11111, B11110, B11100, B10001, B00111, B11111};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
lcd.createChar(2, logo1b);
lcd.createChar(3, logo1c);
lcd.createChar(6, logo1f);
lcd.createChar(7, logo1g);
lcd.createChar(4, logo1d);
lcd.setCursor(12,0);
lcd.write(1);
lcd.setCursor(13,0);
lcd.write(2);
lcd.setCursor(14,0);
lcd.write(3);
lcd.setCursor(15,0);
lcd.write(4);
lcd.setCursor(12,1);
lcd.write(5);
lcd.setCursor(13,1);
lcd.write(6);
lcd.setCursor(14,1);
lcd.write(7);
lcd.setCursor(15,1);
lcd.write(8);
}

Si tenéis dudas, ya sabeis, pedid que yo intentaré responder cuando pueda!
Saludos y DIY (Do IT Yourself) !





24 comentarios:

  1. me parece un curro DE LA OXT!, y, lo que lo hace más importante aun, una explicación de la REOXT! :) Estoy retomando el asunto, y aunque el concepto es parecido, mi camino es mu distinto :/ Pero tu ayuda es inestimable :) Ole por el currazo! :)

    ResponderEliminar
  2. Fantástico trabajo, enhorabuena.
    Puntualizar un detalle, según un autor de las librerias I2C es recomendable colocar unas resistencias, te dejo el enlace:
    http://garagelab.com/profiles/blogs/tutorial-lcd-using-only-2-arduino-pins-with-pcf8574-and-i2c

    ResponderEliminar
  3. Grácias por la info Aficionado!
    Lo tendré en cuenta, pero hasta el momento , y con 4 dispositivos que he montado nunca me han dado problemas. Es más hay 3 de ellos que están en funcionamiento las 24 horas del dia (2 relojes tipo DS1307 y un sensor barométrico)
    Pero si lo dice el que lo diseñó...yo me callo!

    ResponderEliminar
  4. Gracias a ti.
    Con respecto al relé, tienes la alternativa de utilizar un optoacoplador que además de aislar la cámara tiene una respuesta mas rápida que el relé, aunque no creo que sea un requisito fundamental la velocidad de respuesta en time-lapse.
    Por otra parte quería preguntarte si no te has planteado incluir un segundo relé o optoacoplador y además de disparar poder controlar el enfoque de la cámara?
    Y por último el desplazamiento del carro permitiría usar una cámara de video? (Me refiero si el movimiento sería lo suficientemente suave para este uso.

    ResponderEliminar
  5. Lo del tema optoacoplador soy un poco reacio, más que nada porque me "pone" el oir el click del enclavamiento del relé. Todo y que debo reconocer que una vez usé unos EL817. El tiempo de "retardo" del enclavamiento no me es un tema nada preocupante, al menos en este invento, siempre hay detalles mismos de programación que pueden hacer ir al proyecto más rápido, pero en este caso no es necesario.
    Lo de montar el sistema de enfoque de cámara lo estoy experimentando con un servo MG995 también gestionado por Arduino, pero en vez de jugar con el anillo de enfoque uso el anillo de focal, pero creo que es un tema "delicado" ya que si luego dejas la camara en autofocus, a saber si alguna vez no la lía jiji.
    Sinceramente no he probado de grabar un video (no corre mucho esta dolly, quizas 1 metro por minuto). Algún dia lo pruebo....buff cuantas cosas por hacer y probar!

    Gracias por tus comentarios y visita Aficionado!

    ResponderEliminar
  6. Tal vez no me he explicado la idea del enfoque, tienes toda la razón, yo también suelo evitarlo en este tipo de tomas, pero como decía el enfoque la idea es activarlo durante un tiempo prefijado antes de disparar, esto lo podrías realizar con otro relé (si es lo que te gusta) y mediante la programación decidir si se usa o no se usa y en caso afirmativo cuanto tiempo quieres que actue el autofocus. Esta es una técnica que he visto que se usa especialmente para timelapse de naturaleza, lógicamente en casos muy concretos, pero que creo que seria un completo ideal para este proyecto tuyo. Saludos.

    ResponderEliminar
  7. Hola, si quisira poner un display lcd gráfica 128x64 por ejemplo, ahí que modificar mucho el programa.
    Saludos

    ResponderEliminar
  8. Hola Jus!
    dame más pistas ! No sé a que tipo de pantalla te refieres. Yo utilizo la típica de 16 caracteres y dos lineas alimentada via I2C, que debe tener Cada caracter creo recordar que son 7 filas y 5 columnas.
    Tu pantalla funciona via I2C ?

    ResponderEliminar
  9. Hola,
    La he montado con una 16x2 líneas y funciona bien la duda es si sería muy complicado en tema de código poner una de estas gráficas por ejemplo una de un nokia 3310.
    Y tengo otra duda para poner dos pulsadores más para el motor ande de forma manual, me refiero para poder mover la cámara de forma manual y ponerla en una posición o otra.
    Gracias por responder

    ResponderEliminar
  10. Yo creo que no deberia ser difícil... has visto esta web ? http://learn.adafruit.com/nokia-5110-3310-monochrome-lcd/overview aqui bien conectan el LCD con el Arduino. Te hara falta posiblemente una libreria que puedes descargar desde esa web.

    Segun el programa que me has enviado, no costaria mucho hacer mover el motor paso a paso, pero yo te recomendaria que utilizaras la libreria Stepper que viene instalada en el software de Arduino, reducirias el programa y lo simplificarias. En mi caso seria tan simple como:

    if(digitalRead( PinEntradaPulsadorAvance )==HIGH){
    myStepperX.step( PasosAvanzar );
    }

    if(digitalRead( PinEntradaPulsadorRetroceso )==HIGH){
    myStepperX.step(- PasosRetroceder );
    }

    ResponderEliminar
  11. Gran tutorial. Hay alguna razón por la cuál has puesto 12 (rpm) como velocidad impulsos motor, cómo has definido ese 12?

    Por ejemplo, pruebo poner 2000mm, 10 fotos en 17 minutos y el motor apenas se mueve. solo vibra!
    un saludo!



    ResponderEliminar
  12. Hola javier desde Mexico, has intentado hacerlo con motor Dc?, y con el mismo rive l298d para hacer la prueba estaria genial. ya que estos motores no ocupan mucho voltaje y tienen muchisimo mas torque en vertical.

    ResponderEliminar
  13. Hola Julio!
    No lo he probado. La "gracia" de hacerlo con steppers es que si no se descuenta ni pierde ningún paso es facil tener la posición definida. COn un motor DC habria que poner un encoder y eso complicaria más el programa.
    ¿ tienes conocimientos de encoders con arduinp?
    Saludos!

    ResponderEliminar
  14. Hola Xavier.

    Pues agradecer tu esfuerzo y el que lo hayas compartido con tan buena explicación.

    Acabo de terminar mi Dolly y con muy poquitos ajustes ya funciona de maravilla :)

    Un abrazo

    ResponderEliminar
  15. Hola, esta buenísimo el tutorial y voy a ver si lo puedo realizar! quería saber si a la programación también le podemos agregar para setear el tiempo de parada de la doly para cuando hace la foto (así podríamos setear mayores tiempos de exposición) muchas gracias! espero me puedas ayudar

    ResponderEliminar
  16. Javier, eres un maestro, me encanta el curso de programación que al que nos has sometido..jajjajaja el proyecto te ha quedado genial y agradezco el que lo compartas.. tengo una consulta sobre este tu proyecto. A la hora de disparar la cámara, ¿el tiempo de obturación o exposición de la captura, dónde se tiene en cuenta? a ver si me explico... si yo quisiera tomar fotos de un intervalo concreto, pongamos 24seg de exposición,¿como haría para que ese tiempo pudiera ser programado? imagina que quiero hacer un time lapse nocturno, donde quiero que haga 700 fotos y con una cadencia asi: activo motor(anda un número de pasos)se detiene, espera dos segundos para estabilizar la cámara, dispara la cámara por 10, 15, 50seg ò 1/240...cualquier tiempo definido, cierra el obturador, espera 500mseg, vuelve a conectar el motor y así hasta que llegue o al final de carrera, o al numero de tomas, ò a la distancia requerida... es posible??

    La idea es poder decidir el tiempo de obturación por encima de todo, no me importa la distancia pues la mayor parte de las veces uso el slider de extremo a extremo. lo ideal sería poder decidir el tiempo total empleado., ejpl haz 700 tomas en 7 horas y que cada toma tenga esta determinada duración y que él solo decida a la velocidad y el tamaño de los pasos. crees que serías capaz de ayudarme en esto. quizás tú también le encuentres utilidad. Muchisimas gracias artista.

    ResponderEliminar
  17. Hola, en primer lugar agradecerte todo el trabajo que has hecho, he mirado varias opciones para un slider motorizado y este es el mejor que he visto.

    Escribo para comentar algunos problemas que he tenido con el display, al compilar el programa me daba errores con la librería LiquidCrystal_I2C, al final conseguí que funcionara bien con las librerías LiquidCrystal_I2C-1.1.1 o LiquidCrystal_I2C-1.1.2, con otras me daba errores, hay que tener en cuenta que hay varias librerías para el display.

    Otro problema es que al ejecutar el programa no salía nada en el display, tuve que cambiar la línea LiquidCrystal_I2C lcd(0x20, 16,2) por LiquidCrystal_I2C lcd(0x27, 16,2) es decir la dirección 20 por la 27 , ahora me funciona perfectamente.

    Lo he probado en con un Arduino UNO y con un Arduino Mini en una placa de prototipos simulando la cámara y el motor con leds y funciona perfectamente.

    Muchas gracias por todo el proyecto.

    ResponderEliminar
    Respuestas
    1. Buenos dias Alberto!
      Uno de los "problemas" habituales con los códigos vienen dados por cambios en el hardware o en las librerias, pero por lo que comentas lo has resuelto.
      Deseo que te sirva el programa de "base" y puedas implementar nuevas mejoras .
      Saludos!

      Eliminar
  18. Hola Xavier, gran trabajo el que has realizado y sobre todo muchas gracias por compartirlo. Enhorabuena.
    Necesito algo de ayuda, el programa lo he cargado en arduino y me compila prefectamente, el problema lo tengo con el lcd, solo se ilumina la linea superio marcando los rectangulos, la direccion la tenia mal, tenia 20 en lugar de 27, lo he corregido y me hace lo mismo, sabrias decirme donde puede estar el problema? Le he cargado un sketch sencillo y si que visualizo el mensaje, hasta he dejado esa libreria y a continuacion le he cargado tu sketch sin la libreria esa y me hace lo mismo. Sabes que podria ser?

    gracias y un saludo.

    David

    ResponderEliminar
    Respuestas
    1. tengo el mismo problema y no tengo ni idea q hacer....

      Eliminar
  19. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  20. al cargar me sale este problema alguien sabría la solución.

    Arduino:1.8.6 Hourly Build 2018/06/18 03:33 (Windows 10), Tarjeta:"Arduino/Genuino Uno"

    El Sketch usa 9574 bytes (29%) del espacio de almacenamiento de programa. El máximo es 32256 bytes.
    Las variables Globales usan 581 bytes (28%) de la memoria dinámica, dejando 1467 bytes para las variables locales. El máximo es 2048 bytes.
    avrdude: ser_open(): can't open device "\\.\COM1": El sistema no puede encontrar el archivo especificado.


    Problema subiendo a la placa. Visita http://www.arduino.cc/en/Guide/Troubleshooting#upload para sugerencias.

    Este reporte podría tener más información con
    "Mostrar salida detallada durante la compilación"
    opción habilitada en Archivo -> Preferencias.

    ResponderEliminar
  21. Hola XavierGP, lo primero felicitarte por tu trabajo y lo segundo yo tengo una lcd 1602 KeyPad Shield con 6 botones incorporados y no soy capaz de cambiar los parámetros de la programación para que me funcione con dicha lcd, creo entender por tus palabra que el primero lo hiciste usando dicha lcd. Si fueras tan amable de ayudarme te estaría muy agradecido. Un saludo y de nuevo muy buen trabajo.

    ResponderEliminar
  22. Hola XavierGP,lo primero felicitarte por el curro que te has dado y por compartirlo.Una pregunta de los 2 Skecth que has subido cual es el bueno?pues primero que tiene mas lineas me da error y el segundo si me compila bien, son el mismo modificado?.Un saludo José

    ResponderEliminar