Controllo di un servomotore SG90 con ESP32 e display touchscreen

Introduzione

Il progetto “Controllo di un servomotore SG90 con ESP32 e display touchscreen” unisce le potenzialità dell’ESP32, un microcontrollore versatile e potente, con la versatilità di un display touchscreen interattivo, per creare un sistema di controllo sofisticato ed efficiente.

Il servo SG90, con la sua ampia gamma di movimento e precisione, è una scelta ideale per una varietà di applicazioni, dalla robotica alla domotica. L’integrazione di questo servo con l’ESP32, noto per le sue capacità di connettività e di elaborazione, apre le porte a una vasta gamma di progetti innovativi e personalizzati.

Il display touchscreen aggiunge un livello di interattività senza precedenti, consentendo agli utenti di interagire direttamente con il sistema e controllare la posizione del servo con un semplice tocco del display. Questo approccio intuitivo rende l’interfaccia utente accessibile anche a coloro che non hanno familiarità con la programmazione o l’elettronica avanzata.

Attraverso questo progetto, esploreremo i dettagli di come configurare e programmare l’ESP32 per controllare il servo SG90, come creare un’interfaccia utente interattiva utilizzando il display touchscreen e come integrare i due componenti per creare un sistema di controllo completo e funzionale. Dalla teoria alla pratica, passeremo in rassegna ogni passaggio del processo di sviluppo, offrendo istruzioni dettagliate e suggerimenti utili per aiutarti a realizzare il tuo progetto con successo.

Approfondiremo il funzionamento e le capacità del display LCD TFT da 2.8″, con particolare attenzione alle sue specifiche tecniche. Analizzeremo anche il ruolo chiave dell’ESP32 nella gestione del display e utilizzeremo le librerie TFT_eSPI e TFT_eWidget per creare un’interfaccia utente intuitiva e personalizzata

Unendo la potenza dell’elettronica moderna con la semplicità dell’interfaccia utente intuitiva, il progetto “Controllo di un servomotore SG90 con ESP32 e display touchscreen” promette di dare nuove possibilità nel mondo dell’automazione e della robotica fai-da-te sia che tu sia un principiante curioso o un esperto del settore.

Come al solito, per lo sviluppo del progetto, useremo l’ottimo IDE PlatformIO.

Il servomotore SG90

Il servomotore SG90 è un componente elettromeccanico ampiamente utilizzato in una vasta gamma di applicazioni, che va dalla robotica ai modelli radiocomandati, dalla domotica agli hobby elettronici. La sua popolarità è dovuta alla sua compattezza, alla sua affidabilità e alla sua facilità d’uso, rendendolo uno dei servomotori più diffusi e accessibili sul mercato.

Il funzionamento del servomotore SG90 si basa su un meccanismo di controllo di posizione preciso. All’interno del servomotore, è presente un motore elettrico, un insieme di ingranaggi e un circuito di controllo. Il segnale di controllo inviato al servomotore determina la posizione dell’albero del motore, consentendo un controllo accurato della sua rotazione su un intervallo di circa 180 gradi.

Un punto chiave del servomotore SG90 è la sua capacità di operare in modalità di feedback a posizione, che significa che il motore monitora costantemente la posizione dell’albero e si impegna a mantenerlo nella posizione desiderata. Questo lo rende ideale per applicazioni che richiedono un controllo di posizione preciso e affidabile, come ad esempio controlli di sterzo in veicoli radiocomandati, movimenti di bracci robotici o aperture di porte automatiche.

Per utilizzare un servomotore SG90, è necessario alimentarlo con una tensione appropriata e fornire un segnale di controllo tramite un pin di input. Il segnale di controllo è un impulso a larghezza di impulso modulata (PWM) con una frequenza tipica di 50 Hz e un duty cycle variabile, che determina la posizione desiderata del servomotore. È possibile pilotare il servomotore utilizzando un microcontrollore come l’ESP32, che genera i segnali di controllo in base alle istruzioni del programma.

In sintesi, il servomotore SG90 è un componente fondamentale per una varietà di progetti elettronici che richiedono un controllo di posizione preciso e affidabile. Con la sua semplicità d’uso e le sue prestazioni affidabili, è diventato uno degli attuatori preferiti per gli hobbisti e gli ingegneri che cercano di dare vita alle loro idee.

Il servomotore SG90 che utilizzeremo qui ha 3 fili di collegamento: due per l’alimentazione a 5V e il terzo per il suo controllo. Essendo piccolo e poco potente è adatto per agire su carichi piccoli.

Cosa è il PWM?

Il PWM, acronimo di Pulse Width Modulation (Modulazione in Larghezza di Impulso), è una tecnica utilizzata per controllare la potenza fornita a dispositivi elettronici come motori, LED e servomotori. Questa tecnica funziona variando la larghezza degli impulsi di un segnale digitale a una frequenza costante, permettendo di regolare la quantità di energia consegnata al carico.

Il principio di base del PWM consiste nella rapida alternanza tra due stati: uno stato alto (ON) e uno stato basso (OFF). Durante lo stato alto, il segnale è attivo e il carico riceve energia; durante lo stato basso, il segnale è inattivo e il carico non riceve energia. La durata dell’impulso alto rispetto alla durata totale del ciclo definisce il rapporto di duty cycle, espresso generalmente come percentuale. Ad esempio, un duty cycle del 50% indica che il segnale è attivo per la metà del tempo totale del ciclo.

L’utilizzo del PWM consente di regolare la potenza fornita al carico variando il rapporto di duty cycle. Aumentando il duty cycle, si aumenta la potenza fornita al carico, mentre diminuendo il duty cycle, si riduce la potenza fornita. Questa capacità di regolare la potenza in modo rapido e efficiente rende il PWM ideale per il controllo di dispositivi che richiedono modulazione di potenza, come motori DC, motori brushless, lampade a LED e servomotori.

Nel contesto del controllo di servomotori, il PWM viene utilizzato per inviare segnali di controllo che determinano la posizione desiderata del servo. Questi segnali, detti impulsi PWM, hanno una frequenza tipica di 50 Hz e una durata dell’impulso che varia tra 1 ms e 2 ms, con una durata di 1,5 ms che corrisponde solitamente alla posizione centrale del servo. Modificando la durata dell’impulso PWM, è possibile regolare la posizione del servo su un intervallo di circa 180 gradi, consentendo un controllo preciso della sua rotazione.

In conclusione, il PWM è una tecnica fondamentale nell’elettronica moderna, utilizzata per controllare la potenza fornita a dispositivi elettronici in modo efficiente e preciso. La sua versatilità lo rende ampiamente impiegato in una varietà di applicazioni, dalla regolazione della velocità dei motori alla regolazione dell’intensità luminosa dei LED, fino al controllo di posizione dei servomotori.

Cos’è ILI9341 Display LCD TFT SPI 2.8″?

Il ILI9341 Display LCD TFT SPI 2.8″ è un tipo di schermo a cristalli liquidi (LCD) a colori ad alta risoluzione progettato per fornire un’interfaccia visiva in progetti elettronici. Comunica col microcontrollore tramite interfaccia SPI dedicata. Oltre alla visualizzazione a colori, dispone di un touchscreen resistivo che consente l’interazione diretta con l’utente (tramite interfaccia SPI dedicata), e di uno slot per schede SD (Secure Digital) che consente di memorizzare e accedere facilmente ai dati (tramite interfaccia SPI dedicata).

Caratteristiche tecniche:

Dimensioni dello schermo: 2.8 pollici (diagonale)
Risoluzione: 320 x 240 pixel
Controller: ILI9341, un popolare controller per display TFT che supporta la comunicazione tramite SPI (Serial Peripheral Interface)
Interfaccia: SPI (Serial Peripheral Interface) per la comunicazione con microcontrollori e altri dispositivi
Touchscreen: resistivo, che consente l’interazione diretta con l’utente attraverso la pressione
Slot per schede SD: per l’accesso a schede di memoria SD tramite l’interfaccia SPI, consentendo la memorizzazione e l’accesso ai dati
Colori: supporta la visualizzazione di 65.536 colori (16-bit)
Angolo di visione: buono, con una visione chiara da diverse angolazioni
Retroilluminazione: retroilluminazione a LED per una luminosità uniforme e regolabile
Compatibilità: compatibile con una vasta gamma di microcontrollori e schede di sviluppo

Funzionalità e utilizzo: il ILI9341 Display LCD TFT SPI 2.8″ con touchscreen resistivo e alloggio per SD card può essere utilizzato in una varietà di applicazioni, tra cui:

  • Creazione di interfacce utente interattive per dispositivi embedded e progetti IoT
  • Visualizzazione di testo, grafici e immagini con la possibilità di interazione tramite touchscreen
  • Implementazione di sistemi di registrazione dati che utilizzano lo slot per schede SD per archiviare e accedere ai dati
  • Realizzazione di dispositivi multimediali portatili, come lettori video e visualizzatori di immagini, con la possibilità di memorizzare i file multimediali sulla scheda SD

Vantaggi:

  • Interazione utente: il touchscreen resistivo consente un’interazione diretta e intuitiva con l’interfaccia utente.
  • Memorizzazione esterna: lo slot per schede SD offre la possibilità di memorizzare una grande quantità di dati esternamente al dispositivo principale (per esempio immagini da usare come sfondo o come icone).
  • Versatilità: grazie alla sua interfaccia SPI e alle sue caratteristiche tecniche avanzate, è adatto a una vasta gamma di applicazioni nell’ambito dell’elettronica e dell’informatica embedded.

Limitazioni:

  • Complessità della programmazione: Potrebbe richiedere una certa familiarità con la programmazione embedded e le librerie grafiche per sfruttare appieno le sue funzionalità.
  • Dimensioni limitate dello schermo: Le dimensioni del display potrebbero essere limitate per alcune applicazioni che richiedono una visualizzazione più ampia.

In sintesi, il ILI9341 Display LCD TFT SPI 2.8″ con touchscreen resistivo e alloggio per SD card è una scelta versatile per progetti embedded che richiedono una visualizzazione a colori di alta qualità, interazione utente e capacità di memorizzazione esterna. Con le sue caratteristiche avanzate e la compatibilità con una vasta gamma di microcontrollori, è ideale per una varietà di applicazioni nell’ambito dell’elettronica e dell’informatica embedded.

Le librerie grafiche utilizzate: TFT_eSPI e TFT_eWidget

Libreria TFT_eSPI: La libreria TFT_eSPI è una libreria grafica altamente ottimizzata progettata per fornire un’interfaccia semplice e potente per la gestione dei display TFT a colori con microcontrollori basati su architettura ESP8266 e ESP32. Ecco alcune delle caratteristiche principali della libreria TFT_eSPI:

  • Compatibilità: supporta una vasta gamma di display TFT con controller compatibili con SPI, tra cui il popolare controller ILI9341 utilizzato nei display TFT da 2.8″.
  • Alta velocità: sfrutta le funzionalità hardware dei microcontrollori ESP8266 e ESP32 per ottenere prestazioni elevate e tempi di aggiornamento rapidi dello schermo.
  • Risparmio di memoria: è progettata per occupare meno spazio in memoria possibile, consentendo l’utilizzo di altre risorse del microcontrollore per funzionalità aggiuntive.
  • Supporto touchscreen: integra funzionalità di gestione del touchscreen resistivo, consentendo l’interazione utente tramite tocchi e pressioni sul display.
  • Gestione avanzata del colore: offre un’ampia gamma di funzionalità per la gestione dei colori, inclusa la visualizzazione di immagini a colori, la creazione di grafici e molto altro ancora.
  • Libreria di grafica: fornisce una varietà di funzioni e metodi per disegnare forme geometriche, testo e altri elementi grafici sul display TFT.

Libreria TFT_eWidget: La libreria TFT_eWidget è una libreria aggiuntiva progettata per semplificare ulteriormente lo sviluppo di interfacce utente interattive sui display TFT utilizzando la libreria TFT_eSPI. Ecco alcune delle caratteristiche principali della libreria TFT_eWidget:

  • Componenti UI predefiniti: fornisce una vasta gamma di componenti di interfaccia utente (UI), come pulsanti, barre di scorrimento, caselle di testo e molto altro ancora, che possono essere facilmente integrati nei progetti.
  • Personalizzazione avanzata: consente una personalizzazione completa dei componenti UI, inclusa la modifica del colore, delle dimensioni e dello stile per adattarsi alle esigenze specifiche del progetto.
  • Facile integrazione: grazie alla sua interfaccia intuitiva e ai metodi di programmazione semplici, consente un’integrazione rapida e senza problemi dei componenti UI nei progetti esistenti.
  • Supporto touchscreen: gestisce automaticamente l’interazione utente tramite il touchscreen resistivo, consentendo l’utilizzo intuitivo dei componenti UI attraverso tocchi e pressioni sul display.

In sintesi, le librerie TFT_eSPI e TFT_eWidget offrono un’esperienza di sviluppo completa e potente per la creazione di interfacce utente interattive su display TFT con microcontrollori ESP8266 e ESP32. Con le loro caratteristiche avanzate e la facilità d’uso, sono ideali per una vasta gamma di applicazioni nell’ambito dell’elettronica embedded e dell’IoT.

Interfaccia SPI

Il display, il touchscreen e il lettore SD card usano ciascuno delle interfacce SPI dedicate. Esso è un protocollo seriale di comunicazione a 4 fili comunemente utilizzato in progetti embedded. Questi quattro fili sono:

  1. MISO (Master In Slave Out): questo è il pin attraverso il quale il modulo riceve dati dal dispositivo master, che di solito è l’Arduino o un altro microcontrollore.
  2. MOSI (Master Out Slave In): questo è il pin attraverso il quale il modulo invia dati al dispositivo master.
  3. SCK (Serial Clock): questo è il pin del clock che sincronizza la trasmissione dei dati tra il modulo e il dispositivo master.
  4. CS (Chip Select): questo pin viene utilizzato per selezionare il modulo RFID e inizializzare le operazioni di invio/ricezione dei dati.

Di che componenti abbiamo bisogno?

La lista dei componenti non è particolarmente lunga:

  • una breadboard per connettere la NodeMCU ESP32 agli altri componenti
  • alcuni fili DuPont (maschio – maschio, maschio – femmina, femmina – femmina)
  • un servomotore SG90
  • un ILI9341 Display LCD TFT SPI 2.8″
  • e, ovviamente, una NodeMCU ESP32 !

Il modello di ESP32 scelto per questo progetto è quello dell’azienda AZ-Delivery.

Realizzazione del progetto

Lo schema elettrico

Prima di realizzare il circuito vero e proprio diamo un’occhiata al pinout della board:

Pinout della ESP32
Pinout della ESP32

Mentre il servomotore si presenta così:

Il servomotore SG90 utilizzato in questo progetto
Il servomotore SG90 utilizzato in questo progetto

Da notare i colori inusuali utilizzati per i fili di collegamento. In particolare abbiamo:

  • il filo marrone andrà alla massa
  • il filo rosso andrà al pin 5V dell’ESP32
  • il filo arancio è quello di controllo e lo collegheremo al GPIO 16 dell’ESP32

Pinout del display lcd tft: sul lato sinistro ci sono i collegamenti (in alto) per il touchscreen e (in basso) per il display. Sul lato destro i collegamenti per il modulo SD card
Pinout del display lcd tft: sul lato sinistro ci sono i collegamenti (in alto) per il touchscreen e (in basso) per il display. Sul lato destro i collegamenti per il modulo SD card

I collegamenti tra il display e il modulo ESP32 seguiranno le indicazioni di questa tabella:

TOUCHSCREENESP32
T_IRQGPIO27
T_DOGPIO19
T_DINGPIO23
T_CSGPIO14
T_SCKGPIO18
DISPLAYESP32
MISOGPIO19
LED3.3V
SCKGPIO18
MOSIGPIO23
D/CGPIO04
RESETGPIO22
CSGPIO05
GNDGND
VCC5V
Tabella dei collegamenti fra il display e l’ESP32

In questo progetto non useremo il modulo SD card incorporato nel display. Come puoi osservare il touchscreen condivide i collegamenti MISO, MOSI e SCK col display.

Nell’immagine seguente è visibile lo schema elettrico realizzato con Fritzing:

Schema elettrico realizzato con Fritzing
Schema elettrico realizzato con Fritzing

Per chi lo preferisse, di seguito vediamo lo schema elettrico realizzato tramite EasyEda:

Schema elettrico realizzato con EasyEda
Schema elettrico realizzato con EasyEda

Il connettore U2 serve a connettere il servomotore. In particolare il pin 1 corrisponde al segnale di controllo mentre il 2 e il 3 rispettivamente ai 5V e alla massa.

Lo sketch

Creiamo il progetto PlatformIO

Abbiamo già visto la procedura di creazione di un progetto PlatformIO nell’articolo Come creare un progetto per NodeMCU ESP8266 con PlatformIO.

Anche se si riferisce alla board ESP8266, la procedura è simile.
Semplicemente, nella scelta della piattaforma, dovrai scegliere la AZ-Delivery ESP-32 Dev Kit C V4.

Non installare nessuna delle librerie indicate nell’articolo.

Ora modifica il file platformio.ini per aggiungere queste due righe:

monitor_speed = 115200
upload_speed = 921600

in modo che il file abbia un aspetto del genere:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
monitor_speed = 115200
upload_speed = 921600
framework = arduino

Le librerie (ESP32Servo, TFT_eSPI e TFT_eWidget) le possiamo installare direttamente modificando il file platformio.ini nel modo seguente:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = 
	bodmer/TFT_eSPI@^2.5.43
	bodmer/TFT_eWidget@^0.0.6
	madhephaestus/ESP32Servo@^1.2.0

Inoltre dobbiamo sostituire un file nella libreria TFT_eSPI dopo che è stata installata. Si tratta del file User_Setup.h che puoi scaricare dal link qui sotto:

e, una volta scompattato, mettere nella cartella di progetto NOME_DEL_PROGETTO/.pio/libdeps/az-delivery-devkit-v4/TFT_eSPI/ questo file scaricato (dopo magari aver rinominato il vecchio file come User_Setup.h_OLD giusto per conservarlo).

NOTA BENE: il path NOME_DEL_PROGETTO/.pio/libdeps/az-delivery-devkit-v4/TFT_eSPI/ è lo stesso sia per Linux che per Windows.

Per finire bisogna scaricare dal link qui sotto il file Free_Fonts.h e copiarlo nella cartella include del progetto:

Puoi scaricare il progetto dal link seguente:

decomprimerlo, prendere il file main.cpp e sostituirlo al posto di quello che hai nel progetto precedentemente creato.

Vediamo ora come funziona lo sketch.

Inizialmente vengono incluse le librerie necessarie e il file contenente i fonts:

#include <Arduino.h>
#include "Free_Fonts.h" // Include the header file attached to this sketch
#include <ESP32Servo.h>
#include <TFT_eSPI.h> 
#include <TFT_eWidget.h>           
#include <SPI.h>

Poi vengono definiti l’oggetto myservo che gestisce il servomotore, il GPIO 16 a cui è collegato e la variabile servoPosition che ne conserverà il valore di posizione:

Servo myservo;  // create servo object to control a servo
static const int servoPin = 16;
int servoPosition = 0;    // variable to store the servo position

Vengono poi definiti lo schema dei colori dell’indicatore grafico e la frequenza del suo aggiornamento:

// Meter colour schemes
#define RED2RED 0
#define GREEN2GREEN 1
#define BLUE2BLUE 2
#define BLUE2RED 3
#define GREEN2RED 4
#define RED2GREEN 5

#define TFT_GREY 0x2104 // Dark grey 16-bit colour

#define LOOP_PERIOD 100 // Display updates every 100 ms

Poi si definisce l’oggetto tft che gestisce il display, l’oggetto knob che gestisce il cursore dello slider, la definizione di REPEAT_CAL che metteremo a true ogni volta che vorremo calibrare il touchscreen e l’oggetto s1 che gestisce lo slider:

TFT_eSPI tft = TFT_eSPI(); 

TFT_eSprite knob = TFT_eSprite(&tft); // Sprite for the slide knob

#define REPEAT_CAL true      // put true to perform a touchscreen calibration

SliderWidget s1 = SliderWidget(&tft, &knob);    // Slider 1 widget

Quando REPEAT_CAL è messo a true, lo sketch inizierà facendoci fare la calibrazione del touchscreen. Quindi stamperà sul Serial Monitor i valori di calibrazione da mettere manualmente in un opportuno array chiamato calData presente nella funzione setup. Una volta fatta la calibrazione e riempito l’array, possiamo mettere REPEAT_CAL a false in modo da non dover più ripetere l’operazione di calibrazione in quanto i valori di calibrazione li abbiamo inseriti manualmente nell’array nella funzione setup. Ovviamente, se la calibrazione non ci soddisfa, siamo sempre liberi di ripeterla ponendo nuovamente REPEAT_CAL a true.

Segue la funzione rainbow che stabilisce i colori dei vari quadranti dell’indicatore circolare mostrato dal display e indicante i gradi di rotazione:

unsigned int rainbow(byte value)
{
  // Value is expected to be in range 0-127
  // The value is converted to a spectrum colour from 0 = blue through to 127 = red

  byte red = 0; // Red is the top 5 bits of a 16-bit colour value
  byte green = 0;// Green is the middle 6 bits
  byte blue = 0; // Blue is the bottom 5 bits

  byte quadrant = value / 32;

  if (quadrant == 0) {
    blue = 31;
    green = 2 * (value % 32);
    red = 0;
  }
  if (quadrant == 1) {
    blue = 31 - (value % 32);
    green = 63;
    red = 0;
  }
  if (quadrant == 2) {
    blue = 0;
    green = 63;
    red = value % 32;
  }
  if (quadrant == 3) {
    blue = 0;
    green = 63 - 2 * (value % 32);
    red = 31;
  }
  return (red << 11) + (green << 5) + blue;
}

Troviamo poi la funzione touch_calibrate che viene invocata quando REPEAT_CAL è messo a true e che fornisce i valori di calibrazione da inserire nell’array calData presente nella funzione setup:

// Code to run a screen calibration, not needed when calibration values set in setup()
void touch_calibrate()
{
  uint16_t calData[5];
  uint8_t calDataOK = 0;

  // Calibrate
  tft.fillScreen(TFT_BLACK);
      
  tft.setCursor(20, 0);
  tft.setTextFont(2);
  tft.setTextSize(1);
  
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.println("Touch corners as indicated");
  tft.setTextColor(TFT_RED, TFT_BLACK);
  tft.println("Set REPEAT_CAL to false to stop this running again!");

  tft.setTextFont(1);
  tft.println();

  tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);

  Serial.println(); Serial.println();
  Serial.println("// Use this calibration code in setup():");
  Serial.print("  uint16_t calData[5] = ");
  Serial.print("{ ");

  for (uint8_t i = 0; i < 5; i++)
  {
    Serial.print(calData[i]);
    if (i < 4) Serial.print(", ");
  }

  Serial.println(" };");
  Serial.print("  tft.setTouch(calData);");
  Serial.println(); Serial.println();

  tft.fillScreen(TFT_BLACK);
  
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.println("Calibration complete!");
  tft.println("Calibration code sent to Serial port.");

  delay(4000);
}

La funzione ringMeter disegna materialmente il misuratore angolare sullo schermo:

// #########################################################################
//  Draw the meter on the screen, returns x coord of righthand side
// #########################################################################
int ringMeter(int value, int vmin, int vmax, int x, int y, int r, const char *units, byte scheme)
{
  // Minimum value of r is about 52 before value text intrudes on ring
  // drawing the text first is an option
  
  x += r; y += r;   // Calculate coords of centre of ring

  int w = r / 3;    // Width of outer ring is 1/4 of radius
  
  int angle = 180;  // Half the sweep angle of meter (300 degrees)

  int v = map(value, vmin, vmax, -angle, angle); // Map the value to an angle v

  byte seg = 3; // Segments are 3 degrees wide = 100 segments for 300 degrees
  byte inc = 6; // Draw segments every 3 degrees, increase to 6 for segmented ring

  // Variable to save "value" text colour from scheme and set default
  int colour = TFT_BLUE;
 
  // Draw colour blocks every inc degrees
  for (int i = -angle+inc/2; i < angle-inc/2; i += inc) {
    // Calculate pair of coordinates for segment start
    float sx = cos((i - 90) * 0.0174532925);
    float sy = sin((i - 90) * 0.0174532925);
    uint16_t x0 = sx * (r - w) + x;
    uint16_t y0 = sy * (r - w) + y;
    uint16_t x1 = sx * r + x;
    uint16_t y1 = sy * r + y;

    // Calculate pair of coordinates for segment end
    float sx2 = cos((i + seg - 90) * 0.0174532925);
    float sy2 = sin((i + seg - 90) * 0.0174532925);
    int x2 = sx2 * (r - w) + x;
    int y2 = sy2 * (r - w) + y;
    int x3 = sx2 * r + x;
    int y3 = sy2 * r + y;

    if (i < v) { // Fill in coloured segments with 2 triangles
      switch (scheme) {
        case 0: colour = TFT_RED; break; // Fixed colour
        case 1: colour = TFT_GREEN; break; // Fixed colour
        case 2: colour = TFT_BLUE; break; // Fixed colour
        case 3: colour = rainbow(map(i, -angle, angle, 0, 127)); break; // Full spectrum blue to red
        case 4: colour = rainbow(map(i, -angle, angle, 70, 127)); break; // Green to red (high temperature etc.)
        case 5: colour = rainbow(map(i, -angle, angle, 127, 63)); break; // Red to green (low battery etc.)
        default: colour = TFT_BLUE; break; // Fixed colour
      }
      tft.fillTriangle(x0, y0, x1, y1, x2, y2, colour);
      tft.fillTriangle(x1, y1, x2, y2, x3, y3, colour);
      //text_colour = colour; // Save the last colour drawn
    }
    else // Fill in blank segments
    {
      tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREY);
      tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREY);
    }
  }
  // Convert value to a string
  char buf[10];
  byte len = 3; if (value > 999) len = 5;
  dtostrf(value, len, 0, buf);
  buf[len] = ' '; buf[len+1] = 0; // Add blanking space and terminator, helps to centre text too!
  // Set the text colour to default
  tft.setTextSize(0);


  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  // Uncomment next line to set the text colour to the last segment value!
  tft.setTextColor(colour, TFT_BLACK);
  tft.setTextDatum(MC_DATUM);
  // Print value, if the meter is large then use big font 8, othewise use 4
  if (r > 84) {
    tft.setTextPadding(55*2); // Allow for 3 digits each 55 pixels wide
    tft.drawString(buf, x, y, 4); // Value in middle
  }
  else {
    tft.setTextPadding(3 * 14); // Allow for 3 digits each 14 pixels wide
    tft.drawString(buf, x, y, 4); // Value in middle
  }
  tft.setTextSize(0);
  tft.setTextPadding(0);
  // Print units, if the meter is large then use big font 4, othewise use 2
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  // if (r > 84) tft.drawString(units, x, y + 60, 4); // Units display
  // else tft.drawString(units, x, y + 15, 2); // Units display
  tft.drawString(units, x, y + 25, 4);

  // Calculate and return right hand side x coordinate
  return x + r;
}

Incontriamo poi la funzione setup che appare abbastanza articolata:

Inizialmente attiva la porta seriale, attiva il display settandone l’orientamento, lo sfondo e il font:

Serial.begin(115200);
delay(1000);
tft.begin();

tft.setRotation(1);

tft.fillScreen(TFT_BLACK);
tft.setFreeFont(FF18);

Poi calibra il touchscreen nel caso in cui REPEAT_CAL sia messo a true. In questo caso stamperà sul Serial Monitor i valori di calibrazione che dovremo inserire nell’array calData presente nell’else. Nel caso in cui REPEAT_CAL fosse messo a false, non farebbe la calibrazione ma eseguirebbe l’else usando direttamente i valori di calibrazione precedentemente inseriti nell’array calData.

// Calibrate the touch screen and retrieve the scaling factors when REPEAT_CAL is true
if (REPEAT_CAL) {
    touch_calibrate();
    tft.fillScreen(TFT_BLACK);
} else {
// Fill this array with values calculated by touch_calibrate() function when REPEAT_CAL is true and printed on Serial Monitor
    uint16_t calData[5] = { 400, 3459, 257, 3471, 7 };
    tft.setTouch(calData);
}

Per essere più chiari nell’uso del nostro dispositivo:

  • inizialmente poniamo REPEAT_CAL a true;
  • compiliamo e carichiamo lo sketch;
  • eseguiamo lo sketch che ci farà eseguire l’operazione di calibrazione, stampando alla fine i valori di calibrazione sul Serial Monitor ;
  • prendiamo questi valori e li ricopiamo all’interno dell’array calData presente nel blocco else (al posto di 400, 3459, 257, 3471, 7);
  • poniamo ora REPEAT_CAL a false, compiliamo e carichiamo lo sketch.

Quando l’ESP32 si riavvierà non ci farà più eseguire la calibrazione perché userà i valori inseriti al punto 4 dell’elenco precedente.

I valori indicati nello sketch sono quelli ricavati da me e non necessariamente coincideranno con quelli ricavati da te.

Segue poi la definizione dei parametri dello slider e la sua posizione nello schermo:

// Create a parameter set for the slider
slider_t param;

// Slider slot parameters
param.slotWidth = 4;           // Note: ends of slot will be rounded and anti-aliased
param.slotLength = 200;        // Length includes rounded ends
param.slotColor = TFT_BLUE;    // Slot colour
param.slotBgColor = TFT_BLACK; // Slot background colour for anti-aliasing
param.orientation = H_SLIDER;  // sets it "true" for horizontal

param.knobColor = TFT_WHITE;   // Anti-aliased with slot backgound colour
param.knobLineColor = TFT_RED; // Colour of marker line (set to same as knobColor for no line)

param.startPosition = 0;      // Start position for control knob

param.orientation = V_SLIDER; // sets it "false" for vertical

param.knobWidth = 19;
param.knobHeight = 19;
param.knobRadius = 19/2; // Half w and h so creates a circle

param.sliderLT = 180;     // Top for vertical slider
param.sliderRB = 0;       // Bottom for vertical slider
param.sliderDelay = 200; // 200ms per pixel movement delay (movement is blocking until complete)

// Create slider using parameters and plot at 10,15
s1.drawSlider(10, 15, param);

// Show bounding box (1 pixel outside slider working area)
int16_t x, y;    // x and y can be negative
uint16_t w, h;   // Width and height
s1.getBoundingRect(&x, &y, &w, &h);     // Update x,y,w,h with bounding box
tft.drawRect(x, y, w, h, TFT_DARKGREY); // Draw rectangle outline

delay(1000);
s1.setSliderPosition(100);
delay(1000);
s1.setSliderPosition(0);

La funzione termina con il settaggio dei parametri per comandare il servomotore:

ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
myservo.setPeriodHertz(50);    // standard 50 hz servo
myservo.attach(servoPin, 790, 4000);   // choose these values ​​by trial and error in order to calibrate the two extremes of the servomotor between 0 and 180 degrees

In particolare i valori 790 e 4000 nella funzione myservo.attach(servoPin, 790, 4000); devono essere determinati per tentativi in modo da tarare i valori estremi (da 0 a 180 gradi) dell’angolo assunto dal rotore del servomotore.

Chiude lo sketch la funzione loop che ogni LOOP_PERIOD ms rileva la posizione dello slider (e quindi il valore da impostare in gradi sul servomotore) e imposta la posizione del servomotore con la funzione myservo.write(s1.getSliderPosition());. Inoltre, ad ogni ciclo, chiama la funzione ringMeter che, come detto più sopra, disegna materialmente il misuratore angolare sullo schermo:

void loop() {
  static uint32_t updateTime = 0;
  uint16_t t_x = 9999, t_y = 9999; // To store the touch coordinates
  if (millis() - updateTime >= LOOP_PERIOD) 
  {
    updateTime = millis();

    if( tft.getTouch(&t_x, &t_y, 250) ) {
      if (s1.checkTouch(t_x, t_y)) {
        Serial.print("Slider = "); Serial.println(s1.getSliderPosition());
        myservo.write(s1.getSliderPosition()); 
      }
    }

    // Set the the position, gap between meters, and inner radius of the meters
    int xpos = 0, ypos = 5, gap = 4, radius = 52;


    // Draw a large meter
    xpos = 480/2 - 160, ypos = 0, gap = 15, radius = 100;

    // Comment out above meters, then uncomment the next line to show large meter
    ringMeter(s1.getSliderPosition(),0,180, xpos,ypos,radius," degrees",GREEN2RED); // Draw analogue meter
  }
}

Video del funzionamento

Segue il video che mostra inizialmente la fase di calibrazione e poi l’uso vero e proprio del dispositivo.

Newsletter

Se vuoi essere aggiornato sui nuovi articoli, iscriviti alla newsletter. Prima dell’iscrizione alla newsletter leggi la pagina Privacy Policy (UE)

Se ti vuoi disiscrivere dalla newsletter clicca sul link che troverai nella mail della newsletter.

Inserisci il tuo nome
Inserisci la tua email
0 0 votes
Valutazione articolo
guest
0 Commenti
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
Torna in alto