ElCap Documentation
Loading...
Searching...
No Matches
Manufacturing Considerations

Overview


This page covers how to use elcap in automated manufacturing and production-line environments. The focus is on scripting elcap reliably without any user interaction — no prompts, no update dialogs, and machine-readable output that your scripts can parse.

Key points for scripting:

  • Always pass --json to suppress interactive prompts and receive structured output
  • elcap never checks for updates or displays update prompts when --json is used
  • All output goes to stdout; errors are reported in the JSON envelope rather than thrown to stderr

Using the –json Flag


The --json flag is the recommended way to integrate elcap into automated scripts and factory tooling.

When --json is passed:

  • No update prompts — elcap skips its self-update check entirely
  • No interactive prompts — all commands complete without waiting for user input (see command-specific notes below)
  • Structured output — every command prints a single JSON object to stdout that your script can parse

Response Envelope

Every --json response uses a consistent envelope:

Success:

{
"error": null,
"result": { }
}

Error:

{
"error": {
"msg": " ",
"exit_code": 1,
"type": " ",
"remediations": [ ],
"log_path": " "
}
}

Check error first. If it is null, the command succeeded and the payload is in result. If error is non-null, msg contains the human-readable description, type identifies the error category, remediations lists suggested fixes, and log_path points to the full log file for that invocation. A non-zero process exit code also indicates failure.

Commands That Support –json


The following commands support the --json flag. Call --help without the --json flag for usage details and options for each command.

Command Description
elcap --json flash Flash a hex file to a connected device
elcap --json tokens list List available tokens for a chip target
elcap --json tokens write Write one or more tokens to a connected device
elcap --json tokens read Read one or more tokens from a connected device
elcap --json tokens erase Erase one or more tokens on a connected device
elcap --json tokens patch Patch token data into a hex file (no device required)
elcap --json device discover List all connected programmers and devices
elcap --json device info Read device information (e.g. EUI64)
elcap --json device erase Erase device flash
elcap --json device update-firmware Update the programmer's firmware
elcap --json device rename Assign an alias to a connected device
elcap --json device lock Enable flash readout protection on the target MCU
elcap --json device unlock Temporarily disable flash readout protection on the target MCU

Device Selection


All hardware commands (flash, tokens write, tokens read, tokens erase, device info, etc.) accept the following mutually exclusive options for identifying which device to target:

Option Description
--usb <serial> Target a specific device by its USB serial number
--ip <address> Target a network-connected device by IP address
--alias <name> Target a device by a previously assigned alias

If only one device is connected, these options may be omitted and elcap will select it automatically. When multiple devices are connected and no selector is given with json commands requiring a device, elcap will error — so always specify a device selector when scripting to avoid blocking on input.

Use elcap --json device discover to enumerate connected devices and extract their serial numbers, IP addresses, or aliases before running further commands.

Example:

elcap --json device discover
{
"error": null,
"result": {
"devices": [
{
"connection": "USB",
"serial": "123456789",
"name": "CZ20-1",
"COM": "COM3",
"max_speed_khz": "4000"
}
]
}
}

Example Script: Flash and Program MFG Tokens


The following example shows a complete factory programming flow in Bash. It flashes a firmware image and then writes a set of manufacturing tokens, checking for errors at each step.

#!/usr/bin/env bash
set -euo pipefail
DEVICE_USB="123456789" # USB serial number from device discover
TARGET="T32CZ20B" # Chip part number
FIRMWARE="build\\T32CZ20.Debug\\app\\my_project_dkncz20_usb_t32cz20_signed_combined.hex" # Pre-built firmware image
MFG_TOKENS="mfg_tokens.json" # Token data file (see format below)
# 1. Flash the firmware image
echo "Flashing firmware..."
FLASH_RESULT=$(elcap --json \
flash \
--usb "$DEVICE_USB" \
--target "$TARGET" \
--file "$FIRMWARE")
if [ "$(echo "$FLASH_RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['error'])")" != "None" ]; then
echo "Flash failed: $FLASH_RESULT" >&2
exit 1
fi
echo "Flash succeeded."
# 2. Write MFG tokens from a JSON file
echo "Writing MFG tokens..."
TOKENS_RESULT=$(elcap --json \
tokens write \
--usb "$DEVICE_USB" \
--target "$TARGET" \
--file "$MFG_TOKENS")
if [ "$(echo "$TOKENS_RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['error'])")" != "None" ]; then
echo "Token write failed: $TOKENS_RESULT" >&2
exit 1
fi
echo "Tokens written successfully."

Example mfg_tokens.json:

{
"TR_MFG_TOKEN_MFG_NAME": {
"value": "My Company",
"value_type": "ascii"
},
"TR_MFG_TOKEN_MODEL_NAME": {
"value": "My Product",
"value_type": "ascii"
},
"TR_MFG_TOKEN_CUSTOM_EUI": {
"value": "0x0011223344556677",
"value_type": "hex"
}
}

See the Tokens page for the full list of available MFG tokens and the complete token write file format.

Alternative: Pre-Patching Tokens Into a Hex File


If per-device token values are known before programming begins, tokens patch can embed them directly into the hex file. This avoids a second hardware connection step and is well-suited to workflows where a unique hex is prepared per device serial number.

# Patch a per-device EUI into a copy of the base firmware
elcap --json \
tokens patch \
--target "$TARGET" \
--name TR_MFG_TOKEN_CUSTOM_EUI \
--value "0x0011223344556677" \
--value-type hex \
firmware_base.hex \
"firmware_device_${DEVICE_USB}.hex"
# Flash the patched image
elcap --json \
flash \
--usb "$DEVICE_USB" \
--target "$TARGET" \
--file "firmware_device_${DEVICE_USB}.hex"

tokens patch operates entirely on files and requires no connected hardware, so it can run on a host system that prepares images in advance of the programming station.

Example: XTAL Calibration Workflow


Crystal (XTAL) frequency calibration is typically performed on the production line to correct for part-to-part variation and ensure the radio meets specification. Your calibration fixture measures the trim value; elcap then writes it to the TR_MFG_TOKEN_XTAL_TRIM token and reads it back to confirm.

The example below assumes a calibration fixture or script produces a 16-bit hex trim value (e.g. 0x00A3). Adapt the XTAL_TRIM assignment to however your tooling exposes the result.

#!/usr/bin/env bash
set -euo pipefail
DEVICE_USB="123456789" # USB serial number from device discover
TARGET="T32CZ20B"
# Step 1: Run your calibration fixture and capture the trim value ---
# Replace this line with however your fixture reports the result.
XTAL_TRIM="0x00A3"
# Step 2: Write the trim value to the device ---
echo "Writing XTAL trim: $XTAL_TRIM"
WRITE_RESULT=$(elcap --json \
tokens write \
--usb "$DEVICE_USB" \
--target "$TARGET" \
--name TR_MFG_TOKEN_XTAL_TRIM \
--value "$XTAL_TRIM" \
--value-type hex)
if [ "$(echo "$WRITE_RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['error'])")" != "None" ]; then
echo "XTAL trim write failed: $WRITE_RESULT" >&2
exit 1
fi
# Step 3: Read back and verify ---
READ_RESULT=$(elcap --json \
tokens read \
--usb "$DEVICE_USB" \
--target "$TARGET")
WRITTEN=$(echo "$READ_RESULT" | python3 -c "
import sys, json
d = json.load(sys.stdin)
print(d['result']['data']['TR_MFG_TOKEN_XTAL_TRIM'])
")
echo "Trim value on device: $WRITTEN"
# Step 4: Run calibration fixture again to verify tolerance ---
# Re-run your calibration fixture with the new trim value applied. If the measured
# frequency error is within your acceptance tolerance, calibration is complete.
# If not, return to Step 1 with the updated trim value and repeat until the result
# is satisfactory.

If your workflow pre-patches the trim value into the firmware image rather than writing it live, use tokens patch instead. The --name, --value, and --value-type arguments are the same, but tokens patch takes the input hex file (and an optional output hex file) as positional arguments:

elcap --json \
tokens patch \
--target "$TARGET" \
--name TR_MFG_TOKEN_XTAL_TRIM \
--value "$XTAL_TRIM" \
--value-type hex \
firmware_base.hex \
"firmware_device_${DEVICE_USB}.hex"

If the output file is omitted, the input file is patched in place.

JSON Output Reference


flash

$ elcap -j flash --usb 123456789 --target T32CZ20B
{
"error": null,
"result": {
"success": true,
"file_flashed": "firmware_signed.hex",
"target": "T32CZ20B",
"serial_number": "123456789",
"com_port": "COM4",
"ip_address": null
}
}

tokens write

$ elcap -j tokens write --usb 123456789 --target T32CZ20B --name TR_MFG_TOKEN_MFG_NAME --value "My Company" --value-type ascii
{
"error": null,
"result": {
"success": true,
"type": "MFG",
"target": "T32CZ20B",
"tokens_written": "TR_MFG_TOKEN_MFG_NAME",
"message": "Successfully wrote MFG token(s)"
}
}

tokens read

$ elcap -j tokens read --usb 123456789 --target T32CZ20B
{
"error": null,
"result": {
"type": "MFG",
"target": "T32CZ20B",
"data": {
"TR_MFG_TOKEN_VERSION": "FFFF",
"TR_MFG_TOKEN_CUSTOM_EUI": "0011223344556677",
"TR_MFG_TOKEN_MFG_NAME": "4D7920436F6D70616E79FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_MODEL_NAME": "4D792050726F64756374FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_HW_VERSION": "FF",
"TR_MFG_TOKEN_MANUF_ID": "FFFF",
"TR_MFG_TOKEN_SERIAL_NUM": "FFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_XTAL_TRIM": "FFFF",
"TR_MFG_TOKEN_PHY_CONFIG": "FFFF",
"TR_MFG_TOKEN_CCA_THRESHOLD": "FFFF",
"TR_MFG_TOKEN_CBKE_DATA": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_INSTALLATION_CODE": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_DISTRIBUTED_KEY": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_SECURITY_CONFIG": "FFFF",
"TR_MFG_TOKEN_CBKE_283K1_DATA": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_NVM_CRYPTO_KEY": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_BOOTLOAD_AES_KEY": "4AA78BD2BE159D65C0B244ACD9FB33D6",
"TR_MFG_TOKEN_SECURE_BOOTLOADER_KEY": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_SIGNED_BOOTLOADER_KEY_X": "85B6E561B942A15C10D03422C02C7F0D5D421A34C50F732B177198A4109B414F",
"TR_MFG_TOKEN_SIGNED_BOOTLOADER_KEY_Y": "5A42AD67C8E66A1BD768383EC35FF0424A49D159098D1F5476ACF78E2CD49142",
"TR_MFG_TOKEN_SERIAL_BOOT_DELAY_SEC": "FF",
"TR_MFG_TOKEN_THREAD_JOIN_KEY": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_ZWAVE_COUNTRY_FREQ": "FF",
"TR_MFG_TOKEN_ZWAVE_INITIALIZED": "00",
"TR_MFG_TOKEN_ZWAVE_QR_CODE": "9001425381321711445389306395278346246124163255333230001016385007680220011220000300001002560803003",
"TR_MFG_TOKEN_ZWAVE_PUK": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"TR_MFG_TOKEN_ZWAVE_PRK": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
}
}
}

device lock

$ elcap -j device lock --usb 123456789 --target T32CZ20B
{
"error": null,
"result": {
"success": true
}
}

device unlock

Unlocking a T32CZ20B target requires the ADAC private key that was provisioned during lock. T32CM11C targets do not require a key.

$ elcap -j device unlock --usb 123456789 --target T32CZ20B --adac-private-key-path adac_private.pem
{
"error": null,
"result": {
"success": true
}
}