Dolly Photo – quarta parte

Questo articolo riprende e continua il filone del progetto Dolly Photo la nuova versione del Dolly Project che ho realizzato sulla base delle richieste giunte da molti appassionati fotografi e makers come te.

Keypad LCD Dolly Photo

Nella quarta parte del progetto troverai come collegare la motor shield arduino, il motore stepper ed il keypad LCD  e provi il reset del motore.

L’operazione di reset ti consente di verificare che il progetto funzioni e che i collegamenti tra arduino-motor shield-keypad lcd-motore siano corretti.

Il montaggio del Dolly Photo

Questa versione del Dolly Photo è studiata per essere decisamente più semplice da montare rispetto al suo predecessore infatti tutte le shield sono già vendute pre-assemblate.

Puoi semplicemente comporle come in figura:

Dolly Photo shield kit

una sull’altra nell’ordine, partendo dal basso verso l’alto:

  1. Arduino Uno R3
  2. Motor shield Arduino
  3. Keypad LCD

Nella figura sopra vedi anche che l’alimentazione arriva dall’esterno attraverso un trasformatore stabilizzato da 12v che fornisce allo stesso tempo alimentazione a tutto lo stack di schede o panettone ( come alcuni appassionati lo chiamano )

Il motore passo-passo del Dolly Photo

Il motore passo-passo che vedi in figura è quelo che ho utilizzato come dimostrazione e test, quello che trovi nel kit Dolly Photo è lo stesso motore previsto per la versione precedente del Dolly Project:

Motore passo-passo bipolare

in cui puoi riconoscere le fasi osservando questo video:

Il motore passo-passo che ho utilizzato per i test è questo:

DollyPhoto stepper

il modo in cui i due motori sono connessi alla motor shield è lo stesso, cambiano i colori dei fili ma le coppie A e B del motore sono le medesime.

La motor shield arduino per il Dolly Photo

La motor shield arduino hai già avuto modo di vederla all’opera con i motori passo-passo in questo articolo: “Motor Shield R3 Arduino – motore passo-passo” ho deciso di utilizzarla anche per il nuovo Dolly Photo:

Dolly Photo motor shield per dolly

Collega i fili della coppia A sul canale 1 e i fili della coppia B sul canale 2, ricordati di alimentare la shield e tutto arduino con un alimentatore esterno da 12v ed esegui i tuoi test senza la connessione al Pc, ossia con la porta USb scollegata:

DollyPhoto alimentazione

questa situazione ti premette di evitare inconvenienti dovuti a carico eccessivo delle porte USB del tuo computer o peggio di bruciarle se qualche componente non si comporta in modo adeguato.

Lo sketch del DollyPhoto con funzione reset

Ho arricchito lo sketch aggiungendo righe di codice nella funzione reset:

/**********************************************************
 * Dolly Photo Arduino
 *
 * Data creazione 25 febbraio 2013
 *
 * autore: Mauro Alfieri
 * web:    mauroalfieri.it
 * tw:     @mauroalfieri
 *
 *
/**********************************************************/

#include <LiquidCrystal.h>

/**********************************************************/

#define BUTTON A0
#define BACKLIGHT 10
#define MOTDTIME 2000
#define LCDTIME  3000
#define SETMODETIME 2000
#define DEBUG

/**********************************************************/

#define PWMA 3
#define PWMB 11
#define DIRA 12
#define DIRB 13
#define TIMEFASE 35

/**********************************************************/

char line0[16] = "Dolly Foto ver1";
char line1[16] = "mauroalfieri.it";

/**********************************************************/

int SELECT[]     =  {720,760};
int LEFT[]       =  {480,520};
int RIGTH[]      =  {0,20};
int UP[]         =  {120,160};
int DOWN[]       =  {300,350};

int buttonPress  = 0;
int nFase        = 0;

/**********************************************************/

unsigned long timeToBlacklight  =  0;
unsigned long timeToSetMode     =  0;
unsigned long timeSetting       =  0;

/**********************************************************/

boolean blacklightStatus = false;

/**********************************************************/

byte symbolselect[8] = {B00001,B00011,B00111,B01111,B00111,B00011,B00001};
byte symbolplay[8]   = {B10000,B11000,B11100,B11110,B11100,B11000,B10000};
byte symbolstop[8]   = {B00000,B11111,B11111,B11111,B11111,B11111,B00000};
byte symbolpause[8]  = {B00000,B10001,B10001,B10001,B10001,B10001,B00000};

/**********************************************************/
char* principale[4]   =  {"Impostazioni","Verifica","Reset Carrello","Avvio"};
char* secondario[10]  =  {"Numero passi","per scatto","Intervallo tra","i passi","Tempo di ALT","prima","Tempo di ALT","dopo","Numero scatti","totali"};
char buffer[16];

/**********************************************************/

int passiXscatto      = 0;
int intervaloScatto   = 0;
int tempoAltPrima     = 0;
int tempoAltDopo      = 0;
int numeroScatti      = 0;

/**********************************************************/

LiquidCrystal lcd(8,9,4,5,6,7);

/**********************************************************/

void setup() {

  #ifdef DEBUG
    Serial.begin( 9600 );
    Serial.println("Avvio");
  #endif

  /**********************************************************/

  pinMode( BACKLIGHT,OUTPUT );
  digitalWrite( BACKLIGHT,LOW );

  /**********************************************************/

  lcd.begin(16, 2);
  lcd.createChar(1, symbolselect);
  lcd.createChar(2, symbolplay);
  lcd.createChar(3, symbolstop);
  lcd.createChar(4, symbolpause);

  /**********************************************************/

  digitalWrite( BACKLIGHT,HIGH );
  lcd.setCursor(0,0);
  lcd.print(line0);
  lcd.setCursor(0,1);
  lcd.print(line1);
  delay( MOTDTIME );
  lcdBlacklightOff();
}

/**********************************************************/

void loop() {

  /**********************************************************/

  #ifdef DEBUG
    Serial.print( " Button: " );           Serial.print( analogRead( BUTTON ) );
    Serial.print( " TimeToBlecklight " );  Serial.print( timeToBlacklight );
    Serial.print( " TimeToSetHour " );     Serial.println( timeToSetMode );
  #endif

  /**********************************************************/

  if ( ctrlButton( analogRead( BUTTON ) ) == 0 || ctrlButton( analogRead( BUTTON ) ) > 1 ) { timeToSetMode = millis(); }
  if (timeToSetMode > 0 && SETMODETIME < (millis() - timeToSetMode) ) { menuMode(); timeToSetMode = 0; }

  /**********************************************************/

  buttonPress = ctrlButton( analogRead( BUTTON ) );

  /**********************************************************/

  if (  (blacklightStatus) && LCDTIME < (millis() - timeToBlacklight) ) { lcdBlacklightOff(); }

  /**********************************************************/

  char line0[16] = " in attesa di  ";
  char line1[16] = "    comandi ...";

  /**********************************************************/

  lcd.setCursor(0,0);
  lcd.print(line0);
  lcd.setCursor(0,1);
  lcd.print(line1);

  /**********************************************************/
}

/**********************************************************/

int ctrlButton( int button ) {

  buttonPress = 0;
  if ( SELECT[0] <= button && button <= SELECT[1] )  { buttonPress = 1; }
  if ( LEFT[0] <= button && button <= LEFT[1] )      { buttonPress = 2; }
  if ( RIGTH[0] <= button && button <= RIGTH[1] )    { buttonPress = 3; }
  if ( UP[0] <= button && button <= UP[1] )          { buttonPress = 4; }
  if ( DOWN[0] <= button && button <= DOWN[1] )      { buttonPress = 5; }

  if (buttonPress > 0) {
    analogWrite( BACKLIGHT,128 );
    blacklightStatus = true;
    timeToBlacklight = millis();
  }

  return buttonPress;
}

/**********************************************************/

void lcdBlacklightOff() {
  digitalWrite( BACKLIGHT,LOW );
  blacklightStatus = false;
  timeToBlacklight = 0;
  lcd.clear();
}

/**********************************************************/

void menuMode() {

  /**********************************************************/

  #ifdef DEBUG
    Serial.println( "Menu Mode" );
  #endif

  /**********************************************************/

  boolean setMode  = true;
  int setModeLevel = 0;
  timeSetting      = 0;
  lcd.clear();

  /**********************************************************/

  delay( 1000 );

  /**********************************************************/  

  while ( setMode ) {

    /**********************************************************/

    if ( ctrlButton( analogRead( BUTTON ) ) != 0 ) { timeSetting = millis(); }

    if ( (ctrlButton( analogRead( BUTTON )) == 5 ) && setModeLevel < 3 ) { lcd.clear(); setModeLevel++; }
    if ( (ctrlButton( analogRead( BUTTON )) == 4 ) && setModeLevel > 0 ) { lcd.clear(); setModeLevel--; }

    if ( (setModeLevel % 2) == 0 ) {
      lcd.setCursor(0,0);
      lcd.print( principale[setModeLevel] );
      lcd.setCursor(0,1);
      lcd.print( principale[(setModeLevel+1)] );  

      lcd.setCursor(15,0);
      lcd.write(1);
      lcd.setCursor(15,1);
      lcd.print(" ");
    } else {
      lcd.setCursor(0,0);
      lcd.print( principale[(setModeLevel-1)] );
      lcd.setCursor(0,1);
      lcd.print( principale[setModeLevel] );  

      lcd.setCursor(15,0);
      lcd.print(" ");
      lcd.setCursor(15,1);
      lcd.write(1);
    }

    if ( ctrlButton( analogRead( BUTTON )) == 1 ) {
      if ( setModeLevel == 0 ) { impostazioni(); }
      if ( setModeLevel == 1 ) { verifica();     }
      if ( setModeLevel == 2 ) { reset();        }
      if ( setModeLevel == 3 ) { avvio();        }

      setMode = false;
      timeSetting = 0;
    }

    if (timeSetting > 0 && (SETMODETIME*2) < (millis() - timeSetting) ) { setMode = false; }
    delay(200);
  }

  /**********************************************************/

  lcd.clear();
}

/**********************************************************/

void impostazioni() {

  /**********************************************************/

  #ifdef DEBUG
    Serial.println( "Menu Impostazioni" );
  #endif

  /**********************************************************/

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print( "Menu " );
  lcd.setCursor(0,1);
  lcd.print( "Impostazioni" );    

  /**********************************************************/

  delay( SETMODETIME );

  /**********************************************************/

  boolean impostazioniMode  = true;
  int setModeLevel = 0;
  lcd.clear();

  /**********************************************************/

  while ( impostazioniMode ) {

    /**********************************************************/

    if ( (ctrlButton( analogRead( BUTTON )) == 5 ) && setModeLevel < 8 ) { lcd.clear(); setModeLevel = (setModeLevel+2); }
    if ( (ctrlButton( analogRead( BUTTON )) == 4 ) && setModeLevel > 0 ) { lcd.clear(); setModeLevel = (setModeLevel-2); }
    if (  ctrlButton( analogRead( BUTTON )) == 1 )                       { impostazioniMode = false; }

    /**********************************************************/

    #ifdef DEBUG
      Serial.print( "setMenuLevel: " );
      Serial.println( setModeLevel );
    #endif

    /**********************************************************/

    switch ( setModeLevel ) {

      case 0:
        if ( (ctrlButton( analogRead( BUTTON )) == 3 ) && passiXscatto <= 99999)  { passiXscatto++; lcd.clear(); }
        if ( (ctrlButton( analogRead( BUTTON )) == 2 ) && passiXscatto >= 1)      { passiXscatto--; lcd.clear();  }

        sprintf(buffer, "%s %d", secondario[(setModeLevel+1)], passiXscatto);
      break;

      case 2:
        if ( (ctrlButton( analogRead( BUTTON )) == 3 ) && passiXscatto <= 9999999)  { intervaloScatto++; lcd.clear();  }
        if ( (ctrlButton( analogRead( BUTTON )) == 2 ) && passiXscatto >=     1)    { intervaloScatto--; lcd.clear();  }

        sprintf(buffer, "%s %ds", secondario[(setModeLevel+1)], intervaloScatto);
      break;

      case 4:
        if ( (ctrlButton( analogRead( BUTTON )) == 3 ) && tempoAltPrima <= 9999999)  { tempoAltPrima++; lcd.clear();  }
        if ( (ctrlButton( analogRead( BUTTON )) == 2 ) && tempoAltPrima >=     1)    { tempoAltPrima--; lcd.clear();  }

        sprintf(buffer, "%s %ds", secondario[(setModeLevel+1)], tempoAltPrima);
      break;

      case 6:
        if ( (ctrlButton( analogRead( BUTTON )) == 3 ) && tempoAltDopo <= 9999999)  { tempoAltDopo++; lcd.clear();  }
        if ( (ctrlButton( analogRead( BUTTON )) == 2 ) && tempoAltDopo >=     1)    { tempoAltDopo--; lcd.clear();  }

        sprintf(buffer, "%s %ds", secondario[(setModeLevel+1)], tempoAltDopo);
      break;

      case 8:
        if ( (ctrlButton( analogRead( BUTTON )) == 3 ) && numeroScatti <= 9999999)  { numeroScatti++; lcd.clear();  }
        if ( (ctrlButton( analogRead( BUTTON )) == 2 ) && numeroScatti >=     1)    { numeroScatti--; lcd.clear();  }

        sprintf(buffer, "%s %d", secondario[(setModeLevel+1)], numeroScatti);
      break;

    }

    /**********************************************************/

    lcd.setCursor(0,0);
    lcd.print( secondario[setModeLevel] );
    lcd.setCursor(0,1);
    lcd.print( buffer );        

    /**********************************************************/

    delay( 200 );

  }

  /**********************************************************/

  lcd.clear();

}

/**********************************************************/

void verifica() {
  /**********************************************************/

  #ifdef DEBUG
    Serial.println( "Menu Verifica" );
  #endif

  /**********************************************************/

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print( "Menu " );
  lcd.setCursor(0,1);
  lcd.print( "Verifica" );    

  /**********************************************************/

  delay( SETMODETIME );

  /**********************************************************/

  boolean verificaMode  = true;
  int setModeLevel = 0;
  lcd.clear();

  /**********************************************************/

  while ( verificaMode ) {

    /**********************************************************/

    if ( (ctrlButton( analogRead( BUTTON )) == 5 ) && setModeLevel < 8 ) { lcd.clear(); setModeLevel = (setModeLevel+2); }
    if ( (ctrlButton( analogRead( BUTTON )) == 4 ) && setModeLevel > 0 ) { lcd.clear(); setModeLevel = (setModeLevel-2); }
    if (  ctrlButton( analogRead( BUTTON )) == 1 )                       { verificaMode = false; }

    /**********************************************************/

    #ifdef DEBUG
      Serial.print( "setMenuLevel: " );
      Serial.println( setModeLevel );
    #endif

    /**********************************************************/

    switch ( setModeLevel ) {

      case 0:
        sprintf(buffer, "%s %d", secondario[(setModeLevel+1)], passiXscatto);
      break;

      case 2:
        sprintf(buffer, "%s %ds", secondario[(setModeLevel+1)], intervaloScatto);
      break;

      case 4:
        sprintf(buffer, "%s %ds", secondario[(setModeLevel+1)], tempoAltPrima);
      break;

      case 6:
        sprintf(buffer, "%s %ds", secondario[(setModeLevel+1)], tempoAltDopo);
      break;

      case 8:
        sprintf(buffer, "%s %d", secondario[(setModeLevel+1)], numeroScatti);
      break;

    }

    /**********************************************************/

    lcd.setCursor(0,0);
    lcd.print( secondario[setModeLevel] );
    lcd.setCursor(0,1);
    lcd.print( buffer );        

    /**********************************************************/

    delay( 200 );

  }

  /**********************************************************/

  lcd.clear();

}

/**********************************************************/

void reset() {

  /**********************************************************/

  #ifdef DEBUG
    Serial.println( "Menu Reset" );
  #endif

  /**********************************************************/

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print( "Menu " );
  lcd.setCursor(0,1);
  lcd.print( "Reset" );    

  /**********************************************************/

  delay( SETMODETIME );

  /**********************************************************/

  boolean resetMode  = true;
  lcd.clear();

  /**********************************************************/

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print( "LEFT antiorario" );
  lcd.setCursor(0,1);
  lcd.print( "RIGTH orario" );    

  /**********************************************************/

  while ( resetMode ) {

    /**********************************************************/

    if ( ctrlButton( analogRead( BUTTON )) == 3 )  { nFase++; }
    if ( ctrlButton( analogRead( BUTTON )) == 2 )  { nFase--; }
    if ( ctrlButton( analogRead( BUTTON )) == 1 )  { resetMode = false; }

    /**********************************************************/

    nFase = fase( nFase );

    /**********************************************************/

    delay( 100 );
  }

  /**********************************************************/

  stop();

  /**********************************************************/

  lcd.clear();
}

/**********************************************************/

void avvio() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print( "Menu " );
  lcd.setCursor(0,1);
  lcd.print( "Avvio" );    

  delay( SETMODETIME );
}

/**********************************************************/

int fase( int nFase ) {

  if ( nFase > 4 ) { nFase = 1; }
  if ( nFase < 1 ) { nFase = 4; }

  #ifdef DEBUG
      Serial.print( "fase() - nFase: " );
      Serial.println( nFase );
  #endif

  switch( nFase ) {
  case 1:
    digitalWrite(DIRA, HIGH);
    digitalWrite(DIRB, LOW);
    digitalWrite(PWMA, HIGH);
    digitalWrite(PWMB, LOW);
    break;

  case 2:
    digitalWrite(DIRA, LOW);
    digitalWrite(DIRB, HIGH);
    digitalWrite(PWMA, LOW);
    digitalWrite(PWMB, HIGH);
    break;

  case 3:
    digitalWrite(DIRA, LOW);
    digitalWrite(DIRB, LOW);
    digitalWrite(PWMA, HIGH);
    digitalWrite(PWMB, LOW);
    break;

  case 4:
    digitalWrite(DIRA, LOW);
    digitalWrite(DIRB, LOW);
    digitalWrite(PWMA, LOW);
    digitalWrite(PWMB, HIGH);
    break;
  }

  delay(TIMEFASE);

  return nFase;
}

/**********************************************************/

int stop() {
  digitalWrite(DIRA, LOW);
  digitalWrite(DIRB, LOW);
  digitalWrite(PWMA, LOW);
  digitalWrite(PWMB, LOW);
}

/**********************************************************/

ci sono delle nuove funzioni che ho aggiunto e servono per controllare il motore o fermarlo durante la fase di reset e la prossima fase: Avvio.

Le nuove funzioni sono:

reset()

linea 453: definisci la funzione reset() richiamata alla linea 241 dalla menuMode();

linee 457-459: imposta il controllo sulla definizione di DEBUG per scrivere sul monitor seriale il testo “Menu Reset()”;

linee 463-467: scrivi sul display il testo “Menu” sulla prima linea e “Reset” sulla seconda;

linea 471: attendi il tempo definito nella costante SETMODETIME prima di cancellare il testo dall’LCD;

linea 475: inizializza una variabile di tipo boolean ( il cui valore può essere true o false ) denominata resetMode e impostala a true;

linea 476: cancella ogni linea di testo dal Display LCD;

linee 480-484: scrivi sulla prima riga del display del Dolly Photo il testo “LEFT antiorario” e sulla seconda riga il testo “RIGHT orario”;

linea 488: verifica che il valore di restMode sia true e fino a quando non diventa false esegui un ciclo while();

linea 492: controlla quale bottone della keypad LCD viene premuto e se premi RIGHT che la ctrlButton trasforma in 3 allora incrementa la variabile nFase. Utilizzerai tale variabile per far avanzare alla fase successiva il motore;

linea 493: decrementa la variabile nFase se il pulsante che hai premuto è LEFT, tradotto dalla ctrlButton in 2;

linea 494: controlla che il pulsante premuto sia SELECT ( 1 per la ctrlButton ) ed in tal caso imposta la variabile resetMode a false;

linea 498: richiama la funzione fase() passandole il numero di fase da seseguire ed impostando l’output restituito dalla funzione nella medesima variabile nFase. Vedrai in seguito perchè;

linea 502: attendi 200 millisecondi prima di procedere al ciclo while successivo;

linea 507: richiama la funzione stop() che toglie corrente a tutte le fasi del motore fermandolo;

linea 511: cancella dal display LCD tutto il testo per lasciar posto al testo successivo.

fase( nFase )

rispetto alle precedenti versioni di questa funzione troverai alcune differenze che aiutano a rendere il codice esterno più semplice e demandano il controllo della rotazione del motore a questa funzione;

linea 530: verifica che il valore di nFase non sia superiore a 4 in tal caso imposta la variabile a 1 essendo 4 le fasi ( da 1 a 4 );

linea 531: verifica che il valore di nFase non sia inferiore a 1 per evitare che in caso di rotazione inversa il motore finisca in una fase che non  può controllare;

linee 533-536: imposta il DEBUG scrivendo sul monitor seriale il valore di nFase ogni volta che questa funzione viene richiamata;

linee 540-543: per la fase 1 del motore imposta la sequenza: HIGH,LOW,HIGH,LOW sui rispettivi pin della motor shield arduino;

linee 547-564: imposta le altre 3 fasi del motore;

linea 568: attendi per ogni eseguzione di questa funzione un tempo definito nella costante TIMEFASE. Questa attesa serve ad evitare che la velocità con cui Arduino richiama la funzione si rifletta sul motore che non è in grado dieseguirla e resta fermo o peggio oscilla su se stesso. Dovrai variare il valore di TIMEFASE alla linea 30 in funzione del motore che scegli, se ti attieni al motore del kit tale valore è già corretto;

linea 570: ritorna alla funzione chiamante, ossia la reset() in questo caso, il valore di nFase. Avrai quindi capito il perché della ri assegnazione di tale valore alla medesima variabile ( nFase ) alla linea 498. Se nFase è 5 quando avviva alla funzione fase() viene automaticamente corretta in 1, si posiziona il motore in fase 1 e restituisci il nuovo valore.

stop()

è una funzione abbastanza semplice, il cui scopo è evitare che il motore resti alimentato anche nelle fasi di stop, evitando consumo di corrente e sopratutto di surriscaldarlo:

linee 576-579: imposti a LOW tutti i pin relativi alla motor shield arduino;

Il video della funzione reset del Dolly Photo

Come per tutta la serie di articoli dedicati al Dolly Photo ho realizzato anche per questo un video demo:

Buon reset !!!

Prima di inserire un commento, per favore, leggi il regolamento

Permanent link to this article: https://www.mauroalfieri.it/elettronica/dolly-photo-quarta-parte.html

Lascia un commento

Your email address will not be published.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.