Skip to content

Build · Connect services

Connect services

Services exchange typed messages through named, bounded channels. Each subscriber has its own read position; one reader does not consume another's message.

01 · Define one shared message

In your project, create protos/reading/reading.proto:

syntax = "proto3";
message Reading {
  uint32 value = 1;
}

Beside it, create interface.toml:

[interface]
interface = "example.reading"
schema = "reading.proto"

[delivery]
policy = "latest"

In the producer's services/sensor/service.toml, add:

[service.publications.reading]
interface = "../../protos/reading/interface.toml"

In the consumer's services/display/service.toml, add:

[service.subscriptions.reading]
interface = "../../protos/reading/interface.toml"

These are additions to complete service contracts, not implementations of a sensor or display. Select both services in your unit and add:

[channels.reading]

The same-name ports attach to this channel. Its contract and payload capacity are derived from the shared interface. Exact protobuf compatibility is checked; the current format does not author major/minor versions.

02 · Generate and implement

./micros generate
./micros compose

Read each generated API_ipc.h for the actual type and operation names. A manual service opens the generated publication/subscription, publishes or reads typed messages, and closes the handle. Hook services receive generated port handles and subscription callbacks; application code still publishes values explicitly. Use scaffold-service --preview for the current hook signatures.

The implemented DHT source shows publication and cleanup; its hardware driver is ESP32-specific. TOML alone does not implement message production or consumption.

03 · Choose retention

Policy Retains Reader behavior
latest Newest value, depth 1 Intermediate values can be skipped.
stream Bounded sequence A slow reader can lose messages.

For a stream, change [delivery].policy in the shared interface and select a unit depth, for example [channels.reading] with depth = 8. A subscriber may require min_depth; a smaller explicit unit depth is rejected.

Manual reads return MICROS_ERROR_EMPTY when there is no unread value. MICROS_ERROR_MESSAGES_MISSED reports a stream gap and returns no message on that call; handle the gap, then read again. Generated hook services receive their loss callback. Sequence/loss metadata is per subscriber.

Successful publication means bytes were copied, not that another service acted. Add timestamps/validity to your own schema if freshness matters. Separate channel reads are not an atomic snapshot, and retained messages are not automatically new actions after restart.

For a network boundary, use Coordination. For remote visualization, use Service channels; capture and coordination have distinct declarations and resource costs.