Archivi categoria: i miei allievi

Come ottenere più valori da una funzione in Arduino: variabili globali, riferimenti e struct – lezione 1/2

Durante la correzione delle esercitazioni di laboratorio mi accorgo spesso che il passaggio dei valori alle funzioni è un punto che genera parecchia confusione. Per questo ho deciso di preparare una breve lezione di ripasso, per chiarire i concetti fondamentali e aiutare a scegliere l’approccio più corretto nello sviluppo degli sketch.

Uno dei problemi molto comuni quando si programma in C/C++ su Arduino è: come ottenere più di un valore da una funzione?

In molti casi vorremmo che una funzione ci restituisse, ad esempio, due risultati (come nello scambio di due numeri), ma il linguaggio prevede che una funzione possa avere un solo valore di ritorno con l’istruzione return. Questo non significa che siamo bloccati: esistono più strategie corrette per gestire più risultati in modo chiaro e sicuro.

Partiremo da una soluzione semplice e intuitiva, basata su variabili globali, utile per capire il concetto ma generalmente sconsigliata nei programmi reali perché può creare effetti collaterali difficili da controllare. Passeremo poi a un approccio molto più robusto: il passaggio per riferimento, che permette a una funzione di modificare direttamente le variabili del chiamante senza ricorrere a globali. Infine vedremo l’uso dell’istruzione struct, cioè “contenitori” di più campi, che consentono di raggruppare dati correlati e di restituirli come un unico oggetto.

L’obiettivo non è solo far funzionare gli esempi, ma capire cosa succede davvero in memoria quando una funzione riceve parametri “per valore” (copie) oppure “per riferimento” (accesso diretto all’originale) e perché la scelta del metodo influisce su leggibilità, manutenzione e affidabilità del codice. Al termine della lezione saprete riconoscere quale tecnica usare a seconda del problema e scrivere funzioni più pulite, modulari e facili da riutilizzare.

Vedremo poi nella prossima lezione una quarta variante molto diffusa nel mondo embedded.

01. Versione “semplice” (ma sconsigliata): usare variabili globali

Esempio 01: scambio di valori

La funzione non “restituisce” due valori: modifica direttamente due variabili definite fuori da loop().

/*
  Prof. Maffucci Michele
  13.02.26
  Scambio di due valori usando variabili globali.
*/

int valoreA;
int valoreB;

void scambiaGlobali() {
  int temporaneo = valoreA;
  valoreA = valoreB;
  valoreB = temporaneo;
}

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

void loop() {
  valoreA = random(10);
  valoreB = random(10);

  Serial.print("Valori prima dello scambio (A, B): ");
  Serial.print(valoreA);
  Serial.print(", ");
  Serial.println(valoreB);

  scambiaGlobali();

  Serial.print("Valori dopo lo scambio (A, B):  ");
  Serial.print(valoreA);
  Serial.print(", ");
  Serial.println(valoreB);

  Serial.println();
  delay(1000);
}

Esempio 02: la funzione restituisce minimo e massimo aggiornando variabili globali.

Sulla serial monitor vengono mostrati ogni 5 secondi: la serie di 20 numeri generati, il minimo della serie, il massimo della serie.

/*
  Prof. Maffucci Michele
  13.02.26
  Calcola minimo e massimo su una serie di campioni usando variabili globali.
*/

int minimoCampione;
int massimoCampione;

void calcolaMinMaxGlobali(int numeroCampioni) {
  minimoCampione = 9999;
  massimoCampione = -9999;

  Serial.print("Campioni: ");

  for (int i = 0; i < numeroCampioni; i++) {
    int campione = random(0, 101); // 0..100

    // Stampa i campioni separati da virgola
    Serial.print(campione);
    if (i < numeroCampioni - 1) {
      Serial.print(", ");
    } else {
      Serial.println(); // a capo dopo l'ultimo campione
    }

    // Aggiorna minimo e massimo
    if (campione < minimoCampione) {
      minimoCampione = campione;
    }
    if (campione > massimoCampione) {
      massimoCampione = campione;
    }
  }
}

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0));
}

void loop() {
  const int numeroCampioni = 20;

  Serial.print("Genero ");
  Serial.print(numeroCampioni);
  Serial.println(" campioni casuali (0..100) e calcolo min/max...");

  calcolaMinMaxGlobali(numeroCampioni);

  Serial.print("Minimo trovato: ");
  Serial.println(minimoCampione);

  Serial.print("Massimo trovato: ");
  Serial.println(massimoCampione);

  Serial.println();
  delay(5000);
}

02. Passaggio per riferimento

Esempio 01: Scambia due valori passati per riferimento.

/*
  Prof. Maffucci Michele
  13.02.25
  Scambia due valori passati per riferimento.
*/

void scambiaPerRiferimento(int &a, int &b) {
  int temporaneo = a;
  a = b;
  b = temporaneo;
}

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

void loop() {
  int numero1 = random(10);
  int numero2 = random(10);

  Serial.print("Valori prima dello scambio (n1, n2): ");
  Serial.print(numero1);
  Serial.print(", ");
  Serial.println(numero2);

  scambiaPerRiferimento(numero1, numero2);

  Serial.print("Valori dopo lo scambio (n1, n2):  ");
  Serial.print(numero1);
  Serial.print(", ");
  Serial.println(numero2);

  Serial.println();
  delay(1000);
}

Esempio 02: divisione con resto (quoziente + resto)

Nello sketch che segue una funzione calcola due risultati (quoziente e resto) tramite riferimenti, e in più restituisce true/false per gestire il caso del divisore zero.

/*
  Prof. Maffucci Michele
  13.02.26
  Calcola due risultati (quoziente e resto) tramite riferimenti, 
  restituisce true/false per gestire il caso del divisore zero.
*/

bool dividiConResto(int numeratore, int denominatore, int &quoziente, int &resto) {
  if (denominatore == 0) {
    return false; // errore: divisione per zero
  }

  quoziente = numeratore / denominatore;
  resto = numeratore % denominatore;
  return true;
}

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0));
}

void loop() {
  int a = random(0, 101);   // 0..100
  int b = random(0, 11);    // 0..10 (include lo zero apposta)

  int q = 0;
  int r = 0;

  Serial.print("Operazione: ");
  Serial.print(a);
  Serial.print(" / ");
  Serial.println(b);

  if (dividiConResto(a, b, q, r)) {
    Serial.print("Quoziente: ");
    Serial.println(q);

    Serial.print("Resto: ");
    Serial.println(r);
  } else {
    Serial.println("Errore: divisione per zero (b = 0).");
  }

  Serial.println();
  delay(1500);
}

03. Scambio di valori versione migliorata. Trattare più valori insieme: usare una struct

Con la struct impacchettiamo più campi in un solo tipo (una “coppia”), la passiamo alla funzione e la funzione restituisce una nuova struct con i campi scambiati.

Esercizio 01: scambio di valori

/*
  Prof. Maffucci Michele
  13.02.26
  Scambio di valori usando una struct.
*/

struct Coppia {
  int primo;
  int secondo;
};

Coppia scambiaCoppia(Coppia c) {
  int temporaneo = c.primo;
  c.primo = c.secondo;
  c.secondo = temporaneo;
  return c;
}

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

void loop() {
  Coppia dati = { random(10), random(10) };

  Serial.print("Valori prima dello scambio (p, s): ");
  Serial.print(dati.primo);
  Serial.print(", ");
  Serial.println(dati.secondo);

  dati = scambiaCoppia(dati);

  Serial.print("Valori dopo lo scambio (p, s):  ");
  Serial.print(dati.primo);
  Serial.print(", ");
  Serial.println(dati.secondo);

  Serial.println();
  delay(1000);
}

Esempio 02: Lettura analogica completa (raw + volt + percentuale)

La funzione ritorna un oggetto con tre valori: lettura grezza, tensione stimata e percentuale.

/*
  Prof. MAffucci Michele
  13.02.26
  Restituisce più valori raggruppandoli in una struct.
*/

struct LetturaAnalogica {
  int raw;          // 0..1023
  float volt;       // tensione stimata
  int percentuale;  // 0..100
};

const int PIN_INGRESSO = A0;
const float VREF = 5.0; // se usi una scheda a 3.3V, imposta 3.3

LetturaAnalogica leggiAnalogicoCompleto(int pin) {
  LetturaAnalogica risultato;

  risultato.raw = analogRead(pin);
  risultato.volt = risultato.raw * (VREF / 1023.0);
  risultato.percentuale = map(risultato.raw, 0, 1023, 0, 100);

  return risultato;
}

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

void loop() {
  LetturaAnalogica misura = leggiAnalogicoCompleto(PIN_INGRESSO);

  Serial.print("Ingresso A0 -> raw: ");
  Serial.print(misura.raw);

  Serial.print(" | V: ");
  Serial.print(misura.volt, 2);

  Serial.print(" | %: ");
  Serial.println(misura.percentuale);

  delay(500);
}

Abbiamo visto tre strategie diverse per gestire più risultati in una funzione. E’ fondamentale scegliere quale usare nei vostri sketch, è importante capire bene cosa cambia tra passaggio per valore, passaggio per riferimento e uso di una struct.

Riassumo di seguito.

Passaggio dei valori a una funzione

Quando viene chiamata una funzione, potete consegnare i dati in due modi principali:

  1. Per valore (default)
  • la funzione riceve una copia dei parametri;
  • se dentro la funzione modificate quei parametri, state modificando la copia, non le variabili originali;
  • risultato: fuori dalla funzione i valori non cambiano.
  1. Per riferimento (&) — tipico in Arduino perché è C++

  • la funzione riceve un alias delle variabili originali;
  • dentro la funzione, quando assegnate a = …, state scrivendo proprio nella variabile chiamante.
  • risultato: fuori dalla funzione i valori cambiano davvero.

Nota: in C “puro” non esistono i riferimenti, quindi si userebbero puntatori (int *a, int *b). Arduino, però, compila in C++: i riferimenti sono disponibili.

Quando usare una struct (o un tipo “aggregato”)

La struct è utile quando:

  • volete trattare più valori come un unico oggetto logico (es. coordinate, min/max, stato di sensori, ecc.);
  • volete restituire più informazioni in un colpo solo (una sola return, ma con più campi dentro);
  • volete migliorare leggibilità: c.primo, c.secondo comunica il significato meglio di due variabili scollegate.

Costo/beneficio nell’uso della struct

  • Pro: codice più espressivo, dati “impacchettati”, interfaccia pulita.
  • Contro: c’è una copia della struct quando la passi/ritorni (di solito trascurabile se la struct è piccola). Su microcontrollori molto limitati, per struct grandi conviene passare per riferimento anche la struct.

Come detto ad inizio di questo post, nella prossima lezione vedremo una quarta variante molto diffusa nel mondo embedded: gli output parameters con puntatori (stile C) e una versione più moderna che usa const per rendere immediatamente chiaro quali dati sono solo in ingresso e quali vengono modificati dalla funzione.

Buon Coding a tutti 🙂

Arduino nello zaino: 4 “laboratori” trasportabili stampabili in 3D per avere sempre una base di prototipazione pronta

Chi fa didattica laboratoriale (o semplicemente ama sperimentare) lo sa: il problema non è avere gli strumenti, ma riuscire a portare con sé una dotazione minima che permetta di accendere il cervello “maker” ovunque ci si trovi.

Io ho già i miei laboratori completi, a casa e a scuola, dove posso fare tutto ciò che mi serve, però nello zaino porto sempre una piccola base di sperimentazione: una scheda Arduino, una breadboard, qualche cavetto e pochi componenti selezionati. Non è un “mini laboratorio” nel senso classico del termine, ma è qualcosa di altrettanto prezioso: un sistema ordinato e trasportabile che riduce l’attrito e rende immediata qualsiasi micro-attività.

In questo post vi segnalo 4 tipologie di contenitori/workstation stampabili in 3D pensate proprio per questo: tenere insieme scheda, breadboard, jumper e componenti, proteggere il setup e arrivare in aula (o ovunque) già pronto a partire.

Perché ritengo che questi organizer siano utili anche se avete un laboratorio completo

  • setup più veloce: appoggi, apri, colleghi l’USB e inizi;
  • ordine e componenti “a prova di zaino”: meno dispersione, meno “dov’è finito quel cavetto?”;
  • micro-attività replicabili: perfetti per dimostrazioni, tutoraggi, ASL, attività itineranti, laboratori in aule non attrezzate;
  • continuità nelle sperimentazioni personali: potete riprendere un prototipo esattamente dov’era rimasto, senza ricostruire tutto da zero.

All’organizer associo anche un piccolo contenitore rigido in cui dispongo ulteriori componenti elettronici.

The Folding Arduino Lab (il “classico” da zaino)

È quello che uso da sempre: The Folding Arduino Lab di Jason Welsh già segnalato qualche tempo fa su questo sito.
L’idea è semplice: un contenitore compatto che integra Arduino + breadboard (400 punti) + cassetti per cavetti e componenti. In uno spazio ridotto (indicativamente 10×9×8 cm) vi portate dietro l’essenziale.

I motivi per cui uso questa soluzione sono:

  • struttura “a libro”: protegge il setup e lo rende trasportabile, ovviamente non bisogna pretendere molti componenti sulla breadboard;
  • due vani/cassetti: ottimi per jumper, LED, resistenze, pulsanti, piccoli sensori;
  • è un progetto con molte varianti e remix: facile trovare adattamenti e accessori;
  • nota personale: l’autore ha sviluppato diverse versioni; io attualmente utilizzo la versione 2, su cui ho montato un Arduino UNO R4 WiFi.

Vi segnalo che con questo kit potete evitare, per le cerniere, l’uso di elementi metallici che possono essere sostituiti con filamento per la stampa 3D che risultano relativamente robusti e rimovibili.

Supporto/Stazione di lavoro modulare per Arduino UNO e breadboard (modulare e “scalabile”)

Questa soluzione è pensata come una workstation regolabile e modulare, ideale se vuoi una base ordinata non solo per trasporto, ma anche per lavorare “pulito” su banco (e poi richiudere e portare via).
La Modular Arduino UNO Breadboard Holder/Workstation (V2) è dichiarata come progetto modulare, con inserti, tray e mount selezionabili, e con impostazioni/accortezze legate alla stampa “print-in-place” in alcune parti.

Caratteristiche interessanti

  • modularità reale: potete scegliere vassoi/inserti in base a cosa usate (jumper, sensori, viteria, ecc.);
  • approccio “workstation” più che “scatola”: molto comoda per lavorare in modo ordinato;
  • ottima se volete aggiungere nel tempo moduli e supporti (display, sensori, ecc.).

Portable Arduino Lab (PAL) – molto compatto

Il Portable Arduino Lab (PAL) è una versione più piccola della workstation, pensata esplicitamente per “piccoli progetti on the go”, è dichiarato compatibile con Arduino UNO R3 e UNO R4.

Cosa mi piace

  • È progettato come “compagno di laboratorio”: base + chiusura + possibilità di storage;
  • ha indicazioni di stampa molto chiare: per esempio layer height 0,2 mm e attenzione alle tolleranze della cerniera/parti in movimento;
  • pPrevede anche opzioni e aggiornamenti: ad esempio supporti per breakout (ESP32 / Arduino Nano) sono citati tra gli update del progetto.

Nota pratica

In alcune configurazioni richiede viteria (M3x25) e una piccola fase di assemblaggio; non è un difetto, ma una scelta per robustezza e funzionalità.

Arduino R4 Laboratorio Portatile – “print in place” dedicato a UNO R4

Questa soluzione è centrata su Arduino UNO R4 e punta alla massima portabilità con la logica “print-in-place”. È indicato l’uso con breadboard da 400 punti e il fissaggio della scheda con viti M2.5 (fino a una certa lunghezza). (Seguire il link per prelevare i file per la stampa)

Scegliere questa soluzione se:

  • lavorate principalmente con UNO R4 e volete un contenitore dedicato.
  • se vi interessa un progetto già impostato con un layout essenziale: scheda + breadboard + vano.

Cosa mettere nello zaino

Se volete rendere davvero efficace uno di questi organizer, la differenza la fa la scelta dei componenti. Io ragiono per “massima resa didattica con minimo volume”:

  • Arduino (nel mio caso UNO R4 WiFi) + cavo USB;
  • breadboard 400 punti + cavetti jumper (M-M, M-F, F-F in piccola quantità);
  • set micro: LED + resistenze, 1–2 pulsanti, 1 potenziometro;
  • 1 sensore “jolly” (LDR o temperatura/umidità) + 1 attuatore (buzzer o micro-servo);
  • mini cacciavite / pinzetta, un paio di cavetti Dupont extra;
  • (opzionale) Power bank se volete lavorare senza PC per attività specifiche.

Sto realizzando in questi mesi una versione di un mini laboratorio trasportabile contenuto in una rugged bag, le classiche valigette a tenuta stagna molto robuste, le uso spesso per la realizzazione di giochi escape o per contenere apparati delicati, se riuscirò vi mostrerò più avanti il risultato.

Buon Making a tutti 🙂

Configurare Arduino UNO R4 WiFi per Arduino Cloud

Nelle prossime settimane svolgeremo una serie di attività di laboratorio dedicate alla realizzazione di semplici applicazioni IoT con Arduino Cloud, utilizzando come scheda di riferimento Arduino UNO R4 WiFi. Per lavorare in modo ordinato ed efficace, questa guida introduttiva nasce con un obiettivo preciso: accompagnare gli studenti passo dopo passo nella configurazione della scheda e nel collegamento al Cloud, così da essere operativo fin da subito.

Queste attività sono importanti perché ci permetteranno di passare dal classico circuito “che funziona sul banco” a un sistema realmente utile e osservabile: potremo monitorare da computer o da dispositivi mobili lo stato dei sensori (temperatura, luce, presenza, umidità, ecc.), visualizzare i dati su dashboard, registrare misure nel tempo e, quando necessario, intervenire da remoto attivando uscite e comandi (ad esempio LED, relè o attuatori). In altre parole, costruiremo piccoli prototipi che riproducono lo stesso schema operativo di molte soluzioni industriali e domotiche: sensore > rete > dashboard > controllo, con attenzione a affidabilità, leggibilità dei dati e sicurezza di base.

Questa guida è tratta dalla guida ufficiale su cui ho apportato alcune mie modifiche e le ho rese “più didattiche”.

Di seguito dettaglio la procedura essenziale per portare Arduino UNO R4 WiFi “online” collegandola all’Arduino Cloud.

Arduino UNO R4 WiFi, come già dettagliato in precedenti tutorial, integra un modulo radio dedicato (ESP32-S3) che mette a disposizione la connettività WiFi: grazie a questo componente la scheda può collegarsi a Internet e dialogare con i servizi Cloud senza hardware aggiuntivo. Da qui nasce il passaggio chiave dei nostri laboratori: non ci limiteremo a far “funzionare un circuito”, ma impareremo a pubblicare dati, visualizzarli su una dashboard e, quando serve, inviare comandi da remoto.

Al termine della configurazione potrete:

  • caricare sketch anche via rete (OTA, over-the-air) quando previsto dal flusso Cloud,
  • costruire dashboard consultabili da PC o smartphone,
  • monitorare variabili e sensori in tempo reale e, in prospettiva, realizzare prototipi utili per domotica, automazione e telemetria.

Cosa serve

  • Arduino UNO R4 WiFi
  • Arduino Cloud (account gratuito e accesso alla piattaforma)

Configurazione e procedura

Prima di iniziare, se non avete mai utilizzato Arduino Cloud, vi consiglio di dare un’occhiata alla guida “Getting Started With the Arduino Cloud” che vi aiuterà a capire i concetti base e vi farà risparmiare tempo durante la configurazione, però durante le lezioni che seguiranno vi fornirò tutte le indicazioni.

Continua a leggere

Flashcard per lo studio – utilizzare Anki

Visto che siamo in vacanza riprendo i miei appunti sull’organizzazione dello studio, iniziata con le schede Quick Reference, tutto con l’idea di realizzare in un futuro un manuale con esempi da fornire agli studenti. Ho ripreso lo studio delle lingue e quindi ho pensato di realizzare qualcosa in merito all’uso delle flashcard strumento che ritengo utile.

Parto da una domanda che mi è stata fatta da uno studente di seconda superiore poco prima delle vacanze natalizie:

“Prof, ma lei come studiava quando aveva la nostra età?”

È una domanda che torna spesso, e ogni volta mi ricorda una cosa importante: molti studenti immaginano che esista un “metodo segreto”, una tecnica risolutiva che rende lo studio facile e veloce. La mia risposta, invece, è sempre piuttosto disarmante: non studiavo in modo così diverso da voi. Riassunti, schemi, appunti ordinati… e soprattutto flashcard.

Le flashcard, però, non erano una soluzione “magica”. Erano uno strumento semplice, che funzionava solo a una condizione: usarle con costanza, a piccole dosi, nel tempo. Ho sempre pensato che nello studio valga una regola che sembra banale ma è decisiva: meglio poco, ma ripetuto nel tempo. È questo che trasforma un ripasso occasionale in un apprendimento stabile. E naturalmente, con gli anni, ognuno costruisce il proprio equilibrio: c’è chi rende più efficaci gli schemi, chi preferisce i riassunti, chi trova nelle flashcard la leva migliore per fissare concetti, definizioni, formule o passaggi procedurali.

Oggi, quando mi capita di usare le flashcard per imparare (ad esempio per le lingue o per memorizzare lessico tecnico), mi affido spesso ad Anki. Non perché sia “l’ennesima app per fare flashcard”, ma perché introduce un’idea molto concreta: invece di ripassare tutto allo stesso modo e negli stessi momenti, ti aiuta a ripassare solo ciò che serve, quando serve. Ti porta a fare una cosa che tende a essere faticosa ma estremamente efficace: provare a ricordare attivamente prima di guardare la risposta. E quel tentativo di richiamo, ripetuto con regolarità, è spesso la differenza tra “l’ho letto” e “lo so”.

Per gli studenti questo significa ridurre l’effetto delle maratone dell’ultimo minuto e costruire memoria a lungo termine con un metodo più sostenibile. Per i docenti significa avere uno strumento che rende più gestibile il ripasso distribuito e la personalizzazione, senza limitarsi al classico “ripasso pre-verifica”.
C’è poi un aspetto che, a scuola, conta più di quanto sembri: Anki è un progetto libero e open source (in particolare nella versione desktop), sostenuto da una comunità ampia e da un ecosistema di estensioni. In pratica, non sei legato a una piattaforma chiusa: puoi costruire nel tempo un archivio di materiali che resta tuo, riutilizzabile e migliorabile anno dopo anno.

Nei paragrafi che seguono vediamo cosa rende Anki diverso da altri strumenti di studio, quali vantaggi offre in modo concreto e come iniziare con un tutorial essenziale e due esempi pratici, subito adattabili alle vostre discipline.

Gli appunti che seguono sono una sintesi del manuale che trovate sul sito di riferimento e che ho riformulato e sintetizzato, troverete poi alcuni esempi di flashcard sull’uso di Arduino.

Anki nello studio

C’è un momento che studenti e docenti conoscono bene: hai studiato “davvero”, magari anche con impegno costante, eppure dopo pochi giorni ti accorgi che alcuni passaggi si sono sfilacciati. Non è (solo) una questione di volontà, è più spesso una questione di come ripassi e quando lo fai.

Anki è un programma di flashcard “intelligenti” che pianifica per voi il ripasso: vi mostra una domanda, vi chiede di rispondere, poi usa il vostro feedback per decidere quando rivedrete quella stessa informazione. In altre parole: voi pensate ai contenuti, Anki si occupa del calendario del ripasso.

Anki nasce esattamente per rispondere a questo problema quotidiano: non vi chiede di studiare di più, vi aiuta a sprecare meno. Invece di ripassare tutto allo stesso modo e nello stesso momento, vi porta a rivedere ciò che state per dimenticare, quando serve davvero. La logica di fondo è quella di allenare il cervello con due leve molto concrete: richiamo attivo (provare a ricordare prima di controllare) e ripetizione distanziata (rivedere a intervalli crescenti).

Perché Anki è interessante rispetto ad altri applicativi

  1. Trasforma lo studio passivo in apprendimento che resta
    Rileggere, sottolineare, “ripassare scorrendo” sono abitudini comprensibili: danno l’idea di essere produttivi. Ma spesso si basano sul riconoscimento, non sul recupero. Quando invece vi trovate davanti a una domanda e dovete rispondere senza aiuti, state facendo ciò che il manuale chiama test di richiamo attivo: è faticoso, sì, ma è proprio quella fatica “buona” che rende la memoria più stabile.
  2. Ripassate meno, ma meglio (ripetizione distanziata)
    Il cervello tende a scartare in fretta ciò che non viene usato. Ecco perché il ripasso “a ondate” (tutto la sera prima) funziona male sul lungo periodo. Anki applica invece la ripetizione distanziata: ciò che ricordate bene torna dopo più tempo; ciò che vi è difficile ricompare prima. Il risultato pratico è semplice: non ripassate “a caso”, ma in modo mirato, con intervalli che crescono mentre la memoria si consolida.
  3. È personalizzabile: funziona per materie diverse e livelli diversi
    Anki non è “solo per le lingue” o “solo per medicina”. Se una cosa si può chiedere in forma di domanda (e verificare con una risposta), allora si può allenare con Anki: definizioni, formule, passaggi di procedure, date, classificazioni, concetti chiave e potete farlo anche con materiali ricchi: immagini, audio, video, notazione scientifica (LaTeX/Math).
  4. Open source e free: vantaggi concreti
    Qui Anki ha un valore particolare, soprattutto a scuola:

    • Desktop open source: il codice di Anki è pubblicato con licenza GNU AGPL v3 o successiva (con alcune parti sotto licenze differenti, come indicato nel repository). Questo significa continuità, verificabilità e un progetto che non dipende da una “scatola chiusa”.
    • Ecosistema di add-on: Anki si estende con componenti aggiuntivi, spesso nati da bisogni reali di docenti e studenti (lingue, layout, gestione, workflow).
    • Controllo e portabilità dei materiali: puoi esportare e trasferire mazzi “impacchettati” con contenuti e media, utile per backup e condivisione ragionata.
    • Multi-piattaforma e sync: esiste un servizio di sincronizzazione gratuito (AnkiWeb) per tenere allineati i contenuti tra dispositivi.
    • Android open source: AnkiDroid è su GitHub e dichiara licenze libere (GPL-3.0, con componenti specifiche sotto altre licenze).

Nota utile per evitare equivoci: su iOS l’app ufficiale AnkiMobile è a pagamento; la pagina App Store spiega che le vendite finanziano lo sviluppo.

  1. Un “sistema” più che un’app
    Molti strumenti per flashcard si fermano al “fronte/retro”. Anki, invece, è un piccolo sistema di studio: statistiche, ricerca, tag, opzioni di pianificazione, modelli delle carte, sincronizzazione, backup. Per chi vuole ottimizzare ulteriormente, oggi esiste anche un pianificatore alternativo (FSRS) integrato nelle opzioni.

Mini-tutorial usare Anki

Continua a leggere

Dirimere i conflitti con il pensiero computazionale – educazione civica – sketch Arduino – lezione 03

Ora traduciamo lo pseudocodice in uno sketch Arduino. Usiamo input da Serial Monitor per simulare le scelte e il LED integrato (e un buzzer opzionale) per avere feedback fisico. L’obiettivo non è “automatizzare i conflitti”, ma allenare il pensiero procedurale e la gestione consapevole dei passi. Si faccia riferimento alla lezione precedente (seguire il link) in cui è stato mostrato il diagramma di flusso nella modalità Mermaid e classica.

Nella dimostrazione viene usato un Arduino UNO R4 WiFi.

Di seguito una possibile soluzione, il funzionamento lo trovate nei commenti.

/* Prof. Maffucci Michele
  data: 18.11.25
  Risoluzione di un conflitto
  Input da Serial Monitor

  - LED integrato (pin 13) usato come feedback visivo
  - Buzzer opzionale su pin 5 (beep brevi)
  Istruzioni:
  1) Aprire la Serial Monitor (115200 baud).
  2) Rispondere alle domande con i tasti indicati (y/n, a/f) o premi invio quando richiesto.
*/

const int pinLed = LED_BUILTIN;  // pin 13
const int pinBuzzer = 5;         // pin a cui è connesso il Buzzer
int numeroBeep = 0;

bool chiaveMsg = 0;  // per controllo stampa msg

// emissione della nota
void beep(int durataMs) {
  tone(pinBuzzer, 880);  // nota La4
  delay(durataMs);
  noTone(pinBuzzer);
}

// sequenza di beep ripetuta all'avvio del programma
void sequenzaAvvio() {
  for (int i = 0; i < 10; i++) {
    beep(100);
    delay(100);
  }
}

// Attende che ci sia almeno un carattere sulla seriale e lo legge (minuscolo)
char leggiChar() {
  while (!Serial.available()) {
    // lampeggio lento del LED per indicare "in attesa"
    digitalWrite(pinLed, HIGH);
    delay(200);
    digitalWrite(pinLed, LOW);
    delay(200);
  }

  // Legge un solo byte dalla seriale e lo mette in c (di tipo char).
  // Questo è il primo carattere che l’utente ha inviato.
  char c = Serial.read();

  // Svuota il buffer rimanente leggendo tutto quello che è ancora arrivato
  // (ad esempio il \n dell’invio, o più caratteri se l’utente ha incollato del testo).
  while (Serial.available())
    Serial.read();
  if (c >= 'A' && c <= 'Z')  // Conversione in minuscolo se il carattere è una lettera maiuscola.
    c = c - 'A' + 'a';
  return c;
}

// -- Utility: attende qualsiasi tasto (usata come "invio per continuare")
// “un puntatore a char costante”, cioè una stringa in stile C non modificabile
// (tipicamente una stringa letterale come "Ciao").

void attendiConferma() {
  const char* messaggio = "Premi invio per continuare";
  Serial.println(messaggio);
  while (!Serial.available()) {
    digitalWrite(pinLed, HIGH);
    delay(150);
    digitalWrite(pinLed, LOW);
    delay(150);
  }
  // svuota
  while (Serial.available())
    Serial.read();
  beep(100);
}

// -- Utility: domanda sì/no
bool domandaSiNo(const char* domanda) {
  Serial.println(domanda);
  Serial.println("Digita 'y' per SI, 'n' per NO");

  //ripetiamo finché l’utente non inserisce una risposta valida.
  // Il ciclo si interrompe con un return (es. quando l’utente digita ‘y’ o ‘n’).
  for (;;) {
    char c = leggiChar();
    if (c == 'y') {
      Serial.println("Hai risposto: SI");
      beep(100);
      return true;
    }
    if (c == 'n') {
      Serial.println("Hai risposto: NO");
      beep(100);
      return false;
    }
    Serial.println("Risposta non valida. Usa 'y' o 'n'.");
  }
}

// Pausa "simulata breve" al posto di 2 minuti reali
void pausaBreve(const char* motivo, int secondi = 5) {
  Serial.print("Pausa: ");
  Serial.print(motivo);
  Serial.print(" (");
  Serial.print(secondi);
  Serial.println("s)");
  for (int i = 0; i < secondi; i++) {
    digitalWrite(pinLed, HIGH);
    delay(300);
    digitalWrite(pinLed, LOW);
    delay(700);
    Serial.print(".");
  }
  Serial.println();
  beep(100);
}

void setup() {
  pinMode(pinLed, OUTPUT);
  Serial.begin(115200);
  delay(1000);
  Serial.println("=== Risoluzione Conflitto - VERSIONE BASE ===");
  Serial.println("Benvenuto! Segui le istruzioni sul Serial Monitor.");
  pinMode(pinBuzzer, OUTPUT);
  sequenzaAvvio();
}

void loop() {
  // 1) Sicurezza del confronto
  bool sicuro = false;
  do {
    sicuro = domandaSiNo("E' sicuro parlare adesso?");
    if (!sicuro) {
      Serial.println("OK, facciamo un time-out breve al posto di 2 minuti.");
      pausaBreve("time-out", 5);
    }
  } while (!sicuro);

  // 2) Definizione del problema
  Serial.println("Definisci il problema in UNA frase (in classe va bene scriverla su carta).");
  attendiConferma();

  // 3) Turni + Parafrasi
  bool parafrasiOk = false;
  do {
    Serial.println("Turno A (60s simulati).");
    attendiConferma();
    Serial.println("Turno B (60s simulati).");
    attendiConferma();
    parafrasiOk = domandaSiNo("Parafrasi reciproca corretta?");
  } while (!parafrasiOk);

  // 4) Opzioni di soluzione (almeno 2)
  int numeroOpzioni = 0;
  Serial.println("Genera almeno 2 opzioni di soluzione.");
  for (;;) {
    Serial.println("a = aggiungi opzione, f = fine (consentita solo se >= 2 opzioni)");
    char c = leggiChar();
    if (c == 'a') {
      numeroOpzioni++;
      Serial.print("Opzione aggiunta. Totale: ");
      Serial.println(numeroOpzioni);
      beep(100);
    } else if (c == 'f') {
      if (numeroOpzioni >= 2) {
        Serial.println("OK, passiamo alla decisione.");
        beep(100);
        break;
      } else {
        Serial.println("Servono almeno 2 opzioni prima di terminare.");
      }
    } else {
      Serial.println("Scelta non valida. Usa 'a' o 'f'.");
    }
  }

  // 5) Decisione finale
  bool accordo = domandaSiNo("C'e' accordo su una opzione?");
  if (accordo) {
    Serial.println("Piano d'azione: definire chi/fa/cosa/entro quando (scrivere su carta).");
    Serial.println("Promemoria: verifica dopo 24 ore.");
    Serial.println("FINE. Riavvio tra 5 secondi...");
    pausaBreve("chiusura", 5);
  } else {
    Serial.println("Coinvolgi un mediatore o rinvia il confronto.");
    Serial.println("Ritorno al controllo 'E' sicuro parlare?'.");
    pausaBreve("ritorno", 3);
  }
}