Photo by Kevin Ku on Unsplash

6 Strategies for Learning ESP-IDF (without going insane)

If you try to write firmware for ESP32 microcontroller, you will have two popular choices:

  1. Arduino framework
  2. ESP-IDF

Arduino is easier. And you probably have good experience with AVR-based Arduino boards (like UNO, Nano, Mega2560, etc.), so writing firmware for ESP32 using Arduino is seamless.

You already know the basics.

But because Arduino framework is designed to be simple, lots of concepts are hidden from you. When you work with a slightly more complex project, you will quickly realize that Arduino won't cut it.

For example, let's say you want to build a BLE scanner using ESP32.

The goal is simple: to scan nearby BLE devices as fast as possible, if the scanning process is too slow, you will miss some BLE devices. It's often required if you want to track BLE device movements.

Now, you can scan BLE devices using Arduino framework, yes. But it will be noticeably slower. So, there's no other choice: use ESP-IDF for better performance.

However, using ESP-IDF is not without downsides. Just take a look at this code comparison below:

Left: Arduino code. Right: ESP-IDF example code for ADC

Both essentially do the same thing: sample analog signal, and printout the value.

But the right side is hard to follow, especially if you're not familiar with ESP-IDF. There are too many things to unpack here:

  • Okay I know ADC. But what's that oneshot thing?
  • What ESP_ERROR_CHECK will do?
  • Why ADC needs calibration?
  • Wait, there are TWO ADCs inside ESP32?
  • Why there's so many #include?
  • Why it's called vTaskDelay() and not just delay()?

You see, there are many concepts hidden in Arduino framework. No wonder people said they struggle to learn ESP-IDF.

So, I will share with you my 6 strategies (plus 1 bonus strategy) to learn ESP-IDF without going insane.

Strategy #1: Hone Your C Skill, Especially Function Pointer and Callback

ESP-IDF is written in C. It's actually a good thing because as a framework, it should ensure interoperability with many other languages. C is the lowest common denominator for embedded programming.

ESP-IDF can be sometimes heavy on OOP concepts, and because it's written in C, the framework uses lots of function pointers and callback and pass-by-reference to mimic C++ OOP.

You can be confused if you don't understand the basics of function pointer and callback in C.

Strategy #2: Think in Subsystem, not Linear Thinking

I'm sorry for saying jargon.

Basically by thinking in subsystem, you focus your attention on different parts of the system and how all those parts interact and influence each other.

Linear thinking is when you think step-by-step, like BASIC or Assembly programming. It's procedural thinking.

ESP-IDF can sometimes be heavy on event-driven as well. So System thinking will be extremely helpful for you. Now let me give you an example.

Let's check the ESP-IDF example code for WiFi:

it's just a code to initialize the ESP32 WiFi to act as an Access Point.

Notice that ugly esp_event_handler_instance_register function?

It's responsible for adding event handling to the API. Whenever the WiFi state changes (like something connected, or disconnected), the function wifi_event_handler (the callback function) will be called later. And here is the function implementation:

Don't worry about those alien macros and structs yet.

Now, the question is. When exactly this function will be executed? Like how many seconds after bootup?

The answer is we don't know. It's the nature of event-driven programming. The function will only be executed only if the WiFi state is changed.

That's why we can't rely on linear thinking, because we would be confused as hell. With system thinking, we will understand that the WiFi event will interact with our wifi_event_handler function later.

ESP-IDF has an insanely large amount of event-driven programming, especially if you work with WiFi and Bluetooth.

So, force yourself really hard in system thinking, not procedural/linear thinking.

Strategy #3: Use VSCode 'Goto Definition' Feature

ESP-IDF encapsulates many data formats and structures inside struct, and sometimes we want to know the full structure of the datatype. Look at this GPIO example code:

If I want to know the exact structure of gpio_config_t, I could look it up from the documentation, but that's tedious!

Instead, I just right-click it and select 'Goto Definition'. It's enough for me to understand the data structure. But if I want more explanation, then I'll go to the official docs.

That way, I'll understand the API context much easier and faster.

Strategy #4: Go back to the theory!

I'll admit, the reason why ESP-IDF seems hard to learn is because it's actually more complex. And by more complex, I mean the framework doesn't hide many details in the code (unlike Arduino framework).

Even if we open the example code, it still feels overwhelming.

Remember, ESP-IDF provides example projects, not tutorialsMind the difference!

The goal is to provide as many details as possible, covering many use cases. Not being tutorials, ESP-IDF expects you to be familiar with lots of terms before.

For example, just open the example code for BLE. It's complex:

What you see above is the parameter for BLE payload advertisement.

Confused with BLE advertisement? Why would the BLE device advertise something?

You won't understand the code if you don't understand the theory behind BLE standard. There are tons of terms and jargon, so you need to be familiar with:

  • BLE advertisements
  • BLE scanning
  • GATT profile
  • BLE Services
  • BLE architecture
  • BLE software stack (nimble vs. Bluedroid)
  • etc.

If you work with ADC, then you need to understand first:

  • Different types of ADC (SAR, Sigma-Delta, Dual-slope ADC, etc.)
  • Bit-width
  • Sampling rate
  • Sampling mode (continuous vs. oneshot)
  • ADC resolution and accuracy
  • Quantization
  • Reference Voltage
  • Calibration and compensation (temperature etc.)

If you never heard most of the concepts above, no wonder you will struggle to understand ESP-IDF because it assumes you're already familiar with them.

So, go back to your textbooks and reference manuals from major semiconductor vendors.

Strategy #5: Don't Create Project from Scratch, Copy from Example Code Instead

Building firmware is hard enough, so don't torture yourself by creating projects from scratch.

Use example code. Pick one that's closest to your needs. For example, if you want to create firmware to interface I2C EEPROM, don't write from zero. ESP-IDF already provides that.

Use it.

ESP-IDF license should be good for you to copy their code (It's not legal advice though).

Strategy #6: Read ESP-IDF docs over and over again

This is the most obvious tip.

You need to read the documentation over and over again.

Because first read you won't understand a thing.

But second read you will pick up some more details.

The third read you will understand the context and the nuance.

The fourth read you're already familiar where to look for extra details.

The fifth read you will become more and more comfortable with the ESP-IDF inside out.

But that requires time!

Duh? In which world learning doesn't require time?

Bonus Strategy: Learn FreeRTOS concepts

This is probably an underrated strategy.

You should understand that ESP-IDF uses FreeRTOS APIs almost all the time.

So, you need to also familiar with FreeRTOS concepts to understand ESP-IDF code.

  • Task management (creation, delete, suspend, resume)
  • Queue
  • Semaphore
  • Mutex
  • Event Group
  • Notification
  • etc.

Good luck with learning ESP-IDF!


Whenever you're ready, there are 2 ways I can help you:

1. Professional Firmware Development Guide. If you're looking to build professional-grade firmware, I share 6+ years of expertise developing firmware. This guide shows you the exact workflow I use to build high-quality firmware for my company and my freelance clients.

2. Arduino Hobbyist into Pro Embedded Engineer Book. I'm writing a book to help Arduino hobbyists turn themselves into Pro Embedded Engineers.