A device profile is a short piece of JSON that tells Axiom how to find, identify, set up and read a device. This page explains the format and the Advanced JSON editor, so you can write a profile for a device that isn't in the catalogue.
The easiest way to write a profile is to copy one for a similar device. Open a profile from the Devices tab (Definition, then Customise this profile), or add one from the Catalogue. Then edit it in Advanced JSON.
| Button | What it does |
|---|---|
| New profile | Starts a new, empty profile in your draft. You'll be asked for a type name. |
| Edit package | Opens the whole library (every profile) as one JSON document. |
| Save to draft | Saves the editor's JSON to your draft. Unsaved edits are not written or exported. |
| Discard edits | Throws away unsaved changes in the editor. |
| Remove custom | Removes the selected profile from your draft. |
| Import JSON | Loads a library or single-profile .json file from your computer. A whole library replaces your draft (you'll be asked first). A single profile is added to it. |
| Import from URL | Loads a shared profile or library from an https:// address. The server must allow browser access (CORS). |
| Export draft / Export selected profile | Saves your draft, or one profile, as a .json file. |
| Clear browser draft | Empties only the browser draft after confirmation. It does not remove Axiom's stored library. Unsaved editor changes require a separate discard confirmation. |
Below the editor, the page shows the draft's size and any Check messages. Checks are advice: they point out likely mistakes but don't stop you writing. The only verdict that counts is what Axiom reports after you install and restart: the rejected count and first error on the Install tab.
Clearing the draft, or removing its last profile, does not change Axiom. On Install, an empty draft offers the appropriate next step:
Write to Axiom is disabled for an empty draft. Use the explicit removal action above if you intend to remove the device's library. Canceling either the clear confirmation or the unsaved-editor confirmation leaves your browser work unchanged. See Backup & updates for removal and reset details.
A library is one JSON object. Its devTypes object holds one entry per device type, keyed by the type's name:
{
"devTypes": {
"AHT20": { ... },
"VL53L1X": { "disable": true }
}
}
Type names can use 1–48 letters, digits, underscores and hyphens (e.g. LTR-390).
This is the catalogue profile for the AHT20 temperature and humidity sensor:
{
"_notes": "Temperature and humidity sensor. Data format: Status(8bit) + Humidity(20bit) + Temperature(20bit).",
"addresses": "0x38",
"deviceType": "AHT20",
"detectionValues": "",
"initValues": "0xbe0800=",
"pollInfo": {
"c": "=r6&0xac3300=",
"i": 500,
"s": 1
},
"devInfoJson": {
"name": "Temperature & Humidity Sensor",
"desc": "",
"manu": "Asair",
"type": "AHT20",
"clas": ["TEMP", "RH"],
"resp": {
"b": 6,
"a": [
{ "n": "status", "t": "B", "u": "", "f": "02x", "o": "uint8", "vs": false },
{ "n": "humidity", "t": ">I", "u": "%", "r": [0, 100],
"m": "0xfffff000", "s": 12, "d": 10485.76, "f": "3.1f", "o": "float" },
{ "n": "temperature", "at": 2, "t": ">I", "u": "°C", "r": [-40, 80],
"m": "0x000fffff", "d": 5242.88, "a": -50, "f": "3.2f", "o": "float" }
]
}
}
}
In words:
| Field | Required | Meaning |
|---|---|---|
| deviceType | No | The type name. If present, it must equal the key. |
| addresses | Yes, for an I²C device | Where to look: one address, a list or an inclusive range, e.g. “0x38”, “0x29,0x30”, “0x10-0x18”. |
| deviceTypeId | Robotical modules only | The ID that Robotical's own add-on modules report, instead of an address, e.g. “0x0084”. |
| detectionValues | No | How to recognise the device (see below). Empty means “anything at this address”. |
| initValues | No | Commands sent once when the device is found. |
| pollInfo | No | What to read and how often. Leave it out for an actuator that isn't read. |
| pollInfo.c | The poll command. | |
| pollInfo.i | Polling interval in milliseconds (e.g. 500 = twice a second). | |
| pollInfo.s | Number of samples Axiom keeps between deliveries. | |
| devInfoJson | Yes | Name, maker, classes and how to decode the data (see below). |
| disable | No | true disables this device type. |
scanPriority and fields starting with _ (such as _notes) are accepted but not used. Profiles in a library are always scanned at the highest priority.
detectionValues, initValues and pollInfo.c use the same syntax. A command is a list of steps separated by & or ;. Each step is bytes to write=what happens next. The bytes before the = are written to the device as hex (the 0x is optional). What comes after the = depends on the field:
| After = | In initValues and pollInfo.c | In detectionValues |
|---|---|---|
| rN | Read N bytes | — |
| pN | Pause N milliseconds | Pause N milliseconds |
| 0x… or hex | Ignored | The value expected back. Separate alternatives with commas. |
| 0b… | A byte count (don't use) | A bit pattern to compare. x marks a bit to ignore. |
| nothing | Write only | Write only |
Examples:
| Command | Meaning |
|---|---|
| 0x01=r2 | Write 0x01 (select a register), then read 2 bytes. |
| 0xbe0800= | Write the three bytes 0xbe 0x08 0x00. |
| 0x0001=p25 | Write 0x00 0x01, then wait 25 ms. |
| 0x0f=0x69,0x6a,0x6c | (detection) Write 0x0f and expect 0x69, 0x6a or 0x6c back. |
| 0x00=0b000000xx | (detection) Write 0x00 and expect six zero bits followed by two bits that are ignored. |
You can test a detection command on a connected device before installing it. See Try a detection command in Testing and tuning devices.
| Field | Meaning |
|---|---|
| name, desc, manu | Friendly name, description and maker, shown in apps. |
| type | Type label (normally the same as the key). |
| clas | Classes used for searching and grouping, e.g. [“TEMP”, “RH”]. |
| resp.b | The number of bytes one poll reads. Axiom sizes its buffer from this, so it must match the reads in pollInfo.c. |
| resp.a | The list of values (attributes) to decode from those bytes. |
Each attribute in resp.a describes one value:
| Key | Meaning | Example |
|---|---|---|
| n | Name | “temperature” |
| t | How the bytes are read, in Python struct style: B is an unsigned byte, h a signed 16-bit value, H an unsigned 16-bit value, I an unsigned 32-bit value, and so on. Put < in front for little-endian byte order, or > for big-endian. | “>I” |
| at | Byte position to start from. Otherwise each value follows the previous one. | 2 |
| m | Bit mask applied to the raw value | “0x000fffff” |
| s | Shift right by this many bits (left if negative) | 12 |
| sb, ss | Sign bit position, and the value to subtract when it is set | 11, 4096 |
| d | Divide by this | 5242.88 |
| a | Then add this | -50 |
| u | Units | “%”, “°C” |
| r | Expected range [min, max] | [-40, 80] |
| f | Display format, printf-style | “3.2f” |
| o | Output type | “float”, “uint8” |
| vs | false hides the value from graphs | false |
For the AHT20 temperature, the steps are: read 4 bytes starting at byte 2, keep the low 20 bits, divide by 5242.88, then subtract 50.
Some built-in profiles include a small decoding program for data that can't be described with the keys above. For safety, live readings in the Device Library ignore decoding code supplied in a profile. Only reviewed decoders for a few built-in devices are used. Describe your device's data with the attribute keys wherever you can.