Dolly Photo – seconda parte

Se hai letto il mio articolo di Venerdì sai a quale progetto mi riferisco: il Dolly Photo è l’evoluzione del DollyII presentato alcuni mesi fa.

Dolly Photo shield kit

I motivi che mi hanno spinto a ridisegnare e scrivere nuovi sketch per questo progetto te li ho già raccontati nella prima parte del progetto ora non resta che iniziare.

Il materiale per il Dolly Photo

Per questa versione ho deciso di non utilizzare schede da assemblare, del resto è vero che sei un makers ma non è detto che tu debba possedere un’attrezzatura in grado di saldare componenti smd e simili, lascia che sia qualcun’altro a fare questa parte e dedicati alla parte meccanica e sopratutto alle foto.

La lista della spesa fino ad ora è semplice:

  • n.1 arduino uno
  • n.1 keypad LCD con diplay 16×2
  • n.1 motor shield arduino
  • n.1 motore stepper con massimo 1A per fase
  • tanta voglia di fare e di sperimentare 🙂

L’ultimo punto non so consigliarti dove acquistarlo ma tutto il resto si trova su molti negozi ed ho composto anche un kit che a giorni sarà in vendita sul sito robot-domestici.it.

La scelta della motor shield arduino per il Dolly Photo

Se l’idea è semplificarti la vita sulla parte elettrica la soluzione migliore per il Dolly Photo mi è sembrata quella di scegliere una motor shield che possieda le adeguate caratteristiche elettriche ( 1A per canale ) e i pin che non vadano in conflitto con la Keypad LCD, non potendo cambiarli:

Dolly Photo motor shield per dolly

la motor shield arduino utilizza i seguenti pin per i due canali A e B destinati ai motori in corrente continua:

motor shield r3 arduino pin

inoltre puoi usarla anche per realizzare il Dolly Camera o Sliders il cui progetto è tra quelli a cui sto lavorando, ti basta cambiare motore e sketch ed ecco che il Dolly Photo diventa Dolly Camera e viceversa.

Lo sketch del Dolly Photo

Lo sketch che ho scritto per il Dolly Photo è evoluzione del precedente, lo riporto tutto ma ti soffermerai solo sulle differenze, consultando la prima parte dell’articolo per le parti comuni:

/**********************************************************
 * 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

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

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;

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

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 > 1 ) { 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() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print( "Menu " );
  lcd.setCursor(0,1);
  lcd.print( "Verifica" );    

  delay( SETMODETIME );
}

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

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

  delay( SETMODETIME );
}

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

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

  delay( SETMODETIME );
}

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

ecco cosa è cambiato:

linee 58-59: aggiungi due nuove variabili di tipo array di caratteri secondario e buffer la prima di 10 elementi e la seconda di 16, sono differenti nell’uso, per adesso concentrati sulla linea 48 in cui la variabile secondario contiene le voci del menu secondario, ogni due elementi compongono il testo da visualizzare sulla prima linea del display e sulla seconda;

linee 63-67: crea 5 variabili, una per ciascuna voce di menù, in cui memorizzerai il corrispondente valore di tipo integer in fase di settaggio del Dolly Photo.

linea 250: la funzione Impostazioni() è cambiata;

linee 254-256: se la costante DEBIUG è definita scrivono sul monitor seriale il contenuto della stringa presente alla linea 255;

linee 260-264: sono sostanzialmente invariate rispetto allo sketch precedente, hanno solo cambiato numero di linea;

linea 272: definisci una variabile di tipo boolean ( true o false ) il cui valore determina se sei ancora in modalità impostazioni o se deve tornare al menu principale per attendere nuovi comandi;

linea 278: fino a quando la variabile impostazioniMode resta come impostato alla linea 272 ossia true il ciclo continua e lo sketch ti consente di effettuare tutte le impostazioni;

linea 282: controlla che il valore del pulsante premuto sia 5 ossia DOWN e allo stesso tempo che il valore dell’indice setModeLevel, che ti indica a quale livello del menu secondario sei, non sia superiore a 8, in tal caso pulisci l’LCD ed incrementa il valore di setModeLevel di 2 punti. Il motivo di questo controllo è che se il valore di setModeLevel superasse 8 sommando a questo 2, andresti oltre l’indice 9 infatti come sai se una array di caratteri è di dieci elementi il suo indice è 0-9, quindi 10 è oltre;

linea 283: esegui un’operazione simile a quella della linea precedente con la sola differenza che setModeLevel deve essere superiore a 1 in modo che sottraendo 2 a tale valore al massimo ti ritrovi 0 che il valore minimo dell’indice del menu secondario;

linea 284: questa linea ti permette di uscire dal menu infatti alla pressione del pulsante SELECT la funzione ctrlButton() ti restituisce 1 che rende vera la condizione if testata in questa linea e di conseguenza impostazioniMode diventa false uscendo dal ciclo in cui ti trovi e iniziato alla linea 268;

linee 288-291: le puoi utilizzare in fase di DEBUG per verificare che il cambio dell’indice di menu avvenga correttamente ad ogni pressione del pulsante UP o DOWN;

linea 295: grazie al comando switch puoi decidere in funzione del valore di setModeLevel che operazioni eseguire;

linee 297-302: imposta una delle condizioni da verificare, se il valore di setModeLevel è 0 lo sketch esegue il contenuto delle linee 298-301 ossia in funzione della pressione del pulsante LEFT ( valore 2 ) o RIGHT ( valore 3 ) del  keypad LCD incrementa o decrementa il valore della variabile passiXscatto, che come immagini serve a memorizzare quanti passi per scatto il Dolly Photo deve eseguire. La linea 301 compone la seconda linea del display LCD essendo composta da un valore dinamico: “testo del menu secondario con indice dispari” + “valore di passiXscatto“;

linee 304-330: esegui le stesse operazioni viste alle linee 297-302 per gli altri valori definiti alle linee 64-67;

linee 336-337: scrivi sulla prima riga del display la voce di menu con indice pari prelevata dall’array secondario;

linee 338-339: scrivi sulla seconda riga del display il risultato dell’array buffer composto alle linee precedenti;

linea 339: prima di uscire dalla funzione impostazioni() pulisci il display.

Per adesso le funzioni successive restano invariate, nei prossimi articoli lavorerò su quelle e troverai come usarle nel tuo Dolly Photo.

Il video del menu impostazioni del Dolly Photo

Ecco il video di come funziona questo sketch, so che RIGHT è scritto male ma sul Keypad LCD in mio possesso è scritto così e non vorrei generare confusione tra chi legge la label sul keypad e guarda il filmato.

Buona visione !!!

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

Permanent link to this article: https://www.mauroalfieri.it/elettronica/dolly-photo-seconda-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.