Confused about file naming conventions or needs

Coming from Arduino some things in pio still confuse me. My starting file is named main.cpp, and entering that in search I saw there is at least one more main.cpp deep within .pio/*.

Which makes me wonder: why does it even work, when I name my file the same as an existing one?

While a C++ program needs a main() function, it does not have to be in a main.cpp file, or does it? Or is this dependent on the IDE?

Since code in the Arduino framework does not have this main() function, how does pio determine the starting code / file? Based on setup() & loop()?

Anything in the .pio directory, or beneath it, is generated by PlatformIO. So you don’t have to worry.

You can clean generated files out by running pio run -t clean from the command line, or by clicking on the dustbin/trash-can toolbar icon in VSCode.

Failing those, you can just delete the .pio folder and everything in it. It will be regenerated on the next build.



Not a problem for cpp files. The framework and each library will get its own folder where the object files (.o) will be generated.

You should however abide from using the same header names in your code – e.g., you shouldn’t put your own Arduino.h in src/ (unless you really know what you’re doing and it’s a extremely special case).

No, the name of the efile that function is in is not relevant. To the compiler these are all object files and will search for a function within all these object files for linking.

Wrong. The Arduino core always controls the entry point. For the AVR core, this is e.g.

The Arduino core calls into your setup() and loop() functions. You should not re-implement main().

For course, the real entry point (first instruction executed on the processor) is often auto-provided by the compiler, as it is e.g. for the AVR case. There is no startup assembly file that does some low-level init and then calls into main(), here. For other cores, there is.

1 Like

But this is the avr case. I am using ESP*, where it does seem to be handled differently. In ~/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp I only see the loopTask definition plus this function:

void loopTask(void *pvParameters)
    for(;;) {
        if (serialEventRun) serialEventRun();

Where is the main() function, my grep search comes up empty (except for examples and loads of Python code)?

The ESP32 startup process is way more complicated. Arduino-ESP32 builds upon ESP-IDF as a precompiled base, as a first info. The main entry point of the application is app_main(), not main(). From this, a FreeRTOS task is created to run

Every ESP-IDF firmware also starts from app_main().

The complete process is:

  1. ESP32 gets powered on
  2. Starts executing the code stored in its internal mask-ROM (unchangable), which is a 1st stage bootloader.
  3. The bootloader will try to load the actual application from the user-accessible flash from a fixed address
  4. This is where for all ESP-IDF (and thus also Arduino-ESP32) applications where the second stage bootloader lives (to be able to handle OTAA etc.)
  5. The second stage bootloader will call into the entry function pointer of the firmware image, which will be app_main().

See Application Startup Flow - ESP32 - — ESP-IDF Programming Guide latest documentation.

The entry point of the second-stage bootloader, as called from the Mask-ROM, is call_start_cpu0

which eventually ends up loading the flash image data and calling into the entry point function

Oh dear. So, there is a firm rule: in C and C++ the first function called is always main(), except when it is not! :wink:

Thankfully, Espressif themselves say:

Understanding all stages of ESP-IDF app initialization is often not necessary.

I think I’ll take this to heart…

Thanks for the very helpful insight.