C/C++

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 GCC error: unknown type name ‘size_t’ (STM32)

Problem:

When you try to compile your C/C++ project (typically a STM32 project):

C:/Users/User/MyProject/MyHeader.h:9:7: error: unknown type name 'size_t'
    9 | const size_t MySize = 15;
      |       ^~~~~~

Solution:

At the top of the file where this error occurs, add the following line:

#include <stddef.h>

 

Posted by Uli Köhler in C/C++, GCC errors, STM32

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

ESP32 minimal httpd_uri_t example (ESP-IDF)

This httpd_uri_t handler just answers Hello World!:

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

 

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

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

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

How to cut shapes using OCCUtils for OpenCascade (boolean difference)

The OCCUtils library provides an easy way to compute the difference, or cut one shapefrom the other, in OpenCASCADE. Also see How to create a Box TopoDS_Solid in OpenCASCADE and How to create a Cylinder TopoDS_Solid in OpenCASCADE for more details on how we can generate those shapes using OCCUtils.

If we have two shapes:

#include <occutils/Primitive.hxx>

// ...
TopoDS_Solid box = Primitive::MakeBox(10, 10, 10 /* mm */);

TopoDS_Solid cylinder = Primitive::MakeCylinder(3 /* mm diameter */,
        100 /* mm length */, Primitive::Orientation::Y);

we can use Boolean::Cut from OCCUtils to compute the boolean difference:

TopoDS_Shape result = Boolean::Cut(box, cylinder);

Complete main.cpp example with STEP export:

#include <occutils/Primitive.hxx>
#include <occutils/STEPExport.hxx>
#include <occutils/Boolean.hxx>

using namespace OCCUtils;

int main() {
    // Make basic box
    TopoDS_Solid box = Primitive::MakeBox(10, 10, 10 /* mm */);

    TopoDS_Solid cylinder = Primitive::MakeCylinder(3 /* mm diameter */,
        100 /* mm length */, Primitive::Orientation::Y);

    TopoDS_Shape result = Boolean::Cut(box, cylinder);

    STEP::ExportSTEP(result, "out.step");
}

The result (out.step) will look like this when viewed in FreeCAD

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

OCCUtils full example: Make box and export it to STEP

This example creates a box using OCCUtils’s Primitive::MakeBox (or see How to create a Box TopoDS_Solid in OpenCASCADE ) and exports it to a STEP file.

First, initialize the git repository:

git init

Now we can add OCCUtilsusing the submodule method:

git submodule init
git submodule add https://github.com/ulikoehler/OCCUtils.git OCCUtils

Now we can add the CMake config:

project(mkbox)
cmake_minimum_required(VERSION 3.3)

add_subdirectory(OCCUtils)

add_executable( mkbox main.cpp )
add_dependencies( mkbox occutils )
target_include_directories( mkbox PUBLIC /usr/include/opencascade/ )
target_link_libraries( mkbox
    occutils
    TKernel
    TKMath
    TKFillet
    TKBinXCAF 
    TKBRep
    TKBO
    TKFeat
    TKG2d
    TKXDESTEP
    TKG3d
    TKGeomAlgo
    TKGeomBase
    TKHLR
    TKIGES
    TKPrim
    TKShHealing
    TKSTEP
    TKSTEP209
    TKSTEPAttr
    TKSTEPBase
    TKXSBase
    TKSTL
    TKTopAlgo 
    TKV3d
    TKOffset
    TKService
)

And the main source code:

#include <occutils/Primitive.hxx>
#include <occutils/STEPExport.hxx>
#include <vector>

using namespace OCCUtils;

int main() {
    TopoDS_Solid myCube = Primitive::MakeBox(5.0 /* X size */, 7.0 /* Y size */, 9.0 /* Z size */);

    STEP::ExportSTEP(myCube, "out.step");
}

Now we can configure & build using

cmake .
make

When running the program using

./mkbox

we see the following debug output:

*******************************************************************
******        Statistics on Transfer (Write)                 ******

*******************************************************************
******        Transfer Mode = 0  I.E.  As Is       ******
******        Transferring Shape, ShapeType = 2                      ******
** WorkSession : Sending all data
 Step File Name : out.step(350 ents)  Write  Done

and mkbox will generate out.step which you can open, for example, in FreeCAD to view the generated STEP file:

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

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 fix esp-tls-mbedtls: mbedtls_x509_crt_parse returned -0x2180

Problem:

While trying to use TLS on the ESP32, you are using a certificate and private key e.g. from NVS or from the filesystem. However when you try to connect using SSL, you see error messages like

E (9774) esp-tls-mbedtls: mbedtls_x509_crt_parse returned -0x2180
E (9775) esp-tls-mbedtls: Failed to set server pki context
E (9775) esp-tls-mbedtls: Failed to set server configurations, returned [0x8015] (ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED)
E (9786) esp-tls-mbedtls: create_ssl_handle failed, returned [0x8015] (ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED)
E (9795) esp_https_server: esp_tls_create_server_session failed

Solution:

-0x2180 means MBEDTLS_ERR_X509_INVALID_FORMAT

The most common issue here is that conf.cacert_len and conf.prvtkey_len must include the NUL terminator in the length.

Working Example:

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

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

Note the + 1 here: Without the + 1, you’ll see the mbedtls_x509_crt_parse returned -0x2180

Other causes:

If the length isn’t the issue, you likely have a malformed certificate. I suggest to print out the certificate’s content via the serial port, saving it to file we’ll call cert_esp32.pem and then running

openssl x509 -in cert_esp32.pem -noout -text

to verify that the certificate is correct. You can do the same for the private key, but typically either both the private key and the certificate are fine or both are not.

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

How to print string as sequence of Hex bytes in Arduino

The following code will print a std::string to Serial as sequence of Hex characters. It can easily be adapted to work with other types of strings.

const char* cstr = str.c_str();
for (size_t i = 0; i < str.size(); i++)
{
    Serial.printf("%02X ", cstr[i]);
}

This code is based on our previous post How to print byte as two hex digits in Arduino

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

How to Serial.println() a std::string

In Arduino, if you have a std::string:

std::string str = "test";

you can’t directly print it – trying to do so leads to the following error messages:

src/main.cpp: In function 'void setup()':
src/main.cpp:122:22: error: no matching function for call to 'HardwareSerial::println(std::__cxx11::string&)'
   Serial.println(cert);
                      ^
In file included from /home/uli/.platformio/packages/[email protected]2/cores/esp32/Stream.h:26,
                 from /home/uli/.platformio/packages/[email protected]2/cores/esp32/Arduino.h:166,
                 from src/main.cpp:1:
/home/uli/.platformio/packages/[email protected]2/cores/esp32/Print.h:96:12: note: candidate: 'size_t Print::println(const __FlashStringHelper*)'
     size_t println(const __FlashStringHelper *);
            ^~~~~~~
/home/uli/.platformio/packages/[email protected]2/cores/esp32/Print.h:96:12: note:   no known conversion for argument 1 from 'std::__cxx11::string' {aka 'std::__cxx11::basic_string<char>'} to 'const __FlashStringHelper*'
/home/uli/.platformio/packages/[email protected]2/cores/esp32/Print.h:97:12: note: candidate: 'size_t Print::println(const String&)'
     size_t println(const String &s);

 

Solution:

You can use .c_str() to convert it to a NUL-terminated char* which can be printed directly:

Serial.println(str.c_str());

 

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

How to convert Arduino String to std::string

If you have an Arduino String:

String arduinoStr = "test123";

you can easily convert it to a std::string by using:

std::string stdStr(arduinoStr.c_str(), arduinoStr.length());

The std::string constructor will copy the data, therefore you can de-allocate the Arduino String instance safely.

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

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 GCC elaborated-type-specifier for a scoped enum must not use the ‘class’ keyword

Problem:

In C++, you are declaring an enum class like this:

enum class Shape : uint8_t {
    Circle = 0,
    Square = 1
};

but when you try to compile the project, you see an error message like

include/Shape.hpp:4:6: warning: elaborated-type-specifier for a scoped enum must not use the 'class' keyword
 enum class Shape : uint8_t {
 ~~~~ ^~~~~
      -----

Solution:

The issue here is that you are deriving the enum class from another type –  uint8_t in this example –  but that type has not been declared.

So the solution is to #include the header where the type from which the enum class is inherited.

In our example – for the type uint8_t and for many other integral types such as int32_t , you can do that by adding

#include <cstdint>

before the enum class declaration.

Posted by Uli Köhler in C/C++, GCC errors

How to fix STM32 error: expected constructor, destructor, or type conversion before __declspec(dllexport)

Problem:

When trying to compile a firmware for an STM32 microcontroller, you see a compiler error message like

myheader.h:23:12: error: expected constructor, destructor, or type conversion before ‘(’ token
   23 |  __declspec(dllexport) int myfunc(

Solution:

We previously explored the same problem for Linux platforms.

__declspec(dllexport) is a Windows-specific feature and not available on non-Windows platform such as the ARM embedded API platform (e.g. STM32). In order to fix it in a compatible way with both Windows and the STM32, add the following code either in a header that is included in every file containing __declspec(dllexport) or add it in each file where the error occurs:

#ifdef __ARM_EABI__
#define __declspec(v)
#endif

This will basically ignore any __declspec() call on the preprocessor level.

By using __ARM_EABI__ specfically, the definition will not trigger for ARM platforms for Windows.

Posted by Uli Köhler in C/C++, GCC errors, STM32

How to read ESP32 NVS value into std::string

In our previous post, we discussed How to get the length / size of NVS value on ESP32. Based on that, we can read a NVS value into a std::string.

Strategy

  1. Determine size of value in NVS
  2. Allocate temporary buffer of the determined size
  3. Read value from NVS into temporary buffer
  4. Create std::string from value
  5. Cleanup temporary buffer

Utility function to read NVS value as std::string

In case the key does not exist in NVS, this function will return the empty string ("").

#include <nvs.h>
#include <string>

std::string ReadNVSValueAsStdString(nvs_handle_t nvs, const char* key) {
    /**
     * Strategy:
     *  1. Determine size of value in NVS
     *  2. Allocate temporary buffer of determined size
     *  3. Read value from NVS into temporary buffer
     *  4. Create std::string from value
     *  5. Cleanup
     */
    // Step 1: Get size of key
    esp_err_t err;
    size_t value_size = 0;
    if((err = nvs_get_str(nvs, _key.c_str(), nullptr, &value_size)) != ESP_OK) {
        if(err == ESP_ERR_NVS_NOT_FOUND) {
            // Not found, no error
            return "";
        } else {
            printf("Failed to get size of NVS key %s: %s\r\n", key, esp_err_to_name(err));
            return;
        }
    }
    // Step 2: Allocate temporary buffer to read from
    char* buf = (char*)malloc(value_size);
    // Step 3: Read value into temporary buffer.
    esp_err_t err;
    if((err = nvs_get_str(nvs, _key.c_str(), buf, &value_size)) != ESP_OK) {
        // "Doesn't exist" has already been handled before, so this is an actual error.
        // We assume that the value did not change between reading the size (step 1) and now.
        // In case that assumption is value, this will fail with ESP_ERR_NVS_INVALID_LENGTH.
        // This is extremely unlikely in all usage scenarios, however.
        printf("Failed to read NVS key %s: %s\r\n", key, esp_err_to_name(err));
        free(buf);
        return "";
    }
    // Step 4: Make string
    std::string value = std::string(buf, value_size);
    // Step 5: cleanup
    free(buf);
    
    return value;
}

Usage example

This assumes that you have setup myNvs as we have shown in our previous post How to initialize NVS on ESP32

std::string value = ReadNVSValueAsStdString(myNvs, "MyKey");

C++17 optimizations

Starting from C++17, you can possibly create a std::string directly instead of using the temporary buffer, since there is an overload of .data() that returns a non-const pointer – so you can write directly to the std::string‘s buffer.

However, since my PlatformIO-based toolchain currently doesn’t support that, I have not written that code yet.

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

How to fix C/C++ ‘size_t’ does not name a type

Problem:

When compiling your C or C++ program. you see the following error message:

src/main.cpp:4:11: error: 'size_t' does not name a type
 size_t mySize = 32;

Solution:

In the file where the error occurs (in our example, that would be src/main.cpp), add the following line at or near the top. It must be added before the line where size_t is first used, and usually you would add that line after the last existing #include statements:

#include <stddef.h>

After adding that line, the error should be gone. Possibly you need to add said line to other files as well, so carefully check any compiler error messages if the error re-appears in other files.

Posted by Uli Köhler in C/C++, GCC errors

Minimal boost::asio::serial_port read (read_some) example

The following example shows how to initialize a boost::asio serial_port , set its baud rate, parity & stop bits and then read data from it in a loop, printing the data to stdout as-is.

#include <boost/asio.hpp>
#include <iostream>

#define BUFSIZE 256

int main() {
    boost::asio::io_service io;
    // Open serial port
    boost::asio::serial_port serial(io, "/dev/ttyUSB0");

    // Configure basic serial port parameters: 115.2kBaud, 8N1
    serial.set_option(boost::asio::serial_port_base::baud_rate(115200));
    serial.set_option(boost::asio::serial_port_base::character_size(8 /* data bits */));
    serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
    serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));

    // Read data in a loop and copy to stdout
    while(true) {
        char data[BUFSIZE];
        size_t n = serial.read_some(boost::asio::buffer(data, BUFSIZE));
        // Write data to stdout
        std::cout.write(data, n);
    }
}

For a more complete example that also shows how to open the serial port, see How to open & initialize boost::asio::serial_port

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