Nei precedenti post vi ho introdotto il nuovo chip esp32 e vi ho spiegato come effettuare il setup dell’ambiente di sviluppo, incluso l’utilizzo – opzionale – di un IDE grafico, Eclipse.
Prima di poter iniziare a sviluppare nuovi programmi, è necessario che vi parli ancora di tre elementi fondamentali: la flash, il bootloader e il sistema operativo FreeRTOS. Ma non preoccupatevi! Già alla fine di questo post scriveremo un programma (seppur “minimale”) e dal prossimo tutorial inizieremo con esempi pratici.
Flash
Come detto nel primo post di questa serie, il chip esp32 richiede una memoria flash esterna, nella quale memorizzare software, dati, parametri di configurazione…
Questa memoria esterna viene collegata al chip via bus SPI e può avere una dimensione massima di 16Mb. Il modulo ufficiale Espressif (ESP-WROOM-32) monta un chip da 4Mb prodotto da GigaDevice (GD25Q32):
La memoria flash può contenere diversi elementi: programmi, dati… per questo viene divisa in sezioni (partizioni). L’elenco delle diverse partizioni, della loro dimensione e della loro posizione all’interno della flash è memorizzato nella flash stessa (all’indirizzo 0x8000) e prende il nome di partition table.
Due tipi di partizioni sono definite di default:
[checklist]
- app (tipo 0) – partizione che contiene una applicazione
- data (tipo 1) – partizione che contiene dati
[/checklist]
Durante lo sviluppo di un nuovo programma, lo sviluppatore può decidere come organizzare la memoria flash in base alle specifiche esigenze. Il framework di sviluppo offre due configurazioni già pronte, definite all’interno del menu di configurazione:
E’ inoltre possibile visualizzare la partition table del proprio progetto lanciando il comando make partition_table:
Nello screenshot sopra si vede la partition table relativa alla configurazione più semplice (Single factory app, no OTA):
- una partizione data (chiamata “nvs“) da 24Kb all’indirizzo 0x9000
- una partizione data (chiamata “phy_init“) da 4Kb all’indirizzo 0xf000
- una partizione app (chiamata “factory“) da 1Mb all’indirizzo 0x10000
Bootloader
Per spiegare il motivo della divisione della memoria flash in più partizioni, dobbiamo comprendere come funziona il bootloader del chip esp32, ovvero come viene avviata la nostra applicazione.
Il chip esp32 ha memorizzato al suo interno (nella memoria ROM) un piccolo programma detto first-stage bootloader. Questo programma viene eseguito ad ogni reset del chip e si occupa di inizializzare l’accesso alla memoria flash e, se richiesto, di memorizzare dei nuovi dati (comando flash). Una volta terminati i propri compiti, questo primo bootloader accede alla memoria flash (indirizzo 0x1000) e carica il second-stage bootloader.
Il second-stage bootloader legge la partition table dall’indirizzo 0x8000 della flash alla ricerca di partizioni di tipo app e decide quale eseguire in base a quanto trovato nella partizione otadata. Se tale partizione è vuota, viene eseguita l’applicazione presente nella partizione factory. In questo modo è possibile implementare un meccanismo di aggiornamento over-the-air (OTA): si invia la nuova versione dell’applicazione al chip esp32 mentre è in funzione; tale versione viene memorizzata in una nuova partizione. Una volta che l’invio è terminato con successo, viene scritto l’id della nuova partizione della otadata e riavviato il chip; in questo modo il bootloader eseguirà la nuova applicazione:
FreeRTOS
Il framework esp-idf si basa sul Real-Time Operating System FreeRTOS. Può sembrare strano parlare di sistema operativo in esecuzione su un chip quale esp32… non dobbiamo però pensare che FreeRTOS sia un sistema operativo come Windows, Linux o MacOS. Il compito fondamentale di un sistema operativo in ambito embedded è quello di offrire, tramite un proprio schedulatore, il multitasking, ovvero la possibilità di eseguire contemporaneamente più programmi (tasks). In realtà sappiamo che un core di un microprocessore è in grado di eseguire una sola istruzione in una unità di tempo: il sistema operativo da l’impressione che i programmi siano in esecuzione contemporaneamente mandandoli rapidamente in esecuzione uno dopo l’altro.
passaggi di stato per i tasks in FreeRTOS
Un sistema operativo real-time garantisce che la schedulazione dei vari tasks sia deterministica, ovvero che sia possibile predire il comportamento dello scheduler del sistema operativo. FreeRTOS consente al programmatore di definire delle priorità per i propri tasks; lo scheduler utilizza i valori di priorità per definire la sequenza di esecuzione dei vari tasks.
Il primo programma
Iniziamo ora a scrivere il nostro primo programma che utilizza il framework esp-idf. Questo programma, disponibile nel mio repository Github, sarà la base per i prossimi tutorial.
Per prima cosa dobbiamo includere alcune librerie:
La libreria stdio.h (Standard I/O) viene utilizzata per le funzioni di stampa a video (printf…) mentre le due librerie freertos servono per definire ed eseguire i vari tasks.
Ogni programma viene eseguito a partire dal metodo app_main():
All’interno di app_main() viene definito un nuovo task, tramite xTaskCreate. Questo metodo richiede come parametri il puntatore al metodo (&loop_task) che contiene il codice del task, il nome da assegnare al task (“loop_task”)la dimensione in words (512) dello stack in memoria da assegnare al task, eventuali parametri (NULL), la priorità (5) e il puntatore – opzionale – per ottenere un handler (NULL).
Una volta creato il task, lo schedulatore di FreeRTOS lo manda in esecuzione in base alle diverse priorità.
Il task ha il seguente codice:
è un loop continuo che ogni secondo stampa a video la scritta “Hello World!”. Il metodo vTaskDelay mette in pausa il task per il numero di ticks specificati come parametro. La costante portTICK_RATE_MS definisce la durata, in millisecondi, di un tick; dividendo quindi 1000 per tale costante si ottiene proprio il numero di ticks in ogni secondo.
Files aggiuntivi
Per poter compilare il progetto, è necessario preparare un paio di files aggiuntivi:
- un Makefile nella cartella principale che contenga almeno il nome del progetto e il riferimento al makefile principale del framework:
- un file component.mk anche vuoto nella cartella dove è presente il codice sorgente per indicare al compilatore che deve processare i files presenti in tale cartella:
Test
Una volta pronto il programma, possiamo compilarlo e caricarlo come spiegato nel precedente tutorial
make make flash
Se è tutto ok, collegandoci con un emulatore terminale dovremmo vedere:
Ottimi articoli (mi riferisco anche alle puntate precedenti) chiaro e coinciso: capito tutto in un baleno–
Spero nelle prossimi si esamini il framework per accedere ai pin (accendere led e leggere un valore analogico….) e magari usare i due core (esp32 ha 2 core invece di uno del esp8266).
Comunque grazie e attendo con trepidazione la prossima puntata..Per Ora Buon Natale e Buon Anno
Mauro
Ottimo tutorial!!!!! Complimenti e molto molto interessante!! …. spero in altri articoli su esp32
Ciao Michele e grazie! Ho postato poco fa proprio un nuovo articolo… ho intenzione di approfondire molto l’utilizzo di questo chip quindi segui periodicamente il blog 😉
Ciao, Sto smanettando anche io con l’esp32, sono riuscito a far funzionare il progetto blink in esp-pdf, poi ho provato a creare un progetto nuovo ed ho copiato il tuo sorgente wifi_scanner ma quando assemblo mi da un sacco di errori, nel tuo articolo dici che “In fase di compilazione del programma, è fondamentale attivare il modulo wifi dal menu di configurazione” ora però non sono riuscito a lanciare questo menu di configurazione? dovrebbe apparire quando lancio il make?
Ciao Roberto… si la prima volta che lanci il comando make ti deve apparire il menu; in alternativa puoi lanciare “make menuconfig” per eseguirlo esplicitamente. Ti consiglio di seguire tutti i tutorial dall’inizio: da quello che scrivi sembra che il tuo ambiente di compilazione non sia configurato completamente.
hey! once I run the original code, the ESP32 board will get panic,and finally I discovery that the parameter “512” should be set as 1024 or 2048 in the function xCreateTask() ,then the program will run perfectly,why??
Hi, that parameter is the stack size, so it depends on how much memory (= variables…) the task will allocate
hi, you have given marvellous explaination.
Thank a lot,
Hi,
in many examples from the esp-idf, like the simple_wifi one, the app_mail just terminates, and does not start a task.
What is the difference between the two approaches ?
Thanks !
Hi, for simple programs you can implement everything in the main… but if you need to use some multi-tasking features (like the wifi driver…) you have to start a thread otherwise your program simply ends.
Ciao,
sono un giovane studente universitario, e seguo corsi di informatica al politecinico di Torino. Recentemente sono impegnato in un progetto che vede proprio protagonista la board esp32. Sono molto interessanti i tutorial che hai realizzato e ti ringrazio per aver dato parte de tuo tempo per realizzarli.
Volevo solo fare una considerazione, dopo aver letto il tuo aggiornamento al progetto 00_empty del repo che hai creato relativo all’aumento della dimensione dello stack per il task, mi sono accorto che se nella stampa della celeberrima frase ‘Hello World!’ se non mettessi un \n a terminazione della stringa mi continua ad andare in overflow il programma.
Volevo segnalare questa mia osservazione, ovviamente da verificare, ma se fosse come dicessi, eviterebbe problemi o malditesta per futuri lettori poco esperti come me!
Grazie ancora per aver realizzato questi tutorial.
Grazie Francesco per il commento. Hai ragione, la corretta definizione dello stack dei vari task è un argomento molto importante in FreeRTOS e in generale. Credo a breve preparerò un tutorial su di esso.
Dear luca, thanks for such a great tutorial site for ESP32. Lot of things are more clear to me because of these tutorials.
Hi,
Great post/s. I am looking at this example because I’m am trying to measure lowest possible current in the awake mode. Would this example be suitable for that? Can you also share some thought about other configurations (single core, 80mhz etc…) for esp32 in order to make it consume as little current as possible in the awake mode?
Thanks for much.
Hi Igor, at the moment I don’t have an article about it but on the Internet there are other good tutorials about low-power modes in esp32.
Luca, Is there a way to directly write to the NVS on the ESP32 without running a sketch?
yes, there’s a specific tool to prepare a binary image that can be directly written to the NVS partition.
Ciao Luca, ottimo articolo! Sicuramente tra i più dettagliati e precisi in lingua Italiana.
Avrei una curiosità: questo “first stage bootloader” situato di fabbrica nella ROM permette quindi di poter prendere un nuovo chip ESP32, inserirlo ad esempio all’interno di una custom breakout board e programmarlo senza dover ricorrere a bootloader caricati esternamente?
Al contrario di quanto avviene con i chip vergine Atmel in ambiente Arduino insomma..
Grazie
ciao Alessandro, si corretto!
Hi! Using the manually installed ESP-IDF after sourcing it in bash on Linux make partition_table will not work but idf.py partition_table does.
It would be great if you add this to the blog.