ccf_core/mbot.rs
1//! mBot2 reference sensor vocabulary.
2//!
3//! The [mBot2](https://www.makeblock.com/mbot2) is a programmable robot by Makeblock,
4//! retailing for around **$50–$80 USD**. It runs a CyberPi microcontroller with onboard
5//! light, sound, and motion sensors.
6//!
7//! This module provides a ready-to-use [`SensorVocabulary`] implementation for the mBot2's
8//! six onboard sensor dimensions. It is the vocabulary used in the CCF reference demo —
9//! the same robot that demonstrates emergent social behaviour through accumulated experience,
10//! entirely on-device with no cloud or ML model required.
11//!
12//! # Why this exists
13//!
14//! The mBot2 demo is the existence proof for what ccf-core makes possible: a sub-$100
15//! robot that develops genuine context-sensitive social behaviour. Not scripted. Not
16//! rule-based. The trust field accumulates through real interaction, and the robot's
17//! responses emerge from what it has actually experienced in each environment.
18//!
19//! This module ships as a concrete reference so you can see exactly what a production
20//! `SensorVocabulary` implementation looks like. Your own hardware vocabulary follows
21//! the same pattern — just swap in your sensor dimensions.
22//!
23//! # See also
24//!
25//! - `examples/mbot2.rs` — full simulated CCF loop for the mBot2
26//! - [`SensorVocabulary`] — the trait to implement for your own hardware
27
28use crate::vocabulary::{ContextKey, SensorVocabulary};
29
30/// mBot2 sensor vocabulary — 6-dimensional context for the CyberPi microcontroller.
31///
32/// Covers the six onboard sensor dimensions most relevant to social behaviour:
33/// ambient light, ambient sound, nearby presence, robot motion, orientation, and
34/// time of day (set by the host application).
35///
36/// ```rust
37/// use ccf_core::mbot::{MbotSensors, BrightnessBand, NoiseBand,
38/// PresenceSignature, MotionContext, Orientation, TimePeriod};
39/// use ccf_core::vocabulary::ContextKey;
40///
41/// let key = ContextKey::new(MbotSensors {
42/// brightness: BrightnessBand::Bright,
43/// noise: NoiseBand::Quiet,
44/// presence: PresenceSignature::Close,
45/// motion: MotionContext::Static,
46/// orientation: Orientation::Upright,
47/// time_period: TimePeriod::Day,
48/// });
49/// ```
50#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct MbotSensors {
53 /// Ambient light level (CyberPi light sensor).
54 pub brightness: BrightnessBand,
55 /// Ambient sound level (CyberPi microphone).
56 pub noise: NoiseBand,
57 /// Nearby presence signature (proximity / IR sensor).
58 pub presence: PresenceSignature,
59 /// Robot motion context (derived from wheel encoders).
60 pub motion: MotionContext,
61 /// Robot orientation relative to starting heading (IMU).
62 pub orientation: Orientation,
63 /// Time of day period (set by host application or RTC).
64 pub time_period: TimePeriod,
65}
66
67/// Ambient light level — quantised from the CyperPi light sensor.
68#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70pub enum BrightnessBand {
71 /// Very low ambient light (night, dark room).
72 Dark,
73 /// Moderate ambient light (indoor daytime, lamp).
74 Dim,
75 /// High ambient light (bright room, direct sunlight).
76 Bright,
77}
78
79/// Ambient sound level — quantised from the CyberPi microphone.
80#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub enum NoiseBand {
83 /// Very low ambient noise (silent room).
84 Quiet,
85 /// Moderate ambient noise (background conversation, music).
86 Moderate,
87 /// High ambient noise (crowd, machinery, shouting).
88 Loud,
89}
90
91/// Nearby presence signature — person or object detection.
92#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94pub enum PresenceSignature {
95 /// No person detected in sensor range.
96 Absent,
97 /// Person detected at a distance (outer detection zone).
98 Far,
99 /// Person detected in close proximity (inner detection zone).
100 Close,
101}
102
103/// Robot motion context — derived from wheel encoder state.
104#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub enum MotionContext {
107 /// Robot is stationary (encoders at rest).
108 Static,
109 /// Robot is moving slowly (below speed threshold).
110 Slow,
111 /// Robot is moving quickly (above speed threshold).
112 Fast,
113}
114
115/// Robot orientation relative to its starting heading (IMU pitch/roll).
116#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118pub enum Orientation {
119 /// Robot is upright — tilt within acceptable range.
120 Upright,
121 /// Robot is tilted beyond the upright threshold (picked up, on slope).
122 Tilted,
123}
124
125/// Time of day period — set by the host application or RTC.
126#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
128pub enum TimePeriod {
129 /// Daytime hours (context: active, well-lit environments typical).
130 Day,
131 /// Evening hours (context: winding down, lower light typical).
132 Evening,
133 /// Night-time hours (context: quiet, dark environments typical).
134 Night,
135}
136
137impl SensorVocabulary<6> for MbotSensors {
138 fn to_feature_vec(&self) -> [f32; 6] {
139 let b = match self.brightness {
140 BrightnessBand::Dark => 0.0,
141 BrightnessBand::Dim => 0.5,
142 BrightnessBand::Bright => 1.0,
143 };
144 let n = match self.noise {
145 NoiseBand::Quiet => 0.0,
146 NoiseBand::Moderate => 0.5,
147 NoiseBand::Loud => 1.0,
148 };
149 let p = match self.presence {
150 PresenceSignature::Absent => 0.0,
151 PresenceSignature::Far => 0.5,
152 PresenceSignature::Close => 1.0,
153 };
154 let m = match self.motion {
155 MotionContext::Static => 0.0,
156 MotionContext::Slow => 0.5,
157 MotionContext::Fast => 1.0,
158 };
159 let o = match self.orientation {
160 Orientation::Upright => 0.0,
161 Orientation::Tilted => 1.0,
162 };
163 let t = match self.time_period {
164 TimePeriod::Day => 0.0,
165 TimePeriod::Evening => 0.5,
166 TimePeriod::Night => 1.0,
167 };
168 [b, n, p, m, o, t]
169 }
170}
171
172/// Type alias for the canonical mBot2 context key.
173pub type MbotContextKey = ContextKey<MbotSensors, 6>;