ESP32 - undefined reference to `esp_mesh_is_scan_allowed'

Hi. So I’m working on a project that uses the ESP32’s WiFi, however, the compilation fails with the following error:

/Users/me/.espressif/tools/xtensa-esp32-elf/esp-2021r2-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /Users/me/Downloads/pwos_riot/RIOT/build/pkg/esp32_sdk/build-libs/libnet80211.a(ieee80211_api.o):(.text.esp_wifi_scan_get_ap_records+0x8): undefined reference to `esp_mesh_is_scan_allowed'
/Users/me/.espressif/tools/xtensa-esp32-elf/esp-2021r2-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /Users/me/Downloads/pwos_riot/RIOT/build/pkg/esp32_sdk/build-libs/libnet80211.a(ieee80211_api.o): in function `esp_wifi_scan_get_ap_records':
(.text.esp_wifi_scan_get_ap_records+0x100): undefined reference to `esp_mesh_is_scan_allowed'
collect2: error: ld returned 1 exit status
gmake: *** [/Users/me/Downloads/pwos_riot/pixelweather/../RIOT/Makefile.include:728: /Users/me/Downloads/pwos_riot/pixelweather/bin/esp32-wroom-32/pixelweather.elf] Error 1

It looks like the esp_wifi_scan_get_ap_records function is using esp_mesh_is_scan_allowed internally, but the linker can’t find it’s definition. Could someone please give me some suggestions on how I could fix this? I tried looking up the esp_mesh_is_scan_allowed function but it’s like it doesn’t exist at all. Google can’t find anything about it and neither can the search feature in the ESP-IDF documentation.
(I’m using IDF v4.4 on macOS Monterey)

Did you install the IDF using the install script provided by RIOT? AFAIR @gschorcht needed to add some patches to the IDF that should be applied automatically if you follow the instructions in the documentation.

No, I did not, because that script doesn’t support macOS.
(Sorry, I probably should have mentioned that :smiley:)

Update: Just tried to do the compilation using the Docker images riot/riotbuild and schorcht/riotbuild_esp32_espressif_gcc_8.4.0. In both cases I got the same error

Uh what’s the trouble on macOS? Does it work with /bin/bash instead of /bin/sh?

Which app are you trying to build?

Running /bin/bash ./install.sh esp32 and /bin/sh ./install.sh esp32 display the same error:

./install.sh: line 11: ldconfig: command not found
error: OS architecture Darwin-x86_64 not supported

(AFAIK macOS does not have a ldconfig command)

I’m building a custom project, not from examples. I have #include "esp_wifi.h" in my C++ source file and USEMODULE += esp_wifi in my Makefile.
By the way, do any of those two Docker images have the ESP-IDF toolchain pre-installed?

riotbuild@container-id:~$ find / -name "idf.py" 2>/dev/null
riotbuild@container-id:~$

Oh but Release esp-2022r1 · espressif/crosstool-NG · GitHub has downloads for macOS, they are just not handled by the switch in the script.

Ok, let’s rewind to the beginning of the discussion.

  • The reason why the dist/tools/esptool/install.sh script doesn’t support macOS is quite simple, I’m not able to test it for macOS. But obviously, you have already installed the right tool chain version manually in

    /Users/me/.espressif/tools/xtensa-esp32-elf/esp-2021r2-8.4.0/xtensa-esp32-elf/
    
  • The ESP-IDF is used as a package in RIOT, that is, it is automatically cloned directly from GitHub during compilation and patched as soon as it is needed. So there is no need to install it. Once you have tried an application you will find it in ${RIOTBASE}/build/pkg/esp32_sdk. If you execute the find command for idf.py, you will find it there. Please note, the whole tool infrastructure using the idf.py command is not supported in RIOT.

  • Yes, both RIOT docker images have already installed the xtensa-esp32-elf/esp-2021r2-8.4.0 toolchain.

  • Function esp_mesh_is_scan_allowed is defined in ${IDF_PATH}/components/esp_wifi/lib/esp32/libmesh.a that is linked with RIOT applications if either any esp_wifi* module or the esp_now module is used as netdev.

  • Function esp_wifi_scan_get_ap_records in turn is used in RIOT’s ESP-NOW netdev driver (${RIOTBASE}/cpu/esp_common/esp-now/esp_now_netdev.c) to scan for other peers. But, we had never compilation or linking problems with it.

Finally, the question is why you get this linking error.

1 Like

Can you build any of the examples (e.g. gnrc_border_router)?

So I decided to start off completely from scratch. I build other ESP-related projects, like MicroPython so my ESP-IDF configuration was probably a complete mess.

I deleted my copy of the ESP-IDF toolchain and all xtensa compilers. Then I redownloaded RIOT from GitHub (latest release) as well as ESP-IDF v4.3. I went through Espressif’s setup documentation. It looks like I had xtensa-esp32-elf-gcc version 8.4.0. Now I have 11.2.0. I ran export.sh (from the ESP-IDF toolchain) in my project directory and ran make -j8. This time the compilation failed with the following error:

"make" -C /Users/me/Downloads/pwos_riot/RIOT/sys/frac
/Users/me/Downloads/pwos_riot/RIOT/cpu/esp32/startup.c: In function 'system_init':
/Users/me/Downloads/pwos_riot/RIOT/cpu/esp32/startup.c:322:31: error: implicit conversion from 'enum <anonymous>' to 'esp_log_level_t' [-Werror=enum-conversion]
  322 |     esp_log_level_set("wifi", LOG_DEBUG);
      |

Running make again results in a huge ton of errors. I thought I should go back to esp-2021r2-8.4.0. I managed to make the install.sh script (partially) work on macOS like this:

case ${PLATFORM} in
    linux-amd64|linux64|Linux-x86_64|FreeBSD-amd64)
        OS=linux-amd64
        ;;
    linux-arm64|Linux-arm64|Linux-aarch64|Linux-armv8l)
        OS=linux-arm64
        ;;
    linux-armel|Linux-arm|Linux-armv7l)
        OS=linux-armel
        ;;
    linux-armhf)
        OS=linux-armhf
        ;;
    linux-i686|linux32|Linux-i686|FreeBSD-i386)
        OS=linux-i686
        ;;
    Darwin-x86_64)
        OS=macos
        ;;
    *)
        echo "error: OS architecture ${PLATFORM} not supported"
        exit 1
        ;;
esac

Although this will only work on Intel Macs. As for the ldconfig command, I’m not familiar with it, but it looks like it’s checking the version of ncursesw. On macOS, you could do this using pkg-config --modversion ncursesw, but this will require pkg-config to be installed. Now the script can download the xtensa compiler. I ran it to install the older gcc version. I re-opened my terminal and in my project directory I ran . ../RIOT/dist/tools/esptools/export.sh esp32 and make -j8.
I got the same undefined reference error, but it looks a bit longer now:

Users/me/.espressif/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /Users/me/Downloads/pwos_riot/RIOT/build/pkg/esp32_sdk/build-libs/libnet80211.a(ieee80211_api.o):(.text.esp_wifi_scan_get_ap_records+0x8): undefined reference to `esp_mesh_is_scan_allowed'
/Users/me/.espressif/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /Users/me/Downloads/pwos_riot/RIOT/build/pkg/esp32_sdk/build-libs/libnet80211.a(ieee80211_api.o): in function `esp_wifi_scan_get_ap_records':
(.text.esp_wifi_scan_get_ap_records+0x100): undefined reference to `esp_mesh_is_scan_allowed'
/Users/me/.espressif/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /Users/me/Downloads/pwos_riot/pixelweather/bin/esp32-wroom-32/esp_idf_nvs_flash//nvs_pagemanager.o:(.text._ZN3nvs11PageManager12activatePageEv+0x4): undefined reference to `nvs::Page::setSeqNumber(unsigned long)'
/Users/me/.espressif/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /Users/me/Downloads/pwos_riot/pixelweather/bin/esp32-wroom-32/esp_idf_nvs_flash//nvs_pagemanager.o: in function `nvs::PageManager::activatePage()':
nvs_pagemanager.cpp:(.text._ZN3nvs11PageManager12activatePageEv+0x34): undefined reference to `nvs::Page::setSeqNumber(unsigned long)'
collect2: error: ld returned 1 exit status
make: *** [/Users/me/Downloads/pwos_riot/pixelweather/../RIOT/Makefile.include:728: /Users/me/Downloads/pwos_riot/pixelweather/bin/esp32-wroom-32/pixelweather.elf] Error 1

Edit:

gnrc_border_router compiles successfully and without any warnings for esp32-wroom-32

UPDATE (Fixed):
The undefined reference to 'nvs::Page::setSeqNumber(unsigned long)' somehow fixed itself by running make clean and make. Turns out that my Makefile was missing USEMODULE += esp_now. I did not know this module was a requirement for WiFi.

Turns out that my Makefile was missing USEMODULE += esp_now . I did not know this module was a requirement for WiFi.

Uh that is a bug! That was probably introduced recently when the esp32 subsystem was refactored in preparation for suporting new families.

There is now a PR for supporting macOS in the setup script: #18385

RIOT as well as the used ESP-IDF version 4.4.1 are not compatible with gcc-11.2.0 and definitely only work with gcc-8.4.0.

Since the precompiled qemu-esp32 only works for Linux, there is no need to test the ncursesw version for macOS in the scripts.

Unfortunately, gnrc_border_router is an unsuitable example for your problem, since it uses both esp_now and esp_wifi as netdevs. A better example would be examples/gnrc_networking with esp_wifi module enabled.

USEMODULE=esp_wifi BOARD=esp32-wroom-32 make -j8 -C examples/gnrc_networking

This is something I can’t really believe. The module that enables the linking of the libmesh.a library and thus defines the esp_mesh_is_scan_allowed symbol is the esp_wifi_any module. This module is automatically enabled if EITHER the esp_now module OR the esp_wifi module is enabled. So there is definitely no need to enable esp_now in addition to esp_wifi. Just try the following two commands to verify this:

BOARD=esp32-wroom-32 make -C examples/gnrc_networking info-modules | grep esp_
USEMODULE=esp_wifi BOARD=esp32-wroom-32 make -C examples/gnrc_networking info-modules | grep esp_

You should observe that the esp_wifi_any module is enabled in both cases, while in the first case you have enabled only esp_now and in the second case only esp_wifi. So there is no need to add esp_now in addition to esp_wifi.

You can also check with

USEMODULE=esp_wifi BOARD=esp32-wroom-32 make -j8 -C examples/gnrc_networking QUIET=0 | grep mesh

that libmesh.a is linked even if the esp_now module is not used.

So please try whether you can compile:

USEMODULE=esp_wifi BOARD=esp32-wroom-32 make -j8 -C examples/gnrc_networking

Unfortunately, gnrc_border_router is an unsuitable example for your problem, since it uses both esp_now and esp_wifi as netdevs. A better example would be examples/gnrc_networking with esp_wifi module enabled.

USEMODULE=esp_wifi BOARD=esp32-wroom-32 make -j8 -C examples/gnrc_networking

This also compiles successfully.

USEMODULE=esp_wifi BOARD=esp32-wroom-32 make -j8 -C examples/gnrc_networking QUIET=0 | grep mesh

This does not show anything about linking libmesh.a. Only output I get is:

Replacing /Users/me/Downloads/pwos_riot/RIOT/examples/gnrc_networking/bin/esp32-wroom-32/riotbuild/riotbuild.h.in ( != 2f7b236c98f767c02d5104824328d36c)
Parsing CSV input...

(The compilation then ends successfully.)

I went back into my project directory and removed USEMODULE += esp_now from the Makefile. Interestingly it compiled just fine :confused: I am so confused, it did not work yesterday.

Indeed, it seems to be strange. Perhaps there were object files as leftovers from previous compilation attempts.

RIOT’s make system does not use archives, but simply links all object files of a module that are found in the target directory of that module. While it automatically takes care of dependencies and compiles all necessary object files, it does not remove object files that are no longer needed. So if something seems strange, it is better to use make clean before recompiling.