Chiudiamo la panoramica sulla scheda di prototipazione basata sul popolare modulo WiFi, con ulteriori applicazioni. Terza puntata.
Nelle due puntate precedenti abbiamo introdotto la demoboard per il famoso e diffusissimo SoC ESP32 della Espressif, che nello specifico รจ montato a bordo di una scheda con ESP32 commercializzata da www.futurashop.it con il codice YB555. Oltre ad avervi proposto alcuni sketch scritti da noi per introdurvi nel mondo dello sviluppo con questo hardware, in ottica IoT; abbiamo descritto nel dettaglio firmware e creato degli esempi che abbiamo iniziato a presentarvi nella prima e nella seconda puntata. Adesso proseguiamo su questa strada proponendo gli ultimi esempi applicativi che utilizzano lโhardware di bordo e i moduli collegabili esternamente tramite gli appositi connettori. Nello specifico andiamo a utilizzare questi componenti di bordo e moduli esterni:
- pulsanti UP, DOWN, LEFT, RIGHT, OK;
- Servomotore;
- Display OLED 0,96โ;
- Relรฉ 5V (RL1);
- LED rosso (LD1);
- Led verde (LD2).
Ricordiamo che il cuore della demoboard รจ la scheda di sviluppo e prototipazione YB555 basata sullโESP32 (ESP-WROOM-32) che integra un Wi-Fi 802.11 b/g/n, un Bluetooth dual-mode (classico e BLE) e 25 GPIO. Supporta una velocitร di trasmissione dati fino a 150 Mbps, ha una potenza di uscita inโantenna di 20,5 dBm e unconsumo in modalitร sleep di soli 5 ยตA.
Interazione LED e PULSANTI
Iniziamo con il vedere unโapplicazione nella quale utilizziamo i pulsanti a bordo della scheda e ne visualizziamo la condizione sul display OLED on-board (cod. OLED096C oppure cod. OLEDGVSCSD reperibili on-line su www.futurashop.it); questo esempio applicativo fornisce unโidea di come si possono utilizzare e integrare i pulsanti in unโapplicazione vera e propria, ottenendo un feedback sul display oppure determinate azioni.
Lo sketch da caricare in questo caso si chiama OLED_Pulsanti.ino e una volta mandato in esecuzione sulla scheda ATmega2560 che governa la demoboard, sul display viene indicato di volta in volta quale pulsante รจ stato premuto fra quelli disposti a joystick, ossia UP, DOWN, LEFT, RIGHT e OK (P1, P2, P3, P4, P5). Inoltre il codice di esempio permette di associare la pressione di uno dei predetti pulsanti allโaccensione dei LED a bordo della scheda; per lโesattezza, se viene premuto LEFT o RIGHT si accenderร il LED verde (LD2), mentre se si preme UP o DOWN verrร comandata lโaccensione del LED rosso (LD1). Nel caso venga premuto il pulsante OK si accenderanno entrambi i LED.
Questa applicazione e il codice che segue, proposto nel Listato 1, possono essere integrate in altre per ottenere un modulo di gestione dei pulsanti e per collegare i relativi input a determinate azioni, che possono essere non solo quelle descritte, ma anche lโattivazione di altre uscite, non ultima quella a relรฉ.
Per lโutilizzo dello sketch dovete copiare la cartella โlibrariesโ che si trova negli esempi scaricabili dal nostro sito www.elettronicain.it fra i file di questo numero della rivista, sotto โ/Documentiโ/Arduinoโ, quindi aprire lโIDE di Arduino e caricare il codice di esempio disponibile nella cartella โOLED_Pulsanti.

Listato 1
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
const int P_UP = 23;
const int P_DOWN = 4;
const int P_LEFT = 16;
const int P_RIGHT = 17;
const int P_OK = 5;
const int LEDV = 26;
const int LEDR = 25;
void setup() {
Serial.begin(9600);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F(โSSD1306 allocation failedโ));
for(;;); // Donโt proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(5, 0);
display.print(โ** Test **โ);
display.setCursor(20, 30);
display.print(โ PUSH โ);
// Show the display buffer on the screen. You MUST call display() after
// drawing commands to make them visible on screen!
display.display();
delay(2000);
// display.display() is NOT necessary after every single drawing command,
// unless thatโs what you want...rather, you can batch up a bunch of
// drawing operations and then update the screen all at once by calling
// display.display(). These examples demonstrate both approaches...
/*
// Invert and restore display, pausing in-between
display.invertDisplay(true);
delay(1000);
display.invertDisplay(false);
delay(1000);
*/
pinMode(P_UP, INPUT_PULLUP);
pinMode(P_DOWN, INPUT_PULLUP);
pinMode(P_LEFT, INPUT_PULLUP);
pinMode(P_RIGHT, INPUT_PULLUP);
pinMode(P_OK, INPUT_PULLUP);
pinMode(LEDV, OUTPUT);
pinMode(LEDR, OUTPUT);
}
void loop()
{
display.clearDisplay();
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
switch (Ctrl_Push())
{
case 1:
Serial.println(โPush UPโ);
display.setCursor(50, 30);
display.print(โUPโ);
digitalWrite(LEDR,HIGH);
break;
case 2:
Serial.println(โPush DOWNโ);
display.setCursor(40, 30);
display.print(โDOWNโ);
digitalWrite(LEDR,HIGH);
break;
case 3:
Serial.println(โPush LEFTโ);
display.setCursor(40, 30);
display.print(โLEFTโ);
digitalWrite(LEDV,HIGH);
break;
case 4:
Serial.println(โPush RIGHTโ);
display.setCursor(40, 30);
display.print(โRIGHTโ);
digitalWrite(LEDV,HIGH);
break;
case 5:
Serial.println(โPush OKโ);
display.setCursor(50, 30);
display.print(โOKโ);
digitalWrite(LEDR,HIGH);
digitalWrite(LEDV,HIGH);
break;
default:
display.setCursor(40, 30);
display.print(โ----โ);
digitalWrite(LEDV,LOW);
digitalWrite(LEDR,LOW);
break;
}
display.display();
delay(500);
}
int Ctrl_Push()
{
int push = 0;
if (digitalRead(P_UP)==LOW) push = 1;
if (digitalRead(P_DOWN)==LOW) push = 2;
if (digitalRead(P_LEFT)==LOW) push = 3;
if (digitalRead(P_RIGHT)==LOW) push = 4;
if (digitalRead(P_OK)==LOW) push = 5;
return push;
}Controllo Servocomandi
Passiamo adesso a un secondo esempio applicativo che รจ molto interessante perchรฉ riguarda la gestione dei servo analogici (da modellismo, robotica ecc.) attraverso la nostra demoboard; per implementarla utilizziamo lโuscita dedicata ai servocomandi, che sul circuito stampato รจ quella siglata CNU11, a tre poli: S (1) 5V (2) e GND (3). Si tratta della connessione standard per i connettori a passo 2,54 mm utilizzati nei servo, come ad esempio quello reperibile presso la Futura Elettronica (www.futurashop.it) con il codice prodotto SERVO206, con coppia di 1,5kg, rotazione: 160ยฐ, velocitร a 4,8V: 0,12 s,/60ยฐ.
Per la gestione dei servo abbiamo scritto lo sketch dโesempio OLED_Servo.ino, il quale, quando viene eseguito, imposta la demoboard in modo che il servocomando collegato al connettore CNU11 ruoterร prima in senso orario di 180ยฐ e poi di altrettanti gradi in senso antitorario; questo codice esegue quindi un test di funzionamento dei servocomandi, facendo compiere al loro alberino una rotazione completa, divisa in due passaggi che corrispondono alle semirotazioni opposte rispetto alla posizione di riposo (0 gradi). Per lโutilizzo dello sketch dovete copiare la cartella โlibrariesโ che si trova negli esempi scaricabili dal nostro sito www.elettronicain.it fra i file di questo numero della rivista, sotto โ/Documentiโ/Arduinoโ, quindi aprire lโIDE di Arduino e caricare uno dei codici di esempio disponibili nelle cartelle โOLED_Servoโ oppure โServoโ. Il codice completo per la gestione dei servocomandi รจ riportato nel Listato 2.

Listato 2
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//for Servo
#include <Servo.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED displdfsfdfsfdsfdsfdsfdsay height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
//for Servo
static const int servoPin = 18;
Servo servo1;
void setup()
{
Serial.begin(9600);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F(โSSD1306 allocation failedโ));
for(;;); // Donโt proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(5, 0);
display.print(โ** Test **โ);
display.setCursor(10, 30);
display.print(โ Servo โ);
// Show the display buffer on the screen. You MUST call display() after
// drawing commands to make them visible on screen!
display.display();
delay(2000);
// display.display() is NOT necessary after every single drawing command,
// unless thatโs what you want...rather, you can batch up a bunch of
// drawing operations and then update the screen all at once by calling
// display.display(). These examples demonstrate both approaches...
/*
// Invert and restore display, pausing in-between
display.invertDisplay(true);
delay(1000);
display.invertDisplay(false);
delay(1000);
*/
servo1.attach(servoPin);
}
void loop()
{
for(int posDegrees = 0; posDegrees <= 180; posDegrees++)
{
servo1.write(posDegrees);
Serial.println(posDegrees);
delay(20);
}
for(int posDegrees = 180; posDegrees >= 0; posDegrees--)
{
servo1.write(posDegrees);
Serial.println(posDegrees);
delay(20);
}
}Utilizzo della connessione WiFi
Il modulo ESP32 che governa lโintera demoboard si distingue per una caratteristica: lโavere a bordo la connessione WiFi ed รจ questa la ragione per cui lโabbiamo scelta: avere a bordo una connettivitร per lโaccesso a Internet e per affacciare alla Rete le nostre applicazioni, in perfetta ottica IoT.
Per quello che riguarda la sezione ricetrasmittente WiFi, opera in un range di frequenza compreso fra 2,4 GHz e 2,5 GHz e le sue caratteristiche tecniche principali sono le seguenti:
- conformitร IEEE 802.11 b/g/n HT40;
- stack in banda base e LwIP;
- modalitร Sniffer / Station / SoftAP e Wi-Fi direct mode;
- massimo data-rate di 150 Mbps@11n HT40, 72 Mbps@11n HT20, 54 Mbps@11g e 11 Mbps@11b;
- massima potenza di trasmissione 19,5 dBm@11b, 16,5 dBm@11g, 15,5 dBm@11n;
- sensibilitร in ricezione: -97 dBm.
Il transceiver รจ anche predisposto per supportare le modalitร di protezione della connessione WEP, WPA/WPA2 PSK/Enterprise e la crittografia con accelerazione hardware AES / SHA2 / ECC / RSA-4096. Andiamo dunque a vedere lโesempio applicativo che utilizza questo hardware, il quale prevede che il dispositivo si connetterร alla propria rete WiFi per eseguire un test di connettivitร .
A connessione avvenuta, verrร mostrato lโindirizzo IP assegnato al modulo ESP sul display. Al termine il modulo rimarrร in attesa di una connessione da parte di un client interno alla rete, sulla porta TCP 8088. A connessione avvenuta un messaggio fornirร tale informazione e ogni carattere scritto sul terminale del client verrร mostrato sul display, nei limiti di capienza del display. Se a connessione avvenuta si preme uno dei cinque pulsanti a bordo della scheda, il client riceverร sul terminale lโindicazione del pulsante premuto.
Lo sketch di test si chiama OLED_WiFi.ino e si trova sotto la cartella degli esempi, ovvero sotto โ/Documentiโ/Arduinoโ; aprite lโIDE di Arduino e caricate lo sketch, quindi andate a modificare le variabili โapnโ e โssidโ con i parametri della propria rete, perchรฉ queste ultime avranno parametri generici in quanto non possiamo sapere a quale rete il codice sarร applicato.
In particolare, nel codice di esempio, che vedete riportato nel Listato 3, dovete modificare le righe seguenti:
const char* ssid = โNOME APNโ;
const char* password = โPASSWORD APNโ;
inserendo al posto del testo virgolettato NOME APN il nome del vostro access point e al posto di PASSWORD APN la password (o passkey) che utilizzate per lโaccesso alla rete WiFi.

Listato 3
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//for WiFi
#include <WiFi.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED displdfsfdfsfdsfdsfdsfdsay height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
//for WiFi
const char* ssid = โNOME APNโ;
const char* password = โPASSWORD APNโ;
/* create a server and listen on port 8088 */
WiFiServer server(8088);
const int P_UP = 23;
const int P_DOWN = 4;
const int P_LEFT = 16;
const int P_RIGHT = 17;
const int P_OK = 5;
const int LEDV = 26;
const int LEDR = 25;
void setup() {
Serial.begin(9600);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F(โSSD1306 allocation failedโ));
for(;;); // Donโt proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(5, 0);
display.print(โ** Test **โ);
display.setCursor(5, 30);
display.print(โ WiFi โ);
// Show the display buffer on the screen. You MUST call display() after
// drawing commands to make them visible on screen!
display.display();
delay(2000);
// display.display() is NOT necessary after every single drawing command,
// unless thatโs what you want...rather, you can batch up a bunch of
// drawing operations and then update the screen all at once by calling
// display.display(). These examples demonstrate both approaches...
/*
// Invert and restore display, pausing in-between
display.invertDisplay(true);
delay(1000);
display.invertDisplay(false);
delay(1000);
*/
pinMode(P_UP,INPUT_PULLUP);
pinMode(P_DOWN,INPUT_PULLUP);
pinMode(P_RIGHT,INPUT_PULLUP);
pinMode(P_LEFT,INPUT_PULLUP);
pinMode(P_OK,INPUT_PULLUP);
Serial.begin(9600);
OLED_Message(โConnecting to โ,ssid);
Serial.print(โConnecting to โ);
Serial.println(ssid);
/* connecting to WiFi */
WiFi.begin(ssid, password);
/*wait until ESP32 connect to WiFi*/
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(โ.โ);
}
Serial.println(โโ);
Serial.println(โWiFi connected with IP address: โ);
String testo = WiFi.localIP().toString();
OLED_Message(โWiFi connectedโ,testo);
Serial.println(WiFi.localIP());
delay(2000);
/* start Server */
server.begin();
}
void loop()
{
/* listen for client */
WiFiClient client = server.available();
uint8_t data[30];
if (client)
{
OLED_Message(โClient Connectedโ,โโ);
Serial.println(โnew clientโ);
display.setCursor(0, 20);
/* check client is connected */
while (client.connected())
{
switch (Ctrl_Push())
{
case 1:
Serial.print(โPush UPโ);
client.write(โ\nUPโ);
break;
case 2:
Serial.println(โPush DOWNโ);
client.write(โ\nDOWNโ);
break;
case 3:
Serial.println(โPush LEFTโ);
client.write(โ\nLEFTโ);
break;
case 4:
Serial.println(โPush RIGHTโ);
client.write(โ\nRIGHTโ);
break;
case 5:
Serial.println(โPush OKโ);
client.write(โ\nOKโ);
break;
default:
break;
}
Attendi_Push();
if (client.available())
{
int len = client.read(data, 30);
if(len < 30)
{
data[len] = โ\0โ;
}else
{
data[30] = โ\0โ;
}
Serial.print(โclient sent: โ);
Serial.println((char *)data);
display.print((char *)data);
display.display();
}
}
}
OLED_Message(โClient Disconnectedโ,โโ);
}
void OLED_Message(String text_display1,
String text_display2)
{
int lunghezza = 0;
display.clearDisplay();
display.setTextSize(1);
// Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
lunghezza = text_display1.length();
lunghezza=21-lunghezza;
lunghezza=lunghezza/2;
for (int i=0;i<lunghezza;i=i+1)
{
display.print(โ โ);
}
display.print(text_display1);
display.setCursor(0, 20);
lunghezza = text_display2.length();
lunghezza=21-lunghezza;
lunghezza=lunghezza/2;
for (int i=0;i<lunghezza;i=i+1)
{
display.print(โ โ);
}
display.print(text_display2);
display.display();
delay(1500);
}
int Ctrl_Push()
{
int push = 0;
if (digitalRead(P_UP)==LOW) push = 1;
if (digitalRead(P_DOWN)==LOW) push = 2;
if (digitalRead(P_LEFT)==LOW) push = 3;
if (digitalRead(P_RIGHT)==LOW) push = 4;
if (digitalRead(P_OK)==LOW) push = 5;
return push;
}
void Attendi_Push()
{
while (( digitalRead(P_UP)==LOW) ||
(digitalRead(P_DOWN)==LOW) ||
(digitalRead(P_LEFT)==LOW) ||
(digitalRead(P_RIGHT)==LOW) ||
(digitalRead(P_OK)==LOW)) delay(10);
}Scansione del bus IยฒC
Lโultimo esempio applicativo che proponiamo รจ in realtร unโutility, giacchรฉ il relativo codice permette di interrogare il bus IยฒC per rilevare cosa รจ affacciato ad esso e fornire sul display o tramite Serial Monitor lโindirizzo di tutti i dispositivi collegati ad esso.
Come vedete nel Listato 4, il codice fa uso della libreria wire.h, che permette di comunicare con i dispositivi IยฒC/TWI attraverso le due linee SDA (linea dati) e SCL (Serial Clock) che si trovano sia a bordo scheda, sia riportate sul connettore di espansione siglato I2C, che vale per i dispositivi connessi al bus esternamente alla scheda.
A partire da Arduino 1.0, la libreria si รจ allineata alle altre librerie di lettura/scrittura; per questo motivo, le funzioni send() e receive() originarie sono state sostituite con read() e write(). Le versioni recenti della libreria Wire possono utilizzare i timeout per prevenire un blocco a fronte di determinati problemi sul bus, ma questi non sono abilitati per impostazione predefinita nelle versioni correnti (si consiglia di abilitare sempre questi timeout quando si utilizza la libreria Wire). Caricate dunque lโultima versione della libreria.

Listato 4
#include <Wire.h>
void setup()
{
Wire.begin();
Serial.begin(9600);
while (!Serial);
Serial.println(โ\nI2C Scannerโ);
}
void loop()
{
byte error, address;
int nDevices;
address=0x76;
Serial.println(โScanning...โ);
nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print(โI2C device found at address 0xโ);
if (address<16)
Serial.print(โ0โ);
Serial.print(address,HEX);
Serial.println(โ !โ);
nDevices++;
}
else if (error==4)
{
Serial.print(โUnknown error at address 0xโ);
if (address<16)
Serial.print(โ0โ);
Serial.println(address,HEX);
}
}
if (nDevices == 0)
Serial.println(โNo I2C devices found\nโ);
else
Serial.println(โdone\nโ);
delay(5000); // wait 5 seconds for next scan
}Conclusioni
Bene, con questo esempio di utilizzo terminiamo la panoramica sulla demoboard ESP32, che in tre articoli vi ha presentato le possibilitร di utilizzo dellโhardware, seppure non tutte quelle possibili, sia a bordo che connesso tramite i connettori dedicati. Ogni esempio รจ stato corredato di sketch per implementarlo e testarlo. Ora lasciamo a voi e alla vostra inventiva lโimplementazioni di ulteriori applicazioni, che testerete e debuggerete sulla demoboard, per poi uscire dalla fase prototipale e realizzarle in maniera definitiva. Buon lavoro!


