ESP32 (4) – Flash, bootloader and FreeRTOS

by luca

In the previous posts I’ve described the new esp32 chip and explained how to setup the development environment, including the use of one – optional – graphical IDE, Eclipse.

Before starting to develop your programs, you need to understand three key components of the framework: flash, bootloader and FreeRTOS operating system. But don’t worry! At the end of this post you’ll write, compile and execute your first program (even if “minimal”) and starting from the next tutorial we’ll work together on more complex examples.


As I wrote in the first post of this tutorial, the esp32 chip requires an external flash memory to store programs, data, configuration parameters…

The external memory is connected to the chip via the SPI bus and the supported capacity is up to 16Mb. The official Espressif module (ESP-WROOM-32) includes a 4Mb flash memory made by GigaDevice (GD25Q32):


The flash memory can store different elements: programs, data… hence it’s divided into sections (partitions). The list partitions, their size and position within the flash memory is stored in the memory itself (at address 0x8000) and it’s called partition table.

Two partition types are defined by default:


  • app (type 0) – partition that contains an application (program)
  • data (type 1) – partition that contains data


When writing a new program, the developer can decide how to organize the flash memory based on the program’s specific needs. The esp-idf framework offers two pre-configured memory layouts, you can choose from the configuration menu:


You can also show the project’s partition table with the make partition_table command:


In the screenshot above, you can see the partition table layout that corresponds to the simplest configuration (Single factory app, no OTA):

  • a data partition (named “nvs“) with a size of 24Kb starting from address 0x9000
  • a data partition (named “phy_init“) with a size of 4Kb starting from address 0xf000
  • an app partition (named “factory“) with a size of 1Mb starting from address 0x10000


To better understand why the flash memory is divided into partitions, I have to describe you how the esp32 bootloader works, that is how the application is started and how a new firmware is uploaded.

In the esp32 ROM memory there’s a small program, named first-stage bootloader. This program is executed at each reset of the chip: it configures the access to the external flash memory and, if required, stores on it new data coming from the serial/USB port (flash process). Once finished, it accesses the flash memory (at address 0x1000) and loads and executes the second-stage bootloader.

Even if the standard behavior of the esp-idf framework is to use this second-stage bootloader, you can also develop a stand-alone application that, stored at address 0x8000 of the flash memory, is directly executed by the first-stage bootloader.

The second-stage bootloader reads the partition table at address 0x8000 and searches for app partitions. It decides which application has to be executed based on the content of the otadata partition: if this partition is empty or doesn’t exist, the bootloaded executes the application stored in the factory partition. This allows to implement an over-the-air (OTA) application update process: you send the new version of your application to the esp32 chip; the version is stored in a new app partition. Once the upload is completed, the id of the partition is saved in otadata and the chip is rebooted; the bootloader will execute the new version:



The esp-idf framework is based on the FreeRTOS Real-Time Operating System. It may sound strange to talk about operating systems when working on a “small” chip like the esp32… but you don’t have to think that FreeRTOS is an operating system like Windows, Linux or MacOS. The main feature an embedded operating system offers – thanks to its internal scheduler, is multitasking, that is the ability to run different tasks in parallel. We know that a microprocessor core can execute an instruction at a time: it seems that different applications run simultaneously because of the scheduler is rapidly switching between their tasks.


different task statuses in FreeRTOS

A real-time operating system is design to guarantee that task scheduling is deterministic: you can predict the behavior of its scheduler. FreeRTOS allows the developer to define a priority for each task: the scheduler uses priority values to define the execution pattern of the different tasks.

Our first program

Let’s write our first program that used the esp-idf framework. This program, available in my Github repository, will be the skeleton for all the next tutorials.

First we must include some libraries:


The stdio.h library (Standard I/O) is used for input/output functions (printf…) and the two freertos header files are used to define and execute the different tasks.

Every program is executed starting from the app_main() function:


In the app_main() a new task is created using the xTaskCreate method. This method requires the following parameters: the pointer to the function (&loop_task) that contains task code, the name of the task (“loop_task”), the size, in words, of the stack memory to be assigned to the task (512), additional parameters for the task (NULL), the task’s priority (5) and the pointer – optional – to retrieve an handler of the task (NULL).

Once the task is created, the FreeRTOS scheduler executes it based on the different tasks and priorities.

The task has the following code:


it’s a continuous loop that every second prints the sentence Hello World!”. The vTaskDelay method pauses the task for the specified number of ticks. The portTICK_RATE_MS constant defines the duration, in milliseconds, of a tick; if you therefore divide 1000 for that constant you get the number of ticks in a second.

Additional files

To be able to compile your project, you need to add a couple of additional files:

  • a Makefile in the main folder of your project that contains the name of the project and an include for the main makefile of the framework:


  • file – it can be empty – in the folder where the source code is saved into to tell the compiler to process the files in that folder:



When the program is ready, you can compile and load it on the board as explained in a previous post:

make flash

If everything is ok, when you connect to the board with a serial emulator you should see:



Related Posts


Mauro Saturday December 24th, 2016 - 06:53 PM

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


Michele Wednesday January 4th, 2017 - 03:20 PM

Ottimo tutorial!!!!! Complimenti e molto molto interessante!! …. spero in altri articoli su esp32

luca Monday January 9th, 2017 - 08:27 AM

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 😉

Roberto Monday January 23rd, 2017 - 12:56 AM

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?

luca Monday January 23rd, 2017 - 08:40 AM

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.

Reply – ESP32 (19) – NVS Saturday June 3rd, 2017 - 10:25 AM

[…] ricordate, in un precedente articolo vi ho parlato di come viene organizzata la memoria flash esterna al chip esp32. Il compito […]

flyword Friday September 8th, 2017 - 01:38 PM

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??

luca Friday September 8th, 2017 - 11:47 PM

Hi, that parameter is the stack size, so it depends on how much memory (= variables…) the task will allocate

Reply – ESP32 (22) – SPIFFS Saturday September 30th, 2017 - 09:57 AM

[…] già imparato dal mio quarto tutorial sul chip esp32 che la memoria flash collegata al chip viene suddivisa in partizioni, per poter […]

arun Friday February 23rd, 2018 - 06:19 PM

hi, you have given marvellous explaination.
Thank a lot,

aschmitt Friday June 22nd, 2018 - 09:15 PM

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 !

luca Tuesday June 26th, 2018 - 04:08 PM

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.

Francesco Saturday August 4th, 2018 - 08:24 AM

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.

luca Saturday August 4th, 2018 - 01:20 PM

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.

Zeni Friday January 11th, 2019 - 02:22 PM

Dear luca, thanks for such a great tutorial site for ESP32. Lot of things are more clear to me because of these tutorials.

Igor Wednesday January 30th, 2019 - 09:43 PM

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.

luca Friday February 1st, 2019 - 02:34 PM

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.

Kevin Friday June 28th, 2019 - 09:36 PM

Luca, Is there a way to directly write to the NVS on the ESP32 without running a sketch?

luca Wednesday July 17th, 2019 - 10:06 AM

yes, there’s a specific tool to prepare a binary image that can be directly written to the NVS partition.

Alessandro Wednesday May 13th, 2020 - 09:21 AM

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..

luca Monday June 1st, 2020 - 10:56 AM

ciao Alessandro, si corretto!


Leave a Reply to Kevin Cancel Reply

1 × one =