Skip to main content

Pre-production checklist

Work through this before you build the image that goes onto hardware you cannot get back. Most items are one Kconfig line; the cost of missing one is a field recall.

Project hygiene

  • Your project lives in your own repository, not in esp-rainmaker-neo-firmware/examples/. The SDK is a pinned dependency. See Start your own project.
  • The SDK is pinned to a tag or commit, not tracking a branch.
  • sdkconfig is gitignored and every setting you depend on is in sdkconfig.defaults (plus per-target variants). A configuration that only exists on one developer's machine is a configuration you will lose.
  • PROJECT_VER is set and part of your release process. It must increase on every release or OTA will reject your updates. See OTA.
  • project(<name>) is your product name, not light. It becomes the OTA image name and the reported node model.

OTA — get this right first

If OTA does not work, nothing else on this list can be fixed after shipping.

  • esp_rmaker_neo_ota is linked and esp_rmaker_ota_enable() is called before esp_rmaker_start().
  • An ota_diag callback is provided and it verifies something real. With ota_diag = NULL no rollback is ever performed, so a firmware that boots but cannot connect will never be reverted. See Rollback and diagnostics.
  • Rollback is enabledCONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE (implied by CONFIG_RMNG_OTA_FORCE_ENABLE_ROLLBACK, default y) — and the partition table has two app slots.
  • CONFIG_RMNG_OTA_SIGNATURE_VERIFY_ENABLE=y, with a codesign_cert provisioned in factory NVS. Without it a node will flash whatever image a job document points at.
  • You have performed a real end-to-end OTA on production hardware: upload, job, download, flash, reboot, validate.
  • You have performed a deliberately failing OTA and confirmed the rollback recovered the device.
  • CONFIG_RMNG_OTA_REQUEST_TIMEOUT_MS suits your worst-case network, not your desk Wi-Fi.

Identity and manufacturing

  • You have decided how nodes get credentialsassisted claiming (the default) or pre-flashed factory NVS — and the Kconfig matches that decision.
  • If claiming: the shipping phone app supports claiming, provisioning is BLE, and you have claimed a device end to end on production hardware.
  • If pre-flashing: factory NVS is generated and flashed per device, each device got its own image — not a copy of one — and every node is registered with the cloud. factory_nvs_gen alone does not register; use factory_autoreg or the dashboard.
  • CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME and _NAMESPACE match the labels used by your generation tooling and the name in partitions.csv.
  • The fctry partition is large enough24 KB (0x6000) when claiming is enabled, since credentials are written at runtime; enough for the generated blob plus any codesign certificate when pre-flashing.
  • Only the capabilities you use are enabled at registration (s3, kvs, bridge). Each attaches a server-side policy.
  • Private keys and factory binaries are in secure storage, are not in git, and access is logged.
  • QR codes are generated off-device and printed on labels or packaging — not read off the serial console.

Lock down debug surfaces

  • CONFIG_RMNG_CONSOLE_ENABLED=n, or at least CONFIG_RMNG_CONSOLE_PARAM_CMDS_ENABLED=n. An exposed UART with reset-to-factory and set-param on it is a real attack surface.
  • CONFIG_RMNG_HOST_CTRL=n. Remote control is a test harness; it has no place in shipping firmware.
  • CONFIG_RMNG_TESTING=n.
  • Local control keeps its PoP. CONFIG_ESP_RMAKER_LOCAL_CTRL_SEC_VERSION_2 is the default — keep it. Security 1 with CONFIG_ESP_RMAKER_LOCAL_CTRL_SEC1_POP=n runs without a proof of possession, which means anyone on the LAN can control the device. See Local control.
  • Provisioning uses Security 2 (CONFIG_APP_PROV_SECURITY_VERSION_2, the default) and a PoP. CONFIG_APP_NETWORK_POP_TYPE_NONE is a development setting.
  • CONFIG_APP_NETWORK_PROV_TIMEOUT_PERIOD is non-zero. A device that advertises for provisioning forever is a device anyone can claim.
  • CONFIG_APP_NETWORK_PROV_MAX_POP_MISMATCH is non-zero, so brute-forcing the PoP is bounded.
  • Log level is reduced. Verbose logging costs flash, time, and leaks internals over the UART.
  • Consider ESP-IDF flash encryption and secure boot. They are outside RainMaker Neo, but this is the point in the process to decide. See the ESP-IDF security documentation.

Behaviour on real networks

  • MQTT budgeting matches your traffic. Defaults are 100 messages with 1 revived every 5 seconds. If your device reports faster than that sustained, raise the budget or slow the reporting — over-budget messages are dropped, including state reports and timeseries. See Configure and build.
  • Timeseries queue length suits your sampling rate. A full queue drops data points silently.
  • You have tested with the internet down, and the device still works locally and recovers cleanly when it comes back.
  • You have tested a slow, lossy link, not just a good one.
  • CONFIG_MBEDTLS_HAVE_TIME_DATE and your time-sync flow are a deliberate choice. With it set, a node that cannot reach NTP hangs at startup. See Time and timezone.
  • MQTT port 443 unless you have a specific reason for 8883 — 443 gets through more firewalls.
  • Wi-Fi retry behaviour is sane. CONFIG_APP_NETWORK_PROV_MAX_RETRY_CNT erases credentials after this many failures; make sure that is the behaviour you want on a router that is briefly down.

Data model

  • Standard device and parameter types are used wherever one fits. Custom types work, but lose phone-app UI and voice-assistant support. See Data model.
  • A primary parameter is assigned on every device, so app tiles and voice commands resolve.
  • No device or parameter ID contains a . — it breaks timeseries paths.
  • PROP_FLAG_PERSIST is on the values that must survive a power cut, and off the ones that would wear NVS.
  • PROP_FLAG_INDEXED is only on slow-changing values. Every update writes the indexed shadow too.
  • Cumulative vs simple timeseries flags are correct for each parameter.
  • The Name parameter is handled if you use a bulk write callback — otherwise renaming silently stops working. See Callbacks and events.
  • Write callbacks confirm before reporting. Don't update a parameter after a failed hardware write.

Recovery paths

Assume something will go wrong in the field and the user cannot reach a serial port.

  • The system service is enabled with all three flags, so a user can reboot, network-reset, and factory-reset from the app. See System service.
  • A network_reset_fn is registered, or the reset paths fail.
  • There is a physical reset path — a long button hold, like the examples' two-stage app_reset hold. See Troubleshooting.
  • Reset event handlers leave the hardware safe — motors parked, heaters off — within the configured delay.
  • You have verified that a factory reset makes the device re-provisionable by a different account.

Before you press go

  • Build a release image from a clean checkout at a tagged commit and confirm it works on production hardware.
  • Flash a device with the release image and a production factory partition and take it through the whole user journey: provision, associate, control, schedule, OTA.
  • Record which SDK commit, ESP-IDF release, and target each release image was built against.