Archivi tag: sistemi embedded

OpenStudy Desktop Companion: costruire uno strumento per imparare a gestire il tempo di studio

Un progetto con ESP32-S3 ed e-paper che nasce con un duplice obiettivo: realizzare un dispositivo realmente utile e trasformarne la costruzione in un’attività di laboratorio per gli studenti

Da qualche settimana sto lavorando a un nuovo progetto che unisce alcuni temi che ricorrono spesso nelle attività che sviluppo: elettronica, making, progettazione di interfacce, tecnologie per lo studio e didattica laboratoriale, il progetto si chiama:

OpenStudy Desktop Companion.

Si tratta di un piccolo dispositivo da scrivania dotato di display e-paper che sto sviluppando per accompagnare sessioni intenzionali di studio e lettura.

La domanda da cui sono partito è molto semplice:

perché costruire un dispositivo dedicato allo studio quando uno smartphone può già fare praticamente tutto?

Posso impostare un timer sul telefono, installare applicazioni per la tecnica Pomodoro, registrare quanto studio e consultare statistiche estremamente dettagliate, proprio qui, però, si trova anche il problema.

Quando porto lo smartphone sulla scrivania non porto soltanto un timer, porto notifiche, messaggi, social network, applicazioni e un dispositivo progettato per richiamare continuamente la mia attenzione.

OpenStudy Desktop Companion nasce quindi da un’idea diversa: provare a costruire una tecnologia che faccia poche cose, ma in modo intenzionale, e che possa restare sulla scrivania senza diventare un’ulteriore fonte di distrazione.

Piccolo dispositivo OpenStudy Desktop Companion con display e-paper acceso accanto a un libro e a un quaderno.

OpenStudy Desktop Companion sulla scrivania durante una normale sessione di lavoro.

Non soltanto misurare il tempo, ma imparare a gestirlo

Uno degli aspetti che mi interessa maggiormente riguarda proprio il rapporto con il tempo.

OpenStudy non nasce con l’obiettivo di dire semplicemente allo studente:

“devi studiare per 25 minuti”.

Vorrei che diventasse invece uno strumento attraverso il quale allenarsi progressivamente a compiere alcune scelte:

che cosa sto per fare? Quanto tempo penso di poter dedicare a questa attività? Riesco a mantenere l’impegno che ho scelto? Alla fine della sessione, come è andata?

Il timer diventa quindi soltanto una parte del processo.

Prima della sessione c’è una scelta intenzionale, durante la sessione c’è la gestione del tempo, alla fine c’è una breve riflessione e rimane una traccia del lavoro svolto.

Questo passaggio mi interessa particolarmente dal punto di vista didattico perché la gestione del tempo di studio non è una competenza che possiamo dare per acquisita, può essere osservata, discussa e allenata ed è proprio qui che emerge il secondo obiettivo del progetto.

Un dispositivo da usare, ma anche da costruire

OpenStudy Desktop Companion ha infatti per me due obiettivi paralleli:

  • il primo è costruire un oggetto realmente utilizzabile per accompagnare studio e lettura.
  • il secondo è trasformare progressivamente il progetto in una attività laboratoriale per gli studenti.

Non vorrei quindi consegnare semplicemente agli studenti un dispositivo finito dicendo loro come utilizzarlo. L’obiettivo più interessante è portarli, progressivamente, a comprenderne e costruirne almeno una parte.

Questo permette di lavorare contemporaneamente su elettronica e programmazione, ma anche su problemi molto concreti di progettazione.

  • Come deve essere organizzata una schermata di soli 200 × 200 pixel?
  • Quali informazioni sono realmente necessarie?
  • Quando deve essere aggiornato un display?
  • Come si gestisce un’interfaccia touch?
  • Come si conserva un’impostazione anche dopo lo spegnimento?
  • Come si registra localmente una sessione?
  • Come si leggono temperatura e umidità da un sensore?
  • Come si gestiscono data e ora attraverso un RTC?

E, soprattutto:

stiamo costruendo una funzione perché possiamo farlo oppure perché serve realmente alla persona che utilizzerà il dispositivo?

Quest’ultima domanda, dal punto di vista didattico, vale probabilmente più di molte spiegazioni puramente tecniche.

OpenStudy Desktop Companion accanto a un computer con Visual Studio Code utilizzato per lo sviluppo del firmware.

Il firmware di OpenStudy Desktop Companion viene sviluppato in C++ con framework Arduino, Visual Studio Code e PlatformIO.

L’hardware: perché ho scelto la Waveshare ESP32-S3-Touch-ePaper-1.54 V2

Per questo prototipo sto utilizzando una Waveshare ESP32-S3-Touch-ePaper-1.54 V2, la scelta non è stata determinata semplicemente dalla potenza dell’ESP32-S3, ci sono molte schede con le quali sarebbe possibile costruire un dispositivo simile, ciò che mi interessava era soprattutto il livello di integrazione.

In un oggetto estremamente compatto questa scheda mette a disposizione:

  • ESP32-S3-PICO-1-N8R8, con 8 MB di Flash e 8 MB di PSRAM;
  • display e-paper bianco e nero da 1,54 pollici, 200 × 200 pixel;
  • interfaccia touch;
  • RTC PCF85063 per mantenere data e ora;
  • sensore SHTC3 per temperatura e umidità;
  • slot microSD;
  • codec audio ES8311, altoparlante e microfono;
  • USB-C e gestione della batteria al litio;
  • Wi-Fi e Bluetooth disponibili attraverso l’ESP32-S3, anche se al momento non vengono utilizzati dalle funzioni del Companion.

Questa integrazione permette di ridurre notevolmente cablaggi e componenti esterni e, soprattutto nella fase di prototipazione, consente di concentrare molto del lavoro sul firmware e sull’esperienza d’uso.

Dal punto di vista laboratoriale trovo inoltre interessante avere nello stesso progetto tecnologie differenti: bus I²C, touch, sensori, RTC, memoria persistente, microSD, audio e display e-paper.

OpenStudy può quindi diventare anche una sorta di laboratorio compatto di sistemi embedded.

Continua a leggere

Esercitazione 3 – Scheduler cooperativo con tre task e supervisione dei tempi

Allenamento per l’esame di maturità
Percorso di laboratorio con Arduino per studenti di quinta ITIS

Obiettivo didattico

Organizzare il programma come scheduler cooperativo con tre task indipendenti: acquisizione analogica, lampeggio di stato e trasmissione seriale periodica. L’attività mostra come il loop() possa diventare un piccolo supervisore software.

Materiali suggeriti

  • Arduino UNO R3 o UNO R4;
  • 1 potenziometro;
  • 2 LED;
  • 2 resitori (per i LED);
  • breadboard;
  • cavetti jumper.

Schema di collegamento

Richiamo teorico

In un sistema embedded semplice non si usa un vero sistema operativo, ma si può costruire uno scheduler cooperativo con millis(). Ogni task possiede il proprio intervallo e il proprio istante dell’ultima esecuzione. Il loop() controlla se ciascun task è pronto e lo richiama.

Schema logico dell’attività

Il programma inizializza i timer dei tre task. Nel loop legge il tempo corrente e verifica uno dopo l’altro se è il momento di eseguire il task di acquisizione, quello di segnalazione e quello di stampa seriale. Se il tempo non è scaduto, passa al controllo successivo.

Diagramma di flusso

Diagramma di flusso Mermaid

flowchart TD
    A[Inizio] --> B[Configura pin, seriale e timer]
    B --> C[Leggi tempo attuale]
    C --> D{Task acquisizione pronto?}
    D -- Sì --> E[Esegui acquisizione]
    D -- No --> F{Task LED pronto?}
    E --> F
    F -- Sì --> G[Commuta LED di stato]
    F -- No --> H{Task seriale pronto?}
    G --> H
    H -- Sì --> I[Invia dati in seriale]
    H -- No --> J[Ritorna al loop]
    I --> J
    J --> C

Programma

/*
  Prof. Maffucci Michele
  Esercizio 3: scheduler cooperativo con tre task indipendenti
*/

const int PIN_SENSORE = A0;
const int PIN_LED_STATO = 8;
const int PIN_LED_SOGLIA = 9;

// ---------------------------
// Intervalli dei tre task
// ---------------------------
const unsigned long PERIODO_ACQUISIZIONE = 50;
const unsigned long PERIODO_LED = 300;
const unsigned long PERIODO_SERIALE = 500;

// ---------------------------
// Istanti ultima esecuzione
// ---------------------------
unsigned long ultimoTaskAcquisizione = 0;
unsigned long ultimoTaskLed = 0;
unsigned long ultimoTaskSeriale = 0;

// ---------------------------
// Variabili condivise
// ---------------------------
int valoreGrezzo = 0;
float tensione = 0.0;
bool statoLed = false;

void setup() {
  pinMode(PIN_LED_STATO, OUTPUT);
  pinMode(PIN_LED_SOGLIA, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // Il loop assume il ruolo di piccolo scheduler.
  unsigned long adesso = millis();

  // ---------------------------
  // Task 1: acquisizione
  // ---------------------------
  if ((adesso - ultimoTaskAcquisizione) >= PERIODO_ACQUISIZIONE) {
    ultimoTaskAcquisizione = adesso;
    taskAcquisizione();
  }

  // ---------------------------
  // Task 2: LED di stato
  // ---------------------------
  if ((adesso - ultimoTaskLed) >= PERIODO_LED) {
    ultimoTaskLed = adesso;
    taskLed();
  }

  // ---------------------------
  // Task 3: seriale
  // ---------------------------
  if ((adesso - ultimoTaskSeriale) >= PERIODO_SERIALE) {
    ultimoTaskSeriale = adesso;
    taskSeriale();
  }
}

// ----------------------------------------------------------
// Legge il sensore e calcola una tensione equivalente.
// ----------------------------------------------------------
void taskAcquisizione() {
  valoreGrezzo = analogRead(PIN_SENSORE);

  // Conversione esplicita ADC -> tensione.
  tensione = (valoreGrezzo * 5.0) / 1023.0;

  // Uso della misura per attivare un LED di soglia.
  if (tensione >= 2.50) {
    digitalWrite(PIN_LED_SOGLIA, HIGH);
  } else {
    digitalWrite(PIN_LED_SOGLIA, LOW);
  }
}

// ----------------------------------------------------------
// Task di segnalazione periodica.
// ----------------------------------------------------------
void taskLed() {
  if (statoLed == false) {
    statoLed = true;
    digitalWrite(PIN_LED_STATO, HIGH);
  } else {
    statoLed = false;
    digitalWrite(PIN_LED_STATO, LOW);
  }
}

// ----------------------------------------------------------
// Task di comunicazione seriale.
// ----------------------------------------------------------
void taskSeriale() {
  Serial.print("ADC = ");
  Serial.print(valoreGrezzo);
  Serial.print("  Tensione = ");
  Serial.print(tensione, 2);
  Serial.println(" V");
}

Continua a leggere