Build · Write a service
Write a service¶
Create a hook-based counter in the project from Quickstart. The generator owns lifecycle plumbing; your C file owns behavior.
01 · Declare the counter¶
Create services/counter/service.toml:
[service]
name = "example.counter"
version = "0.1.0"
[service.build]
api = "counter"
lifecycle = "hooks"
sources = ["src/service.c"]
descriptor = "micros_descriptor"
[service.config]
[service.types.Count]
type = "uint32"
[service.commands.increment]
returns = { count = "Count" }
From the application directory:
./micros scaffold-service services/counter
./micros generate --service services/counter
Scaffolding creates src/service.c and src/counter_state.h once; it refuses to
overwrite them. Generation updates derived files under build/services/counter/generated/.
02 · Implement behavior¶
In counter_state.h, replace the placeholder reserved field with:
uint32_t count;
In service.c, replace the generated counter_handle_increment body:
micros_result_t counter_handle_increment(counter_context_t *ctx,
uint32_t *out_count)
{
*out_count = ++ctx->state.count;
return MICROS_OK;
}
Leave the generated start/step/stop stubs returning MICROS_OK. State begins
zeroed for each lifetime; this unsigned counter wraps after UINT32_MAX and
resets on restart. It is a demonstration, not persistent storage.
Hooks execute serially on the service worker and must return in bounded time. Generated lifecycle code opens declared resources before start and releases them after stop, including partial startup cleanup. Do not close generated-owned handles inside your hooks. See Service API for the native contract.
03 · Select it on your board¶
Add to your unit file, alongside its existing identity and board:
[services.counter]
source = "../services/counter"
contract = "service.toml"
memory = { arena = "RAM", stack_bytes = 4096 }
expose_commands = ["increment"]
Run ./micros compose to validate. Exposing commands adds transport resources;
see Service commands before deploying. On a supported
ESP32 base, deploy, open ./micros shell -d NAME, and run:
call example.counter increment
The first completed call returns count 1; the next returns 2. Without command exposure the service can load, but the remote call is unavailable.
04 · Change the contract¶
After committing an application baseline, compare later edits with:
./micros contract-diff services/counter --base HEAD
./micros scaffold-service services/counter --preview --base HEAD
./micros generate --service services/counter
The preview prints changed hook signatures without replacing your implementation.
Bounds or enum changes can alter behavior without a C signature change; the diff
makes them visible. Add behavior tests in your project's tests/ directory;
./micros test runs *_test.py files.
The starter hello uses the supported manual ABI lifecycle. Its complete source remains the example for that style; new hook services need not duplicate its resource plumbing.