ESP8266/ESP32

Which pins on the ESP32-S2 are DAC outputs?

Only two pins on the ESP32-S2 are capable of being DAC outputs:

  • GPIO23 is DAC_1
  • GPIO24 is DAC_2

Source: ESP32-S2 datasheet

Posted by Uli Köhler in ESP8266/ESP32

How to output 2.048MHz clock on any pin on the ESP32

You can use the LEDC timer (typically used for PWM) to output a 50% duty cycle clock with 3.3V P-P amplitude on any output-capable GPIO pin.

First,

#include <driver/ledc.h>

then setup the timer. You only need to do this once on startup, no code in your loop function is required.

/**
 * Setup 2.048MHz clock output on GPIO33
 */
ledc_timer_config_t ledc_timer = {
    .speed_mode = LEDC_HIGH_SPEED_MODE,
    .bit_num    = LEDC_TIMER_2_BIT,
    .timer_num  = LEDC_TIMER_0,
    .freq_hz    = 2048000
};
ledc_channel_config_t ledc_channel = {
    .gpio_num   = GPIO_NUM_33,
    .speed_mode = LEDC_HIGH_SPEED_MODE,
    .channel    = LEDC_CHANNEL_0,
    .timer_sel  = LEDC_TIMER_0,
    .duty       = 2
};
ledc_timer_config(&ledc_timer);
ledc_channel_config(&ledc_channel);

 

Posted by Uli Köhler in ESP8266/ESP32

How does the ESP32 DAC cosine generator waveform look on an Oscilloscope?

The ESP32 DAC has a built-in cosine waveform generator. Even though it’s an 8-bit DAC, the waveform looks pretty clean.

For an example on how to generate this wavefrm in firmware, see How to use the ESP32 DAC sine/cosine waveform generator using Arduino / PlatformIO

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

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

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

How to determine how many percent of memory (heap) are free on ESP32?

This approach works for Arduino / PlatformIO as well as ESP-IDF projects.

#include <esp_heap_caps.h>

uint32_t freeHeapBytes = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
uint32_t totalHeapBytes = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
float percentageHeapFree = freeHeapBytes * 100.0f / (float)totalHeapBytes;

// Print to serial
Serial.printf("[Memory] %.1f%% free - %d of %d bytes free\n", percentageHeapFree, freeHeapBytes, totalHeapBytes);

Also see How to find number of free bytes in ESP32 memory / heap?

Posted by Uli Köhler in ESP8266/ESP32

How to find number of free bytes in ESP32 memory / heap?

This approach works for Arduino / PlatformIO as well as ESP-IDF projects.

#include <esp_heap_caps.h>

uint32_t freeHeapBytes = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);

 

Posted by Uli Köhler in ESP8266/ESP32

How to use vPortGetHeapStats() on the ESP32?

Problem:

You see an error message like the following one while compiling your ESP32 project

src/main.cpp:128:3: error: 'vPortGetHeapStats' was not declared in this scope
   vPortGetHeapStats(&heapStats);

Solution:

Although vPortGetHeapStats() is typically defined in freertos/portable.h, you can not use   vPortGetHeapStats() on the ESP32 since the frameworks do not use the FreeRTOS heap implementation.

In order to find informatio about heap usage, use the ESP heap API such as esp_get_free_heap_size().

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

What caps argument to give to ESP32 heap_caps_…() functions?

Almost all of the ESP32 heap_caps_...() functions take a uint32_t caps argument.

In case you just want to have general information about the heap, use

MALLOC_CAP_DEFAULT

as an argument.

Most applications will rarely use any other value than MALLOC_CAP_DEFAULT. Other values which are used semi-frequently include:

  • MALLOC_CAP_SPIRAM
  • MALLOC_CAP_INTERNAL (memory must not be located in SPI RAM)
For more details on how the memory of the ESP32 is organized, see the official documentation.
Posted by Uli Köhler in ESP8266/ESP32

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 -0x3F80 mean?

When you see an error message such as

E (169535) esp-tls-mbedtls: mbedtls_ssl_handshake returned -0x3F80

on your microcontroller (e.g. ESP32), this means

MBEDTLS_ERR_PK_ALLOC_FAILED

In other words, there is not enough memory for mbed-tls to work – specifically, there is not enough memory to allocate the public key. Try to reduce the memory usage of your application.

Posted by Uli Köhler in 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

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

When you see an error message such as

E (61175) esp-tls-mbedtls: mbedtls_ssl_setup returned -0x7F00

on your microcontroller (e.g. ESP32), this means

MBEDTLS_ERR_SSL_ALLOC_FAILED

In other words, there is not enough memory for mbed-tls to work. Try to reduce the memory usage of your application.

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

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