Reference for triggers, events, and conditional mechanisms that control Plan behavior and activation.
Pro Tip
Use debouncing and hysteresis for sensor triggers to avoid oscillation. A robot that rapidly switches between "obstacle detected" and "clear" states is harder to work with than one that uses a deadband - for example, trigger at 25cm but don't clear until 35cm.
Activate when sensor crosses threshold:
// Activate when distance drops below 30cm
trigger on sensors.distance.front < 30 {
activate("ObstacleStop")
}
// Deactivate when safe
trigger on sensors.distance.front > 50 {
deactivate("ObstacleStop")
}
Activate when sensor is within range:
trigger on sensors.distance.front in 20..40 {
slow_down()
}
Combine multiple sensor conditions:
trigger on (sensors.distance.front < 30 &&
sensors.distance.left < 30) {
// Trapped in corner
backup_and_turn()
}
Activate on value change:
// Trigger on any change
trigger on change(sensors.line.center) {
log("Line detection changed")
}
// Trigger when value increases
trigger on rising(sensors.light) {
log("Light increasing")
}
// Trigger when value decreases
trigger on falling(battery.level) {
if battery.level < 20 {
activate("ReturnToBase")
}
}
Execute after delay:
trigger after 5000 {
// Runs 5 seconds after Plan starts
log("Warmup complete")
}
Execute repeatedly:
trigger every 1000 {
// Runs every second
log("Status check")
check_sensors()
}
Execute at specific times:
trigger at time.hour == 22 {
// Run at 10 PM
activate("NightMode")
}
trigger at time.hour == 6 {
// Run at 6 AM
deactivate("NightMode")
activate("DayMode")
}
Cron-style scheduling:
// Every hour
trigger schedule "0 * * * *" {
upload_status()
}
// Every day at midnight
trigger schedule "0 0 * * *" {
reset_daily_stats()
}
// Weekdays at 9 AM
trigger schedule "0 9 * * 1-5" {
start_work_routine()
}
React to Plan state changes:
trigger on plan_activated("EmergencyStop") {
// Stop all other activities
deactivate_group("normal_ops")
}
trigger on plan_deactivated("EmergencyStop") {
// Resume normal operations
activate_group("normal_ops")
}
React to variable changes:
var mode = "idle"
trigger on mode == "active" {
log("Entering active mode")
start_motors()
}
trigger on mode == "idle" {
log("Entering idle mode")
stop_motors()
}
React to system conditions:
trigger on battery.low {
activate("LowBatteryMode")
}
trigger on battery.charging {
log("Now charging")
}
trigger on connection_lost("RemoteControl") {
activate("AutonomousMode")
}
trigger on connection_restored("RemoteControl") {
deactivate("AutonomousMode")
}
React to location:
trigger on position.x > 100 {
log("Crossed X=100 boundary")
}
trigger on distance_to(home_position) < 10 {
log("Arrived home")
}
React to entering/leaving zones:
var danger_zone = {x: 50, y: 50, radius: 20}
trigger on entering_zone(danger_zone) {
log("Warning: Entering danger zone")
slow_down()
}
trigger on leaving_zone(danger_zone) {
log("Left danger zone")
resume_speed()
}
var boundary = [
{x: 0, y: 0},
{x: 100, y: 0},
{x: 100, y: 100},
{x: 0, y: 100}
]
trigger on outside_boundary(boundary) {
log("Outside operating area!")
navigate_inside()
}
Handle incoming messages:
on_message("command") { data ->
log("Received command: " + data.action)
execute_command(data)
}
on_message("emergency") { _ ->
emergency_stop()
}
Handle specific message content:
on_message("command") where data.action == "stop" {
stop()
}
on_message("command") where data.action == "go" {
drive(50, 50)
}
Handle broadcast messages:
on_broadcast("status_request") { sender ->
send_to(sender, "status_response", get_status())
}
// When specific sensor triggers
on_sensor("bump.front") { triggered ->
if triggered {
backup()
}
}
// When sensor error occurs
on_sensor_error("distance.front") { error ->
log_error("Front sensor error: " + error.message)
use_backup_sensor()
}
on_hardware_connected() {
log("Hardware connected")
calibrate_sensors()
}
on_hardware_disconnected() {
log_error("Hardware lost!")
activate("SafeMode")
}
on_button_press("panic") {
emergency_stop()
}
on_button_release("hold") {
resume()
}
| Operator | Description | Example |
|---|---|---|
== |
Equal | state == "active" |
!= |
Not equal | state != "error" |
< |
Less than | speed < 50 |
> |
Greater than | speed > 50 |
<= |
Less or equal | battery <= 20 |
>= |
Greater or equal | battery >= 80 |
| Operator | Description | Example |
|---|---|---|
&& |
AND | a > 0 && b > 0 |
|| |
OR | error || warning |
! |
NOT | !active |
// Value in range
if value in 10..50 {
// value is between 10 and 50
}
// Value not in range
if value not in 10..50 {
// value is outside 10-50
}
if typeof(value) == "number" {
process_number(value)
}
if typeof(value) == "string" {
process_string(value)
}
// Check for null
if value == null {
value = default_value
}
// Null coalescing
var safe_value = value ?? default_value
var result = match state {
"idle" -> 0
"moving" -> 1
"paused" -> 2
_ -> -1 // Default case
}
match sensor_reading {
x where x < 0 -> log("Invalid reading")
x where x < 30 -> activate("TooClose")
x where x < 60 -> log("Caution range")
_ -> log("Safe distance")
}
match incoming_data {
{type: "command", action: a} -> execute(a)
{type: "status"} -> send_status()
[head, ...tail] -> process_list(head, tail)
_ -> log("Unknown data format")
}
Block until condition is true:
// Wait for sensor
wait_for(sensors.distance.front < 30)
// Wait with timeout
wait_for(sensors.distance.front < 30, 5000) // 5 second timeout
Wait for any condition:
var which = wait_for_any([
() -> sensors.distance.front < 30,
() -> sensors.bump.front,
() -> battery.low
])
match which {
0 -> handle_distance()
1 -> handle_bump()
2 -> handle_battery()
}
Wait for all conditions:
wait_for_all([
() -> sensors.calibrated,
() -> motors.ready,
() -> gps.fix
])
log("All systems ready")
Prevent rapid triggering:
// Require condition to be stable for 500ms
trigger on debounce(sensors.bump.front, 500) {
// Only triggers if bump stays true for 500ms
handle_bump()
}
Limit trigger frequency:
// Max once per second
trigger on rate_limit(sensors.distance.front < 30, 1000) {
log("Obstacle detected")
}
Filter noisy sensors:
// Moving average of last 10 readings
var smooth_distance = smooth(sensors.distance.front, 10)
trigger on smooth_distance < 30 {
// Only trigger on stable readings
stop()
}
var all_clear = all([
sensors.distance.front > 50,
sensors.distance.left > 30,
sensors.distance.right > 30
])
if all_clear {
drive_forward()
}
var obstacle = any([
sensors.distance.front < 30,
sensors.bump.front,
sensors.bump.left,
sensors.bump.right
])
if obstacle {
stop()
}
var safe = none([
sensors.bump.front,
sensors.bump.left,
sensors.bump.right,
battery.low
])
if safe {
continue_operation()
}
// Define auto-activation rules
auto_activate("ObstacleStop") when sensors.distance.front < 20
auto_activate("LowBattery") when battery.level < 15
auto_activate("NightMode") when time.hour >= 22 || time.hour < 6
// With priority
auto_activate("Emergency", {priority: 100}) when sensors.bump.front
// Define conditional groups
condition_group "autonomous_conditions" {
sensors.distance.front > 30,
!sensors.bump.front,
battery.level > 10
}
// Check group
if check_conditions("autonomous_conditions") {
activate("AutonomousPlan")
} else {
activate("SafeMode")
}
Check most important conditions first:
loop {
// Safety first
if sensors.bump.front || sensors.distance.front < 10 {
emergency_stop()
continue
}
// Battery second
if battery.low {
activate("ReturnToBase")
continue
}
// Normal operation
do_normal_work()
delay(50)
}
Prevent oscillation:
var THRESHOLD_LOW = 25
var THRESHOLD_HIGH = 35
var is_close = false
loop {
var dist = sensors.distance.front
// Different thresholds for on/off
if !is_close && dist < THRESHOLD_LOW {
is_close = true
slow_down()
} else if is_close && dist > THRESHOLD_HIGH {
is_close = false
resume_speed()
}
delay(50)
}
Always handle timeouts:
try {
var result = wait_for(condition, 5000)
process_result(result)
} catch error {
if error.code == "TIMEOUT" {
log_warn("Condition not met in time")
handle_timeout()
} else {
throw error
}
}