esp-idf: Conditional inclusion of components with same functions - c

I'm working on a project which requires to develop the firmware for several esp32. All the microcontrollers share a common code that takes care of wifi and mqtt, however they all have a different behavior, which is defined in a specific component. The structure of my project is something like this:
- CMakeLists.txt
- Makefile
- sdkconfig
- main
- CMakeLists.txt
- main.c
- components
- wifi_fsm
- wifi_fsm.h
- wifi_fsm.c
- CMakeLists.txt
- mqtt_fsm
- mqtt_fsm.h
- mqtt_fsm.c
- CMakeLists.txt
- entity_1
- entity_1.h
- entity_1.c
- CMakeLists.txt
- entity2
- entity2.h
- entity2.c
- CMakeLists.txt
...
Each entity defines some functions with standard names, which implement specific logic for the entity itself and which are called within the shared code (main, wifi_fsm, mqtt_fsm).
void init_entity(); // called in main.c
void http_get(char *buf); // called in wifi_fsm
void http_put(char *buf);
void mqtt_msg_read(char *buf); // called in mqtt_fsm
void mqtt_msg_write(char *buf);
My idea was to have a conditional statement to include at will a specific behavior, so that depending on the entity included, the compiler would link the calls to the functions above to those found in the specific included library. Therefore, at the beginning of main.c I just added the following lines with the goal of having to change the only defined pre-processor symbol to compile for different enity behaviors.
#define ENTITY_1
#ifdef ENTITY_1
#include "entity_1.h"
#elif defined ENTITY_2
#include "entity_2.h"
#elif ...
#endif
#include "wifi_fsm.h"
#include "mqtt_fsm.h"
void app_main(void)
{
while(1){
...
}
}
On the one hand the compiler apparently works fine, giving successful compilation without errors or warnings, meaning that the include chain works correctlty otherwise a duplicate name error for the standard functions would be thrown. On the other hand, it always links against the first entity in alphabetical order, executing for instance the code included in the init_entity() of the component entity_1. If I rename the standard functions in entity_1, then it links against entity_2.
I can potentially use pointers to standard calls to be linked to specific functions in each entity if the approach above is wrong, but I would like to understand first what is wrong in my approach.
EDIT in response to Bodo's request (content of the CMakeFile.txt)
Project:
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(proj)
Main:
set(COMPONENT_REQUIRES )
set(COMPONENT_PRIV_REQUIRES )
set(COMPONENT_SRCS "main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()
Component:
set(COMPONENT_SRCDIRS "src")
set(COMPONENT_ADD_INCLUDEDIRS "include")
set(COMPONENT_REQUIRES log freertos driver nvs_flash esp_http_server mqtt)
register_component()

This answer is based on guessing because I don't have enough information. For the same reason it is incomplete in some parts or may not fully match the use case of the question.
The details about how the project will be built seems to be hidden in a cmake include file like project.cmake or nested include files.
My guess is that the build system creates libraries from the source code of every individual component and then links the main object file with the libraries. In this case, the linker will find a symbol like init_entity in the first library that fulfills the dependency. This means the library (=component) listed first in the linker command line will be used.
If the linker command line would explicitly list the object files entity_1.o and entity_2.o, I would expect an error message about a duplicate symbol init_entity.
I can propose two ways to solve the problem:
Make sure only the selected entity is used to build the program.
Make the identifier names unique in all entities and use preprocessor macros to choose the right one depending on the selected entity.
For the first approach you can use conditionals in CMakeLists.txt. See https://stackoverflow.com/a/15212881/10622916 for an example. Maybe the register_component() is responsible for adding the component to the build. In this case you could wrap this in a condition.
BUT modifying the CMakeLists.txt might be wrong if the files are generated automatically.
For the second approach you should rename the identifiers in the entities to make them unique. The corresponding header files can define a macro to replace the common name intended for the identifier with the specific identifier of the selected entity.
In the code that uses the selected entity you will always use the common name, not the individual names.
Example:
entity_1.c:
#include "entity_1.h"
void init_entity_1(void)
{
}
entity_2.c:
#include "entity_2.h"
void init_entity_2(void)
{
}
entity_1.h:
void init_entity_1(void);
// This replaces the token/identifier "init_entity" with "init_entity_1" in subsequent source lines
#define init_entity init_entity_1
// or depending on the parameter list something like
// #define init_entity() init_entity_1()
// #define init_entity(x,y,z) init_entity_1(y,x,z)
entity_2.h:
void init_entity_2(void);
#define init_entity init_entity_2
main.c
#define ENTITY_1
#ifdef ENTITY_1
#include "entity_1.h"
#elif defined ENTITY_2
#include "entity_2.h"
#elif ...
#endif
void some_function(void)
{
init_entity();
}
In this example case with #define ENTITY_1, the preprocessor will change some_function to
void some_function(void)
{
init_entity_1();
}
before the compilation step and the linker will use init_entity_1 from entity_1.c. An optimizing linker may then omit the object file entity_2.o or the corresponding library because it is unused.

Related

how to avoid multiple definitions when making copies of header files

I came across this problem and was trying to figure out the best solution.
I am currently working on a library called lib1.h and making some changes to it. I don't want to modify the original file directly so I made a copy and named it lib2.h.
All declaration and definitions are coded in lib1.h and lib2.h, lib1.c and lib2.c, respectively. The code looks like this:
/* lib1.h */
#ifndef LIB1_H
#define LIB1_H
int method1();
int method2();
...
#endif
/* lib2.h */
#ifndef LIB2_H
#define LIB2_H
int method1();
int method2();
...
#endif
/* lib1.c */
#include "lib1.h"
int method1()
{
// old implementation
};
int method2()
{
// old implementation
};
...
/* lib2.c */
#include "lib2.h"
int method1()
{
// new implementation
};
int method2()
{
// new implementation
};
...
Since I am not changing the names of all functions, I am getting multiple definition errors. My current solution is to move the original library out of the current directory and do a make clean and then compile. While this solution works for me, I am just curious if there is any way to keep the two header files in the same directory, or what is a better workflow.
I'd appreciate any pointers.
Multiple definitions is a linker error, not compilation error. Why do you link both libraries? I guess you need only the newer (fixed) one.
There are a few solutions:
Delete the old code,
Put #ifdef 0 on the old code, or move it to separate directory
Use -DNEW_CODE flag in make file and use #ifndef NEW_CODE in old library and #ifdef NEW_CODE in new library. You can quickly switch between versions by editing compilation flags
Just dont include the older version in your make file. Even if you compile it, dont link it.
If you need both versions of library (to compare results?) use prefix to functions or compile as C++ and wrap the functions in name spaces
Use macro hacks to automatically add prefix or suffix to function names. Really dont recommend this approach.
Use source control, like git, and change the library in place, and maintain only 1 copy. you can always revert your commits to get the previos version

How to structure a C (embedded) project with shared header and code files

This is a question of an embedded application, but I imagine the solution is language (C) based and not specific to the embedded compiler (XC16) I'm using.
Problem
I have a number of code files I am abstracting from a single project to create a shared collection of files that can be re-used across multiple projects. These files will require a config.h file in the main application to #define a number of parameters for that project.
Example
Files in project
config.h
#define BUFF_SIZE 4
main.c
#include "config.h"
#include <lib.h>
/*Application Code*/
Files in 'Library' (i.e. another folder NOT in the project structure)
lib.h
extern uint8_t Buffer [BUFF_SIZE];
lib.c
#include "lib.h"
uint8_t Buffer [BUFF_SIZE];
Question
This produces the issue that 'BUFF_SIZE is un-declared in lib.h'. My understanding was that the compiler would start in main.c load in the `#define' values from config.h THEN try to process the lib.h header. But it seems this is not the case.
Do I have to back reference the library to the config.h file? This seems to work, but it then forces the application to have specific file names.
Are there any good examples of how this sort of structuring should take place?
Additional Notes
The same issue arises when I try and map pin outputs for bit-bang functions. i.e.
config.h
#define DATA_OUT LATBbits.LATB4
lib.c
void SetPin(void)
{
DATA_OUT = 1;
}
Cheers :)
C code is compiled on translation unit basis. A translation unit being one single .c file and all the h files it includes. So you must include "config.h" from "lib.h".
You also need to use so-called "header guards"/"include guards" in every header. See
Creating your own header file in C

Including .h and .c files to project on the IAR

I am programming stm8s and sht20 from sensirion company with I2C on the IAR. I'm using sht20 sample code: this link
I edited this sample code to my mcu. Then, for example I included i2c_hal.h to my main.c, but functions not working in my main.c file and IAR error is
ERROR LI005 no defition for I2c_Init()
Linking error
For example:
main.c
#include "stm8s.h"
#include "i2c_hal.h"
I2c_Init();
i2c_hal.h
#ifndef I2C_HAL_H
#define I2C_HAL_H
void I2c_Init ();
#endif
i2c_hal.c
#include "I2C_HAL.h"
void I2c_Init ()
{
SDA=LOW;
SCL=LOW;
SDA_CONF=LOW;
SCL_CONF=LOW;
SDA=HIGH;
SCL=HIGH;
}
I copied sht20 files to my project directory. What should I do for this error?
The header file is read by the preprocessor not the linker; if you get as far as linking, it is not a header file issue. The three basic build steps for C code are:
preprocess
compile
link
Your build is failing at the link state. The linker requires all compiled object files and any necessary libraries that constitute your application as input. In your case the most likely issue is that you have not compiled and linked i2c_hal.c (or strictly compiled i2c_hal.c and linked i2c_hal.obj). In the IAR IDE you simply explicitly add i2c_hal.c to your project along with main.c, and all should be good (all other dependencies being satisfied).
I suspect that i2c_hal.c will infact fail compilation since it is missing any declaration of SDA, SCL etc. - you probably need to include stm8s.h there also.
In general the process looks like this (this diagram actually omits pre-processing - i.e. expansion of headers, macros and conditional compilation etc. - but it was the otherwise clearest example I found; the original page does however mention the pre-processor stage, and the preprocessor is normally run automatically when you invoke the compiler in any case):
I have also the same issue with the spi. I got hal_spi_init() linking problem. To resolve the issue you need to enable the I2C in your stm32 hal drivers. In stm32xx_hal_conf.h file we have different #define modules. There you can enable the I2C module or just include the defined symbol in your IAR tool. Then Issue resolved
You need to add the C source files to the project. Header files shall not have any code or data, only the declarations of types , extern variables, macros, static inline functions and function prototypes.

Can't compile multiple files for Arduino

I'm having an issue with compiling code for Arduino if the code is in multiple files. What I have been doing in the past is have a script concatenate the files in another directory and make the project there. I would like to be able to compile directly from my build folder without having to jump through hoops of making sure everything is defined in the right order, etc.
I'm using avrdude to compile from Linux command line, because the Arduino IDE doesn't work very well with my window manager. When I make with multiple files (with appropriate #include statements, I get errors of the following nature, but for all of my methods and variables.
./../lib/motor.ino:3:21: error: redefinition of ‘const long unsigned int MOVE_DELAY’
./../lib/motor.ino:3:21: error: ‘const long unsigned int MOVE_DELAY’ previously defined here
The only other place that MOVE_DELAY is used is inside the void loop() function, and it doesn't redefine it there. The code also compiles fine if concatenate it into one file and run make in that directory, but not if they are in separate files with includes.
I believe your problem is solvable by declaring the objects with the "extern" prefix or external. For example. I often use the SdFat library, in which it is included in both my main sketch and instanced in other libraries.
/**
* \file test.ino
*/
#include <SdFat.h>
#include <foo.h>
SdFat sd;
...
Where I also use the same object in other libraries, such as foo.h.
/**
* \file foo.h
*/
#include <SdFat.h>
extern SdFat sd;
...
If it was not for the prefix of "extern" it would error like yours, as "sd" can not exist twice. Where the extern prefix tells the linker don't make a new instantiation, rather link to the externally instance elsewhere.

Separating a group of functions into an includable file in C?

I know this is common in most languages, and maybe in C, as well. Can C handle separating several functions out to a separate file and having them be included?
The functions will rely on other include files, as well. I want the code to retain all functionality, but the code will be reused in several C scripts, and if I change it once I do not wish to have to go through every script and change it there, too.
Most definitely! What you're describing are header files. You can read more about this process here, but I'll outline the basics below.
You'll have your functions separated into a header file called functions.h, which contains the following:
int return_ten();
Then you can have a functions.c file which contains the definition of the function:
int return_ten()
{
return 10;
}
Then in your main.c file you can include the functions.h in the following way:
#include <stdio.h>
#include "functions.h"
int main(int argc, char *argv[])
{
printf("The number you're thinking of is %d\n", return_ten());
return 0;
}
This is assuming that your functions.h file is in the same directory as your main.c file.
Finally, when you want to compile this into your object file you need to link them together. Assuming you're using a command-line compiler, this just means adding the extra definition file onto the end. For the above code to work, you'd type the follow into your cmd: gcc main.c functions.c which would produce an a.out file that you can run.
Declare the functions in header files and then implement them in .c files. Now you can #include the header files into any program that uses the functions and then link the program against the object files generated from the .c files.
Say you have a file with a function that you want to use elsewhere:
int func2(int x){
/*do some stuff */
return 0;
}
what you would do is split this up into a header file and a source file. In the header file you just have the function declaration, this tells the compiler that that function does exist, even if it is in a different file. The header might be called func2.h might look like this:
#ifndef HEADER_FUNC2_H
#define HEADER_FUNC2_H
int func2(int x);
#endif /*HEADER_FUNC2_H*/
The #ifndef HEADER_FUNC2_H part is to make sure that this only gets used once so that there are no multiple definitions going on.
then in the source func2.c file you have the actual function itself:
int func2(int x){
/*do some stuff */
return 0;
}
and in any other file now that you use func2 you have to include the header. You do this with #include "func2.h". So for example if we wanted to call func2 from randomfile.c it would be like this:
#include "func2.h"
/* rest of randomfile.c */
func2(1);
Then the last step is to link the object file that contains the function with the compiler when you compile.
If you want to reuse functions across multple programs, you should place them in a library and link it with the rest of your code.
If you want to share the same definitions (e.g. macros, types, ...) you can place them in a header file and include them with #include.
Please refrain from directly "#include" function code into a source file, it's a bad practice and can lead to very problematic situations (especially if you are a beginner, as your tag suggests).
Consder that normally when you have a set of functions you want to share and reuse, you will need both! You will usually end up with a myfuncs.lib (or libmyfuncs.a) library and a myfuncs.h header.
In the programs where you want to reuse your existing functions, you will include the header and link against the library.
You can also look at how to use dynamic libraries once you have mastered the usage of static libraries.

Resources