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.
The Advanced JSON tab
- Profiles (left) lists every profile in your draft, with its address, draft state (custom draft or disabled), polling interval and name. Search by name or address.
- The editor (right) shows the selected profile as JSON. The label above it tells you whether you are editing your local draft or looking at a read-only definition from Axiom’s runtime type table.
- The Descriptor guide (below) is a quick reference to the fields and command syntax.
| 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.
After clearing the draft
Clearing the draft, or removing its last profile, does not change Axiom. On Install, an empty draft offers the appropriate next step:
- If Axiom has a custom library file, Remove library & restart deletes it after a separate confirmation and returns Axiom to its built-in definitions.
- To keep Axiom's file and undo browser-only changes, use Reset draft from Axiom instead. A file that cannot be read or parsed must be recovered before this reset can be used.
- If Axiom has no custom file, there is nothing to write. If removed definitions are still running, restart Axiom to stop using them; otherwise add a profile from Catalogue to begin again.
- If the file state is unknown, refresh successfully before changing it.
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.
The library file
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 }
}
}
- A key that doesn't match a built-in profile adds a new type.
- A key that matches a built-in profile customises it: your version is tried first, with the built-in as a fallback.
- “disable”: true disables that type. If the entry also contains a full profile, it is kept but ignored until you enable it again.
Type names can use 1–48 letters, digits, underscores and hyphens (e.g. LTR-390).
A complete example: AHT20
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:
- Look for it at address 0x38. There is no identification check (detectionValues is empty), so anything at 0x38 is treated as an AHT20.
- When it's found, write 0xbe 0x08 0x00 to initialise it.
- Every 500 ms, read the 6-byte result of the previous measurement, then write 0xac 0x33 0x00 to start the next one. Keep 1 sample.
- Decode the 6 bytes as a status byte, a 20-bit humidity value and a 20-bit temperature value.
Profile fields
| 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.
Command syntax
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. |
- Pauses need an =. Write =p20. A bare p20 is sent to the device as bytes.
- To write data, put it before the =. In initValues, 0x10=0x60 writes only 0x10 and ignores 0x60. To write both bytes, use 0x1060.
- An empty detectionValues claims anything at the address. Your profile is tried before the built-in ones, so it can take over a device a built-in profile would have recognised. If the datasheet has an ID or “who am I” register, check it.
You can test a detection command on a connected device before installing it. See Try a detection command in Testing and tuning devices.
Describing the data (devInfoJson)
| 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.
Limits
- The connected Axiom reports how many custom profiles it can hold and how big the library can be. The Install tab shows both, e.g. 1 of 19 profiles · 1.6 KiB of 64 KiB.
- Addresses Axiom uses for its own hardware (including 0x25, 0x36 and 0x70–0x77) are reserved. The battery fuel gauge profile (MAX17048) can't be replaced or disabled.
- A profile you add needs an address (or a Robotical deviceTypeId). A library can't create devices that are wired directly to Axiom.