ESP32 I2C Arduino

In questo articolo vediamo come far comunicare questi due microcontrollori sfruttando il bus I2C.

La famiglia ESP lavora a 3,3V mentre la famiglia Arduino usa i 5V, questa differenza rende complicata la comunicazione tra i due microcontrollori se non andiamo a interporre un adattatore di livelli sulla UART. Il protocollo I2C puo’ lavorare sia a 3,3 che a 5V pertanto useremo questo BUS per la comunicazione tra i due microcontrollori.

img1

SCHEMA ELETTRICO

Nel caso che vedremo andremmo a settare l‘esp32 come Master mentre l’arduino nano verrà impostato come slave. Il protocollo I2C prevede che il master “interroghi” ogni singolo slave e attenda la sua risposta.

L’img1 mostra il semplicissimo schema di collegamento tra le due schede. Per il progetto useremo i pin di default I2C per entrambi ( pin21,22 per ESP e pin A4,A5) che corrispondono rispettivamente SDA linea dati e SCL clock di sincronismo. Il protocollo prevede che il bus abbia la linea SDA a livello logico alto che viene abbassato al momento della comunicazione, il master generera’ il clock (SCL). I due dispositivi devono avere in comune la massa GND.

La comunicazione tra i due device puo avvenire in due modi, nel primo il master invia una richiesta di n byte allo slave senza specificare nulla e lo slave risponde con le istruzioni contenute nella funzione specificata in .onRequest(funzione). Mentre nel secondo caso, il master invia prima dei dati allo slave il quale li elabora tramite la funzione richiamata da .onReceive(funzione) poi il master richiede i dati allo slave.

Vediamo nel dettaglio i due casi con l’ausilio di schemi e codici di esempio.

1° caso

Il master invia una richiesta di nbyte dati allo slave tramite la funzione wire.requestFrom(indirizzo, nbyte) allo slave indirizzo e poi si mette in ascolto per la risposta. Lo slave attiva la funzione onRequest() e risponde al master

– Codice –

Vediamo il codice da inserire nel master:

//ESP32 - I2C master

#include "Wire.h" //libreria I2C

void setup(){
  Serial.begin(9600);
  Wire.begin(); //senza valori significa che e' master
}

void loop(){
  Serial.println("Avvio trasmissione...");
  //contatta lo slave con indirizzo 0x1
  Wire.requestFrom(1,12); //invio richiesta e mi aspetto 12 byte di risposta
  while(Wire.available()){
        //stampo i dati provenienti dal bus
        char c = Wire.read();
        Serial.print(c);
  }
  Serial.println("\n Ricezione completata.");
  delay(2000);
}

e il codice dello slave:

//arduino nano I2C slave

#include "Wire.h" //libreria I2C

int LED = 13; 
int roger = 0;

void setup() {
  pinMode(LED,OUTPUT);
  Wire.begin(1); //inizializzo il BUS e assegno indirizzo 1
  Wire.onRequest(richiestaRicevuta); //imposto la funzione da eseguire su chiamata dal master
}

//funzione richiamata su ricezione dal master
void richiestaRicevuta(){
  digitalWrite(LED, HIGH); // accendo il led on board
  Wire.write("HELLO WORLD!"); //invio la risposta sul bus i2c
  roger = 1; // setto il roger a 1 
}

void loop() {
 if(roger){
  int ritardo = 1000*roger; //calcolo il tempo di accensione
  delay(ritardo);
  digitalWrite(LED, LOW); // spengo il led on board
  roger = 0; // resetto il roger
 }
}

I commenti inseriti nel codice sono sufficienti a capire come lavora. In pratica il Master(esp32) invia sul bus I2C una richiesta allo Slave con indirizzo 1 (arduino nano), il quale arrivata la chiamata, risponde al Master con il messaggio “HELLO WORLD!”. Il master a questo punto rimane in ascolto e riceve i 12 byte che aveva richiesto allo slave stampando i caratteri in ordine di arrivo.

2° caso

In questo caso il master invia prima dei dati verso lo slave, lo slave attiva la funzione onReceive() ed elabora i dati inviati dal master e termina. Subito dopo il master invia una richiesta dati e lo slave attiva la funzione onRequest().

Il codice da inserire nel master

//ESP32 - I2C master

#include "Wire.h" //libreria I2C

String msg ="ita";

void setup(){
  Serial.begin(9600);
  Wire.begin(); //senza valori significa che e' master
}

void loop() {
  
  Serial.print("richiesto messaggio in : ");
  Serial.println(msg);
  //inizia la trasmissione verso lo slave 1
  Wire.beginTransmission(1);
  if(msg == "ita"){
    Wire.write("ita");
    msg="eng";
  } else {
    Wire.write("eng");
    msg = "ita";
  }
  //termina la trasmissione 
  Wire.endTransmission();
  //richiede i dati allo slave
  Wire.requestFrom(1, 3);
  while(Wire.available()){
    char rx = Wire.read();
    Serial.print(rx);
  }
  Serial.println();
   delay(1000);
}

e questo e il codice dello slave

//arduino nano I2C slave

#include "Wire.h" //libreria I2C

int LED = 13; 
int roger = 0;
String rx;

void setup() {
  Serial.begin(9600);
  pinMode(LED,OUTPUT);
  Wire.begin(1); //inizializzo il BUS e assegno indirizzo 1
  Wire.onRequest(datiRichiesti); //imposto la funzione da eseguire su richiesta dati dal master
  Wire.onReceive(datiRicevuti); //imposto la funzione da eseguire su invio dati dal master
}

void loop() {
 
  delay(100);

}

void datiRicevuti(int howMany){
  digitalWrite(LED, HIGH); //accendo led onboard per segnalare la ricezione della richiesta
  //ricevo i dati e li inserisco in un array di caratteri
  char rxbuff[3];
  int i = 0;
  while(Wire.available()){
    rxbuff[i]=Wire.read();
    i++;
  }
  //converto l'arrey di caratteri in una stringa
  rx = String(rxbuff);  
 delay(500);
 digitalWrite(LED, LOW); //spengo il led onboard
}

void datiRichiesti(){
   if(rx == "ita"){
    Wire.write("ITA");
  } else {
    Wire.write("ENG");   
    }
  
}

Anche in questo caso i commenti al codice sono abbastanza chiari ma vediamo lo scopo dell’esempio e il flusso delle informazioni.

Il master richiede allo slave il messaggio alternativamente in lingua italiana o ingelse. Per far cio il master invia allo slave il codice della lingua del messaggio tramite la la sequenza .beginTrasmission() -> .write() -> .endTrasmission() lo slave si accorge che il master gli ha inviato dei dati e quindi innesca la funzione specificata tramite .onReceive(), riceve i dati con .read() e li imagazzina in una variabile globale chiamata rx. A questo punto il master invia una richiesta di dati allo slave il quale prima di inviagliela verifica in che lingua deve rispondere.

Conclusioni

Il bus i2c e’ molto comodo per le comunicazioni tra microprocessori, sensori e altre periferiche che si trovano vicine, infatti il bus non può superare i 2mt altrimenti potrebbero esserci problemi di affidabilità, d’altronde e’ stato studiato per lo scambio di dati su dispositivi alloggiati sulla stessa scheda. Per comunicazioni ad alto raggio ci sono altri bus ( rs485, canbus etc etc)

HP Proliant con SmartArray p410

Installare un S.O. su un server HP equipaggiato con un array SmartArray p410 in modalità HBA

PROBLEMA: Il raid hardware p420i e’ configurabile in modalità HBA, questa modalità praticamente consente al sistema operativo di gestire direttamente i dischi connessi al raid in modalità trasparente come se il controller non esistesse. Di contro però vi e’ che nella modalita HBA non e’ possibile usare direttamente i dischi collegati al controller p420 per eseguire il BOOT.

SOLUZIONE:

Esiste una soluzione molto semplice per ovviare a questo limite sui server HP. Questi server all’interno sono provvisti di un ingresso USB e un lettore SD che possono ospitare appunto delle memorie di massa dove sara’ possibile installare il BOOT del sistema operativo mentre i file della directory root andranno installati sui dischi connessi al raid HW .

Vediamo la procedura per l’installazione di PROXMOX (ma puo’ essere usata anche con altri SO) .

STEP 1:

Avviamo il server con la chiavetta di installazione avendo l’accortezza di aver gia’ installato la chiavetta/SD con almeno 4Gb all interno della macchina, procediamo alla normale installazione selezionando come destinazione il disco collegato allo smartarray facendo attenzione a TOGLIERE la spunta sul flag “reboot after sucessful installation”. Al termine della procedura guidata di installazione, invece di cliccare su reboot premere CTRL+SHIFT+F3 o F4 in modo di accedere al terminale.

STEP 2:

Creiamo una directory chiamata /drive sulla chiavetta interna e montamo al suo interno la partizione /dev/pve/root (tramite lsvdisplay verifichiamo il corretto percorso con il seguente comando

root@pve:~# lvdisplay -m

Dovremmo ottenere l’elenco dei volumi con il relativo percorso

A questo punto creiamo una directory chiamata drive e montiamo questa partizione

root@pve:~#mkdir drive
root@pve:~#mount /dev/pve/root /drive

Ora creiamo del directory dev, sys, proc e run sotto la /drive e montiamo in modalita’ bind i rispettivi percorsi. Il montaggio in modalita’ bind delle directory significa replicare il contenuto della directory di origine in un altro punto facendo si che ogni modifica sul punto replicato venga immediatamente trasmesso al punto di origine.

Il comando per eseguire questa operazione e’

mount --bind /percorso/cartella/origine /percorso/cartella/destinazione

root@pve:~# mount --bind /dev /drive/dev
root@pve:~# mount --bind /sys /drive/sys
root@pve:~# mount --bind /proc /drive/proc
root@pve:~# mount --bind /run /drive/run

eseguiamo un chroot sulla directory drive

root@pve:~#chroot /drive

STEP 3:

A questo punto e’ necessario variare la tabella di partizioni della nostra USB/SD (ancora vergine o non partizionata) per renderla utilizzabile al boot. Tramite il comando lsblk -f andiamo ad individuare esattamente il nome del device della usb/sd, individueremo cosi ilpercorso corretto /dev/sdX (dove X sta per la lettera della chiavetta o SD card) lanciamo il comnando gdisk,

# gdisk /dev/sd[x] 
Command: (? for help): r 
Recovery/transformation command (? for help): x 
Expert command (? for help): l 
Enter the sector alignment value (1 - [variable], default = 2048): 34 
Expert command (? for help): m 
Command: (? for help): n 
Partition number (1 - 128, default 1:) 1 
First sector (34 - [variable], default = 34): 34 
Last sector (34 - [variable], default = [variable]): 2047 
Current type is 'Linux file system' Hex code or GUID (L to show codes, Enter = 8300): ef02 
Changed type of partition to 'BIOS boot partition' Command: (? for help): r 
Recovery/transformation command (? for help): x 
Expert command (? for help): l 
Enter the sector alignment value (1 - [variable], default = 2048): 2048 
Expert command (? for help): m 
Command: (? for help): n 
Partition number (1 - 128, default 2:) 2 
First sector (2048 - [variable], default = 2048): 2048 
Last sector ([variable] - [variable], default = [variable]): +1G 
Current type is 'Linux file system' Hex code or GUID (L to show codes, Enter = 8300): 8300 
Changed type of partition to 'Linux file system' Command: (? for help): w 
***writing everythin to disk***

STEP 4:

Siamo all’ultimo passaggio, usando i comandi integrati di proxmox (ringraziamo gli sviluppatori) formattiamo la partizione 2 sulla USB/SD creata tramite le procedura dello step 3 e prepariamola per il boot con il seguente comando

root@pve:~#proxmox-boot-tool format /dev/sdX2

Se dovesse restituire qualche errore usiamo l’opzione –force alla fine, potrebbe capitare se la USB/SD fossero state formattate in una vecchia modalita vfat/fat32.

Ultimo comando da lanciare per concludere la preparazione

root@pve:~#proxmoi-boot-tool init /dev/sdX2

Abbiamo concluso la preparazione e siamo pronti a riavviare il server, smontiamo la chiavetta usata per l’installazione e diamo un bel reboot al server.

Ringraziamenti:

Questo articolo e’ una libera traduzione dell’originale che potete trovare qui ,ringrazio vivamente l’autore che mi ha risolto un grosso problema in quanto fino ad oggi avevo trovato solo altri metodi molto complicati.

Programmazione dei DEV-KIT e moduli ESP32/ESP8266

ESP12-ESP8266-nodeMCU
ESP32-ESP-WROOM

Programmazione e Preparazione IDE Arduino

Per poter programmare questi microcontrollori con l’ IDE di Arduino e’ necessario caricare tramite la gestione delle schede la libreria corretta. Aggiungiamo il percorso per caricare il modulo gestore .

File->impostazioni

aggiungiamo le seguenti rige nel campo “URL aggiuntive per il gestore schede”

http://arduino.esp8266.com/stable/package_esp8266com_index.json (per la esp12)

https:///dl.espressif.com/dl/package_esp32_index.json (per la esp32)

Se volessimo inserirle entrambi lo possiamo fare separando le due stringe con una virgola oppure cliccando sull’icona

e inserendo una sotto l’altra le stringhe 

Clicchiamo su OK e riavviamo l’ide.

Troveremo un nuovo elenco di schede disponibili alla scelta sotto il menu Strumenti->Scheda.

Ora siamo pronti per programmarla tramite l’IDE di arduino.

LINUX – PROCESSO ATTIVO

Vediamo il comando per verificare se un processo e’ attivo. Questo e’ utile per verficare se un servizio e’ avviato oppure se e’ necessario farlo partire, oppure puo essere utile perchè magari il servizio e’ bloccato e voglio killarlo.

Il comando linux e’ pgrep

ESP-WROOM-32

Modulo ESP-WROOM-32

Questo modulo fa parte della famiglia ESP e’ leggermente diverso dall’ESP8266. Sia come piedinatura che come caratteristiche tecniche leggermente superiori, ma per questo cercate in giro su internet i datasheet.

Se montato su una DEV-BOARD e’ programmabile con il tool di arduino tramite USB.

E’ un ottima alternativa all’ESP8266 anche a livello di costo sui siti cinesi si trvano dei moduli montati su develop board a prezzi stracciati. Unica accortezza bisogna collegare un condensatore elettrolitico da 10uF tra il piedino EN e massa altrimenti a ogni tentativo di programmazione il processore si riavvia.

piedinatura

Modelli di DEVELOP-BOARD

Equipaggiati con questo modulo possiamo trovare diverse Develop Board che si differiscono per piedinatura, io ho usato la DEVKITC 38 pins.

Si differenzia per il numero di pin disponibili all’esterno e la loro disposizione pertanto non possono essere pin-to-pin compatibili ma il modulo core rimane lo stesso. La DEVboard puo essere alimentata a 5 V sul pin Vin oppure da USB . ATTENZIONE i segnali in input non possono superare i 3,3 V causa arrostimento del chip.

Di seguito potete scaricare il data sheet completo

Piedinatura e Utilizzo

Non tutti i pin possono essere usati nello stesso modo, nella tabella sotto vediamo in che modo possono essere usati.

GPIOINPUTOUTPUTNOTE
0CON PULL-UPSIEmette segnale PWM al Boot
1TX pinSIUscita seriale debug al boot
2SISIlegato al led on board
3SIRX pinHIGH al boot
4SISI
5SISIEmette segnale PWM al Boot
6NONOconnesso alla flash SPI integrata(SCK/CLK)
7NONOconnesso alla flash SPI integrata(SDO/SD0)
8NONOconnesso alla flash SPI integrata(SDI/SD1)
9NONOconnesso alla flash SPI integrata(SHD/SD2)
10NONOconnesso alla flash SPI integrata(SWP/SD3)
11NONOconnesso alla flash SPI integrata(CSC/CMD)
12SISI Se messo a 3,3V blocca il boot
13SISI
14SISI
15SISI
16SISI
17SISI
18SISI
19SISI
20SISI
21SISI
22SISI
23SISI
24SISI
25SISI
26SISI
27SISI
28SISI
29SISI
30SISI
31SISI
32SISI
33SISI
34SINOSolo ingresso
35SINOSolo ingresso
36SINOSolo ingresso
37SINOSolo ingresso
38SINOSolo ingresso
39SINOSolo ingresso
tabella ingressi uscite

-Pin in solo ingresso

I GPIO dal34 al 36 sono utilizzabili solamente in ingresso, se vengono configurati come output non si riescono a pilotare. Non sono forniti di resistenze di pull interne quindi vanno usate quelle esterne.

-SPI flash interna

Sui GPIO 6 -11 e’ connessa la memoria FLASH interna e vengono messi a disposizione all’esterno tramite i pin relativi, e’ bene NON utilizzare questi pin lasciandoli liberi.

-Pin CAPACITIVI

i GPIO 0-2-4-12-13-14-15-27-32-33 sono dei pin utilizzabili sia in ingresso che in uscita, se usati in ingresso hanno la particolarita’ che possono gestire il TOUCH , cioe’ sono in grado di sentire il tocco del dito sostituendo cosi il pulsante meccanico.

-Convertitori Analogico/Digitale

Tutti i GPIO configurabili come ingressi, hanno la funzione di convertitori analogici digitali con risoluzione di 12bits. sono divisi in due gruppi, ADC1 e ADC2, gli ingressi sono divisi cosi:

ADC1 (GPIO 36,37,38,39,32,33,34,35)

ADC2 (GPIO 4,0,2,15,13,12,14,27,25,26) ADC non utilizzabili se si usa il WIFI.

I canali ADC hanno risoluzione 12 bits pertanto la scala di valori sara’ 0 – 4095, il range va da 0V a 3,3V. Questo significa che hanno una risoluzione di 0.8mV. I converitori pero’ non sono completamente lineari in tutta la loro escursione, in particolare nella zona bassa e quella alta, pertanto e’ consigliabile non usarlo con tensioni inferiori a 0.20V o superiori di 3.00V non essendo affidabili

-Convertitori Digitali/Analogici

E’ fornito di due convertitori D/A a 8 bit 0-254 converte il valore digitale in una tensione analogia con un range 0- 3,3 con risoluzione di circa 13mV

DAC1 GPIO 25

DAC2 GPIO 26

-Ingressi RTC WakeUp

Ci sono alcuni ingressi che possono essere usati per portare il processore dallo stato di SLEEP (funzione che abbatte il consumo energetico cofigurabile)al funzionamento normale. Questi sono i pin associabili alla funzione RTC wakeup:

  • RTC_GPIO0 (GPIO36)
  • RTC_GPIO3 (GPIO39)
  • RTC_GPIO4 (GPIO34)
  • RTC_GPIO5 (GPIO35)
  • RTC_GPIO6 (GPIO25)
  • RTC_GPIO7 (GPIO26)
  • RTC_GPIO8 (GPIO33)
  • RTC_GPIO9 (GPIO32)
  • RTC_GPIO10 (GPIO4)
  • RTC_GPIO11 (GPIO0)
  • RTC_GPIO12 (GPIO2)
  • RTC_GPIO13 (GPIO15)
  • RTC_GPIO14 (GPIO13)
  • RTC_GPIO15 (GPIO12)
  • RTC_GPIO16 (GPIO14)
  • RTC_GPIO17 (GPIO27)
  • RTC_GPIO0 (GPIO36)
  • RTC_GPIO3 (GPIO39)
  • RTC_GPIO4 (GPIO34)
  • RTC_GPIO5 (GPIO35)
  • RTC_GPIO6 (GPIO25)
  • RTC_GPIO7 (GPIO26)
  • RTC_GPIO8 (GPIO33)
  • RTC_GPIO9 (GPIO32)
  • RTC_GPIO10 (GPIO4)
  • RTC_GPIO11 (GPIO0)
  • RTC_GPIO12 (GPIO2)
  • RTC_GPIO13 (GPIO15)
  • RTC_GPIO14 (GPIO13)
  • RTC_GPIO15 (GPIO12)
  • RTC_GPIO16 (GPIO14)
  • RTC_GPIO17 (GPIO27)

-Uscite PWM

Si possono usare tutte le uscite GPIO (all’infuori dei GPIO 34 e 39) per generare segnali PWM diversi per ogni pin sia in frequenza che in dutycicle. per utilizzarli bisogna configurare, tramite il linguaggio scelto per la programmazione, i seguenti parametri per ogni GPIO :

  • Frequenza del segnale;
  • Duty cycle;
  • canale PWM ;
  • GPIO di uscita del segnale .

-I2C

L’ ESP32 puo gestire due canali I2C e possono essere usati tutti i pin GPIO come segnali SDA e SCL, Quando si usa l’ESP32 con l’IDE di Arduino i pin di default sono SDA GPIO21 – SCL GPIO22

Se si vuole utilizzare pin diversi usando la libreria wire.h dobbiamo utilizzare il comando seguente per assegnare i pin:

Wire.begin(pinSDA, pinSCL);

-SPI

La scheda ha due canali SPI chiamati V e H, non ci sono differenze tra i due canali escluso che sono disponibili su pin diversi. L’uso del bus SPI con ESP32 e’ impostato di default sui pin:

SPIMOSIMISOCLKCS
VSPIGPIO23GPIO19GPIO18GPIO5
HSPIGPIO13GPIO12GPIO14GPIO15

-INTERRUPTS

Tutti i pin GPIO in ingresso, possono essere configurati come ITERRUPT esterni

-STRAPPING PINS

Questi pin sono utilizzati per impostare l’ESP32 in modalita’ boot o flashing.

  • GPIO 0
  • GPIO 2
  • GPIO 4
  • GPIO 5 (deve essere HIGH durante boot)
  • GPIO 12 (deve essere LOW durante boot)
  • GPIO 15 (deve essere HIGH durante boot)

Se si usano le DEV BOARD con l’intrerfaccia USB integrata possiamo anche non preoccuparci dello stato di questi pin per la programmazione del BOOT o durante la fase di programmazione del firmware, in quanto l’ IDE di programmazione imposta in automatico questi pin in base al tipo di programmazione da effettuare (boot o flashing).

Se usiamo questi PIN collegandoli a dispositivi esterni, potremmo avere qualche difficolta’ di caricamento del firmware o del boot, in questo caso e’ necessario scollegare l’ESP32 dalla scheda programmarla e poi rimetterla al suo posto. Dopo il reset o il flashing questi pin lavorano normalmente.

-PIN allo stato alto al boot

Alcuni GPIO al momento dell’avvio o del reset si porteranno allo stato alto o genereranno un uscita PWM , se a questi gpio sono collegate apparecchiature esterne come ad esempio LED, RELE’ , sensori etc. etc. questi dispositivi potranno generare anomalie durante la fase di avvio o reset.

  • GPIO 1
  • GPIO 3
  • GPIO 5
  • GPIO 6 to GPIO 11 (connessi al ESP32 SPI della memoria flash integrata – si raccomanda di non usare questi pin).
  • GPIO 14
  • GPIO 15

-Enable (pin EN)

Questo pin abilita il regolatore 3,3V integrato, e’ mantenuto normalmente alto , se collegato a massa disabilita il regolatore 3,3V spegnendo la CPU, puo essere usato per riavviare la CPU con un pulsante , normalmente va collegato a GND con un condensatore di 10uF.

-GPIO dati tecnici

Gli ingressi/uscita hanno una tensione massima di 3,3V allo stato alto e possono fornire un massimo di 40mA.

riferimenti

Questo articolo e’ una traduzione parziale dell’ articolo seguente

ESP32 Pinout Reference: Which GPIO pins should you use?

COME LIMITARE L’USO DELLA CPU

Per limitare l’uso della cpu ad un singolo processo in debian/ubuntu si puo utilizzare un programma scritto in C dal nome cputool.

Questo software non e’ installato di default ma si puo installare dai repository ufficiali con il comando:

$: sudo apt install cputool

al termine dell installazione eseguire un top per identificare il pid del processo da limitare

$: top

se per esempio vogliamo limitare il processo firefox con id = 8726

scriviamo :

$: sudo cputool –cpu-limit 15 -p 8726

dove 15 sta ad indicare che il pid 8726 non potra’ utilizzare piu’ del 15% della cpu

chiaramente il prograsmma firefox risultera’ rallentato ma utilizzabile e non blocchera’ tutto il resto del sistema se dovesse superare la richiesta di CPU.

E’ possibile limitare non solo un pid ma anche un gruppo di pid

$: sudo cputool –cpu-limit 15 -P 8726,1484