Arduino

ESP-IDF HTTP webserver minimal ArduinoJson serialization example

static const httpd_uri_t valueHandler = {
    .uri       = "/api/value",
    .method    = HTTP_GET,
    .handler   = [](httpd_req_t *req) {
        httpd_resp_set_type(req, "application/json");
        // create json docuemnt
        DynamicJsonDocument json(1024);
        json["value"] = 1.0;
        // Serialize JSON to string
        std::string buf;
        serializeJson(json, buf);
        // Send response
        httpd_resp_send(req, buf.c_str(), buf.length());
        return ESP_OK;
    }
};

 

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

How to read length-prefixed binary message from Serial using Arduino

The following function allows you to read a binary message, prefixed by a single length byte, from Serial:

#include <Arduino.h>

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

void HandleMessage(String msg) {
    // TODO: Your code to handle the message goes here.
    // See https://techoverflow.net/2022/11/15/how-to-print-string-as-sequence-of-hex-bytes-in-arduino/
    // for an example of how to print the message as a sequence of hex bytes.
}

void ReadMessageFromSerial() {
    // Wait until the length byte is available on Serial
    while (Serial.available() == 0);

    // Read the length of the message
    int length = Serial.read();

    // Read the rest of the message
    String message = "";
    for (int i = 0; i < length; i++) {
      while (Serial.available() == 0);
      message += char(Serial.read());
    }

    // Handle the message
    HandleMessage(message);
}

void loop() {
    ReadMessageFromSerial();
}

 

 

Posted by Uli Köhler in Allgemein, Arduino, Electronics, Embedded

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

STM32 HAL equivalent of Arduino millis()

The equivalent of Arduino’s millis() function when using the STM32 HAL is

HAL_GetTick()

The ticks occur once every millisecond, so this will also give you a millisecond timer that will overflow after some time equivalently to how millis() overflows.

Posted by Uli Köhler in Arduino, STM32

How to print WiFi MAC address to serial on ESP32 (Arduino)?

It’s as simple as

Serial.println(WiFi.macAddress());

Full example

#include <Arduino.h>

void setup() {
    Serial.begin(115200);
    Serial.println(WiFi.macAddress());
}

void loop() {
    // ...
}

 

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

How to get WiFi MAC address as binary on the ESP32 (Arduino)?

uint8_t mac[6];
WiFi.macAddress(mac);

 

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

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

What does mbed-tls error code -0x0010 mean?

If you see an error message like the following one on your microcontroller (such as ESP32):

E (46462) esp-tls-mbedtls: mbedtls_ssl_handshake returned -0x0010

this means MBEDTLS_ERR_MPI_ALLOC_FAILED. In other words, mbedtls can’t allocate enough memory for its operation.

In order to fix this, try to reduce the amount of memory other parts of your application consume.

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

What does mbed-tls error code -0x2700 mean?

If you see an error message like the following one on your microcontroller (such as ESP32):

E (137011) esp-tls-mbedtls: mbedtls_ssl_handshake returned -0x2700

this means MBEDTLS_ERR_X509_CERT_VERIFY_FAILED.

Either you are using the wrong certificate on the server or you are using the wrong certificate on the mbed-tls side for verifying the certificate.

In order to check the server side, it is often helpful to check the server’s TLS certificate using OpenSSL:

openssl s_client -connect myhostname.com:443

 

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

What does mbed-tls error code -0x3B00 mean

If you see an error message like the following one on your microcontroller (such as ESP32):

E (41544) esp-tls-mbedtls: mbedtls_ssl_handshake returned -0x3B00

this means MBEDTLS_ERR_PK_INVALID_PUBKEY.

As of the version of mbed TLS used in esp-idf v4.4.3, only RSA & (certain types of) Elliptic Curve keys are supported. In my tests, X25519/EC256 keys didn’t work and there were indications that P-384 keys also didn’t work. Generally, using RSA keys is a safe bet when working with mbed-tls.

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

FreeRTOS task with static stack memory (xTaskCreateStatic) example

Also see our previous post which uses dynamically allocated memory using xTaskCreate()How to add FreeRTOS task (thread) to any PlatformIO project

constexpr size_t MY_TASK_STACK_SIZE = 1024;
static StaticTask_t myTaskBuffer;
static StackType_t myTaskStack[ MY_TASK_STACK_SIZE ];


void MyTask(void * parameter)
{
    while(true)
    {
        // TODO Your code goes here
    }
}

void setup()
{
    xTaskCreateStatic(
        MyTask, // Task function
        "MyTask", // Name
        MY_TASK_STACK_SIZE, // Stack size
        nullptr, // Parameter
        tskIDLE_PRIORITY,
        myTaskStack,
        &myTaskBuffer);
}

void loop() {
}

 

Posted by Uli Köhler in Arduino, FreeRTOS

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/framework-arduinoespressif32@src-f2ea83e2545300b10a69ff44ef9dc6cd/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

How to fix Arduino error: ‘class HardwareSerial’ has no member named ‘prinln’; did you mean ‘println’?

When you see the following error message:

error: 'class HardwareSerial' has no member named 'prinln'; did you mean 'println'?

You have a typo in your code: You are trying to call prinln() but you’re missing the t in println(). Add the t so you’re calling println() and retry compiling your project.

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

How to fix Arduino [WiFiUdp.cpp:183] endPacket(): could not send data: 118

When you see a log message like

[ 13701][E][WiFiUdp.cpp:183] endPacket(): could not send data: 118

on your microcontroller, you can use Serial.println(strerror(118)); to find out what the error code 118 means.

In the case of 118 (tested with arduino-espressif32 v2.0.5) the error means

Host unreachable

Typically, this error means that you are not connected to the internet / network but it might also mean that the

Note that the error message by itself does not give any indication what part of your code tried to send the UDP packet – hence, it’s not immediately clear what host specifically can’t be reached. However, this message specifically happens with code that sends UDP packets. In almost all cases, the error won’t happen when the microcontroller responds to UDP packets from another host (such as ArduinoOTA) but when the microcontroller tries to send packets to the network initiated by the microcontroller itself (such as NTP).

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