The host driver turns decoded Axiom measurements into ROS messages. Use a semantic message such as sensor_msgs/msg/Illuminance when the quantity is known, or a scalar topic when no supported semantic adapter applies. The generic sample and data outputs remain available for full decoded values and validity.
Complete Install and connect and leave the host driver acquiring. These instructions apply to USB and Wi-Fi host-driver connections. Sensors must have a supported firmware profile; ROS output mappings do not install a sensor driver or introduce a custom binary decoder.
Availability
Use the current published main source. After updating, rebuild, source the workspace and restart affected ROS processes as described in Install and build. Importing a viewer layout does not update an installed driver. Automatic Illuminance/scalar outputs work without a ROS mapping block; optional descriptor selection uses ros.version: 1 below.
These declarations affect measurement publishers. Servo-angle and LED-colour command subscriptions have their own standard-message contracts in Servo and LED command topics.
Find the actual outputs
In a terminal with the same ROS environment, domain and middleware as the driver:
ros2 topic list -t --no-daemon
BOARD_NS='PASTE_DISCOVERED_BOARD_NAMESPACE_HERE'
ros2 topic echo "$BOARD_NS/devices" std_msgs/msg/String \
--qos-reliability reliable --qos-durability transient_local \
--qos-depth 1 --full-length --once
Replace BOARD_NS with the complete namespace from /axiom_discovery/boards, including its leading slash. Variables are local to the terminal where you set them. The board inventory's topic is a relative sensor root. public_topics[group][suffix] gives its actually published relative outputs; prepend the board namespace to use them as full ROS paths. Unresolved rows have topic: null.
The parallel output_info[group][suffix] map records each published canonical output's message_type and unit. For example, this is an excerpt from a resolved LTR-390 row, using an illustrative bus/address:
{
"topic": "ltr_2d390_b1_53",
"public_topics": {
"x": {
"ambient": "ltr_2d390_b1_53/ambient",
"UVI": "ltr_2d390_b1_53/UVI"
}
},
"output_info": {
"x": {
"ambient": {"message_type": "sensor_msgs/msg/Illuminance", "unit": "lux"},
"UVI": {"message_type": "std_msgs/msg/Float64", "unit": "index"}
}
}
}
The full row also includes generic outputs and device identity/state fields. Group x is the ordinary payload group; other groups have separate paths. A null unit means unspecified/unitless or no single measurement unit, for example an IMU with several physical quantities. Outputs appear after their first published sample and retire with their publishers. Alias paths are listed separately; an alias that reserves a canonical path can suppress that canonical output. See sensor names, lifetime and aliases.
Illuminance and LTR-390
With the existing LTR-390 descriptor, ambient publishes sensor_msgs/msg/Illuminance in lux and UVI publishes std_msgs/msg/Float64 in the descriptor's index unit. No optional ROS block is needed. Attribute case is retained: UVI and uvi are different suffixes.
For example, a board named /axiom might publish /axiom/ltr_2d390_b1_53/ambient and /axiom/ltr_2d390_b1_53/UVI. These are examples; copy your actual complete topic paths:
LIGHT_TOPIC='PASTE_FULL_ILLUMINANCE_TOPIC_HERE'
UV_TOPIC='PASTE_FULL_UVI_TOPIC_HERE'
ros2 topic type "$LIGHT_TOPIC"
ros2 topic echo "$LIGHT_TOPIC" sensor_msgs/msg/Illuminance \
--once --qos-reliability best_effort
ros2 topic type "$UV_TOPIC"
ros2 topic echo "$UV_TOPIC" std_msgs/msg/Float64 \
--once --qos-reliability best_effort
Read illuminance for lux and data for UVI. Illuminance carries the sample's timestamp and configured sensor frame; variance: 0 means the variance is unknown. Negative or unavailable illuminance is NaN, not a measured zero. UVI has no header or unit field; use inventory for its unit and sample for measurement timing and validity.
Automatic Illuminance mapping recognises scalar attributes named ambient, als, white, visible, illuminance or light (case insensitive), with a known lux conversion. The rule applies across compatible profiles rather than only one sensor model. A channel such as ir stays a numeric fallback even if its descriptor labels it lux; that label alone does not identify a photometric light quantity.
Scalar fallbacks
| Decoded attribute | ROS output | Value and invalid-reading behavior |
|---|---|---|
| Numeric scalar | std_msgs/msg/Float64 | Decoded SI value when available; otherwise decoded engineering value and original unit. Invalid or nonfinite readings are NaN. |
| Boolean | std_msgs/msg/Bool | Descriptor o: bool or a ? wire field. Invalid readings are omitted. |
| Text | std_msgs/msg/String | Decoded scalar text. Invalid readings are omitted. |
| Array or structured value | No generic scalar topic | Remains in sample/data, or uses an existing dedicated image/grid adapter. The sound spectrum viewer reads bands directly from sample. |
The decoder already applies masks, divisors, offsets and other descriptor transforms. The ROS adapter does not apply them twice or replace an unavailable reading with a raw counter. Unknown units stay unknown. A unit such as index, ppm or ticks is not silently relabelled as another physical quantity. Numeric scalars use Float64 even when the wire field is an integer, because the engineering result may be fractional. Integer values above 2^53 can lose Float64 precision; sample retains the raw value.
By default, existing semantic/compound adapters consume the attributes they represent. IMU axes, range values, temperature/humidity, servo position, battery voltage and image pixels/dimensions are not also published as automatic scalar copies. Other fields, such as status, counters and auxiliary readings, may get scalar topics. Generic sample and data still contain the full decoded row.
Float64, Bool and String carry only data, with no measurement timestamp, frame or unit field. A headerless fallback is useful for plotting a value, but use sample when timing, validity or raw/decoded values matter. sample.values_json contains raw, engineering and SI values, their units and validity; data is a JSON convenience view. Retain device_metadata with a recording when you need the complete decoder profile.
Aliases and repeated sensor types
Automatic paths retain model, bus and address, so multiple sensors of the same type stay distinct. Fallback suffixes retain attribute case and escape unsafe characters. A name that would be hidden, start with a digit or collide with reserved adapter output names gains an attribute_ prefix; for example, a numeric attribute named data publishes as attribute_data. Read the actual suffix from inventory.
Aliases choose an existing output; they do not change its message type or install a profile. Merge this fragment into the intended board's existing settings, retaining its connection/identity configuration:
topic_aliases:
light: {type: LTR-390, source: ambient}
uv: {type: LTR-390, source: UVI}
For an illustrative /axiom board, these add /axiom/light and /axiom/uv. An alias binds only when exactly one matching sensor is online, resolved and fresh. If several LTR-390 sensors match, add string bus and address selectors copied from inventory, or use their distinct canonical paths. Header-bearing aliases retain the same timestamp/frame; headerless aliases remain headerless. Canonical and alias QoS overrides match exact topic paths. Restart affected processes after changing read-only board configuration.
Optional descriptor mappings
This section requires a host driver containing the mapping support described under Availability. A mapping block is not required for the LTR-390 defaults above.
Add a ros block inside a sensor descriptor's devInfoJson, alongside its existing type, resp and actions. This JSON is a fragment to merge into that metadata object, not a complete Device Library file:
{
"ros": {
"version": 1,
"attributes": {
"photodiode": {"message_type": "sensor_msgs/msg/Illuminance"},
"UVI": {"message_type": "std_msgs/msg/Float64"}
}
}
}
Every key must exactly match an attribute's n in the existing resp.a array. Replace photodiode with the actual name in your profile; an existing LTR-390 uses ambient. An unfamiliar scalar name can explicitly request Illuminance, but its descriptor must already supply meaningful lux values. The mapping is received by the host as devinfo.ros; it is not a new library-envelope field, a boards.yaml option or a native firmware ROS configuration.
Version 1 accepts exactly version and attributes. Each attribute declaration contains exactly message_type, chosen from this list:
| Message type | Required decoded input | Published numeric/text field |
|---|---|---|
| sensor_msgs/msg/Illuminance | Numeric scalar with normalised unit lux | illuminance; lux |
| sensor_msgs/msg/Temperature | Numeric scalar with normalised unit degC | temperature; °C |
| sensor_msgs/msg/RelativeHumidity | Numeric scalar with normalised unit fraction, e.g. converted from %RH | relative_humidity; fraction |
| std_msgs/msg/Float64 | Numeric scalar | data; SI when known, otherwise engineering value |
| std_msgs/msg/Bool | Descriptor boolean | data; boolean |
| std_msgs/msg/String | Decoded scalar text | data; text |
Mapping declarations select types; they do not supply a unit conversion, scale, topic rename, script or arbitrary message-field assignment. The ordinary descriptor and decoder still determine values, units and validity. The output suffix remains the escaped attribute name. Semantic scalar messages use the sample timestamp, configured frame and unknown variance 0. Invalid number/bool/text handling follows the rules above.
Precedence and fallback
- A valid declaration selects that attribute's scalar output type. Mapping temperature to Float64, for example, replaces its automatic Temperature output at the same suffix.
- Existing compound IMU, range, image, joint and battery outputs remain available. An explicit component mapping, such as gx to Float64, additionally exposes that scalar; unmapped components remain consumed by their adapters.
- Unmapped attributes use the existing automatic semantic mapping or scalar fallback. A profile without ros behaves as before.
- An unsupported version or malformed block is ignored with a warning. Unknown attributes, unsupported message types, extra declaration fields, arrays, incompatible scalar types or incompatible units retain the previous automatic handling for that attribute. Valid peer declarations still apply.
- Warnings appear once per issue per device revision. A field absent from one payload group is skipped there rather than fabricated.
- Metadata changes retire old publishers before replacement types appear, including aliases. The output_info inventory reports the resulting type and unit.
Use the Device Library workflow to edit and install a profile, preserving other entries in the library. A saved library file becomes effective after an Axiom restart; editing JSON alone does not change the active descriptor. After applying it with a driver containing mapping support, inspect device_metadata for the returned ros block, inspect devices.output_info and check the topic's actual type. The existing metadata path carries the declaration; firmware and Raft do not interpret it as a native ROS instruction.
After installing a mapping, verify the actual returned metadata and published message type on your board. Software validation records are available in the repository validation notes.
Plot the values in Foxglove
Start the bridge and connect the viewer using the Foxglove scalar plotting instructions. Choose numeric fields directly: Illuminance uses .illuminance, Temperature uses .temperature, RelativeHumidity uses .relative_humidity and Float64 uses .data. These outputs do not require extracting numbers from JSON strings. Use separate plots for different units and label their axes accordingly.
Troubleshooting
| Symptom | Check |
|---|---|
| Only sample and data on an older driver | Update, rebuild, source and restart the host driver that includes Illuminance/scalar outputs. Confirm the installed executable comes from the intended workspace. |
| Numeric fallback instead of an expected semantic type | Check the attribute name and normalised units. Explicit selection requires a host driver containing mapping support; update, rebuild, source and restart the driver if needed. |
| Mapping warning | Read the named attribute/error, correct the declaration or units, or remove the optional declaration to retain default behavior. Other usable outputs should continue. |
| Bool/text field stops updating | Inspect validity in sample; invalid bool/text readings are omitted rather than publishing an invented value. A viewer may still show its last received value. |
| Alias absent but canonical topic exists | Check exact source case and suffix, board namespace and whether multiple sensors make the match ambiguous. |
| Viewer has a topic but no fresh values | Check publisher count, driver acquisition, sensor state, validity and the shared ROS environment. A subscriber can keep a topic name visible after its publisher retires. |
Continue with Sensors and multiple boards or Foxglove dashboards.