Table of Contents

Standard messages, scalar fallbacks and descriptor mappings

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

Illuminance and scalar fallbacks are included in the published main source from commit 2e0c1f6 onward. After updating source, rebuild, source the workspace and restart affected ROS processes as described in Install and build. An old installed driver does not gain new outputs merely by importing a viewer layout.

Optional descriptor mappings are a development preview as of 16 October 2026. The ros.version: 1 implementation described below is not yet committed or pushed to the published main branch. It requires a driver checkout containing that implementation; an ordinary pull of main currently supplies Illuminance/scalar fallbacks but does not interpret the optional block. The normal sensor outputs work without it. This page separates the available defaults from that preview so the schema can be reviewed without implying it is already released.

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 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: development preview

This section requires the optional-mapping development driver described under Availability. It 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

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 that includes the preview, 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.

The implementation has automated ROS Jazzy coverage with simulated firmware peers. Physical Device Library upload/restart/readback and Pi/Kilted acceptance of the mapping block remain pending; this is not a claim of a completed hardware test.

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 the optional-mapping driver; main currently ignores that preview block.
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.