In questo post vi mostrerò come effettuare il collegamento ad una rete wifi.
Il framework esp-idf include un driver wifi che si occupa della gestione dell’interfaccia wifi del modulo esp32. Tale driver mette a disposizione delle API che il programma può utilizzare; alcune di queste API le abbiamo già viste nel precedente tutorial:
ESP_ERROR_CHECK(esp_wifi_init(&wifi_config)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); |
Il driver wifi viene eseguito in parallelo al programma principale e comunica con questo tramite eventi:
Nel programma principale dobbiamo definire una funzione che farà da event handler, ovvero che sarà chiamata dal driver wifi ogni volta che un nuovo evento deve essere notificato:
static esp_err_t event_handler(void *ctx, system_event_t *event) {...} |
dobbiamo quindi indicare al framework il nome di tale funzione:
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); |
L’elenco dei diversi eventi generati dal framework è presente nel file esp_event.h. Per quanto riguarda il driver wifi in modalità station, gli eventi sono quelli con prefisso SYSTEM_EVENT_STA:
Il collegamento ad una rete wifi avviene secondo questo flusso logico:
- il programma principale effettua delle chiamate API per configurare ed avviare il driver
- terminate le attività interne, il driver notifica che l’avvio è avvenuto con successo tramite l’evento SYSTEM_EVENT_STA_START
- l’event handler, una volta ricevuta la notifica di avvio del driver, può eseguire l’API esp_wifi_connect() per chiedere al driver il collegamento alla rete wifi specificata in fase di configurazione
- una volta effettuato il collegamento e ottenuto un indirizzo IP (se utilizziamo il servizio DHCP), il driver lo notifica tramite l’evento SYSTEM_EVENT_STA_GOT_IP
- ora l’event handler può informare il programma principale che il collegamento alla rete è avvenuto con successo
Sincronizziamo i tasks
Il processo descritto sopra prevede che il programma principale aspetti l’avvenuto collegamento alla rete wifi prima di eseguire le sue attività. E’ quindi necessario capire come sincronizzare i diversi elementi coinvolti: programma principale, event handler e driver wifi. Similmente a quanto abbiamo già visto, FreeRTOS mette a disposizione del programmatore un sistema di eventi per fare comunicare i diversi tasks. La gestione degli eventi in FreeRTOS avviene tramite event groups e event bits.
Possiamo immaginare gli event bits come delle “bandiere” (flags) visibili dai diversi tasks:
- il programmatore può definire un numero arbitrario di event bits
- i task possono alzare (set) o abbassare (clear) i diversi bits
- i task possono interrompere la loro esecuzione aspettando che uno o più bit siano attivati
Infine gli event bits sono raggruppati in event groups, ognuno dei quali normalmente contiene 8 event bits. Gli event bits all’interno di un event group sono nominati in base alla loro “posizione” (BIT0, BIT1…):
Il programma
Il programma completo si trova nel mio repository Github. Commentiamo le parti principali:
static EventGroupHandle_t wifi_event_group; const int CONNECTED_BIT = BIT0; [...] wifi_event_group = xEventGroupCreate(); |
Il programma definisce un event group (wifi_event_group) e un event bit (CONNECTED_BIT). All’interno di app_main() tale event group viene creato utilizzando il metodo xEventGroupCreate() di FreeRTOS.
#define WIFI_SSID "MYSSID" #define WIFI_PASS "MYPASSWORD" [...] wifi_config_t wifi_config = { .sta = { .ssid = WIFI_SSID, .password = WIFI_PASS, }, }; ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); |
La configurazione della rete wifi avviene tramite la definizione di due costanti (WIFI_SSID e WIFI_PASS). Tali costanti sono poi utilizzate per creare la struttura wifi_config, struttura che viene passata come parametro al metodo esp_wifi_set_config.
static esp_err_t event_handler(void *ctx, system_event_t *event) { switch(event->event_id) { case SYSTEM_EVENT_STA_START: esp_wifi_connect(); break; case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); break; case SYSTEM_EVENT_STA_DISCONNECTED: xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); break; |
L’event handler gestisce i diversi eventi generati dal driver wifi come illustrato sopra. In particolare, una volta stabilita la connessione (evento STA_GOT_IP), attiva l’event bit CONNECTED_BIT; al contrario in caso di disconnessione disattiva (clear) tale event bit.
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); |
Il task principale (main_task) si mette in pausa fino al completamento della connessione alla rete wifi attendendo l’attivazione del bit CONNECTED_BIT. La costante portMAX_DELAY indica che l’attesa deve avvenire per un tempo indefinito.
tcpip_adapter_ip_info_t ip_info; ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info)); printf("IP Address: %s\n", ip4addr_ntoa(&ip_info.ip)); printf("Subnet mask: %s\n", ip4addr_ntoa(&ip_info.netmask)); printf("Gateway: %s\n", ip4addr_ntoa(&ip_info.gw)); |
Una volta stabilita la connessione, il task principale visualizza i parametri di rete (indirizzo IP, netmask e gateway). La costante TCPIP_ADAPTER_IF_STA rappresenta (vedi file tcpip_adapter.h) l’interfaccia di rete quando il chip esp32 è in modalità station.
Ecco uno screenshot del programma in esecuzione:
Per rendere l’output più chiaro, ho spento il logging di default del driver wifi con l’istruzione:
esp_log_level_set("wifi", ESP_LOG_NONE); |
e disabilitato il buffering di standard output:
setvbuf(stdout, NULL, _IONBF, 0); |
hi
nice post!
hope we read more of this art from you.
best wishes
rudi 😉
Thanks Rudi, it’s great to read you here: I followed your posts on the official forum! I’m going to post a tutorial every week, a lot of peripherals in this little chip 😉
Somehow your code from github doesn’t work on my ESP32. The program is simple and straightforward, thanks for all those details… but it throws ‘Guru Meditation Error of type LoadProhibited occurred on core 0. Exception was unhandled.’ My other WiFi programs work fine though.
Hi Andew… very strange: I’ve just compiled the example again and it runs fine on my ESP32 board. Are you using the release 1.0 of the framework or is it updated with the latest commits?
Hi, I had the same problem, commenting the line :
//setvbuf(stdout, NULL, _IONBF, 0);
solved the problem for me
Thanks! It solved also for me… I’m going to fix all the examples in the repository
Hello Luca,
Thank for your guides! I’m planning to follow them all 🙂
I’m having a problem with this one. I downloaded your code from Github and I have the following errors:
const int CONNECTED_BIT = BIT0; // BIT0 could not be resolved
tcpip_adapter_ip_info_t ip_info; // tcpip_adapter_ip_info_t could not be resolved
I tried including #include “tcpip_adapter.h” (Path and Symbols, GNU C, add the folder tcpip_adapter under components) and adding a #include “tcpip_adapter.h” in the main. But the include is also not resolved.
I tried also rebuilding the index. But still the same problem.
Any advice? Thanks!!
Hi Rodolfo, this is strange… I can compile the example with the latest framework. For example I include esp_event_loop.h, which includes esp_event.h which includes tcpip_adapter.h that defines tcpip_adapter_ip_info_t. Are you sure your esp-idf repository is updated and complete?
Now it’s even stranger. I erased part of the code that was directly using tcpip functions and left esp_event_loop. Now it compiles… When seeing esp_event.h, there is a warning saying that the include is not found. But it’s not an error. I’m using the latest esp-idf on OSX. Maybe it’s a good idea to reinstall everything again. Will keep you posted. Thanks
Hi luca ,
I have been reading your posts on ESP 32 , they are amazing .
I have one question that , form where do you collect the info to write the code ?
I want to write my own code for ESP 32 for some application , How do I proceed ?
Hi, my sources are the official documentation, the official forum (esp32.com) and many tutorials/videos other makers publish everyday on the Internet!
Hi,
congrats for the great job and your dedication to this!!
I guess the code should compile using Arduino IDE, if so, it halts in setup() at “configure the wifi connection and start the interface” with error: C99 designator ‘ssid’ outside aggregate initializer.
The only thing I did is copy/paste in the IDE added void loop() and changed the main to setup().
Is it correct?
Thanks again.
Hi Emilio, this code (and in general my tutorials) are not meant to be used with Arduino IDE but with the official esp-idf framework.
Ciao Luca, per iniziare ti faccio i complimenti per il grande lavoro che hai fatto.
Ho un modulo ESP-WROOM-32 sul quale ho provato un po’ di esempi dell’ambiente ESP-IDF e funzionano.
Ho provato a compilare il tuo esempio sulla connessione wifi, ma appena riavvio l’esp dopo l’upload, sul monitor vedo a rotazione sempre questi messaggi:
[…]
ciao Stefano, ho rivisto recentemente tutti i sorgenti per renderli compatibili con le nuove versioni di esp-idf… prova nuovamente prendendo l’esempio da Github
Risolto.
Alla fine ho cambiato modulo e quest’ultimo funziona.
Dovrò approfondire perchè l’altro da quei problemi
Grazie.
Ciao Luca,
mi spieghi la necessità di
while (1)
{
vTaskDelay(1000 / portTICK_RATE_MS);
}
in mai_task, ho provato a toglierlo, ma non si collega più alla rete wifi. Non capisco il perchè.
Grazie
ciao, serve per mettere in “pausa” quel task in modo che FreeRTOS possa eseguire anche gli altri tasks del programma, tra i quali quello che si occupa proprio della connessione wifi
Very well put!!!!! Nice approach!!!