Skip to main content

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>;