Recipes ======= A recipe is a JSON formatted file that provides a structured way to define the operational setup of the data logger. It is a set of instructions for a logger to execute encapsulating frequently used data logger operations to provision a data logger, or a custom system. The recipe has the ability to configure settings, update operating systems, file management commands [copy, move, delete, rename], format drives, set run options for a program, and more. Recipes are flexible and could be used for a wide variety of data logger configuration tasks or just to combine multiple data logger command and control operations into one file. Recipes provide a convenient way to automate deployment and make data logger setup more efficient and less error-prone. They are especially useful for onboarding devices to specific environments. For example, a recipe can configure a data logger to connect to Campbell Cloud, setting communication parameters and authentication credentials—all in a single step. Recipe Format ------------- Recipes are defined in JSON and consist of several key components that tell the data logger what to do and how to do it. Recipe Components ~~~~~~~~~~~~~~~~~ - **name** *(required)* A human-readable name for the recipe. - **version** *(required)* A version identifier for the recipe. - **id** *(Optional)* An identifier stored with the recipe metadata for tracking. - **function** *(Optional)* How the recipe is applied: ``"onboard"``, ``"startup"``, or ``"run"``. With ``"run"`` the recipe is retained as the data logger's active program recipe — its signature and metadata are stored and its program is set to run — so it persists and is re-applied across restarts. (Loading any new recipe resets the previously stored recipe properties.) - **metadata** *(Object, Optional)* Mostly free-form metadata for tracking or documentation. One key is read by the data logger: ``cert_exp_timestamp`` (an ISO-8601 timestamp) is used to track certificate expiration. - **reboot** *(Boolean, Optional)* When ``true`` the data logger restarts **once at the end of the recipe**, after every operation has been applied, and only if the recipe succeeded. It is a recipe-level directive rather than an operation because a reboot has no meaningful position in the operation sequence — it is always the final action. Use it to force a clean restart into a fresh state, for example from an ``onboard`` recipe that would not otherwise reboot (a recipe delivered by the ``recipe`` command already restarts on success; ``setting``/``settingDefaults``/``program``/``editConst`` operations also reboot on their own when a change requires it). A plain restart; data tables are preserved. - **operations** *(Array of Objects)* A list of operations the data logger should execute. These are saved and may be retried if interrupted. Each operation includes: - **name** — The operation identifier - **data** *(Object)* — Parameters required for the operation .. important:: Values inside an operation's ``data`` object (settings, variables, constants, file parameters) must be **strings** — the data logger converts each string to the appropriate type. Top-level fields such as ``metadata`` may contain nested objects or arrays (e.g. ``specialization_tags``). Generic Recipe Template ----------------------- .. code-block:: json { "name": "", "version": "", "metadata": { "": "", "specialization_tags": ["", ""] }, "reboot": false, "operations": [ { "name": "", "data": { "": "", "": "" } } // Additional main operations ] } Examples -------- **Onboarding to an MQTT environment** This recipe configures a data logger to onboard to an MQTT cloud service that requires mutual TLS authentication. .. code-block:: json { "name": "Cloud9_OnBoard", "version": "1.1.1", "metadata": { "cert_exp_timestamp": "2025-06-03T18:28:08Z" }, "operations": [ { "name": "setting", "data": { "TLSConnections": "2", "MQTTEnable": "1", "MQTTEndpoint": "dashboard.oncloud9.io", "MQTTKeepAlive": "43200", "MQTTPortNumber": "8883", "MQTTCleanSession": "1", "MQTTStateInterval": "1", "MQTTStatusInterval": "1", "MQTTWillMessage": "Gone Fishin' ABCD-EFGH-1234", "MQTTWillQoS": "0", "MQTTWillRetain": "0", "MQTTWillTopic": "cs/v2/last/", "MQTTBaseTopic": "cs/v2/", "MQTTClientID": "ABCD-EFGH-1234", "TLSCertificate": "-----BEGIN CERTIFICATE---------END CERTIFICATE-----", "TLSPrivateKey": "-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----", "TLSRootCA": "-----BEGIN CERTIFICATE----------END CERTIFICATE-----" } } ] } **CRBasic Program Recipe** This recipe downloads and runs a CRBasic program. .. code-block:: json { "name": "ClimaVUE 50", "version": "0.0.1", "metadata": {}, "operations": [ { "name": "program", "data": { "url": "https://s3.us-west-2.amazonaws.com/..." } } ] } Operation List -------------- The data logger supports the following operations: Program Operations ~~~~~~~~~~~~~~~~~~ This operation is intended for downloading either a program or an operating system, not generic files. When the data logger detects a program operation, it will attempt to run the specified file. If the file is a **CRBasic** program: the ``Run Now`` and ``Run on Power Up`` options will be set to true. If the file is an **operating system**: the data logger will install the downloaded OS. .. admonition:: Properties - ``"url":`` {*string*} The web address of the server from which the program should be retrieved. Required unless ``source`` is supplied. - ``"fileName":`` {*string*} *(optional)* The desired name for the program on the data logger. If the name has no drive prefix it is placed on ``CPU:``. If omitted, the CRBasic program is saved as ``CPU:recipe.crb``. - ``"size":`` {*string*} *(optional)* Expected size in bytes of the downloaded file. If provided, the download is rejected unless the file size matches. - ``"source":`` {*string*} *(optional)* The program text itself, embedded in the recipe instead of downloaded. When present, ``url`` is not used. - ``"language":`` {*string*} *(optional)* Source language when using ``source``. Only CRBasic (``"crbasic"``) is supported; any other value is rejected. .. code-block:: json { "name": "program", "data": { "url": "https://DataThings.com/programs/main.crb", "fileName" : "main.crb" } } **Embed CRBasic Program** .. code-block:: json { "name": "program", "data": { "source": "< long crbasic string >", "language": "crbasic", "fileName": "CPU:main_program.CRB" } } OS Operations ~~~~~~~~~~~~~ Downloads and installs an operating system. The ``"data"`` object is handed to the same handler as the command-and-control ``OS`` command, so it accepts the same fields. At minimum supply a ``"url"``; optional fields include ``"version"`` (skip the update if it exactly matches the running version) and the ``"pre_url"`` / ``"post_url"`` URL segments. See the ``OS`` command documentation for the full field list. .. admonition:: Properties - ``"minVersion":`` {*string*} *(optional)* A dotted-decimal version (e.g. ``"1.4.0"``). The OS update is **skipped** when this version is **less than or equal to** the data logger's current version — i.e. the update only proceeds when it moves the device forward. Trailing components default to zero, so ``"1.4"`` is treated as ``"1.4.0"``. (Also available on the command-and-control ``OS`` command.) - ``"manifest_url":`` {*string*} *(optional)* Instead of inlining the download descriptor (``url`` / ``pre_url`` / ``post_url`` / ``size`` / ``chksum32``) in the recipe, the data logger fetches it from this URL with an HTTP GET. The response must be the same JSON object that would otherwise be supplied inline in ``data``; it is used as the OS download descriptor. If the fetch or the parse fails, the OS operation fails. .. code-block:: json { "name": "OS", "data": { "url": "https://DataThings.com/Operating_Systems/CR350.1.8.0.obj", "minVersion": "1.8.0" } } When the OS is delivered in chunks, ``"url"`` is a list of segments and the full URL for each chunk is composed as ````. Use ``"pre_url"`` and ``"post_url"`` to avoid repeating the common parts in every list entry: .. code-block:: json { "name": "OS", "data": { "pre_url": "https://DataThings.com/Operating_Systems/", "url": [ "CR350.1.8.0_0", "CR350.1.8.0_1", "CR350.1.8.0_2" ], "post_url": ".obj", "minVersion": "1.8.0" } } Setting Operations ~~~~~~~~~~~~~~~~~~ Configures data logger settings. The ``"data"`` object is a flat map where each key is a setting **name** (e.g. ``"PakBusAddress"``) or its numeric setting **ID**, and each value is the value to apply. There are two forms: - ``"name": "setting"`` — applies the listed settings, leaving all others unchanged. - ``"name": "settingDefaults"`` — resets the device to factory defaults first, then applies the listed settings on top. .. admonition:: Properties - ``"":`` {*string*} The value to apply. All values must be strings. Only the settings listed in ``data`` are written; every other setting keeps its current value (or, for ``settingDefaults``, its factory default). The settings are applied in a single edit session and committed together — if any committed setting requires a restart, the data logger reboots to apply it. **Set Settings** .. code-block:: json { "name": "setting", "data": { "PakBusAddress": "12", "StationName": "Rufus" } } **Set Settings From Defaults** The ``settingDefaults`` operation first resets the **entire** device configuration to its factory defaults, then applies the settings listed in ``data`` on top of that baseline. The reset and the overrides happen in a single edit session and result in a single reboot, so the device ends up at "factory defaults, plus exactly these values" regardless of its prior configuration. .. code-block:: json { "name": "settingDefaults", "data": { "PakBusAddress": "12", "StationName": "Rufus" } } ``data`` may be empty (``{}``) to reset the device to factory defaults with no overrides: .. code-block:: json { "name": "settingDefaults", "data": {} } .. note:: This is a full factory reset of all settings before the overrides are applied — including network/cloud connectivity settings not listed in ``data``. Make sure any settings required for the device to remain reachable (MQTT, IP, cellular, etc.) are included in ``data``. File Operations ~~~~~~~~~~~~~~~ The ``file`` operation performs file-system and program-control actions, selected by the ``"action"`` key. Supported actions: ``create``, ``download``, ``upload``, ``list``, ``checksum``, ``run``, ``stop``, ``load``, ``format``, ``delete``, ``copy``, and ``move`` (rename is a ``move``). The ``download``, ``upload``, ``list``, and ``checksum`` actions behave exactly as the matching :doc:`fileControl commands `; in a recipe they simply run silently (no command response is published). Where an action takes a target file, it accepts either ``"path"`` (a full ``DRIVE:file`` path) or ``"fileName"`` with an optional ``"drive"`` (defaulting to ``CPU``). The ``run`` and ``stop`` actions reboot the data logger to apply the change. **Format Drive** .. code-block:: json { "name": "file", "data": { "action": "format", "drive": "USR" } } .. note:: Recipes are stored on the ``CPU:`` drive, so formatting ``CPU`` would normally erase the running recipe (and the copy used to resume it if interrupted). When a recipe contains a format operation, the data logger keeps the recipe in memory for the duration of the run and automatically rewrites it after the format if the file is missing. The remaining operations and interrupted-recipe resume therefore continue to work even after a ``CPU`` format. **Move File** .. code-block:: json { "name": "file", "data": { "action": "move", "source": "CRD:file.txt", "destination": "USR:file.txt" } } **Rename File** .. code-block:: json { "name": "file", "data": { "action": "move", "source": "CRD:file.txt", "destination": "CRD:renamed_file.txt" } } **Set Run Options** Sets the program to run and reboots. ``"runOnPowerUp"`` (default ``true``) controls whether the program also runs on power-up, and ``"deleteTables"`` (default ``false``) clears existing data tables on the reboot. .. code-block:: json { "name": "file", "data": { "action": "run", "path": "CPU:main_program.CRB", "runOnPowerUp": "true", "deleteTables": "true" } } **Stop Program** Stops the running program and reboots. Honors ``"runOnPowerUp"`` and ``"deleteTables"`` as above. .. code-block:: json { "name": "file", "data": { "action": "stop" } } **Create File** Writes a file from inline data supplied in ``"source"``. Text files are written verbatim; binary files are supplied base64-encoded together with ``"encoding": "base64"``. .. admonition:: Properties - ``"source":`` {*string*} The file contents to write. For ``"encoding": "base64"`` this is the base64-encoded binary; otherwise it is written verbatim as text. - ``"encoding":`` {*string*} *(optional)* ``"base64"`` to decode ``"source"`` from base64 and write the resulting raw bytes — use this to create binary files. Omit (or ``"none"``) to write ``"source"`` verbatim. - ``"chksum32":`` {*string*} *(optional)* A CRC-32 of the intended contents (the decoded bytes when ``base64`` is used), as a decimal (or ``0x``-prefixed hex) string. If the target file already exists and its CRC-32 matches, the file is left untouched and creation is skipped. Uses the same checksum as the OS operation. .. code-block:: json { "name": "file", "data": { "action": "create", "path": "CPU:notes.txt", "source": "file contents", "chksum32": "3735928559" } } Binary file, supplied base64-encoded: .. code-block:: json { "name": "file", "data": { "action": "create", "path": "CPU:blob.bin", "encoding": "base64", "source": "SGVsbG8sIHdvcmxkIQ==" } } **Download File** Fetches a file over HTTP(S) and saves it to the logger — the general-purpose file downloader. (To download and run a program, or download and install an operating system, use the ``program`` / ``OS`` operations above instead.) .. code-block:: json { "name": "file", "data": { "action": "download", "url": "https://example.com/files/cal.dat", "path": "CPU:cal.dat" } } **Copy File** .. code-block:: json { "name": "file", "data": { "action": "copy", "source": "CRD:file.txt", "destination": "USR:file.txt" } } **Delete File** .. code-block:: json { "name": "file", "data": { "action": "delete", "path": "CPU:file.CRB" } } **Load OS From Drive** .. code-block:: json { "name": "file", "data": { "action": "load", "path": "CPU:CR350.1.8.0.obj" } } EditConst Operations ~~~~~~~~~~~~~~~~~~~~ Sets one or more constants in the running CRBasic program's ``ConstTable`` and triggers a recompile. This is the recipe-embedded form of the ``editConst`` command and uses the same underlying handler (``JSON_const_set``). The operation is identified by ``"name": "editConst"``. The ``"data"`` object is a flat map of constant name → value, where **every value must be a string** (the logger casts each string to the constant's declared type when it rewrites the program). Only the constants listed are changed; all others keep their current values, and every key must name an existing constant or the operation fails. Each value may be at most **79 characters**. Unlike the command-and-control form, the recipe form does **not** honor the ``cmd`` / ``tranID`` / ``apply`` keys — those are command-and-control specific. After the constants are applied successfully the logger reboots to recompile. .. code-block:: json { "name": "editConst", "data": { "THRESHOLD": "10.5", "SENSOR_ID": "99", "STATION_NAME": "North 40" } } Set Variables ~~~~~~~~~~~~~ Sets one or more variables in the running CRBasic program. The ``"data"`` object is a flat map of variable name → value, using the same handler as the command-and-control ``variable`` / ``setVar`` commands. Values must be strings; the data logger converts each string to the variable's type when it is applied. .. admonition:: Properties - ``"":`` {*string*} The value to assign. Variable access and examples: - ``"variable_name": "12.3"`` — Public variable - ``"Public.variable_name": "12.3"`` — Public variable with explicit scope - ``"variable_name(2)": "2.013"`` — Array element of a public variable - ``"StructureName.variable_name": "12.3"`` — Variable inside a structure - ``"StructureName.variable_name(2)": "12.3"`` — Array element of a variable inside a structure - ``"trigger_event": "true"`` — Boolean variables Every key must name a variable that exists in the running program; if any assignment fails (unknown variable, array index out of bounds, etc.) the operation fails. .. code-block:: json { "name": "variable", "data": { "bale_count": "20", "field_name": "north_40", "farmall.pto_engaged": "False", "farmall.engine_rpm": "1780.0" } } Recipe and WEB.OBJ.GZ ---------------------- A Recipe can be included in a WEB.OBJ.GZ for a powerful automated deployment solution .. toctree:: :maxdepth: 1 recipe_in_web_obj_gz.rst