PlatformIO

How to use the ESP32 DAC sine/cosine waveform generator using Arduino / PlatformIO

The ESP32 and its derivatives such as the ESP32-S2 have a built-in sine/cosine waveform generator for the built-in 8-bit DAC.

Using it requires ESP-IDF v5.1+ (see the official example). Using it with Arduino is slightly harder, since the stable version of the arduino-esp32 framework at the time of writing this post is based on ESP-IDF v4.4 which does not provide the DAC cosine generator API.

Therefore, we have to explicitly specify the arduino-espressif32 version (git commit) in platformio.ini:

[env:esp32dev]
platform = espressif32
# Commit f9cddfde697b659b9e818ec514f1505d2bd4a8ae is branch esp-idf-v5.1-libs @2022-02-01
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#f9cddfde697b659b9e818ec514f1505d2bd4a8ae
board = esp32dev
framework = arduino

The example main source code is pretty simple:

#include <Arduino.h>
#include <driver/dac_cosine.h>

void setup() {
    dac_cosine_handle_t chan0_handle;
    dac_cosine_config_t cos0_cfg = {
        .chan_id = DAC_CHAN_1, // GPIO26
        .freq_hz = 1000,
        .clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
        .atten = DAC_COSINE_ATTEN_DEFAULT,  
        .phase = DAC_COSINE_PHASE_0,  
        .offset = 0,
        //.flags.force_set_freq = false,
    };
    ESP_ERROR_CHECK(dac_cosine_new_channel(&cos0_cfg, &chan0_handle));
    ESP_ERROR_CHECK(dac_cosine_start(chan0_handle));
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}

If you want to see how the generated waveform looks on an oscilloscope, see How does the ESP32 DAC cosine generator waveform look on an Oscilloscope?

Posted by Uli Köhler in Arduino, ESP8266/ESP32, PlatformIO

Minimal PlatformIO ESP32 ArduinoOTA example

Based on our Minimal PlatformIO ESP8266 ArduinoOTA example, this is a minimal starting point for your ESP32 program running ArduinoOTA.

#include <Arduino.h>
#include <WiFi.h>
#include <ArduinoOTA.h>


void setup() {
  Serial.begin(115200);
  /**
   * Connect to Wifi
   */
  WiFi.begin("MyWifi", "abc123abc");
  uint32_t notConnectedCounter = 0;
  while (WiFi.status() != WL_CONNECTED) {
      delay(100);
      Serial.println("Wifi connecting...");
      notConnectedCounter++;
      if(notConnectedCounter > 150) { // Reset board if not connected after 15s
          Serial.println("Resetting due to Wifi not connecting...");
          ESP.restart();
      }
  }
  Serial.print("Wifi connected, IP address: ");
  Serial.println(WiFi.localIP());
  /**
   * Enable OTA update
   */
  ArduinoOTA.begin();
}

void loop() {
  // Check for over the air update request and (if present) flash it
  ArduinoOTA.handle();
}

platformio.ini

You can leave your platformio.ini at default values, the only aspect you need to change is to set monitor_speed = 115200

[env:esp32dev]
board = esp32dev
platform = espressif32
framework = arduino
monitor_speed = 115200
Posted by Uli Köhler in ESP8266/ESP32, PlatformIO

ESP32 minimal heap_caps_print_heap_info() example (PlatformIO/Arduino)

On the ESP32, you can use heap_caps_print_heap_info() to print information to the serial port about how much memory is free on the heap (plus other details such as the largest free block).

#include <esp_heap_caps.h>

void setup() {
}

void loop() {
  heap_caps_print_heap_info(MALLOC_CAP_8BIT);
}

Example output

Heap summary for capabilities 0x00000004:
  At 0x3ffb8000 len 6688 free 0 allocated 4404 min_free 0
    largest_free_block 0 alloc_blocks 8 free_blocks 0 total_blocks 8
  At 0x3ffb0000 len 25480 free 0 allocated 22204 min_free 0
    largest_free_block 0 alloc_blocks 70 free_blocks 0 total_blocks 70
  At 0x3ffae6e0 len 6192 free 8 allocated 3860 min_free 8
    largest_free_block 0 alloc_blocks 10 free_blocks 1 total_blocks 11
  At 0x3ffb6388 len 7288 free 0 allocated 4524 min_free 0
    largest_free_block 0 alloc_blocks 38 free_blocks 0 total_blocks 38
  At 0x3ffb9a20 len 16648 free 8 allocated 13964 min_free 0
    largest_free_block 0 alloc_blocks 32 free_blocks 1 total_blocks 33
  At 0x3ffcc5d0 len 80432 free 8 allocated 73140 min_free 8
    largest_free_block 0 alloc_blocks 320 free_blocks 1 total_blocks 321
  At 0x3ffe0440 len 15072 free 0 allocated 12260 min_free 0
    largest_free_block 0 alloc_blocks 41 free_blocks 0 total_blocks 41
  At 0x3ffe4350 len 113840 free 18440 allocated 90724 min_free 2560
    largest_free_block 7796 alloc_blocks 157 free_blocks 12 total_blocks 169
  Totals:
    free 18464 allocated 225080 min_free 2576 largest_free_block 7796

 

Posted by Uli Köhler in Arduino, ESP8266/ESP32, PlatformIO

FreeRTOS task queue with static memory (xQueueCreateStatic) minimal example

Also see our previous post FreeRTOS task queue minimal example which also has examples for how to send & receive with a queue. The post you’re currently viewing is just about xQueueCreateStatic()

enum class MQTTTaskType : uint8_t {
    SendStatus = 0,
    SendInfo
};

// This struct will be inserted into the queue
struct MQTTTask {
    MQTTTaskType task; // The type of work that is requested from the received
    // TODO add your custom fields here if requred
};

constexpr size_t MQTT_TASK_QUEUE_LENGTH = 6;
static QueueHandle_t mqttTaskQueue;
static StaticQueue_t mqttTaskQueueStatic;
static uint8_t mqttTaskQueueStorageArea[ MQTT_TASK_QUEUE_LENGTH * sizeof(MQTTTask) ];

void setup() {
    // Create task queue
    mqttTaskQueue = xQueueCreateStatic( MQTT_TASK_QUEUE_LENGTH,
                                 sizeof(MQTTTask),
                                 mqttTaskQueueStorageArea,
                                 &mqttTaskQueueStatic );
}

 

Posted by Uli Köhler in C/C++, Embedded, FreeRTOS, PlatformIO

How to fix ESP32 FreeRTOS error: too few arguments to function ‘void vPortEnterCritical(portMUX_TYPE*)’

Problem:

On FreeRTOS on the ESP32, you want to use a critical zone like this:

portENTER_CRITICAL();
// Your critical code goes here!
portEXIT_CRITICAL();

but while compiling the procject, you see an error message like

src/main.cpp: In function 'void MyFunc(size_t, int16_t)':
/home/uli/.platformio/packages/[email protected]d/tools/sdk/esp32/include/freertos/port/xtensa/include/freertos/portmacro.h:476:75: error: too few arguments to function 'void vPortEnterCritical(portMUX_TYPE*)'
 #define portENTER_CRITICAL(mux)                     vPortEnterCritical(mux)

Solution:

You need to use portENTER_CRITICAL() and portEXIT_CRITICAL() with a spinlock, i.e.

portENTER_CRITICAL(&mySpinlock);
// TODO Your critical code goes here!
portEXIT_CRITICAL(&mySpinlock);

In order to see a full example on how to initialize a spinlock in FreeRTOS and use it for critical zones, see our previous post ESP32 critical zone example using FreeRTOS / PlatformIO

Posted by Uli Köhler in Arduino, ESP8266/ESP32, FreeRTOS, PlatformIO

ESP32 critical zone example using FreeRTOS / PlatformIO

In order to enter a critical zone on the ESP32 using FreeRTOS, you have to do the following:

Globally declare a spinlock:

portMUX_TYPE mySpinlock;

In setup(), initialize the spinlock:

spinlock_initialize(&mySpinlock);

Now, wherever you want to enter a critical zone, run:

portENTER_CRITICAL(&mySpinlock);
// TODO Your critical code goes here!
portEXIT_CRITICAL(&mySpinlock);

When using this in an interrupt handler, use this instead:

portENTER_CRITICAL_ISR(&mySpinlock);
// TODO Your critical code goes here!
portEXIT_CRITICAL_ISR(&mySpinlock);

 

FreeRTOS will ensure that no two threads using mySpinlock are run at the same time.

Posted by Uli Köhler in Arduino, ESP8266/ESP32, FreeRTOS, PlatformIO

How I fixed PlatformIO Arduino portGET_ARGUMENT_COUNT() result does not match for 0 arguments

Problem:

When trying to compile your Arduino PlatformIO project, you see multiple error messages like the following.

In file included from /home/uli/.platformio/packages/toolchain-xtensa-esp32/xtensa-esp32-elf/sys-include/stdlib.h:19,
                 from /home/uli/.platformio/packages/toolchain-xtensa-esp32/xtensa-esp32-elf/include/c++/8.4.0/cstdlib:75,
                 from /home/uli/.platformio/packages/toolchain-xtensa-esp32/xtensa-esp32-elf/include/c++/8.4.0/stdlib.h:36,
                 from /home/uli/.platformio/packages/framework-arduinoespressif32/cores/esp32/WString.h:26,
                 from /home/uli/.platformio/packages/framework-arduinoespressif32/cores/esp32/Print.h:26,
                 from /home/uli/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFi.h:27,
                 from /home/uli/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src/WiFi.cpp:24:
/home/uli/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/port/xtensa/include/freertos/portmacro.h:717:41: error: static assertion failed: portGET_ARGUMENT_COUNT() result does not match for 0 arguments
 _Static_assert(portGET_ARGUMENT_COUNT() == 0, "portGET_ARGUMENT_COUNT() result does not match for 0 arguments");

Solution:

For me, the solution was as follows. I had

build_flags = --std=c++17

in my platformio.ini. Replacing it by

build_flags = --std=gnu++17

fixed the issue for me.

Posted by Uli Köhler in Arduino, C/C++, PlatformIO

How to generate sin and cos waves using the LECD PWM on the STM32

Based on our previous post How to generate PWM output representing a sine wave on the ESP32 (Arduino/PlatformIO) this post uses two different IO pins to generate both a sine and a cosine wave dynamically.

#include <Arduino.h>
#include <driver/ledc.h>

void setup() {
    Serial.begin(115200);

    ledcSetup(LEDC_CHANNEL_0, 10000 /* Hz */, 12);
    ledcSetup(LEDC_CHANNEL_1, 10000 /* Hz */, 12);

    ledcAttachPin(GPIO_NUM_32, LEDC_CHANNEL_0);
    ledcAttachPin(GPIO_NUM_25, LEDC_CHANNEL_1);
}

/**
 * @brief Calculate the PWM duty cycle (assuming 12 bits resolution) of a sine wave of
 * given frequency. micros() is used as a timebase
 * 
 * @param frequency The frequency in Hz
 * @return int the corresponding 12-bit PWM value
 */
int sinePWMValue(float frequency, int maxPWMValue, float (*sinCos)(float)) {
  unsigned long currentMicros = micros(); // get the current time in microseconds

  // calculate the sine wave value for the current time
  int halfMax = maxPWMValue/2;
  int sineValue = halfMax + (halfMax-10) * sinCos(2 * PI * currentMicros / (1000000 / frequency));
  return sineValue;
}

void loop() {
    // Example of how to change the duty cycle to 25%
    ledcWrite(LEDC_CHANNEL_0, sinePWMValue(1.0, 4096, sinf));
    ledcWrite(LEDC_CHANNEL_1, sinePWMValue(1.0, 4096, cosf));
}

The output, filtered by a 4th order Salley-Key filter each (using the LM324) looks like this:

Posted by Uli Köhler in Analog, Arduino, Electronics, ESP8266/ESP32, PlatformIO

How to generate PWM output representing a sine wave on the ESP32 (Arduino/PlatformIO)

The following function will compute the value of a sine wave using micros() as a timebase, with adjustable frequency. It is hardcoded to expect a 12 bit resolution PWM

/**
 * @brief Calculate the PWM duty cycle (assuming 12 bits resolution) of a sine wave of
 * given frequency. micros() is used as a timebase
 * 
 * @param frequency The frequency in Hz
 * @return int the corresponding 12-bit PWM value
 */
int sinePWMValue(float frequency) {
  unsigned long currentMicros = micros(); // get the current time in microseconds

  // calculate the sine wave value for the current time
  int sineValue = 2048 + 2047 * sin(2 * PI * currentMicros / (1000000 / frequency));
  return sineValue;
}

Based on this, we can use the basic code of our previous post ESP32 minimal Arduino PWM output example (PlatformIO) to generate a 1Hz sine wave (represented by a 10kHz PWM):

#include <Arduino.h>
#include <driver/ledc.h>

void setup() {
    Serial.begin(115200);

    ledcSetup(LEDC_CHANNEL_0, 10000 /* Hz */, 12);

    ledcAttachPin(GPIO_NUM_14, LEDC_CHANNEL_0);
    ledcWrite(LEDC_CHANNEL_0, 2048); // 50%
}

/**
 * @brief Calculate the PWM duty cycle (assuming 12 bits resolution) of a sine wave of
 * given frequency. micros() is used as a timebase
 * 
 * @param frequency The frequency in Hz
 * @return int the corresponding 12-bit PWM value
 */
int sinePWMValue(float frequency) {
  unsigned long currentMicros = micros(); // get the current time in microseconds

  // calculate the sine wave value for the current time
  int sineValue = 2048 + 2047 * sin(2 * PI * currentMicros / (1000000 / frequency));
  return sineValue;
}

void loop() {
    // Example of how to change the duty cycle to 25%
    ledcWrite(LEDC_CHANNEL_0, sinePWMValue(1.0));
}

 

Posted by Uli Köhler in Arduino, ESP8266/ESP32, PlatformIO

ESP32 minimal Arduino PWM output example (PlatformIO)

#include <Arduino.h>
#include <driver/ledc.h>

void setup() {
    Serial.begin(115200);

    ledcSetup(LEDC_CHANNEL_0, 10000 /* Hz */, 12);

    ledcAttachPin(GPIO_NUM_14, LEDC_CHANNEL_0);
    ledcWrite(LEDC_CHANNEL_0, 2048); // 50%
}

void loop() {
    // Example of how to change the duty cycle to 25%
    ledcWrite(LEDC_CHANNEL_0, 1024);
}

 

Posted by Uli Köhler in Arduino, ESP8266/ESP32, PlatformIO

ESP32 minimal LEDC PWM configuration example on PlatformIO/Arduino using ESP-IDF LEDC API

This example configures the LEDC PWM timer at 10 kHz with 12 bit resolution, outputting a 50% duty cycle PWM on IO14. This code uses the ESP-IDF API directly in order to configure the PWM

#include <Arduino.h>
#include <driver/ledc.h>

void setup() {
    Serial.begin(115200);

    ledc_timer_config_t ledc_timer = {
        .speed_mode       = LEDC_HIGH_SPEED_MODE,
        .duty_resolution  = LEDC_TIMER_12_BIT,
        .timer_num        = LEDC_TIMER_0,
        .freq_hz          = 10000,
        .clk_cfg          = LEDC_AUTO_CLK
    };
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

    ledc_channel_config_t ledc_channel = {
        .gpio_num       = GPIO_NUM_14,
        .speed_mode     = LEDC_HIGH_SPEED_MODE,
        .channel        = LEDC_CHANNEL_0,
        .intr_type      = LEDC_INTR_DISABLE,
        .timer_sel      = LEDC_TIMER_0,
        .duty           = 2048, // Set duty to 50%
        .hpoint         = 0
    };
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}

void loop() {
}

 

Posted by Uli Köhler in Arduino, ESP8266/ESP32, PlatformIO

ESP32 PlatformIO minimal HTTP server example using ESP-IDF httpd_handle_t

This example provides a minimal HTTP server that answers Hello World using the ESP-IDF HTTP server library.

The following code is the HTTP server utility class:

#include <esp_http_server.h>

/**
 * HTTP server.
 * Works with ESP-IDF library internally.
 * 
 * Usage:
 * First call http.StartServer()
 * Then call http.RegisterHandler(...) for all handlers
 */
class HTTPServer {
public:
    HTTPServer(): conf(HTTPD_DEFAULT_CONFIG()) {
    }

    void StartServer() {
        if (httpd_start(&server, &conf) != ESP_OK) {
            ESP_LOGE("HTTP server", "Error starting server!");
        }
    }

    void RegisterHandler(const httpd_uri_t *uri_handler) {
        httpd_register_uri_handler(this->server, uri_handler);
    }

    httpd_handle_t server = nullptr;
    httpd_config_t conf;
};

Use like this:

http.StartServer();
http.RegisterHandler(&rootHandler);

for example with the following handler:

static const httpd_uri_t rootHandler = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = [](httpd_req_t *req) {
        httpd_resp_set_type(req, "text/html");
        httpd_resp_send(req, "<h1>Hello World!</h1>", HTTPD_RESP_USE_STRLEN);

        return ESP_OK;
    }
};

Full example code:

This example uses Arduino as framework.

#include <Arduino.h>
#include <WiFi.h>

#include <esp_http_server.h>

/**
 * HTTP server.
 * Works with ESP-IDF library internally.
 * 
 * Usage:
 * First call http.StartServer()
 * Then call http.RegisterHandler(...) for all handlers
 */
class HTTPServer {
public:
    HTTPServer(): conf(HTTPD_DEFAULT_CONFIG()) {
    }

    void StartServer() {
        if (httpd_start(&server, &conf) != ESP_OK) {
            ESP_LOGE("HTTP server", "Error starting server!");
        }
    }

    void RegisterHandler(const httpd_uri_t *uri_handler) {
        httpd_register_uri_handler(this->server, uri_handler);
    }

    httpd_handle_t server = nullptr;
    httpd_config_t conf;
};

static const httpd_uri_t rootHandler = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = [](httpd_req_t *req) {
        httpd_resp_set_type(req, "text/html");
        httpd_resp_send(req, "<h1>Hello World!</h1>", HTTPD_RESP_USE_STRLEN);

        return ESP_OK;
    }
};

HTTPServer http;

void setup() {
    Serial.begin(115200);
    WiFi.begin("MyWifi", "vaiceiShai0phie1cho5sahjeiquez");

    // Connect Wifi, restart if not connecting
    // https://techoverflow.net/2021/01/21/how-to-fix-esp32-not-connecting-to-the-wifi-network/
    uint32_t notConnectedCounter = 0;
    while (WiFi.status() != WL_CONNECTED) {
        delay(100);
        Serial.println("Wifi connecting...");
        notConnectedCounter++;
        if(notConnectedCounter > 300) { // Reset board if not connected after 30s
            Serial.println("Resetting due to Wifi not connecting...");
            ESP.restart();
        }
    }
    Serial.print("Wifi connected, IP address: ");
    Serial.println(WiFi.localIP());

    http.StartServer();
    http.RegisterHandler(&rootHandler);
}

void loop() {
    // Nothing to do here.
    delay(1000);
}
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

 

Posted by Uli Köhler in C/C++, ESP8266/ESP32, PlatformIO

ESP32 Arduino HTTPS webserver example using certificate from LittleFS

The following example reads a HTTPS/TLS certificate from LittleFS (cert.pem and privkey.pem – these are just standard X.509 PEM files) and uses those to initialize a HTTPS server. Most of this example is based on the esp-idf https_server example.

Our code to initialize the HTTPS server uses code from the following posts:

The core initialization sequence works like this:

// These need to be GLOBAL variables, they still need to exist
// after initialization!
std::string cert;
std::string privkey;

httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT();
httpd_handle_t server = nullptr;

void InitializeHTTPServer() {
        std::string cert = ReadFileToString("/cert.pem");
        std::string privkey = ReadFileToString("/privkey.pem");

        conf.cacert_pem = (const uint8_t*)this->cert.c_str();
        conf.cacert_len = this->cert.size() + 1;

        conf.prvtkey_pem = (const uint8_t*)this->privkey.c_str();
        conf.prvtkey_len = this->privkey.size() + 1;

        esp_err_t ret = httpd_ssl_start(&server, &conf);
        if (ESP_OK != ret) {
            ESP_LOGI(TAG, "Error starting server!");
            return;
        }
}

However it is typically far more convenient to use our HTTPSServer class:

class HTTPSServer {
public:

    HTTPSServer(): conf(HTTPD_SSL_CONFIG_DEFAULT()) {
    }

    void ReadCertificateInfoFromFilesystem() {
        cert = ReadFileToString("/cert.pem");
        privkey = ReadFileToString("/privkey.pem");
    }

    void StartServer() {
        // Start the httpd server
        ESP_LOGI(TAG, "Starting server");

        ReadCertificateInfoFromFilesystem();

        conf.cacert_pem = (const uint8_t*)this->cert.c_str();
        conf.cacert_len = this->cert.size() + 1;

        conf.prvtkey_pem = (const uint8_t*)this->privkey.c_str();
        conf.prvtkey_len = this->privkey.size() + 1;

        esp_err_t ret = httpd_ssl_start(&server, &conf);
        if (ESP_OK != ret) {
            ESP_LOGI(TAG, "Error starting server!");
            return;
        }
    }

    void RegisterHandler(const httpd_uri_t *uri_handler) {
        httpd_register_uri_handler(this->server, uri_handler);
    }

    httpd_handle_t server = nullptr;

    httpd_ssl_config_t conf;
    std::string cert;
    std::string privkey;
};

Usage example:

void setup() {
    InitFilesystem();

    // TODO setup wifi network

    httpsServer.StartServer();
    // Register your 
    httpsServer.RegisterHandler(&root);
}

Full example:

#include <Arduino.h>
#include <WiFi.h>
#include <LittleFS.h>
#include <string>
#include <esp_https_server.h>
#include "esp_tls.h"

//AsyncWebServer server(80);
static const char *TAG = "https-littlefs-example";

volatile bool filesystemOK = false;

void InitFilesystem() {
  // Initialize LittleFS
  if (!LittleFS.begin(false /* false: Do not format if mount failed */)) {
    Serial.println("Failed to mount LittleFS");
    if (!LittleFS.begin(true /* true: format */)) {
      Serial.println("Failed to format LittleFS");
    } else {
      Serial.println("LittleFS formatted successfully");
      filesystemOK = true;
    }
  } else { // Initial mount success
    filesystemOK = true;
  }
}

/* An HTTP GET handler */
static esp_err_t root_get_handler(httpd_req_t *req)
{
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, "<h1>Hello Secure World!</h1>", HTTPD_RESP_USE_STRLEN);

    return ESP_OK;
}

static const httpd_uri_t root = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = root_get_handler
};

size_t LittleFSFilesize(const char* filename) {
  auto file = LittleFS.open(filename, "r");
  size_t filesize = file.size();
  // Don't forget to clean up!
  file.close();
  return filesize;
}

std::string ReadFileToString(const char* filename) {
  auto file = LittleFS.open(filename, "r");
  size_t filesize = file.size();
  // Read into temporary Arduino String
  String data = file.readString();
  // Don't forget to clean up!
  file.close();
  return std::string(data.c_str(), data.length());
}

class HTTPSServer {
public:

    HTTPSServer(): conf(HTTPD_SSL_CONFIG_DEFAULT()) {
    }

    void ReadCertificateInfoFromFilesystem() {
        cert = ReadFileToString("/cert.pem");
        privkey = ReadFileToString("/privkey.pem");
    }

    void StartServer() {
        // Start the httpd server
        ESP_LOGI(TAG, "Starting server");

        ReadCertificateInfoFromFilesystem();

        conf.cacert_pem = (const uint8_t*)this->cert.c_str();
        conf.cacert_len = this->cert.size() + 1;

        conf.prvtkey_pem = (const uint8_t*)this->privkey.c_str();
        conf.prvtkey_len = this->privkey.size() + 1;

        esp_err_t ret = httpd_ssl_start(&server, &conf);
        if (ESP_OK != ret) {
            ESP_LOGI(TAG, "Error starting server!");
            return;
        }

        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &root);
        return;
    }

    void RegisterHandler(const httpd_uri_t *uri_handler) {
        httpd_register_uri_handler(this->server, uri_handler);
    }

    httpd_handle_t server = nullptr;

    httpd_ssl_config_t conf;
    std::string cert;
    std::string privkey;
};

HTTPSServer httpsServer;

void setup() {
    Serial.begin(115200);
    delay(500);

    InitFilesystem();

    // Connect Wifi, restart if not connecting
    // https://techoverflow.net/2021/01/21/how-to-fix-esp32-not-connecting-to-the-wifi-network/
    WiFi.begin("MyWifi", "Lobae1tie5achokef8riequohgohx");

    uint32_t notConnectedCounter = 0;
    while (WiFi.status() != WL_CONNECTED) {
        delay(100);
        Serial.println("Wifi connecting...");
        notConnectedCounter++;
        if(notConnectedCounter > 150) { // Reset board if not connected after 5s
            Serial.println("Resetting due to Wifi not connecting...");
            ESP.restart();
        }
    }
    Serial.print("Wifi connected, IP address: ");
    Serial.println(WiFi.localIP());

    httpsServer.StartServer();
    httpsServer.RegisterHandler(&root);
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}
[env:esp32dev]
platform = espressif32
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.5
board = esp32dev
framework = arduino
board_build.filesystem = littlefs
monitor_speed = 115200

 

Posted by Uli Köhler in Arduino, ESP8266/ESP32, Networking, PlatformIO

How to read LittleFS file to std::string (using Arduino/PlatformIO)

The following utility function reads . As a prerequisite, you need to initialize the LittleFS filesystem and configure PlatformIO to use LittleFS as filesystem when uploading the filesystem image.

NoteBefore using any of the functions below, you need to call InitFilesystem() in setup() in order to mount the filesystem. You can find this function in our previous post on how to initialize LittleFS.

#include <string>

std::string ReadFileToString(const char* filename) {
  auto file = LittleFS.open(filename, "r");
  size_t filesize = file.size();
  // Read into temporary Arduino String
  String data = file.readString();
  // Don't forget to clean up!
  file.close();
  return std::string(data.c_str(), data.length());
}

Example usage:

std::string cert = ReadFileToString("/cert.pem");

 

Posted by Uli Köhler in C/C++, ESP8266/ESP32, PlatformIO

How to configure PlatformIO for LittleFS (ESP32)

You can configure PlatformIO to use the LittleFS filesystem as opposed to the default SPIFFS using

board_build.filesystem = littlefs

If you are ussing arduino-esp32, you might also need to specify to use a recent arduino-esp32 framework version, but I suggest to first try it out without using that option:

platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.5

Full platformio.ini example

[env:esp32dev]
platform = espressif32
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.5
board = esp32dev
framework = arduino
board_build.filesystem = littlefs
monitor_speed = 115200

 

Posted by Uli Köhler in ESP8266/ESP32, PlatformIO

How to initialize LittleFS in Arduino on the ESP32 (PlatformIO)

In order to use the LittleFS library that comes with Arduino on the ESP32, use the following function

#include <LittleFS.h>

bool filesystemOK = false;

void InitFilesystem() {
  // Initialize LittleFS
  if (!LittleFS.begin(false /* false: Do not format if mount failed */)) {
    Serial.println("Failed to mount LittleFS");
    if (!LittleFS.begin(true /* true: format */)) {
      Serial.println("Failed to format LittleFS");
    } else {
      Serial.println("LittleFS formatted successfully");
      filesystemOK = true;
    }
  } else { // Initial mount success
    filesystemOK = true;
  }
}

Additionally, you need to configure PlatformIO to use LittleFS:

board_build.filesystem = littlefs

Now, in setup(), call

InitFilesystem();

If you have errors including LittleFS.h, you might need to explicitly use a recent arduino-esp32 version in platformio.ini:

platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.5

In case you absolutely need to use an older arduino-esp32 version that doesn’t support LittleFS out of the box, you can use the lorol/LittleFS library as outlined in our previous post: How to initialize LittleFS in PlatformIO on the ESP32 using the lorol/LittleFS library

Full platformio.ini example:

[env:esp32dev]
platform = espressif32
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.5
board = esp32dev
framework = arduino
board_build.filesystem = littlefs
monitor_speed = 115200

 

Posted by Uli Köhler in Arduino, Embedded, ESP8266/ESP32, PlatformIO

Should you use SPIFFS oder LittleFS?

For new projects, you should exclusively use LittleFS, period.

The only reason why you would you SPIFFS at all if you have data stored on SPIFFS and can’t easily migrate to LittleFS.

LittleFS is just better than SPIFFS in every regard and SPIFFS will at some point in time be deprecated anyways.

Posted by Uli Köhler in Embedded, ESP8266/ESP32, PlatformIO

How to get filesize in LittleFS (ESP32/PlatformIO)

After you have initialized LittleFS (see ESP32 Filesystem initialization code example (LittleFS)) you can get the filesize by first opening the file, and then calling .size() on the opened file. Don’t forget to close the file afterwards.

auto file = LittleFS.open(filename, "r");
size_t filesize = file.size();
// Don't forget to clean up!
file.close();

Utility function to get the size of a file stored on LittleFS:

size_t LittleFSFilesize(const char* filename) {
  auto file = LittleFS.open(filename, "r");
  size_t filesize = file.size();
  // Don't forget to clean up!
  file.close();
  return filesize;
}

Example usage:

Serial.println(LittleFSFilesize("/cert.pem"));

 

Posted by Uli Köhler in C/C++, ESP8266/ESP32, PlatformIO

How to fix ESP32 PlatformIO esp_https_server.h: No such file or directory

Problem:

When compiling your PlatformIO project, you see a compiler error like

src/main.cpp:6:10: fatal error: esp_https_server.h: No such file or directory

at the following line:

#include <esp_https_server.h>

Solution:

The ESP32 HTTPS server library is not included with the older versions of arduino-espressif32. The solution therefore is to use a more recent version of the platform library such as version 2.0.4. In order to do that, add the following line to your platformio.ini:

platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.4

Complete platformio.ini example:

[env:esp32dev]
platform = espressif32
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.4
board = esp32dev
framework = arduino
monitor_speed = 115200

 

Posted by Uli Köhler in Embedded, ESP8266/ESP32, PlatformIO

How to add extra include directory to PlatformIO project

In platformio.ini, add the following build flags:

build_flags = -I../my-extra-include-dir

By using this method, you can also add multiple include directories:

build_flags = -I../my-first-include-dir -I../my-second-include-dir

All paths are relative to the directory where platformio.ini resides in.

Posted by Uli Köhler in PlatformIO