Tools reference
The SDK ships five Python tools under tools/. None of them are needed to build firmware — they exist for manufacturing, OTA management, and development shortcuts.
| Tool | Use it to |
|---|---|
factory_nvs_gen | Turn credentials you already have into a factory NVS image |
factory_autoreg | Create, register, and provision a batch of nodes in one command |
ota_helper | Set up OTA infrastructure and drive AWS IoT jobs from the CLI |
local_ctrl_cli | Talk to a node's local-control or challenge-response service |
esp_network_prov_nvs | Skip provisioning by writing Wi-Fi credentials straight to NVS |
Environment setup
There are two paths, and which you need depends on whether you also want to run the integration tests.
Path A — tools only
Enough for every tool on this page — none of them needs a backend checkout or RMNG_BACKEND_DIR:
python3 -m venv .venv && source .venv/bin/activate
pip install -r tools/requirements.txt
Path B — backend checkout required
Needed for the integration tests under test/ and for tools/common/util/factory_config.py, which import backend test helpers. Install the backend's requirements first so versions resolve predictably:
export RMNG_BACKEND_DIR=/path/to/rmng-backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r "${RMNG_BACKEND_DIR}/requirements.txt"
pip install -r test/requirements.txt
test/requirements.txt pulls in tools/requirements.txt and the POSIX build requirements, so this one install also covers every tool on this page.
Provide network credentials in a .env file at the repository root; see .env.example for the fields.
tools/requirements.txt pins a package that builds a native extension against an old cffi with no prebuilt wheel for recent Python versions, so you need libffi headers:
sudo apt-get install -y libffi-dev # Debian/Ubuntu
brew install libffi # macOS
Without them the install fails with fatal error: ffi.h: No such file or directory. Alternatively, run pip install -U cffi in the environment afterwards.
AWS and stack credentials for the tools that need them go in tools/common/credentials_store/ — see its README for the layout.
factory_nvs_gen
Generates a factory NVS partition from credentials you supply.
python factory_nvs_gen.py <factory_partition_label> <factory_namespace> <json_input_file>
Emits out/<json_basename>/esp-idf/*.bin and out/<json_basename>/posix/nvs_persistent/*.bin.
It does not register the node with the cloud. Use factory_autoreg or the dashboard for that.
Full guide: Factory NVS.
factory_autoreg
Creates node identities, registers them with the admin API, and writes factory artifacts — the manufacturing path.
python3 factory_autoreg.py [-n N] [--config CONFIG] \
[--part-label LABEL] [--namespace NS] [--capabilities CAP ...] username password
Ships alongside factory_attach.py, which merges generated factory partitions into full ESP-IDF flash images.
Full guide: CLI batch flow.
ota_helper
CLI for the AWS IoT side of OTA: one-time infrastructure setup, then job creation, starting, tracking, cancelling, and cleanup.
| Command | Does |
|---|---|
--setup | One-time: ACM certificates, Code Signer profile, S3 bucket, IAM role |
--create-ota-job | Standard FreeRTOS OTA job (create-ota-update). Needs --files plus --thing-name or --thing-group-name. |
--create-custom-job | Custom OTA job (create-job). Optional --job-config. |
--create-rmng-ota-job | RainMaker Neo OTA job. Needs --files and --job-config. |
--start-job | Start a job and monitor it. Needs --job-id. |
--track-job-execution | Poll a job execution for one thing. Needs --job-id and --thing-name. |
--cancel-custom-job / --cancel-ota-job | Cancel. Needs --job-id; optional --force. |
--delete-job | Delete a job and its S3 files. |
--destroy | Tear down the OTA infrastructure — cancels running jobs, removes S3, certs, IAM, streams. |
python ota_helper.py --setup
python ota_helper.py --create-rmng-ota-job --files templates/ota_file_template.json \
--job-config templates/job_config_template.json --thing-name node_switch
python ota_helper.py --start-job --job-id ota-update-12345
python ota_helper.py --track-job-execution --job-id ota-update-12345 --thing-name node_switch
Options: --cert-type {RSA,ECDSA} (default ECDSA) and --platform-id for setup. Configuration comes from your deployment's stack outputs at tools/common/credentials_store/general/rmng-outputs.json — the tool fails at import without it.
--destroy is not reversibleIt cancels running jobs and deletes the S3 bucket, certificates, IAM role, and streams for OTA. Do not point it at a production deployment.
For the dashboard equivalent, see OTA firmware updates.
local_ctrl_cli
Interactive client for a node's local-control and challenge-response services. Discovers the node over mDNS by node ID, connects, and drops into a REPL.
python3 tools/local_ctrl_cli/local_ctrl_cli.py [--pop <pop>] [--auto-reconnect] [--verbose] <node_id>
| Argument | Notes |
|---|---|
node_id | Required. The node ID / thing name — get it from get-node-id on the serial console. |
--pop | Proof of possession. Prompted for if the device needs one and you didn't pass it. |
--auto-reconnect | Retry once after reconnecting when a command fails. |
--verbose | Verbose LocalController logging. |
It connects to the node's local endpoints service (_esp_rmaker_ctrl._tcp), which carries local control and/or challenge-response depending on the node's cap TXT record; commands for a capability the node does not advertise are rejected with a clear message. On connect it prints the node state and the cached local-control endpoint (ip:port), then shows a lc> prompt.
For SEC2 (SRP6a) the username is fixed to wifiprov to match the device — it is neither prompted for nor configurable.
Run it from the repository root so it can import the shared utilities under tools/common.
Full guide: Local control.
esp_network_prov_nvs
Writes Wi-Fi credentials directly into an NVS partition on a connected chip, so you can skip provisioning entirely on the bench.
python esp_network_prov_nvs.py --port /dev/cu.usbserial-0001 --offset 0x9000 --size 0x6000
| Argument | Example |
|---|---|
--port | /dev/cu.usbserial-0001 |
--offset | 0x9000 — the NVS partition offset from your partitions.csv |
--size | 0x6000 — the NVS partition size |
Credentials come from .env via tools/common/util/esp_helpers.py: ESP_WIFI_SSID and ESP_WIFI_PASSWORD.
Override per invocation:
python esp_network_prov_nvs.py --port /dev/cu.usbserial-0001 --offset 0x9000 --size 0x6000 \
--network-type wifi --wifi-ssid "MySSID" --wifi-password "MyPassword"
This is a development convenience, not a production flow — real devices get credentials from an end user through network provisioning.
Integration tests
test/ holds a PyTest suite that drives real firmware — ESP boards over UART and POSIX processes on the host — against a live backend. It needs Path B. Run it from the test/ directory so builds and coverage reports land under test/build/:
pytest -n auto -v
By default it uses both ESP and POSIX instances. --no-esp skips boards; --no-posix skips host processes. With ESP enabled, tests are unique per board target type.
The simulators the suite drives are useful on their own: test/sims/device-sim/ is a serial-driven device with an arbitrary shape, and test/sims/ota-sim/ exercises the OTA state machine. device-sim needs CONFIG_RMNG_HOST_CTRL=y — see Configure and build.