Skip to main content

Start your own project

The examples exist to be copied. Once you have one building, the next step is to lift it out of the SDK tree into a repository you control, so SDK updates never conflict with your product code.

Do this early

Do not develop your product inside esp-rainmaker-neo-firmware/examples/. Copy the example out, commit it to your own repository, and track the SDK as a pinned dependency. Editing the SDK tree in place means every SDK update is a merge conflict.

How an example is laid out

Every in-tree example is a single source tree that builds for both ESP-IDF and POSIX:

examples/light/
├── CMakeLists.txt # dual-mode: if (ESP_PLATFORM) … else () …
├── partitions.csv # ESP-IDF partition table (includes fctry)
├── sdkconfig.defaults # committed baseline config
├── sdkconfig.defaults.esp32c2 # per-target overrides
├── sdkconfig.defaults.esp32h2
├── sdkconfig.defaults.psram_quad # PSRAM builds
└── main/
├── CMakeLists.txt # idf_component_register(...)
├── idf_component.yml # path dependencies on the SDK
├── app_main.c # app_run(): startup logic + parameter callbacks
├── app_driver.c # hardware: LED, button
└── app_priv.h

Two conventions make the single tree possible:

  • app_run() instead of app_main()/main(). The app_entry component owns the real entry point. On ESP-IDF it provides void app_main(void), which calls app_run() once and lets FreeRTOS tasks carry on. On POSIX it provides int main(void), which calls app_run(), waits for a termination signal, runs a generic SDK teardown, and exits. Your example never contains app_main, signal handling, or teardown boilerplate.
  • Hardware behind interfaced components. app_led, app_button, and app_network have real ESP-IDF implementations and no-op POSIX ones. The result is that app_run() reads as straight-line application logic with no OS #ifdef in it.

The top-level CMakeLists.txt branches on ESP_PLATFORM:

if (ESP_PLATFORM)
set(COMPONENTS main) # minimal build
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(light)
else ()
project(light C ASM)
add_executable(${PROJECT_NAME} main/app_main.c main/app_driver.c)
# add_subdirectory() the SDK components, then target_link_libraries()
endif ()

Copying an example out

  1. Copy the example directory to your own repository.

  2. Fix the SDK paths. They are relative in-tree and will not resolve once you move.

    For ESP-IDF, main/idf_component.yml:

    dependencies:
    esp_rmaker_neo:
    path: ../../esp-rainmaker-neo-firmware/components/esp_rmaker_neo
    esp_rmaker_neo_ota:
    path: ../../esp-rainmaker-neo-firmware/components/esp_rmaker_neo_ota

    For POSIX, the RMNG_ROOT variable in CMakeLists.txt:

    set(RMNG_ROOT ${CMAKE_CURRENT_LIST_DIR}/../esp-rainmaker-neo-firmware)

    How you get the SDK on disk is up to you — a git submodule, a vendored copy, or a sibling clone that CI provisions. Pin it to a tag or commit; do not track a moving branch.

  3. Rename the project. Change project(<name>) in CMakeLists.txt. On ESP-IDF this becomes the OTA image name (build/<name>.bin) and the default node model, so pick something meaningful.

  4. Set the firmware version. PROJECT_VER must be set before project(). It becomes the node's reported firmware version and is what OTA compares against:

    set(PROJECT_VER "1.0.0" CACHE STRING "Firmware version")

    See OTA firmware updates for the ESP-IDF Kconfig alternative.

  5. Decide what to keep from examples/common/. These are example scaffolding, not SDK API, and they are free to change between SDK releases. Either copy the ones you use into your own repository (recommended for a product), or keep referencing them and accept the coupling.

    ComponentWhat it doesWorth keeping?
    app_entryPlatform entry-point shim (app_run())Only if you want the dual-platform build
    app_networkWi-Fi/Thread provisioning; wraps the four-call sequence into app_network_provision()Yes — it is a genuine convenience
    app_resetHold-to-reset on the button: network reset, then factory resetYes, unless your product has no reset button
    app_event_loopRegisters a default handler that logs every RainMaker Neo eventUseful during bring-up; replace with your own handler
    app_led, app_buttonReference LED and button driversReplace with your own hardware layer
  6. Review the partition table. partitions.csv from the examples assumes 4 MB of flash with two 1920 KB app slots and fctry at 0x3E0000. Adjust for your flash size and keep the factory partition name in sync with CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME. See Factory NVS.

  7. Start from the right example. multi_device is the most instructive if you want several devices on one node; light if you want the simplest end-to-end path. See Examples.

Then