Linking/using Espressif (esp32) components in a Amazon FreeRTOS project

i-snore wrote on February 18, 2020:

I am trying to use the lwip Espressif component and various header files from it on a ESP32 board. I contacted AWS support and was redirected to this forum.

Our project layout:
/components
----/wifi
--------/include
------------dns.h < this is the file that tries to include the lwip files
--------/src
------------dns.c
--------CMakeLists.txt
/src
----main.c

When I go to include files from the component by writing,

#include "lwip/sys.h"

or

#include "lwip/dns.h"

the linker is unable to find the file and says that it doesn’t exist. (see below)

In file included from ../components/wifi/src/dns.c:5:0:
../components/wifi/include/dns.h:23:22: fatal error: lwip/sys.h: No such file or directory
compilation terminated.

However, I know that the lwip component is being built since the CMake logs show the following line:

Component names: soc log heap freertos vfs newlib esp_ringbuf driver tcpip_adapter esp_event partition_table app_update spi_flash mbedtls micro-ecc bootloader_support efuse xtensa-debug-module app_trace ethernet nvs_flash pthread smartconfig_ack wpa_supplicant espcoredump esp32 cxx i2c lwip ota wifi amazon-freertos-common secure_sockets esp_http_server freertos_plus_tcp bootloader nimble bt console esp_adc_cal esptool_py expat wear_levelling sdmmc fatfs freemodbus nghttp openssl spiffs ulp

This shows all of the components being compiled and on the next line it also shows the paths for each of those components - I didn’t copy & paste that for brevity.

Solutions I’ve tried:

  1. Hardcoding a relative path, e.g.
#include "../../amazon-freertos/lwip.."

and so forth,
However the problem with this was the #includes present within the lwip component itself weren’t able to succusefully include their own files since they were also written in the format of, “#include <lwip/header_file.h>”

  1. Copying and pasting the lwip component into my component directory
    Here I ran into the compiler indicating that structs are being defined twice, which I have pasted below.
In file included from ../components/lwip/apps/dhcpserver/dhcpserver.c:23:0:
../amazon-freertos/vendors/espressif/boards/esp32/components/freertos_tcpip/tcpip_adapter/include/tcpip_adapter.h:52:16: error: redefinition of 'struct ip4_addr'
 typedef struct ip4_addr {
                ^
In file included from ../components/lwip/lwip/src/include/lwip/ip_addr.h:43:0,
                 from ../components/lwip/lwip/src/include/lwip/inet.h:45,
                 from ../components/lwip/apps/dhcpserver/dhcpserver.c:17:
../components/lwip/lwip/src/include/lwip/ip4_addr.h:51:8: note: originally defined here
 struct ip4_addr {

I’ve spent quite a bit of time on this and am not sure how to actually include the lwip component files in my components. I was hoping to get some help with this issue. Please let me know if any more information or context is required. Also, just as a final note, this is not the first time I am compiling my project. I have made other components and had them work fine, so I don’t think its a set up issue or an issue related to the CMake command I am running.

Thanks!

Compiler & CMake Info
I’m using CLion
+CMake Command+

/usr/local/bin/cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=amazon-freertos/tools/cmake/toolchains/xtensa-esp32.cmake -GNinja /Users/vp/esp/Pebble_ESP32_Firmware 

– The C compiler identification is GNU 5.2.0
– The CXX compiler identification is GNU 5.2.0

Edited by: i-snore on Feb 18, 2020 10:47 AM

aws-archit wrote on February 18, 2020:

For compiling the ESP32 lwIP component, the cmake command needs to pass a build flag to enable the component as -DAFR_ENABLE_LWIP=1. Support for lwIP was added in the PR: https://github.com/aws/amazon-freertos/pull/1519

Which release of Amazon FreeRTOS are you using?

The Espressif lwIP support was part of https://github.com/aws/amazon-freertos/tree/201912.00 release.
However, the master branch also contains the PR added for Espressif web server support (that is useful for WiFi provisioning in softAP mode when entering WiFi credentials using a mobile browser) https://github.com/aws/amazon-freertos/pull/1571

Edited by: aws-archit on Feb 18, 2020 11:48 AM

Edited by: aws-archit on Feb 18, 2020 11:49 AM

i-snore wrote on February 19, 2020:

Firstly, thanks for the quick reply!

So I added the

DAFR_ENABLE_LWIP=1

and ran the following CMake command:

/usr/local/bin/cmake -DCMAKE_BUILD_TYPE=Debug -DAFR_ENABLE_LWIP=1 -DCMAKE_TOOLCHAIN_FILE=amazon-freertos/tools/cmake/toolchains/xtensa-esp32.cmake -GNinja /Users/vp/esp/Pebble_ESP32_Firmware

The result:

  1. The CMake console outputs the following warning:
CMake Warning:
  Manually-specified variables were not used by the project:

    AFR_ENABLE_LWIP
  1. However, the following includes successfully get linked which is great!
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/api.h"
#include "lwip/err.h"
#include "lwip/dns.h"
  1. However, the project still does not full compile - I get the following error:
.In file included from ../amazon-freertos/libraries/3rdparty/lwip_osal/src/sys_arch.c:43:0:
../amazon-freertos/libraries/3rdparty/lwip/src/include/lwip/opt.h:51:22: fatal error: lwipopts.h: No such file or directory
compilation terminated.

It seems that lwipopts.h does not get linked.

What Amazon FreeRTOS release?

Now #3 might be related to the question you asked about what build I am running. I used the following repo as a starting point to create an empty project not dependent on demos: https://github.com/tgsong/amazon-freertos-examples

This means I used the version that the amazon freertos submodule was in that repo. Way back when I was first creating my project I attempted to use that repo with the latest release and was running into linking issues so I settled for that version which didn’t seem to be too far behind. The version appears to be: 201908.00

I hope this won’t be an issue. If it is I can attempt to update to the latest release, but I’d rather avoid that if possible.

Background Info
I am trying to use the lwip in order to make the ESP32 have its own local network that acts as a capture portal. When users connect to the network they would insert their WIFI SSID and password. In short, I am using it for WIFI provisioning, but I am trying to avoid using an iOS or Android application.

Edited by: i-snore on Feb 19, 2020 6:27 AM

aws-archit wrote on February 19, 2020:

The lwIP support for Espressif was added only in the 201912.00 release (https://github.com/aws/amazon-freertos/tree/201912.00), and thus, the build does not recognize the flag in the 201908.00 release.
Is it possible for you to upgrade the version and try re-building with the AFR_ENABLE_LWIP flag?

So I updated to the latest version on the master branch and I’m still getting the exact same issues as described above. Any other suggestions?

amazon-freertos @ 45bcc3a

My src/CMakeLists.txt

add_compile_options(-w)

cmake_minimum_required(VERSION 3.13)

project(freertos_examples)

add_executable(esp32_app src/main.c)

# Tell IDF build to link against this target.
set(IDF_PROJECT_EXECUTABLE esp32_app)

# Add some extra components. IDF_EXTRA_COMPONENT_DIRS is an variable used by ESP-IDF
# to collect extra components.
get_filename_component(EXTRA_COMPONENT_DIRS components ABSOLUTE)

# Add some external components to the project
set(IDF_EXTRA_COMPONENT_DIRS ${EXTRA_COMPONENT_DIRS})

# As of now there's no offical way to redefine config files outside of Amazon FreeRTOS source tree.
# This is a temporary approach to inject an include path so that this takes precedence over the
# config file directory inside Amazon FreeRTOS.
include_directories(BEFORE amazon-freertos-configs)

# Add amazon freertos as an subdirectory. AFR_BOARD tells which board to target.
set(AFR_BOARD espressif.esp32_devkitc CACHE INTERNAL "")
add_subdirectory(amazon-freertos)

target_link_libraries(esp32_app PRIVATE AFR::demo_mqtt)
target_link_libraries(esp32_app PRIVATE 3rdparty::lwip)

Wifi Component CMakeLists.txt

# include paths of this components.
set(COMPONENT_ADD_INCLUDEDIRS include)

# source files of this components.
set(COMPONENT_SRCDIRS src)
# add this component, this will define a CMake library target.
register_component()

target_link_libraries(${COMPONENT_TARGET} PRIVATE AFR::wifi)
target_link_libraries(${COMPONENT_TARGET} PRIVATE 3rdparty::lwip)
target_link_libraries(${COMPONENT_TARGET} PRIVATE 3rdparty::lwip_osal)

If I’m understanding your setup correctly, you are trying to bring the lwIP IDF component for use with the FreeRTOS WiFi library through a custom CMake project, but the AFR_ENABLE_LWIP flag I had suggested is not being recognized in the CMake build.
The reason being that the flag is only recognized by demo/tests project for Espressif on the FreeRTOS project repository, here: https://github.com/aws/amazon-freertos/tree/master/vendors/espressif/boards/esp32/ports/wifi ( Refer to the project CMake file : https://github.com/aws/amazon-freertos/blob/master/vendors/espressif/boards/esp32/CMakeLists.txt ) This demo project utilizes the Espressif port of the FreeRTOS WiFi abstraction, and conditionally includes Espressif’s lwIP implementation when the flag is provided.

Can you explain what you are wanting to achieve by bringing in a custom CMake project for the lwIP implementation?
The 201912.00 release (and the latest version on master) provide project example of including the Espressif lwIP stack for the Espressif port for the FreeRTOS WiFi library through the AFR_ENABLE_LWIP flag only when configuring the demo project for Espressif.

If you are looking to create a custom CMake project for the WiFi and/or lwIP components, then you will have to make sure to include all the dependencies for lwIP that have been included in the conditional logic for the AFR_ENABLE_LWIP flag within the demo project CMakeLists.txt file: https://github.com/aws/amazon-freertos/blob/master/vendors/espressif/boards/esp32/CMakeLists.txt

From your earlier reply, it seems like you are trying to achieve WiFi provisioning through the AP mode on ESP32. Currently, FreeRTOS does not have an softAP based WiFi provisioning app. However, Espressif provides an example softAP WiFi provisioning application that you can consider using on FreeRTOS.
Refer here: https://github.com/espressif/esp-idf/tree/release/v3.3/examples/provisioning/softap_prov

Please let us know if that helps.

1 Like

Thanks for the reply :slight_smile:

Ya I’m not trying to build a demo but rather create a custom CMake project. As you mentioned I am trying to create a soft AP for users to connect to from their phone. When they do so, I would like a capture portal to show up. As a result I need a TCP stack and I wanted to use the lwip. I mainly need sockets.h from lwip.

  1. Now I’m a bit confused by when you stated, “make sure to include all the dependencies for lwIP that have been included in the conditional logic”.
    Does this mean I should simply copy and paste all these statements into my root CMakeLists.txt? Would that fix all my issues? Is there any files I need to copy and paste into the amazon-freertos-configs directory? How would you advise me to proceed?

    Update: I added the following to my main CMakeLists.txt (which is all the conditional logic dependent on the AFR_ESP_LWIP flag)

set(AFR_ESP_LWIP 1)
set(afr_ports_dir ./amazon-freertos/vendors/espressif/boards/esp32/ports/)
set(esp_idf_dir ./amazon-freertos/vendors/espressif/esp-idf/)

if(AFR_ESP_LWIP)
    target_compile_definitions(
            AFR::compiler::mcu_port
            INTERFACE $<$<COMPILE_LANGUAGE:C>:${compiler_defined_symbols}>
            -DAFR_ESP_LWIP
    )
endif()

if(NOT AFR_ESP_LWIP)
    list(APPEND kernel_inc_dirs
            "${extra_components_dir}/freertos_tcpip/ethernet/include"
            "${extra_components_dir}/freertos_tcpip/smartconfig_ack/include"
            "${extra_components_dir}/freertos_tcpip/tcpip_adapter/include"
            "${AFR_MODULES_FREERTOS_PLUS_DIR}/standard/freertos_plus_tcp/source/portable/Compiler/GCC"
            )
else()
    list(APPEND kernel_inc_dirs
            "${esp_idf_dir}/components/tcpip_adapter/include"
            )
endif()


if(NOT AFR_ESP_LWIP)
    target_link_libraries(
            AFR::wifi::mcu_port
            INTERFACE
            AFR::freertos_plus_tcp
    )
else()
    target_include_directories(
            AFR::wifi::mcu_port
            INTERFACE
            "${esp_idf_dir}/components/lwip/include/apps"
            "${esp_idf_dir}/components/lwip/include/apps/sntp"
            "${esp_idf_dir}/components/lwip/lwip/src/include"
            "${esp_idf_dir}/components/lwip/port/esp32/include"
            "${esp_idf_dir}/components/lwip/port/esp32/include/arch"
            "${esp_idf_dir}/components/lwip/include_compat"
    )
endif()

if(NOT AFR_ESP_LWIP)
    # FreeRTOS Plus TCP
    afr_mcu_port(freertos_plus_tcp)
    target_sources(
            AFR::freertos_plus_tcp::mcu_port
            INTERFACE
            "${AFR_MODULES_FREERTOS_PLUS_DIR}/standard/freertos_plus_tcp/source/portable/BufferManagement/BufferAllocation_2.c"
            "${AFR_MODULES_FREERTOS_PLUS_DIR}/standard/freertos_plus_tcp/source/portable/NetworkInterface/esp32/NetworkInterface.c"
    )
    # Secure sockets
    afr_mcu_port(secure_sockets)
    target_link_libraries(
            AFR::secure_sockets::mcu_port
            INTERFACE AFR::secure_sockets_freertos_plus_tcp
    )
else()
    # Secure sockets
#    afr_mcu_port(secure_sockets)
    target_sources(
            AFR::secure_sockets::mcu_port
            INTERFACE
            "${afr_ports_dir}/secure_sockets/lwip/iot_secure_sockets.c"
    )
    target_include_directories(
            AFR::secure_sockets::mcu_port
            INTERFACE
            "${esp_idf_dir}/components/lwip/include/apps"
            "${esp_idf_dir}/components/lwip/include/apps/sntp"
            "${esp_idf_dir}/components/lwip/lwip/src/include"
            "${esp_idf_dir}/components/lwip/lwip/src/include/lwip"
            "${esp_idf_dir}/components/lwip/port/esp32/include"
            "${esp_idf_dir}/components/lwip/port/esp32/include/arch"
            "${esp_idf_dir}/components/lwip/include_compat"
    )
    target_link_libraries(
            AFR::secure_sockets::mcu_port
            INTERFACE
            AFR::tls
            AFR::wifi
    )
endif()

The CMake executes without error, and then in my wifi component I added,

set(COMPONENT_REQUIRES lwip)

but when I go to include lwip/sockets.h, it’s still unable to find the file.

  1. Also, I’m not sure if you can help with this but I thought I’d ask for your opinion. For my specific use of the lwip library I plan on creating both a task that handles DNS requests and a http server task that listens for client and then sends over http data. Would you advise this?

Thanks for all the help once again!

  1. Here is an example of how you can include the lwIP stack for the custom CMake project and the Espressif Wifi port on amazon-freertos: https://github.com/yanjos-dev/amazon-freertos-examples/tree/example/lwip_on_esp32
    You just need to specify the the -DAFR_ESP_LWIP=1 flag in the cmake command as mentioned on the README file.
    I realized that you don’t need to explicitly include the lwip directory path in your project’s CMakeLists file as the Espressif CMakeLists.txt on amazon-freertos gets automatically included when specifying the -DCMAKE_TOOLCHAIN_FILE=amazon-freertos/tools/cmake/toolchains/xtensa-esp32.cmake configuration in the cmake command.
    The passed AFR_ESP_LWIP flag in the command makes the Espressif CMakeLists.txt file to configure the WiFi library port of Espressif to use the lwIP stack.
    However, you would still need the set(COMPONENT_REQUIRES lwip) dependency in your custom wifi component’s CMakeLists.txt (shown in the example here: https://github.com/yanjos-dev/amazon-freertos-examples/blob/example/lwip_on_esp32/components/common/CMakeLists.txt#L8) if you want to directly include lwIP header files in your custom component source code (like shown in the example component source code here: https://github.com/yanjos-dev/amazon-freertos-examples/blob/example/lwip_on_esp32/components/common/src/common.c#L2)

  2. The esp http server IDF component exists on the latest version of master on amazon-freertos (See here: https://github.com/aws/amazon-freertos/tree/master/vendors/espressif/esp-idf/components/esp_http_server) that you can consider using for your needs.

Hope the example project helps :slightly_smiling_face:

1 Like

Thank you!
Thanks a lot of making the branch Archit! That really helped :slight_smile:
I was able to get to it to compile and link correctly! Really appreciate it! Linking any components have been a major blocker and pain point during this development :frowning:
I wish the CMake part was documented a bit better.

HTTP Server
As for the http server I wasn’t aware that existed, I’ll look into it! For now I set up a DNS task and it successfully receives and transmits DNS queries - responding with the ESP32’s IP (I can see this using wire shark) and loads the html content when I go to the IP directly. But whenever I type in any other domain, such as “hello.com” it won’ redirect to my IP even though the DNS response from my end is correct.

Glad that the example project helped :slightly_smiling_face:
Thank you for the feedback. We will work towards improving our documentation on use of CMake for Espressif.

I am a bit unclear about your DNS question. Can you please describe your setup:

  • Is the ESP32 device connected to the Internet?
  • Who is initiating the DNS query? How does the DNS task get this DNS query request?
  • Does the DNS task use the lwIP API (and the Espressif lwIP implementation stack) for DNS resolution? What does it do with the resolved address?
  • Who is responding to the DNS query for “hello.com”? Can you also share the Wireshark logs and possibly the source code for DNS task?

So I was able to get it working!

But just as some context the esp is not connected to internet. The purpose was for a user to connect to the ESP’s wifi via their phone and a capture portal to show up, so they could submit their wifi SSID and password. As a result the ESP would be fulfilling the DNS requests.

I was very close, it was just the endian-ness of one uint16_t that was wrong and preventing it from working properly.

Glad that it worked for you. Thanks

1 Like

Hello @isnore, I’m working on a project that needs something similar to what you implemented here, and I was wondering one thing. Did you manage to implement the capture portal using the esp_http_server component from the ESP-IDF? Or did you go another way?

Right now I’m battling with updating some libraries and such, but I’d like to know what’s the best way to approach the provisioning once I get this going.

@TheIronNinja, can I suggest starting a new thread with what you’ve tried and maybe more specific details about what you’re “battling with?”

Nevermind, I figured out what the problem was and I’ve got it working. I might start a thread for the provisioning part, but for now I’m all good, thanks!