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 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 ofapp_main()/main(). Theapp_entrycomponent owns the real entry point. On ESP-IDF it providesvoid app_main(void), which callsapp_run()once and lets FreeRTOS tasks carry on. On POSIX it providesint main(void), which callsapp_run(), waits for a termination signal, runs a generic SDK teardown, and exits. Your example never containsapp_main, signal handling, or teardown boilerplate.- Hardware behind interfaced components.
app_led,app_button, andapp_networkhave real ESP-IDF implementations and no-op POSIX ones. The result is thatapp_run()reads as straight-line application logic with no OS#ifdefin 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
-
Copy the example directory to your own repository.
-
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_neoesp_rmaker_neo_ota:path: ../../esp-rainmaker-neo-firmware/components/esp_rmaker_neo_otaFor POSIX, the
RMNG_ROOTvariable inCMakeLists.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.
-
Rename the project. Change
project(<name>)inCMakeLists.txt. On ESP-IDF this becomes the OTA image name (build/<name>.bin) and the default node model, so pick something meaningful. -
Set the firmware version.
PROJECT_VERmust be set beforeproject(). 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.
-
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.Component What it does Worth 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 reset Yes, unless your product has no reset button app_event_loopRegisters a default handler that logs every RainMaker Neo event Useful during bring-up; replace with your own handler app_led,app_buttonReference LED and button drivers Replace with your own hardware layer -
Review the partition table.
partitions.csvfrom the examples assumes 4 MB of flash with two 1920 KB app slots andfctryat0x3E0000. Adjust for your flash size and keep the factory partition name in sync withCONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME. See Factory NVS. -
Start from the right example.
multi_deviceis the most instructive if you want several devices on one node;lightif you want the simplest end-to-end path. See Examples.
Then
- Code basics — restructure
app_run()around your product - Data model — define your devices and parameters
- Pre-production checklist — before the first hardware build