Arduino – Dividere uno sketch in più file

Domanda dello studente: “Prof. il progetto per controllare il robot sta diventando lunghissimo e anche se commento ogni parte e cerco di creare funzioni specifiche per ogni azione, leggere modificare il codice diventa un lavoro che porta via molto tempo, come posso fare?”

Possiamo agire in due modi:

  1. creazione di librerie specifiche in cui inserire il codice che deve essere richiamato dallo sketch principale, leggere la parte finale della lezione: “Arduino – lezione 08: display a 7 segmenti e creazione di librerie” in cui mostro come fare questa operazione;
  2. utilizzare la funzione specifica che trovate nell’IDE di Arduino “Nuova scheda” o “New Tab” in inglese a destra della finestra.

Supponiamo di aver creato un nuovo sketch vuoto a cui assegniamo un nome, nel mio esempio “ilMioBlink.ino”, come sapete il file verrà inserito in una cartella che avrà lo stesso nome dello sketch principale:

Selezionare “New Tab” a destra della finestra dell’IDE:

noterete che la selezione di questa voce di menù aprirà in basso alla finestra dell’IDE un’area evidenziata in giallo in cui dovrete inserire il nome del file.

Inserirò a titolo di esempio il nome: “impostazioni”.
Verrà immediatamente aggiunta una nuova tab:

 

Appena salvate il progetto, il simbolo a fianco del nome scompare ed il nuovo file “impostazioni” verrà automaticamente inserito nella stessa cartella dello sketch principale.
Il salvataggio del progetto salva automaticamente tutte le variazioni fatte nei file allegati al progetto.

Si tenga presente che la lettura (esecuzione) dei file aggiunti avverrà in ordine alfabetico, pertanto se vengono usate variabili globali all’interno di uno dei file, queste potranno essere viste solamente dai file che seguono nell’ordine alfabetico, pertanto buona regola è inserire tutte le variabili globali nel progetto principale (quello che reca lo stesso nome della cartella di progetto).

Quindi si potrebbe pensare di strutturare un lungo progetto in questo modo:

  1. tutte le variabili globali nello sketch principale;
  2. impostazioni di setup() in un secondo file;
  3. loop() nello sketch principale in cui sono incluse le chiamate alle funzioni esterne principali;
  4. file con tutte le funzioni. Se le funzioni sono lunghe e complesse si potrebbe pensare di separarle in altri file.

Esempio

Come esempio consideriamo il semplicissimo sketch che permette di far lampeggiare ripetutamente due LED e dividiamolo in due parti:

Il file principale, ilMioBlink.ino avrà al suo interno variabili globali e loop(), mentre il file impostazioni avrà al suo interno solo il setup():

Bene!

Per i miei studenti, da questo momento in poi per rendere più veloce e semplice la correzione dei vostri lavori è obbligatorio suddividere sketch di grandi dimensioni in più file i vostri lavori 🙂

Buon Coding a tutti!

 

I miei corsi per Tecnica della Scuola: Laboratori STEAM con Arduino

Sono felice di comunicarvi che svolgerò nel mese di aprile con Tecnica della Scuola il corso: Laboratori Steam con Arduino.
Sono più di 10 anni che utilizzo questa stupenda scheda, stupenda soprattutto perché mi ha permesso di recuperare negli anni molti ragazzi demotivati o con carenze di carattere logico matematico e tali mancanze pregiudicavano inevitabilmente la comprensione di argomenti che insegno: elettronica, informatica, sistemi elettronici.
Arduino nasce come scheda elettronica fatta per NON elettronici, una scheda che doveva servire prevalentemente per prototipare e quindi  permettere a chiunque di superare lo scoglio della complessità elettronica ed informatica, almeno nella prima fase di apprendimento, per produrre un prodotto da utilizzare in diversi ambiti: design, medico, umanistico, ecc…

Successivamente si è constatato che l’uso di Arduino a scuola, in moltissime attività laboratoriali, risultava estremamente utile. Soprattutto nel mio caso Arduino ha permesso di trovare un ausilio didattico in grado di catalizzare interesse e passione da parte anche di chi aveva scarse competenze matematiche.

Quando l’azione didattica veniva condotta con precisione, l’uso di Arduino ha permesso di far comprendere allo studente che, se è relativamente facile realizzare progetti con forte interazione con il mondo reale, ancora più interessanti potevano diventare quegli stessi progetti se le competenze nelle discipline STEAM fossero state maggiori.

Con l’attività pratica basata su Arduino si fornisce un motivo in più per sopportare e superare l’insuccesso scolastico, sviluppando progetti pratici il cui funzionamento dipenderà dall’impegno e dallo studio, quindi lo studente, in modo evidente vede in  Arduino l’oggetto che permette la materializzazione della propria competenza, cosa che non mi stancherò mai di dire e che più volte ho scritto su queste pagine. Se io studente conosco la fisica e la chimica probabilmente saprò gestire progetti che hanno a che fare con l’IoT, la fisiologia umana , le arti, l’economia e molto altro.

Come dico nella presentazione del corso:

Le attività laboratoriali di “fabbricazione digitale” che fanno uso di Arduino, favoriscono lo sviluppo delle competenze metacognitive e relazionali, potenziamento del pensiero logico, della capacità di astrazione e di problem solving.

ed aggiungo: la percezione che imparare è bello.

Quindi l’uso di Arduino diventa il pretesto per mettere in atto processi di analisi e autoanalisi e di messa in pratica di conoscenze e abilità.

Questa è ciò che ho visto e continuo a vedere durante le attività di laboratorio.

Ma è possibile che Arduino, una semplice scheda elettronica, possa fare tutto ciò?
Sì ne sono assolutamente convinto, l’evidenza si ha quando si vive il laboratorio ogni giorno, si percepisce negli studenti la sensazione che è bello imparare, perché imparare mi fornisce un immediato riscontro fisico di ciò che so e ciò che mi serve per far diventare “ancor più bello” il mio progetto.

Non si pensi assolutamente che Arduino è fatto per pochi addetti, non è l’oggetto che manipola solo l’insegnante di elettronica, è l’oggetto che usa l’insegnate di musica per far costruire strumenti musicali, è lo strumento che usa l’insegnate di arte per mostrare come creare installazioni di arte cinetica, è lo strumento che usano gli insegnanti di materie umanistiche quando vogliono sperimentare azioni di educazione civica in cui gli allievi devono progettare un ausilio per la disabilità per un loro compagno di classe, ma Arduino è anche lo strumento utilizzato costruire strumenti per comprendere i cambiamenti climatici.

Vorrei con questo corso farvi percepire come è semplice costruire progetti didattici laboratoriali trasversali, che coinvolgono tutte le discipline.
Spero di vedermi al mio corso, il primo di una serie di corsi a carattere assolutamente laboratoriale che svolgerò nei prossimi mesi con Tecnica della Scuola.

Non mi resta quindi che lasciarvi al programma del corso e sperare che insieme, durante le attività a distanza,  si possano sviluppare oggetti fisici utili per voi.

E’ necessario disporre della scheda Arduino?
E’ consigliato, ma sarà possibile seguire assolutamente il corso anche se non si dispone della scheda, ma il mio consiglio è quello di mettere mani subito sugli oggetti fisici in modo che si assimilino più velocemente le nozioni di base. Vi fornirò tutte le indicazioni e i dettagli tecnici per sapere dove  e cosa acquistare per cominciare il proprio percorso di apprendimento e impostare le lezioni laboratoriali per i vostri studenti.

Presentazione

LABORATORI STEAM CON ARDUINO
Come fare coding, tinkering e making a scuola

Come realizzare laboratori Steam utilizzando Arduino, una delle più affermate tecnologie open-source e open-hardware che sempre di più si sta affermando a scuola. Un corso per conoscere e approfondire le opportunità della robotica educativa e del coding. Il corso, combinando diverse tecnologie innovative, avrà un approccio assolutamente laboratoriale e fornirà tecniche ed idee per supportare l’apprendimento degli studenti favorendo in loro anche lo sviluppo dell’espressione personale e della creatività. Verranno mostrati esempi realmente sperimentati in classe in cui si evidenzieranno gli aspetti legati all’interdisciplinarietà e all’inclusione che hanno trovato largo impiego nella quotidianità scolastica.

Particolare attenzione verrà posta anche all’attività di sperimentazione pratica con Arduino in modalità di didattica digitale integrata utilizzando simulatori online e non solo.

L’intero corso è pensato per tutti gli insegnati, non è necessario in alcun modo avere competenze di elettronica o informatica e la proposta formativa è disegnata su un percorso testato da anni indirizzato a persone non tecniche in particolar modo ad insegnanti e studenti.

Cos’è Arduino:

  • Arduino è un hardware, una scheda elettronica di facilissimo utilizzo, e di basso costo
  • Arduino è un’ambiente di programmazione che permetterà di realizzare programmi che verranno poi eseguiti sulla scheda elettronica per la realizzazione molteplici progetti che potranno avere una forte interazione con il mondo reale;
  • Arduino è un sito ed una comunità online che condivide risorse e progetti utilissimi in campo didattico, soprattutto nelle attività che rientrano all’interno dell’insegnamento delle STEAM (Science, Technology, Engineering, Arts, Mathematics).

Perché usare Arduino nella didattica laboratoriale.

  • Il costo contenuto;
  • le dimensioni ridotte;
  • la semplicità di utilizzo;
  • la possibilità di sviluppare una vasta gamma di attività possibili data dalla notevole modularità della scheda;
  • una comunità di appassionati molto attiva tra cui moltissimi docenti.

L’uso di Arduino diventa un pretesto per mettere in atto processi di analisi e autoanalisi e di messa in pratica di conoscenze e abilità. Le attività laboratoriali di “fabbricazione digitale” che fanno uso di Arduino, favoriscono lo sviluppo delle competenze metacognitive e relazionali, potenziamento del pensiero logico, della capacità di astrazione e di problem solving.

Inoltre, uno degli ambiti che maggiormente viene svolto a livello laboratoriale è quello della robotica educativa. Durante il corso verranno forniti esempi e percorsi didattici completi immediatamente utilizzabili in classe, volti alla realizzazione di attività laboratoriali per la costruzione di robot didattici basati su piattaforma Arduino in cui verranno presi in considerazione le seguenti azioni:

  1. come si pensa e si progetta in gruppo;
  2. come si progetta un robot didattico;
  3. come si realizza un robot con materiali riciclati;
  4. come si programma il robot;

Durante il corso verranno forniti anche consigli sulle dotazioni tecniche necessarie per iniziare ad utilizzare questa piattaforma:

  • cosa comprare;
  • dove comprare;
  • come imparare ad utilizzare in modo semplice Arduino;
  • come avviare un laboratorio di STEAM in cui si fa uso di Arduino.

Saranno svolti 4 incontri in webinar di 2 ore ciascuno, per un totale di 8 ore.

  • Martedì 20 aprile 2021 – Dalle 17.00 alle 19.00
  • Giovedì 22 aprile 2021 – Dalle 17.00 alle 19.00
  • Martedì 27 aprile 2021 – Dalle 17.00 alle 19.00
  • Giovedì 29 aprile 2021 – Dalle 17.00 alle 19.00

Per iscriversi al corso seguire il link allegato.

PCTO A.S. 2020 – 2021 – SumoBot – lezione 4

Come probabilmente avrete avuto modo di verificare gestendo i due servomotori a rotazione continua, pur provenendo dal medesimo costruttore e impostando le medesime configurazione di velocità, hanno un comportamento non sempre identico. Ricordo che si tratta di apparati economici che devono rispondere a specifiche esigenze didattiche, ma in ogni caso è possibile effettuare alcune regolazioni che ne possono migliorare le prestazioni.
Un’altra funzionalità utile è quella dello start/stop del robot che potrebbe servire per evitare immediati movimenti non appena trasferiamo il codice sul robot.

In questa lezione vi dettaglio gli sketch di esempio, lasciando a voi modifiche e miglioramenti.

Regolazione dei motori

Per poter valutare la velocità e l’angolo di rotazione da impostare all’interno dello sketch è possibile attivare la funzione di calibrazione collegando a GND il pin 3, in questo modo sarà possibile dalla Serial Monitor verificare quali sono i giusti valori per:

  • velocità di rotazione;
  • durata della rotazione;
  • motore/i da impostare

e quindi ciò consentirà di impostare i parametri corretti per le funzioni:

  • orarioRobot()
  • antiorarioRobot()

Pertanto da serial monitor potremo comprendere come far compiere una rotazione di 90° in un senso, oppure come far avanzare di una certa quantità di centimetri il robot e molto altro, tenendo però bene a mente che tutto ciò dipenderà fortemente dalla carica della batteria.

La scelta del pin3 è puramente casuale se ritenete potete scegliere un altro pin digitale.

Esempio 1

/*
   Prof. Maffucci Michele
   SumoRobot
   Data: 26.01.2021

   Sketch 03: rotazione oraria e antioraria continua
              con funzioni parametrizzate

              Per valutare la velocità e l'angolo di rotazione è possibile
              attivare la funzione di calibrazione collegando a GND il pin 3,
              in questo modo sarà possibile dalla Serial Monitor verificare
              quali sono i giusti valori per:
              - velocità di rotazione;
              - durata della rotazione;
              - motore/i da impostare

              Ciò consentirà di impostare i parametri corretti per le funzioni:

              - orarioRobot()
              - antiorarioRobot()

   Note:
            L'orientamento dei motori è fatto
            guardano il robot anteriormente

            180: max velocità in senso antiorario
            90 : servomotori fermi
            0  : max velocità in senso orario

*/

// inclusione della libreria servo.h per il controllo dei servomotori
#include <Servo.h>

// Creazione oggetti servo
Servo motoreDX;  // Inizializzazione del servomotore destro
Servo motoreSX;  // Inizializzazione del servomotore sinistro

byte pinDx = 4;     // Inizializza del pin 4 a cui è connesso il pin segnale del servo destro
byte pinSx = 5;     // Inizializza del pin 5 a cui è connesso il pin segnale del servo sinistro

// Per Calibrazione

// per stampare una sola volta il messaggio sulla Serial Monitor
bool abilitaMessaggio = 0;

// per attivare la calibrazione impostare startCalibrazione a 0
bool startCalibrazione = 1;

// Pin di calibrazione se a LOW abilita modalità calibrazione
byte pinCal = 3;

void setup() {

  // inizializzazione della seriale
  Serial.begin(9600);

  // attach() consente di definire a quale pin viene connesso il servomotore
  // e lo collega all'oggetto che gestisce il servomotore

  motoreDX.attach(pinDx); // pinDx collegato al motore destro
  motoreSX.attach(pinSx); // pinSx collegato al motore sinistro

  pinMode(pinCal, INPUT);
}

void loop() {

  // se sul pinCal è LOW è possibile impostare i motori
  if (digitalRead(pinCal) == LOW) {
    calibrazione();
  }
  else
  {
    orarioRobot(30, 250);      // Rotazione in senso orario del robot
    stopRobot(3000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)
    antiorarioRobot(150, 250); // Rotazione in senso antiorario del robot
    stopRobot(3000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)
  }
}

// Funzione da utilizzare una sola volta per impostare
// i tempi necessari per effettuare uno specifico angolo
// di rotazione e la velocità di rotazione desiderata

void calibrazione() {

  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio
    delay(200);
    Serial.println("Calibrazione tempo rotazione");
    Serial.println("velocità (0-180), durata(ms), motore(1: DX, 2: SX, 3: SX+DX)");
    Serial.println();
    abilitaMessaggio = 1;
  }

  // Controlla se è disponibile almeno un carattere sulla seriale
  // La Serial.available() restituisce
  // 1 se presente un cattere,
  // 0 se non è presente un carattere

  if (Serial.available()) {        // Viene controllato se è disponibile un carattere

    // per maggior informazioni sull'uso di parseInt() consultare il link:
    // https://wp.me/p4kwmk-4Ah

    // inserimento da tastiera su Serial Monitor dei parametri di controllo
    // separati da spazio o virgola
    int velocita = Serial.parseInt();
    int durata   = Serial.parseInt();
    int motore   = Serial.parseInt();

    // funzione per il movimento dei servomotori con i parametri
    // inseriti sulla serial monitor.
    calMotoreRobot(velocita, durata, motore);
  }
}

// funzione movimento servomotori per impostazione
// velocità, durata, motore/i 

void calMotoreRobot(int calVel, int calDurata, int nMotore) {
  if (nMotore == 1) { // motore DX
    motoreDX.write(calVel);
    delay(calDurata);
    motoreDX.write(90);   // Ferma il motore DX
    motoreSX.write(90);   // Ferma il motore SX
    Serial.println("Fine calibrazione motore DX");
    Serial.println();
  }
  if (nMotore == 2) { // motore SX
    motoreSX.write(calVel);
    delay(calDurata);
    motoreDX.write(90);   // Ferma il motore DX
    motoreSX.write(90);   // Ferma il motore SX
    Serial.println("Fine calibrazione motore SX");
    Serial.println();
  }
  if (nMotore == 3) { // motore SX+DX
    motoreDX.write(calVel);  // Rotazione oraria del motore DX
    motoreSX.write(calVel);  // Rotazione antioraria del motore SX
    delay(calDurata);
    motoreDX.write(90);   // Ferma il motore DX
    motoreSX.write(90);   // Ferma il motore SX
    Serial.println("Fine calibrazione motore DX e SX");
    Serial.println();
  }
}

// rotazione del robot in senso antiorario
// velMaxOraria: velocità massima in senso antiorario
// durata: durata della rotazione

void antiorarioRobot(int velMaxAntioraria, int durata) {
  motoreDX.write(velMaxAntioraria);  // Rotazione oraria del motore DX
  motoreSX.write(velMaxAntioraria);  // Rotazione antioraria del motore SX
  delay(durata);                     // durata: durata della rotazione
}

// rotazione del robot in senso orario
// velMaxOraria: velocità massima in senso orario
// durata: durata della rotazione

void orarioRobot(int velMaxOraria, int durata) {
  motoreDX.write(velMaxOraria);    // Rotazione antioraria del motore DX
  motoreSX.write(velMaxOraria);    // Rotazione oraria del motore SX
  delay(durata);                   // durata: durata della rotazione
}

// stop del robot
// ferma: durata dello stop del robot

void stopRobot(int ferma) {
  motoreDX.write(90);   // Ferma il motore DX
  motoreSX.write(90);   // Ferma il motore SX
  delay(ferma);         // Durata dello stop
}

La calibrazione viene attivata se la condizione ad inizio loop è vera:

...
// se sul pinCal è LOW è possibile impostare i motori
  if (digitalRead(pinCal) == LOW) {
    calibrazione();
  }
  else
  {
    orarioRobot(30, 250);      // Rotazione in senso orario del robot
    stopRobot(3000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)
    antiorarioRobot(150, 250); // Rotazione in senso antiorario del robot
    stopRobot(3000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)
  }
}
...

Se la condizione dell’if risulta vera viene attivata la calibrazione:

...
// se sul pinCal è LOW è possibile impostare i motori
  if (digitalRead(pinCal) == LOW) {
    calibrazione();
  }
  else
  {
    orarioRobot(30, 250);      // Rotazione in senso orario del robot
    stopRobot(3000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)
    antiorarioRobot(150, 250); // Rotazione in senso antiorario del robot
    stopRobot(3000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)
  }
}
...

In tal caso viene chiamata la funzione calibrazione:

...
void calibrazione() {

  // consente di visualizzare sulla Serial Monitor
  // una sola stampa delle stringa
  if (abilitaMessaggio == 0) {
    // ritardo che evita la doppia stampa del messaggio
    delay(200);
    Serial.println("Calibrazione tempo rotazione");
    Serial.println("velocità (0-180), durata(ms), motore(1: DX, 2: SX, 3: SX+DX)");
    Serial.println();
    abilitaMessaggio = 1;
  }

  // Controlla se è disponibile almeno un carattere sulla seriale
  // La Serial.available() restituisce
  // 1 se presente un cattere,
  // 0 se non è presente un carattere

  if (Serial.available()) {        // Viene controllato se è disponibile un carattere

    // per maggior informazioni sull'uso di parseInt() consultare il link:
    // https://wp.me/p4kwmk-4Ah

    // inserimento da tastiera su Serial Monitor dei parametri di controllo
    // separati da spazio o virgola
    int velocita = Serial.parseInt();
    int durata   = Serial.parseInt();
    int motore   = Serial.parseInt();

    // funzione per il movimento dei servomotori con i parametri
    // inseriti sulla serial monitor.
    calMotoreRobot(velocita, durata, motore);
  }
}
...

La prima parte della funzione calibrazione() esegue una sola volta la stampa sulla Serial Monitor dell’help che spiega come inserire i dati:

velocità (0-180), durata(ms), motore(1: DX, 2: SX, 3: SX+DX)

che sono tutti valori interi separati da virgola.

Nella parte restante del codice della funzione viene verificato con il metodo available() se sono presenti sulla seriale caratteri. Serial.available() restituisce TRUE se sono presenti caratteri e il corpo dell’if verrà eseguito. I valori inseriti vengono letti utilizzando la funzione parseInt() di cui trovate spiegazione approfondita con esercizi seguendo il link.

...
if (Serial.available()) {        // Viene controllato se è disponibile un carattere

    // per maggior informazioni sull'uso di parseInt() consultare il link:
    // https://wp.me/p4kwmk-4Ah

    // inserimento da tastiera su Serial Monitor dei parametri di controllo
    // separati da spazio o virgola
    int velocita = Serial.parseInt();
    int durata   = Serial.parseInt();
    int motore   = Serial.parseInt();

    // funzione per il movimento dei servomotori con i parametri
    // inseriti sulla serial monitor.
    calMotoreRobot(velocita, durata, motore);
  }
...

Non appena premiamo invio sulla tastiera, questi valori vegono acquisiti e passati alla funzione calMotoreRobot()

...
calMotoreRobot(velocita, durata, motore);
...

Tra i parametri che vengono passati alla funzione calMotoreRobot() è presente l’indicazione di quale/i motore/i devono essere controllati, questo parametro può assumere i valori: 1, 2, 3 e questi valori vengono utilizzati per selezionare, tramite i blocchi if corrispondenti, quale azione è da compiere. Nei commenti i dettagli di funzionamento.

...
void calMotoreRobot(int calVel, int calDurata, int nMotore) {
  if (nMotore == 1) { // motore DX
    motoreDX.write(calVel);
    delay(calDurata);
    motoreDX.write(90);   // Ferma il motore DX
    motoreSX.write(90);   // Ferma il motore SX
    Serial.println("Fine calibrazione motore DX");
    Serial.println();
  }
  if (nMotore == 2) { // motore SX
    motoreSX.write(calVel);
    delay(calDurata);
    motoreDX.write(90);   // Ferma il motore DX
    motoreSX.write(90);   // Ferma il motore SX
    Serial.println("Fine calibrazione motore SX");
    Serial.println();
  }
  if (nMotore == 3) { // motore SX+DX
    motoreDX.write(calVel);  // Rotazione oraria del motore DX
    motoreSX.write(calVel);  // Rotazione antioraria del motore SX
    delay(calDurata);
    motoreDX.write(90);   // Ferma il motore DX
    motoreSX.write(90);   // Ferma il motore SX
    Serial.println("Fine calibrazione motore DX e SX");
    Serial.println();
  }
}
...

La parte restante dello sketch riguarda funzioni già analizzate spiegate nelle lezioni precedenti.

Start/stop del robot

Questa semplice funzionalità viene ottenuta nel medesimo modo della calibrazione, utilizziamo il pin2 cortocircuitato a massa per evitare l’avvio del robot, secondo questa regola:

  • Pin 2 a GND: robot fermo
  • Pin 2 a Vcc: robot start

Questo lo sketch generale:

/*
   Prof. Maffucci Michele
   SumoRobot
   Data: 26.01.2021

   Sketch 04: rotazione oraria e antioraria continua
              con funzioni parametrizzate con Start e Stop sistema.

              Per evitare in avvio immediato del robot si realizza un
              un interruttore di start/stop utilizzando il pin 2:
              
              - Pin 2 a GND: robot fermo
              - Pin 2 a Vcc: robot start
   Note:
            L'orientamento dei motori è fatto
            guardano il robot anteriormente

            180: max velocità in senso antiorario
            90 : servomotori fermi
            0  : max velocità in senso orario

            Per avviare il robot collegare a GND il pin 2

*/

// inclusione della libreria servo.h per il controllo dei servomotori
#include <Servo.h>

// Creazione oggetti servo
Servo motoreDX;  // Inizializzazione del servomotore destro
Servo motoreSX;  // Inizializzazione del servomotore sinistro

byte pinDx = 4;     // Inizializza del pin 4 a cui è connesso il pin segnale del servo destro
byte pinSx = 5;     // Inizializza del pin 5 a cui è connesso il pin segnale del servo sinistro
byte pinStart = 2;  // Pin di avvio, se a LOW Start sistema

void setup() {

  // inizializzazione della seriale
  Serial.begin(9600);

  // attach() consente di definire a quale pin viene connesso il servomotore
  // e lo collega all'oggetto che gestisce il servomotore

  motoreDX.attach(pinDx); // pinDx collegato al motore destro
  motoreSX.attach(pinSx); // pinSx collegato al motore sinistro

  pinMode(pinStart, INPUT); // pin per avviare il sistema
}

void loop() {

  // se pinStart a LOW il sistema è in stop
  if (digitalRead(pinStart) == LOW) {
    stopRobot(1);
  }

  // se pinStart è diverso da LOW si avvia la sequenza
  else
  {
    orarioRobot(70, 500);      // Rotazione in senso orario del robot
    stopRobot(1000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)
    antiorarioRobot(130, 500); // Rotazione in senso antiorario del robot
    stopRobot(1000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)

  }
}

// rotazione del robot in senso antiorario
// velMaxOraria: velocità massima in senso antiorario
// durata: durata della rotazione

void antiorarioRobot(int velMaxAntioraria, int durata) {
  motoreDX.write(velMaxAntioraria);  // Rotazione antioraria del motore DX
  motoreSX.write(velMaxAntioraria);  // Rotazione antioraria del motore SX
  delay(durata);                     // durata: durata della rotazione
}

// rotazione del robot in senso orario
// velMaxOraria: velocità massima in senso orario
// durata: durata della rotazione

void orarioRobot(int velMaxOraria, int durata) {
  motoreDX.write(velMaxOraria);    // Rotazione oraria del motore DX
  motoreSX.write(velMaxOraria);    // Rotazione oraria del motore SX
  delay(durata);                   // durata: durata della rotazione
}

// stop del robot
// ferma: durata dello stop del robot

void stopRobot(int ferma) {
  motoreDX.write(90);   // Ferma il motore DX
  motoreSX.write(90);   // Ferma il motore SX
  delay(ferma);         // Durata dello stop
}

Come si può notare lo start/stop è regolato dalla parte di codice:

...
// se pinStart a LOW il sistema è in stop
  if (digitalRead(pinStart) == LOW) {
    stopRobot(1);
  }

  // se pinStart è diverso da LOW si avvia la sequenza
  else
  {
    orarioRobot(70, 500);      // Rotazione in senso orario del robot
    stopRobot(1000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)
    antiorarioRobot(130, 500); // Rotazione in senso antiorario del robot
    stopRobot(1000);           // Stop rotazione per un tempo fissato (vedere variabile ferma)

  }
}
...

Se il pin è collegato a GND, quindi al LOW il robot è in stop, infatti viene chiamata la funzione stopRobot(1);

Nella prossima lezione vedremo come integrare le due funzionalità:

  • calibrazione
  • start/stop

Buon Making a tutti 🙂

Lezione 2 – Arduino GamePad – LCD1602 Keypad Shield della Keyestudio

Pubblico la seconda lezione per il gruppo di lavoro della mia classe terza Automazione che sta operando con lo shield LCD1602 Keypad Shield della Keyestudio, suggerendo alcuni sketch che potranno poi essere riutilizzati e modificati opportunamente per realizzare alcune dinamiche di gioco.
Per chi volesse cimentarsi in queste sperimentazioni ricordo che non è essenziale possedere LCD1602 Keypad Shield della Keyestudio, il tutto può essere ottenuto anche mediante un normale display, a questo dovrete aggiungere i 6 pulsanti che potranno essere connessi o al pin A0, secondo la rete resistiva che potete riprodurre guardando gli schemi elettrici della shield oppure gestendo il tutto collegando i pulsanti a dei pin digitali.

Esempio 01

Il primo degli sketch, molto semplice, permette di far rimbalzare avanti e indietro un carattere. Per questo esempio sono state utilizzate le funzioni: scrollDisplayRight() e scrollDisplayLeft() ciascuna inclusa in un ciclo for, il primo muove il carattere “*” verso destra di 16 posizioni, tante quante sono le colonne del display ed il secondo muoverà il carattere da sinistra verso destra.

/*
   Prof. Michele Maffucci
   Utilizzo dell'LCD Keypad Shield della Keystudio
   Data: 14.03.2021

   Movimento ripetuto di un carattere, avanti e indietro su una riga
*/

// inclusione della libreria LiquidCrystal.h
#include <LiquidCrystal.h>

// inizializza la libreria con i numeri dei pin dell'interfaccia
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Velocità con cui viene stampato il carattere  
int velocita = 200;

void setup() {
  // impostazione del numero di colonne e righe del display
  lcd.begin(16, 2);

  // Inizializzazione della Serial Monitor
  Serial.begin(9600);

  // Carattere stampato nella prima colonna e prima riga (0,0)
  lcd.print("*");
}

void loop() {
  
  // Movimento verso destra del carattere
  for (int contatorePosizioneColonna = 0; contatorePosizioneColonna < 16; contatorePosizioneColonna++) {
    // Spostamento di una posizione verso destra
    lcd.scrollDisplayRight();
    // Attesa di un'istante per percepire il movimento del carattere
    delay(velocita);
  }

  // Movimento verso sinistra del carattere
  for (int contatorePosizioneColonna = 0; contatorePosizioneColonna < 16; contatorePosizioneColonna++) {
    // Spostamento di una posizione verso sinistra
    lcd.scrollDisplayLeft();
    // Attesa di un'istante per percepire il movimento del carattere
    delay(velocita);
  }
}

Esercizio 1

Realizzare le medesime funzionalità dell’esempio 1, quando il carattere giunge nell’ultima colonna a destra, scende nella riga sottostante e ripercorre il tragitto verso sinistra, giunto alla prima colonna a sinistra risale sulla prima riga e si sposta nuovamente verso destra. Far ripetere in loop questa sequenza.

Esempio 02

Nell’esempio che segue vengono utilizzati i pulsanti RIGHT e LEFT del display per spostare verso destra o verso sinistra di un passo il carattere “*”.
L’identificazione del pulsante premuto avviene controllando il valore numerico restituito dall’analogRead() così come spiegato nella lezione 1.

Le due istruzioni if hanno come condizione il controllo del valore analogico, che indicherà il pulsante premuto il tutto è posto in AND con la posizione attuale (la colonna) del carattere, colonna memorizzata nella variabile “contatorePosizioneColonna” che nello spostamento verso destra dovrà essere incrementata e nello spostamento verso sinistra dovrà essere decrementata.

/*
   Prof. Michele Maffucci
   Utilizzo dell'LCD Keypad Shield della Keystudio
   Data: 14.03.2021

   Controllo dello spostamento di un carattere destra/sinistra
   mediante i pulsanti: RIGHT e LEFT sul display
*/

// inclusione della libreria LiquidCrystal.h
#include <LiquidCrystal.h>

// inizializza la libreria con i numeri dei pin dell'interfaccia
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Colonna in cui si trova il carattere
int contatorePosizioneColonna = 0;

// Valore restituito dall'analogRead su A0
int val;

void setup() {
  // impostazione del numero di colonne e righe del display
  lcd.begin(16, 2);

  // Carattere stampato nella prima colonna e prima riga (0,0)
  lcd.print("*");
}

void loop() {

  // Memorizza in val il valore presente su A0
  // per identificare il pulsante che viene premuto.
  int val = analogRead(A0);

  // Premendo il pulsante RIGHT sul display, il carattere si sposta di una posizione
  // a destra fino a quando non si raggiunge l'ultima colonna a destra.
  // Premendo ancora il pulsante RIGHT non si ha l'avanzamento del carattere.
  if ((val >= 0 && val <= 50) && contatorePosizioneColonna < 15) {
    lcd.scrollDisplayRight();
    delay(200);
    contatorePosizioneColonna++;
  }

  // Premendo il pulsante LEFT sul display, il carattere si sposta di una posizione
  // a sinistra fino a quando non si raggiunge l'ultima colonna a sinistra.
  // Premendo ancora il pulsante LEFT non si ha l'avanzamento del carattere.
  if ((val >= 300 && val <= 500) && contatorePosizioneColonna > 0) {
    lcd.scrollDisplayLeft();
    delay(200);
    contatorePosizioneColonna--;
  }
}

Esercizio 2

Realizzare le stesse funzionalità dell’esempio 2 ed aggiungere la possibilità di selezionare il carattere da movimentare, mediante un menù iniziale in cui potrà essere fatta la selezione secondo lo schema di seguito indicato:

Pulsante SELECT: @
Pulsante DOWN : X
Pulsante UP : #

La selezione del carattere avviene una sola volta all’avvio del programma.

Esercizio 3

Eseguire le stesse funzionalità dell’esercizio 3 con la possibilità di visualizzare il menù di scelta carattere in qualsiasi momento, così da permettere la selezione del carattere da movimentare. Scegliere liberamente il pulsante da premere per visualizzare il menù.

Esempio 3

Nell’esempio che segue si mostra come movimentare da destra a sinistra due caratteri: da destra a sinistra “>” e da sinistra verso destra “<“. In questo esempio viene utilizzata il metodo setCursor() che permette di posizionare ad una colonna e riga specifica il cursore.

/*
   Prof. Michele Maffucci
   Utilizzo dell'LCD Keypad Shield della Keystudio
   Data: 14.03.2021

   Movimento ripetuto avanti e indietro:
   - movimento verso destra stampa del carattere: >
   - movimento verso sinistra stampa del carattere: <
   
*/

// inclusione della libreria LiquidCrystal.h
#include <LiquidCrystal.h>

// Velocità con cui viene stampato il carattere  
int velocita = 200;

// inizializza la libreria con i numeri dei pin dell'interfaccia
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
  // impostazione del numero di colonne e righe del display
  lcd.begin(16, 2);

  // Inizializzazione della Serial Monitor
  Serial.begin(9600);
}

void loop() {
  
  // Movimento verso destra del carattere
  for (int contatorePosizioneColonna = 0; contatorePosizioneColonna < 16; contatorePosizioneColonna++) {
    // Cancella il display
    lcd.clear();
    // Spostamento di una posizione verso destra del cursore
    lcd.setCursor(contatorePosizioneColonna, 0);
    // Stampa del carattere: >
    lcd.print(">");
    // Attesa di un'istante per percepire il movimento del carattere
    delay(velocita);
  }

  // Movimento verso sinistra del carattere
  for (int contatorePosizioneColonna = 16; contatorePosizioneColonna > 0; contatorePosizioneColonna--) {
    // Cancella il display
    lcd.clear();
    // Spostamento di una posizione verso sinistra del cursore
    lcd.setCursor(contatorePosizioneColonna, 0);
    // Stampa del carattere: >
    lcd.print("<");
    // Attesa di un'istante per percepire il movimento del carattere
    delay(velocita);
  }
}

Esercizio 4

All’interno del ciclo for, prima di posizionare e stampare il carattere, viene cancellato il display con il metodo clear(), sapresti indicare il motivo di questa scelta? Ci sono altri modi per ottenere il medesimo comportamento (movimento)?

Buon Coding a tutti.

Arduino – Trovare il valore più grande in un array

Durante queste settimane inevitabilmente le attività di PCTO vengono svolte in parte in presenza ed in parte in remoto. Le lezioni in remoto vengono ovviamente utilizzate per risolvere dubbi e spiegare qualcosa di nuovo che possa essere di aiuto ai ragazzi. Ultimamente alcuni mie studenti di classe 3′ sono impegnati chi nella realizzazione di serre idroponiche e chi nel simulare il funzionamento di un’incubatrice neonatale, in entrambe le situazioni bisogna rilavare grandezze fisiche, come ad esempio la temperatura. Spesso è necessario leggere dai sensori in un determinato intervallo di tempo una sequenza di valori che dovranno essere memorizzati in un array e successivamente elaborati.

Come detto sopra, è spesso utile memorizzare una sequenza di numeri, ad esempio una sequenza di letture di temperatura, all’interno di un array e poi dalla lista estrarre il valore che ci interessa, ad esempio: la massima temperatura, la minima temperatura, la media, ecc…

Per fare questa operazione il metodo più semplice ed intuitivo consiste nello leggere la sequenza dei valori per cercare il più grande.

Ma come costruire un algoritmo per fare questa operazione?

Riprendo un esempio che facevo tempo fa ai ragazzi declinato da alcune spiegazioni di un vecchio libro di informatica su cui studiai io 🙂

Immaginate la seguente situazione:

il mio collega con cui lavoro in compresenza, che chiamerò Prof. Rossi, mi detta la lista dei voti assegnati ad uno di voi, non mi dice in anticipo quanti voti mi detterà, me li detta ed io devo costruire un algoritmo che mi permetterà di dire al collega qual è il voto più altro nel momento in cui mi dirà:

“basta non ci sono più voti!”,

istantaneamente, senza rileggere tutta la lista devo fornire questa informazione al collega.

Partiamo!

  • Il primo voto che mi viene dettato è “3.5”, allora lo scriverò su un foglio di carta;
  • il secondo voto è 6.75 che è più grande di 3 quindi potrebbe essere questo il massimo dei voti, cancello 3.5 e scrivo sulla pagina il 6.75;
  • subito dopo mi viene detto: “5.5” che è più piccolo di 6.75 quindi non potrà essere questo il voto più alto, lasceremo scritto sul foglio 6.75;
  • mi viene detto: “9” ed essendo più grande di 6.75 lo indico sul mio foglio, cancellando il 6.75;
  • il voto successivo è 7.25, che è inferiore al 9 e non lo riporto sul foglio;
  • il voto successivo è 8.75, che è inferiore al 9 e non lo riporto sul foglio;
  • Il Prof. Rossi mi avvisa che ha finito di dettarmi i voti ed io dirò immmediatamente che il voto massimo è 9;

Come possiamo tradurre in C questo algoritmo?

Realizziamo un Array in cui inserire la sequenza dei 6 voti:

sequenzaVoti[] = {3.5, 6.75, 5.5, 9, 7.25, 8.75};

Il passo successivo sarà quello di definire un contenitore (una variabile) in cui di volta in volta andremo a memorizzare il numero più grande che abbiamo trovato.

Definisco quindi “votoMassimo” la variabile che contiene il voto massimo temporaneo (quello che nell’esempio mi veniva dettato dal Prof. Rossi)

float votoMassimo = 0;

Sarà necessario utilizzare un ciclo for per leggere uno alla volta i voti della lista.

Il ciclo parte con indice 0 arrivando fino alla fine dell’array, cioè con indice pari a n-1 (n: dimensione dell’array), quindi l’array è costituito da n oggetti e l’indice che identifica la posizione va da 0 a n-1.

In C è possibile calcolare automaticamente la dimensione dell’array con la funzione: sizeof di cui trovate tutte le informazioni sul references di Arduino

sizeof(sequenzaVoti)/sizeof(float))

mi darà la dimensione n dell’array.

Approfondiamo quanto detto.

sizeof permette di ottenere la dimensione in byte di una variabile di qualsiasi tipo (int, float, byte, char, ecc…) o del numero di byte che occupa un array.

Attenzione però per definire un array possiamo farlo in due modi:

modo 1, indicando la dimensione tra parentesi quadre
float sequenzaVoti[6]

modo 2, enumerando gli elementi, nel nostro caso i voti, ovvero l’elenco degli oggetti
float sequenzaVoti[] = {3.5, 6.75, 5.5, 9, 7.25, 8.75};

Per sapere la dimensione in byte dell’array useremo sizeof() che restituirà la dimensione dell’array in byte.

Quindi l’operatore sizeof(sequenzaVoti) non restituirà il numero di celle dell’array, ma la sua dimensioni in byte, pertanto:

sizeof(sequenzaVoti)

restituirà come valore: 24 perchè l’array sequenzaVoti[] è costituita da 6 numeri reali (con la virgola), ciascuno di esso rappresentato da quattro byte pertanto il numero totale di byte dell’array è 6×4=24.

Per calcolare la dimensione dell’array, partendo dalla dimensione in byte ricavata da sizeof(), possiamo operare in questo modo:

for (int i = 0; i < (sizeof(sequenzaVoti)/sizeof(float)); i++) { 
//codice…
}

Per trovare il voto massimo bisogna verificare ad ogni ciclo del for se il valore attuale (sequenzaVoti[i]) risulta maggiore del valore che abbiamo memorizzato all’interno della variabile votoMassimo, per fare questo controllo utilizzeremo l’istruzione if:

if (sequenzaVoti[i] > votoMassimo) { 
   // salviamo il valore massimo attuale...
}

Se la condizione dell’if è verificata, bisogna sovrascriviamo il valore di “votoMassimo” con quello trovato della sequenza dei voti che stiamo leggendo.

votoMassimo = sequenzaVoti[i];

Detto ciò costruiamo lo sketch che, data una lista di 6 voti memorizzati in un array, stampa sulla serial monitor

Il voto massimo è:…

al posto dei puntini dovete stampare il voto massimo tra quelli scritti nell’array.
La lista dei voti, per ora, viene inserita nel codice, non dovrà essere scritta da computer sulla serial monitor.

Potete far eseguire tutto il codice di estrazione del voto massimo nel setup() con il loop() vuoto.

Per completezza con quanto spiegato, aggiungo alla stampa del voto massimo la stampa della dimensione massima dell’array sequenzaVoti[]

Il listato completo sarà:

/*
 * Prof. Michele Maffucci
 * data 15.03.2021
 * 
 * Trovare il valore più grande in un array
 * 
 */

// sequenza di 6 voti di uno studente
float sequenzaVoti[] = {3.5, 6.75, 5.5, 9, 7.25, 8.75};

void setup() {
  Serial.begin(9600);

  // la variabile con il numero massimo:

  int votoMassimo = 0;

  for (int i = 0; i < (sizeof(sequenzaVoti) / sizeof(float)); i++) {
    if (sequenzaVoti[i] > votoMassimo) {
      // se il voto corrente è maggiore di quello
      // salvato dentro votoMassimo allora lo copio  
      votoMassimo = sequenzaVoti[i];
    }
  }
  Serial.print("Il voto massimo è: ");
  Serial.println(votoMassimo);
  Serial.print("Dimensione in byte dell'array ");
  Serial.println(sizeof(sequenzaVoti));  
}

void loop() {}

Esercizio 1
Realizzare uno sketch che accetta l’imputo dei voti (float) da tastiera e restituisce il voto massimo.

Esercizio 2
Realizzare uno sketch che accetta l’imputo dei voti (float) da tastiera e restituisce la media dei voti.

Buon Coding a tutti 🙂