Minimal Complete Tin App
This is a small host-buildable shape for a tin application. It uses:
tin::channel for bounded raw input;
- a payload event for product data;
- a queued
tsm runtime for behavior backlog;
- a caller-owned loop that drains input, steps behavior, and applies outputs.
It intentionally avoids board HAL calls so the same behavior can run in a unit test, a simulator, or a target adapter.
#include <cstdint>
#include <iostream>
struct VoltageSample {
std::uint16_t millivolts{};
};
struct MotorDrive {
struct Booting {};
struct Running {};
struct SafeHold {};
struct BootComplete {};
struct VoltageHigh {
std::uint16_t measured_mv{};
std::uint16_t limit_mv{};
};
void start() {
pwm_enabled = true;
}
void latch_voltage_fault(VoltageHigh const& event) {
pwm_enabled = false;
last_fault_mv = event.measured_mv;
fault_limit_mv = event.limit_mv;
}
bool pwm_enabled{};
std::uint16_t fault_limit_mv{ 3000 };
std::uint16_t last_fault_mv{};
tsm::T<Running,
VoltageHigh,
SafeHold,
&MotorDrive::latch_voltage_fault>>;
};
using DriveRuntime =
struct MotorApp {
bool publish_voltage_sample(VoltageSample sample) {
return samples.try_send(sample);
}
void step() {
VoltageSample sample{};
while (samples.try_receive(sample)) {
if (sample.millivolts > drive.context().fault_limit_mv) {
(void)drive.send_event(MotorDrive::VoltageHigh{
.measured_mv = sample.millivolts,
.limit_mv = drive.context().fault_limit_mv });
}
}
(void)drive.step();
pwm_duty = drive.context().pwm_enabled ? 420U : 0U;
}
DriveRuntime drive{};
std::uint16_t pwm_duty{};
};
int main() {
(void)
app.drive.send_event(MotorDrive::BootComplete{});
(void)
app.publish_voltage_sample(VoltageSample{ .millivolts = 3300 });
std::cout <<
"pwm=" <<
app.pwm_duty
<<
" fault_mv=" <<
app.drive.context().last_fault_mv <<
"\n";
}
runtime::Runtime< Definition, Policy, MachinePolicy > Runtime
Definition: runtime.h:35
consteval auto transitions(TransitionEntries...)
Definition: tsm.h:2448
transition_table< TransitionEntries... > Ts
Definition: tsm.h:2445
runtime::app< Definition, RuntimePolicy, Executor, TimerSlots > app
Definition: app.h:165
Tin runtime-kernel facade.
Tin state-machine layer compatibility header.
What The Example Demonstrates
The driver-shaped ingress point calls publish_voltage_sample. It does not know about states.
The application loop owns execution. It drains bounded input, enqueues typed events into the runtime, steps the runtime, and applies output state.
The HSM owns behavior. It decides that VoltageHigh in Running enters SafeHold and records diagnostic data through the event payload.
The output value is plain data. A board adapter could later map pwm_duty to a timer peripheral, while a host test can inspect the same value directly.
Natural Extensions
Use an actor group when several local components should be stepped in a fixed order. Use a coroutine task when the bridge waits on channels, signals, timers, or multiple asynchronous sources. Use tio and thal when the ingress or egress side needs portable I/O contracts or target profile checks.