Includes and CMakeLists.txt

ignisuti
Posts: 29
Joined: Wed Nov 15, 2023 1:29 pm

Includes and CMakeLists.txt

Postby ignisuti » Sat Dec 21, 2024 4:07 am

I'm new to CMake and must be making a simple mistake. Please help.
I am using Expressif Eclipse IDE with the ESP32-S3-WROOM-1.

Problem: In my I2C.h file, the compiler gives an error saying "No such file or directory" for #include "driver/i2c_master.h

Here is how I have my project structured...
I have copied the ESP-IDF Components folder into my project into a folder called ESP-IDF_Components.
> _APP
> _Middleware
> _Middleware > MCU
> _Middleware > MCU > I2C.c & I2C.h
> ESP-IDF_Components

MCU Folder CMakeLists.txt

Code: Select all

# COMPONENT LOCATION: _Middleware/MCU

# List any directories NOT already referenced by the required Components.
set(include_dirs
		"."
	)

# List all Components PRIVATELY REQUIRED by this Component.
set(priv_requires 
		""
	)

# List all Components REQUIRED by this Component.
set(requires
		"_App"
		"console"
		"esp_driver_i2c"
		"esp_driver_tsens"
	)
	
# List all C/C++ files in this Component.
set(srcs
		"I2C.c"
		"TEMPERATURE.c"
	)

# Register the Component.
idf_component_register( 
	INCLUDE_DIRS  "${include_dirs}"
	PRIV_REQUIRES "${priv_requires}"
	REQUIRES      "${requires}"
	SRCS          "${srcs}")
Main CMakeLists.txt:

Code: Select all

# COMPONENT LOCATION: MASTER

# Minimum Version of CMake allowed.
cmake_minimum_required(VERSION 3.16)

# List the Component Directories. 
# Each of these locations needs a CMakeLists.txt file.
set(COMPONENT_DIRS 
	"_App" 
	"_Middleware"
	"_Middleware/MCU"
	"_Middleware/Tools"
	"ESP-IDF_Components"
	)

# Path to CMake
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# Project name
project(project)

# Enable C Language
enable_language(C)
If I change the include above to be this "#include "../../ESP-IDF_Components/esp_driver_i2c/include/driver/i2c_master.h", then it works, but now it complains about not being able to find i2c_types.h

I think I have misunderstanding about CMake and maybe even the concept of Components. Can someone please point out my mistake?

ignisuti
Posts: 29
Joined: Wed Nov 15, 2023 1:29 pm

Re: Includes and CMakeLists.txt

Postby ignisuti » Sun Dec 22, 2024 4:38 pm

This issue has turned into a roadblock for me. Can anyone help?

MicroController
Posts: 1816
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Includes and CMakeLists.txt

Postby MicroController » Sun Dec 22, 2024 4:39 pm

ignisuti wrote:
Sat Dec 21, 2024 4:07 am
I think I have misunderstanding about CMake and maybe even the concept of Components.
You may have :)
Look into https://docs.espressif.com/projects/esp ... ystem.html

What you described sounds a lot more complicated than it should be.
ESP-IDF is based on "components".
In short, you have one "project", which is a directory with a CMakeLists.txt file that declares the project('s name).

Everything else in your application lives in "components". A "component" is a directory with a CMakeLists.txt file that "registers" the component.

The name of each component is the name of the directory its component CMakeLists.txt file resides in. If the component's CMakeLists.txt is "somepath/to/my-component/CMakeLists.txt", the component would be named/referenced as "my-component".

There are a couple of specialties:

1. The project CMakeLists.txt implicitly expects to find a component named "main" right next to it, i.e. if .../my-project/CMakeLists.txt is your project CMakeLists.txt, then there must be a .../my-project/main/CMakeLists.txt file registering the "main" component of your application.

2. By default, ${IDF_PATH}/components is automatically included in the search path for components. Thus, any IDF component from that directory can be referenced ("required") from any other component, including your project's "main" component, just by its directory's name, e.g. REQUIRES esp_driver_i2c mqtt esp_system ...

3. Also by default, a directory named "components" in your project directory is also included in the component search path, if it exists. This way, you can create your own (project-specific) components inside your project if you want.

4. If you want to organize your own components seperately from your project, and/or if you have some 3rd party components stored somewhere, you can set EXTRA_COMPONENT_DIRS in the project's CMakeLists.txt to point to one or more directories in which to look for more components. Note above: Every single component has its own directory, so in order for an external component to be found, the parent directory of the component directory must be added to EXTRA_COMPONENT_DIRS.
If an external component lives in .../3rdparty/component/flux-cap-driver, the extra component dir is .../3rdparty/component, and flux-cap-driver is the name of the component.

In your specific case, you may want to look into how you can structure your code into "components", noting that there is no actual naming hierarchy with components. So _Middleware > MCU may become a single component named "MCU", living in one of the component search paths (see above). Or you can create a component (directory) "i2c" inside MCU and add MCU to the search path, so that you can e.g. REQUIRE i2c from other components (including "main").

And don't copy the IDF components into your project unless you want/need to modify their code.

ignisuti
Posts: 29
Joined: Wed Nov 15, 2023 1:29 pm

Re: Includes and CMakeLists.txt

Postby ignisuti » Mon Dec 23, 2024 4:44 am

Thank you for your response. I have used your information to try and update my project per your suggestions, but it is still not building...

Let me ask this...

If my _App component needs "_Middleware" and "_Middleware/MCU" components, is this a correct CMakeLists.txt file for _App?

Code: Select all

# COMPONENT LOCATION: ${PROJECT_DIR}/_App

# List any directories NOT already referenced by the required Components.
set(include_dirs
		"."
		"${PROJECT_DIR}/_Middleware"
		"${PROJECT_DIR}/_Middleware/MCU"
	)
	
# List all Components PRIVATELY REQUIRED by this Component.
set(priv_requires 
		""
	)

# List all Components REQUIRED by this Component.
set(requires
		"_Middleware"
		"MCU"
	)

# List all C/C++ files in this Component.
set(srcs
		"APP.c"
	)

# Register the Component.
idf_component_register( 
	INCLUDE_DIRS  "${include_dirs}"
	PRIV_REQUIRES "${priv_requires}"
	REQUIRES      "${requires}"
	SRCS          "${srcs}")
Do I even need to mention the "MCU" component here since it is already required in my "_Middleware" Component?

MicroController
Posts: 1816
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Includes and CMakeLists.txt

Postby MicroController » Mon Dec 23, 2024 9:08 am

ignisuti wrote:
Mon Dec 23, 2024 4:44 am
If my _App component needs "_Middleware" and "_Middleware/MCU" components, is this a correct CMakeLists.txt file for _App?
No. You don't "include" components' directories in another component.
A component is made up of two things: 1) source files (.c, .cpp,...) which are compiled when the component is built (the implementation of the component), and 2) header files (.h, .hpp,...) which this component provides for other components to include (the public interface of the component).
Each component's CMakeLists.txt defines its own source files to be compiled via SRCS, and which headers, i.e. include directories, to provide to other components via INCLUDE_DIRS.

It's not common to nest components in the file system. In your case, for both _Middleware and _Middleware/MCU to be used as components, you'd have to have both _Middleware's and MCU's parent directories in the component search path, with MCU's parent being _Middleware.

Note that components are encapsulated and 'isolated' from each other, 'connected' only by the INCLUDE_DIRS one component provides to another component which REQUIRES the former.

Also, you are not required to declare any components except for "main". Inside any component, including "main", you can spread source files across directories as you like, as long as all source files are named in SRCS (with their paths relative to CMakeLists.txt).
In addition to reading up on the IDF's build system you may also want to look into some of the IDF's components as examples of how CMakeLists.txt can be put together.

ignisuti
Posts: 29
Joined: Wed Nov 15, 2023 1:29 pm

Re: Includes and CMakeLists.txt

Postby ignisuti » Tue Dec 24, 2024 7:56 pm

Thank you! That was very helpful. I am now compiling again after following your suggestions.

Espressif, if you're listening, I have some feedback for you. Not trying to say the information presented is wrong. Just making a point that it didn't work for me. I read through all the documentation and looked at examples as best I could before making this post, but there were still many things unclear/confusing to me. I saw a dozen different possibilities of how I might potentially implement this and tried many of the approaches, all with various degrees of success.

What I would recommend (what would have worked better for me) is to post a more robust example. For anyone else reading this, I'll start by posting more details about my implementation to serve as an example. Note that this example also shows how to change away from the traditional "main" component.

- myProject/
------------ CMakeLists.txt
------------ sdkconfig
------------ _Middleware/
----------------------- CMakeLists.txt
----------------------- SYSTEM.c
----------------------- SYSTEM.h
----------------------- MCU/
---------------------------------- ADC.c
---------------------------------- ADC.h
---------------------------------- I2C.c
---------------------------------- I2C.h
---------------------------------- TEMPERATURE.c
---------------------------------- TEMPERATURE.h
----------------------- Tools/
---------------------------------- TIME.c
---------------------------------- TIME.h
---------------------------------- UNITS.c
---------------------------------- UNITS.h
------------ _App/
------------------------- CMakeLists.txt
------------------------- APP.c
------------------------- APP.h
------------------------- APP__CONFIG.h
------------- build/

myProject CMakeLists.txt file:

Code: Select all

# COMPONENT LOCATION: ${PROJECT_DIR}

# Minimum Version of CMake allowed.
cmake_minimum_required(VERSION 3.16)

# List the Component Directories. 
# Each of these locations needs a CMakeLists.txt file.
set(EXTRA_COMPONENT_DIRS 
	"_App" 
	"_Middleware"
	)

# Path to CMake
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# Project name
project(application)

# Enable C Language
enable_language(C)
_Middleware CMakeLists.txt file:

Code: Select all

# COMPONENT LOCATION: ${PROJECT_DIR}/_Middleware

# List directories that need to be referenced by code accessing this component
set(include_dirs
		"."
		"./MCU"
		"./Tools"
	)
	
# List all Components PRIVATELY REQUIRED by this Component.
set(priv_requires 
		""
	)

# List all Components REQUIRED by this Component.
set(requires
		"_App"
		
		#ESP-IDF Components
		"console"
		"esp_adc"
		"esp_driver_i2c"
		"esp_driver_tsens"
		"freertos"
	)

# List all C/C++ files in this Component.
set(srcs
		"SYSTEM.c"
	
		"MCU/ADC.c"
		"MCU/I2C.c"
		"MCU/TEMPERATURE.c"
		
		"Tools/TIME.c"
		"Tools/UNITS.c"
	)

# Register the Component.
idf_component_register( 
	INCLUDE_DIRS  "${include_dirs}"
	PRIV_REQUIRES "${priv_requires}"
	REQUIRES      "${requires}"
	SRCS          "${srcs}")
_App CMakeLists.txt file:

Code: Select all

# COMPONENT LOCATION: ${PROJECT_DIR}/_App

# List directories that need to be referenced by code accessing this component
set(include_dirs
		"."
	)
	
# List all Components PRIVATELY REQUIRED by this Component.
set(priv_requires 
		""
	)

# List all Components REQUIRED by this Component.
set(requires
		"_Middleware"
		
		#ESP-IDF Components
		"esp_driver_gpio"
		"freertos"
		"nvs_flash"
	)

# List all C/C++ files in this Component.
set(srcs
		"APP.c"
	)

# Register the Component.
idf_component_register( 
	INCLUDE_DIRS  "${include_dirs}"
	PRIV_REQUIRES "${priv_requires}"
	REQUIRES      "${requires}"
	SRCS          "${srcs}")
Notes/Tips:
*_App & _Middleware have underscores because I like the visual appeal of those two items (plus any additional components I create) being grouped next to each other in alphabetical order.
*The project has been renamed to "application" in the top-level CMakeLists.txt file so that the output file is created as "application.bin". This will be helpful later on when instructing others to "load the application file".

MicroController
Posts: 1816
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Includes and CMakeLists.txt

Postby MicroController » Tue Dec 24, 2024 9:30 pm

Good you got it working.

However, especially if instructing others, you may want to adopt more of the 'canonical' way to structure applications and their components. There are some useful best practices which most components employ, and sticking to those in your own code helps in finding your way around yours and others' components.
What I recommend is:
1) Only the 'main' component should be project-specific; all other components should be placed outside of any single project, e.g. in a 'our-common-components' directory somewhere. This way, any future project can re-use any component which was originally developed as part of one project without having to dive into the original project.
2) Try to cleanly separate interface and implementation of each component. This can be done by creating a directory structure like

some-component <- Component directory with the CMakeLists.txt
some-component/includes <- Directory where only the public/interface header files of the component are placed
some-component/priv-includes <- Directory where component-private header files, if any, could be placed

The actual source files of the component may be put directly into the component directory or into some "src" directory therein.
The component's INCLUDE_DIRS is then set to only "./includes", so that only those header files are visible to other components which are explicitly intended to be.
3) As I said above, you won't find components nested inside each other in the file system anywhere else. Looking into the IDF you can see that a sort of naming hierarchy is achieved via prefixes and underscores, c.f. "esp_driver_i2c", "esp_driver_uart", "esp_system",...

ignisuti
Posts: 29
Joined: Wed Nov 15, 2023 1:29 pm

Re: Includes and CMakeLists.txt

Postby ignisuti » Wed Dec 25, 2024 4:43 am

I think what you're explaining is very much what I have in mind for my end-goal. Right now, things are messy as I'm still just getting used to this MCU, while learning CMake, and what appears to be Espressif's own variant of CMake.

Ideally, I want to build out my "_Middleware" component so that it has a quick & easy interface for many (most) of the ESP-IDF components. For example, the ADC example had a LOT of lines of code in it and is not something I want to repeat over and over again in future projects. So, if I make an interface (I call it "_Middleware") to the ESP-IDF, then my application can get away with a couple of very simple function calls like ADC__init() and ADC__read(). With each project, I will add more and more interfaces into this _Middleware component. Once this is robust, I will be able to create new application code VERY quickly.

As I build out my _Middleware component, I will want to keep those C files secret, only exposing the H files. Is that what you meant when you said "The component's INCLUDE_DIRS is then set to only "./includes", so that only those header files are visible to other components"? If so, can you please elaborate? How can I share a compilable version of this project with others while NOT sharing these _Middleware C files? I assume I'll need to compile the _Middleware code and then include it as a library...? Do you have any good links for this I can use for reference?

MicroController
Posts: 1816
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Includes and CMakeLists.txt

Postby MicroController » Wed Dec 25, 2024 9:20 am

ignisuti wrote:
Wed Dec 25, 2024 4:43 am
Ideally, I want to build out my "_Middleware" component so that it has a quick & easy interface for many (most) of the ESP-IDF components.
...
then my application can get away with a couple of very simple function calls like ADC__init() and ADC__read().
Watch out to not accidentally re-invent the Arduino for ESP32 wheel :)

Who is online

Users browsing this forum: No registered users and 72 guests