Getting started with ESP32 firmware development

· 5min · Peter Browning
Table of Contents

An ESP32 is a small and cheap SoC with native WiFi and Bluetooth (with LE) capabilities. They come in all flavors and sizes, but typically they look something like this (in my hand for size reference):

1

This specific version I have is a chinese knockoff of this model that I found on aliexpress. I bought a pack of 10 of them for around ~70 USD. It uses the ESP32-WROOM-32D chip, and has a USB-C connector as well an integrated LED pinned to GPIO-2 (Labelled D2 on the board). This is very useful for testing.

Anyways, when writing code for the ESP32, there are 4 distinct paths that you can choose to go down.

  1. Write C code using esp-idf
  2. Write micropython code
  3. Write C++ code with C externs and callbacks (also with esp-idf)
  4. Write Arduino code

The purpose of this article is to go over the strengths and weaknesses of each method to decide which may be best for you.

Standard C

Writing in C is a great option for firmware development on this chip. The SDK, esp-idf, is well documented and robust. Writing in C however means manual memory management, difficult to read code, and a non-objective programming paradigm. What you do get with C is efficient code and direct access to the hardware. Here's is a simple hello-world type blink example using C code:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "led_strip.h"
#include "sdkconfig.h"

static const char *TAG = "example";
#define BLINK_GPIO 2
static uint8_t s_led_state = 0;

static void blink_led(void) {
    gpio_set_level(BLINK_GPIO, s_led_state);
}

static void configure_led(void) {
    ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
    gpio_reset_pin(BLINK_GPIO);
    gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}

void app_main(void) {
    configure_led();
    while (1) {
        ESP_LOGI(TAG, "Turning the LED %s!", s_led_state == true ? "ON" : "OFF");
        blink_led();
        s_led_state = !s_led_state;
        vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS);
    }

}

Micropython

Micropython is a port of CPython meant to be run on SoC like ESP32. Think of it sort of like a subset of traditional Python 3. To get this working, you'll need to flash a provided Micropython firmware onto the chip, which then provides you with an interactive REPL through a UART connection. It is also possible to use a tool called adafruit-ampy to copy files onto the filesystem to have them run at chip boot instead of simply spawning an REPL, but you can do your own research on that if you'd like.

I have mixed feelings on Micropython. On one hand, it is by far (for me) the easiest way to program an ESP32 to do simple tasks such as make http requests. However, it is limited in hardware access, highly reliant on prebuilt packages, and due to it needing an interpreter (the Micropython firmware), also is very limited in the size of programs and memory it has available.

Here's the same blink example, written in Micropython:

import machine
import time
led = machine.Pin(2, machine.Pin.OUT)
while True:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)

As you can see, it is incredibly simple to read and understand, which makes it very appealing to new users.

C++

This is a strange one. Due to C++ being a superset of C by design, it is possible to write code in C++ and have it use esp-idf in the same way that normal C does. Unfortunately, there are a few things that should be noted.

  • C++ dynamic memory management can be a downside due to the limited resources on board
  • You'll need to use extern statements to integrate with esp-idf
  • Things get finnicky when mixing the two languages

Here's the same blink example, written in C++:

#ifdef __cplusplus
extern "C" {
#endif
#include <esp_log.h>
#include <string>

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#ifdef __cplusplus
}
#endif
static char tag[] = "cpp_helloworld";

extern "C"
{
	void app_main(void);
}

class Blink
{
  public:
	Blink(gpio_num_t blinkPin)
	{
		this->blinkPin = blinkPin;
		gpio_pad_select_gpio(blinkPin);
		gpio_set_direction(blinkPin, GPIO_MODE_OUTPUT);
	}

	void blinkTimes(int x)
	{
		ESP_LOGD(tag, "Blink Times %d", x);

		int delayMs = 50;
		int level = gpio_get_level(blinkPin);
		gpio_set_level(blinkPin, 0);
		vTaskDelay(1000 / delayMs);
		for (int i = 0; i < x; i++)
		{
			gpio_set_level(blinkPin, 1);
			vTaskDelay(1000 / delayMs);
			gpio_set_level(blinkPin, 0);
			vTaskDelay(1000 / delayMs);
		}
		gpio_set_level(blinkPin, level);
	}

  private:
	gpio_num_t blinkPin = GPIO_NUM_2;
};

void app_main(void)
{
	vTaskDelay(1);
	Blink blink(GPIO_NUM_2);
	blink.blinkTimes(5);
}

Arduino

Arduino is strange, and I still don't fully understand it. Arduino as a language is a wrapper and abstraction layer put over C. The great part about arduino is it's extensive libraries and support, as well as it being extremely simple. The arduino core provides almost all of the capabilities of esp-idf, with the nice and easy to read syntax of arduino. The downsides of this are fairly abstract and a little contentious. The main argument I've seen against using arduino to write ESP32 code is that it can eventually become a burden when writing extensive code, usually revolving around an argument against the setup() and loop() functions.

Here's the same example, but in arduino:

const int ledPin = 2;

void setup() {
  pinMode (ledPin, OUTPUT);
}

void loop() {
  digitalWrite (ledPin, HIGH);
  delay(1000);
  digitalWrite (ledPin, LOW);
  delay(1000);
}

TL;DR

  • Writing in C is the manly and cool way to do things", but is a pain in the ass.
  • Writing in C++ is the same as C, but for people who are "too cool for normal C".
  • Writing in micropython is great for small and simple projects, like making a quick HTTP request.
  • Writing in Arduino is mainly the same as C, but without the manly and cool aspects, and is immensely easier to use plus has a larger community.