Interface control document
1. Identification
| Item | Value |
|---|---|
| Document | ICD-CMEMOTIBIT-001 rev 1 |
| Product | EmotiBit module (Emotibit in the module registry, settings section HEAT.EmotibitModule) |
| Product versions covered | 47b58fc |
| Peer | EmotiBit device on the local network, reached through the bundled EmotibitBridge library |
| Host | HEAT Capture, HEAT.Capture.ModuleSDK 1.0.141 |
| Data model | SimulationFrame carrying one Entity with BiometricSensorComponent readings |
| Date | 2026-09-02 |
| Owner | VRAI Simulation |
2. Scope
This document defines the interface between the module and EmotibitBridge, the configuration the
module passes into that bridge, and the readings the module publishes to HEAT Capture.
The module is receive-only with respect to measurement data. It issues no device commands of its own: everything that reaches the EmotiBit is issued by the bridge.
The bridge’s own on-the-wire protocol is not defined by this repository. See section 5.
3. Interface inventory
| ID | Name | Direction | Transport | Peer |
|---|---|---|---|---|
| IF-CMEMOTIBIT-01 | Bridge interop surface | Bidirectional | In-process managed calls and callbacks | EmotibitBridge assembly |
| IF-CMEMOTIBIT-02 | Bridge configuration | Module to bridge | EmotibitConfig instance passed at construction | EmotibitBridge assembly |
| IF-CMEMOTIBIT-03 | Frame publication | Module to host | Module SDK publication path | HEAT Capture |
4. IF-CMEMOTIBIT-01 Bridge interop surface
4.1 Nature of the interface
EmotibitBridge.dll is a managed .NET assembly, not a native library, so there is no P/Invoke,
marshalling or manual memory ownership anywhere in this module. It is referenced as a plain
assembly reference with a HintPath of EmotibitBridge.dll and is copied to the output directory
on every build. The assembly is stored in Git LFS and the release workflow fails the build if it is
absent after the LFS pull.
| Item | Value |
|---|---|
| Assembly name | EmotibitBridge |
| Assembly version | 1.0.0.0 |
| Informational version | 1.0.0+5bfae5821a494c0f2458bea5fd144ef907f6ffe6 |
| Target framework | .NETCoreApp,Version=v9.0 |
| Build configuration | Debug |
| Namespaces used | Emotibit.Core, SenseXR.Core.Demo.OSC.Emotibit.Utils |
The module targets net10.0 and consumes a net9.0 assembly. That works by framework roll-forward
but it is not a matched pair, and the shipped binary is a Debug build.
4.2 Members the module calls
| Member | Use in this module |
|---|---|
new EmotibitManager(EmotibitConfig, bool debug) | Constructed once in InitialiseModule. The debug argument is the DebugLogs setting. |
EmotibitManager.RegisterSensorCallback(string, Action<string, List<float>, EmotibitHeader>) | Called once per whitelisted type tag on capture start. |
EmotibitManager.UnregisterSensorCallback(string) | Called once per whitelisted type tag on capture end. |
EmotibitManager.Update(float) | Called once per capture cycle, always with 0. |
EmotibitManager.Disconnect() | Called on capture end. |
EmotibitManager also exposes Connect() and OnDataReceived(...). This module never calls
either. It never calls Connect() at any point in the lifecycle: the only thing that can start
discovery is construction of the manager. Nothing in the module observes connection state, so there
is no property or event it can consult to report whether a device is attached.
The deltaTime argument to Update is always passed as 0, so any bridge-side logic that depends
on elapsed time between updates receives no elapsed time from this module.
4.3 Callback contract and data ownership
Sensor data arrives through a delegate with the signature
(string sensorName, List<float> values, EmotibitHeader header).
| Parameter | Content |
|---|---|
sensorName | The two-character EmotiBit type tag the callback was registered for. |
values | One or more samples for that tag. Several samples can arrive in one callback when the device batches them into a single packet. |
header | EmotibitHeader, a sequential-layout type with fields Timestamp, PacketNumber, DataLength, TypeTag, ProtocolVersion and DataReliability. |
Both values and header are ordinary managed objects owned by the bridge. The module reads them
inside the callback and retains nothing: each float is copied into a BiometricReading and the
header is ignored entirely. The device timestamp, packet number and reliability flag are therefore
never carried into captured data.
Registration is keyed by tag string. A tag that the bridge never produces registers a callback that is never invoked, and no error is raised for an unknown tag.
4.4 Module lifecycle
| Phase | Action |
|---|---|
InitialiseModule | Copies settings into an EmotibitConfig, snapshots SensorWhiteList, constructs the EmotibitManager, seeds the entity state with the fixed entity id, and logs Initialising Emotibit Data Module.. then Attempting to discover and connect to an Emotibit via Wifi.... |
StartCapture | Logs Mapped Emotibit Sensor Callbacks for collection and registers one callback per whitelisted tag. Returns false if the manager is null. |
HandleCapture | Calls Update(0), then publishes a frame if any reading accumulated, then resets the entity state. |
EndCapture | Unregisters every callback, calls Disconnect(), and logs Disconnected from the Emotibit. |
InitialiseModule and StartCapture are bounded by the SDK StartupTimeout. The module does not
override it, so the default 5 seconds applies. HandleCapture is untimed. The module does not
override CleanupAsync, so the manager is never disposed or torn down beyond Disconnect().
The connect step and the disconnect step are not symmetric. Connection is a side effect of
constructing the manager during initialisation, which the host performs once, while EndCapture
disconnects. A second capture session in the same loaded module re-registers callbacks but has no
path back to a connected bridge, so it can stay silent for the whole session. This is audit finding
EMO-01 and it is a property of this revision. Treat a restart within one loaded module as
unsupported: reload the module, or restart HEAT Capture, between sessions.
4.5 Timing and cadence
The bridge runs its own advertising and data threads, so packets arrive independently of the capture
loop. The module accumulates readings as callbacks fire and drains them on the next cycle, which
runs at the SDK Interval setting, 1000 ms by default. Every sample received in the window is
published; nothing is averaged or dropped. Cycles with no data publish nothing.
Because callbacks and the capture cycle are separate flows, the entity-state builder is mutated by whichever thread the bridge dispatches callbacks on and read by the capture thread. The module applies no locking around it.
4.6 Error behaviour
| Condition | Behaviour |
|---|---|
| No EmotiBit found or reachable | No callback fires, HandleCapture returns null, and no frame is published. The module logs nothing and reports no failure; absence of data is the only signal. |
| Manager is null at capture start | StartCapture returns false; the SDK treats the start as failed. |
| Malformed or empty packet | Handled inside the bridge and never surfaced to the module. |
| Exception in a sensor callback | Propagates on the bridge’s dispatch path; the module installs no handler. |
Exception in HandleCapture | Logged by the SDK as “Error in module” and the capture loop continues. |
| Capture restarted in the same loaded module | See EMO-01 in 4.4. |
5. What this repository does not define
The bridge’s protocol with the device is implemented inside EmotibitBridge.dll, whose source is
not part of this repository. The assembly’s metadata shows the shape of that transport without
specifying it: it uses System.Net.Sockets UDP sockets, keeps separate advertising, data and
control connections, supports broadcast and unicast discovery, tracks discovered devices by IP and
device id, and carries a packet header and type-tag vocabulary matching the EmotiBit protocol.
Concrete port numbers, packet framing, delimiter characters, handshake sequence, ping and
availability timeouts, and the meaning of the DataReliability field are not stated anywhere in
this repository and are not documented here. The authorities are the bridge’s own source repository
and EmotiBit’s published protocol and data-format documentation. Do not infer them from this
document.
6. IF-CMEMOTIBIT-02 Bridge configuration
Every module setting except the generic SDK ones exists solely to populate EmotibitConfig at
initialisation. The mapping is field-for-field with the same names.
| Setting | Default | Effect on the interface |
|---|---|---|
SendAdvertisingInterval | 1000 | Milliseconds between advertising blasts. |
CheckAdvertisingInterval | 100 | Milliseconds between checks for advertising replies. |
AdvertisingThreadSleep | 1000 | Microseconds slept between advertising thread loops. |
DataThreadSleep | 1000 | Microseconds slept between data thread loops. |
EnableBroadcast | true | Enables broadcast discovery. |
EnableUnicast | true | Enables unicast scanning. |
DebugLogs | false | Also passed as the manager’s debug constructor argument. |
CollectMaxNumberOfPackets | 0 | Packets collected per pass; 0 or less means all available. |
UnicastIpRangeLowerBound | 2 | First host octet scanned. |
UnicastIpRangeUpperBound | 254 | Last host octet scanned. |
NUnicastIpsPerLoop | 1 | Addresses probed per loop. |
UnicastMinLoopDelay | 3 | Minimum delay between unicast loops. |
LocalIPs | 192.168.1.188, 192.168.1.167 | Local addresses offered to the bridge for discovery. |
NetworkIncludeList | *.*.*.* | Networks eligible for discovery. |
NetworkExcludeList | 0.0.0.0 | Networks excluded from discovery. |
SensorWhiteList | the 20 tags in 7.1 | The set of tags callbacks are registered for. Not passed to the bridge. |
Units follow the source comments: the two *Interval settings are milliseconds and the two
*ThreadSleep settings are microseconds. UnicastMinLoopDelay carries no unit in source and none
is asserted here.
The LocalIPs default is two specific addresses on a 192.168.1.0/24 network. These are
development-network values, not neutral defaults, and they are absent from the repository README’s
settings table. On any other network they must be set explicitly.
Settings are read through IOptionsMonitor, but every bridge-affecting value is read once during
initialisation, including SensorWhiteList. Changing any of them mid-session has no effect;
Interval is the only setting re-read per cycle, by the SDK.
See Manage capture modules for the generic settings and the SDK contract for the lifecycle those settings drive.
7. IF-CMEMOTIBIT-03 Frame publication and data model reference
| Item | Value |
|---|---|
| Entity id | 1234, a compile-time constant |
| Entity name | Not set |
| Components | One BiometricSensorComponent per callback received in the window |
| Reading name | The raw two-character type tag, unmapped |
| Reading value | The float as delivered by the bridge |
| Capture time | UTC Unix milliseconds, taken when the frame is built |
A cycle with no readings publishes nothing. The entity state is rebuilt with the same fixed id after each publication, so readings never carry over between cycles.
Each callback contributes its own BiometricSensorComponent rather than merging into one, so an
entity in a published frame typically carries as many biometric components as callbacks fired during
the window, each holding the samples from one packet. Consumers must iterate all components rather
than assuming a single one.
The fixed entity id means the module supports exactly one EmotiBit. Two devices on the network cannot be distinguished, and the module has no setting to select one.
7.1 Type tags requested by default
EA, EL, ER, PI, PR, PG, TH, AX, AY, AZ, GX, GY, GZ, MX, MY, MZ,
HR, BI, SA, SF.
The module attaches no meaning to these strings; it registers and republishes them verbatim. Their signal meaning, sensor of origin and sampling rate are defined by EmotiBit’s documentation, not by this module. See Data produced.
8. Units and conventions
| Concept | Convention |
|---|---|
| Reading values | Passed through unconverted, unscaled and unvalidated, in whatever unit the device reports |
| Reading identity | Raw EmotiBit type tag string |
| Time base | Host wall clock, UTC Unix milliseconds, applied at frame build |
| Device time | The Timestamp and PacketNumber in EmotibitHeader are discarded and never published |
Because only host time is recorded, samples batched into one packet all appear under a single frame timestamp and their true intra-packet spacing is lost. Do not treat the frame timestamp as a per-sample acquisition time.
9. Compatibility and versioning
There is no version negotiation of any kind. The module depends on the exact managed surface of the
bundled EmotibitBridge 1.0.0.0 assembly: a change to the EmotibitConfig field set, the
EmotibitManager method signatures, or the callback delegate shape is a breaking change, caught at
compile time rather than at runtime. Because the assembly is committed to the repository, the bridge
version is pinned by the module revision and cannot be upgraded independently.
The type-tag strings are the only loosely coupled part of the interface. A firmware or bridge change that renames a tag silently stops the corresponding reading, because an unmatched registration is not an error.
The repository README documents twelve settings and places the bridge in a bridge/ directory.
Neither matches this revision: the assembly sits at the repository root and four further settings
exist. Treat this document and the source as authoritative over the README.
10. Security considerations
The bridge broadcasts and, by default, unicast-scans the local network across host octets 2 to 254
to find a device. That is active network scanning from the capture workstation, and on a managed
network it may be flagged. Restrict it with NetworkIncludeList, NetworkExcludeList or by
disabling EnableUnicast.
There is no authentication and no encryption on any part of this path. Any device on the network answering the bridge’s advertising is accepted, and any host that can see the traffic can observe it. EmotiBit measurements are physiological personal data: restrict network access and handle captured sessions according to the deployment’s own data-handling policy.
11. Conformance
- Set
LocalIPsto the capture workstation’s actual address before the first run. The default values are development-network addresses. - Power an EmotiBit on the same network, start a capture session, and confirm the log records
Initialising Emotibit Data Module..,Attempting to discover and connect to an Emotibit via Wifi...andMapped Emotibit Sensor Callbacks for collection. - Confirm one entity with id
1234appears in captured data, and that readings appear under the raw tag names in 7.1. - Confirm a consumer reads every biometric component on that entity, rather than only the first, by checking that tags from different packets in one window are all present.
- Set
DebugLogstotrueand confirm bridge-level discovery logging appears; return it tofalsefor normal operation. - Verify EMO-01 applies to your deployment: end the capture session, start it again without reloading the module, and confirm whether readings resume. Until EMO-01 is fixed, reload the module between sessions.
- Confirm that no frame is published when no device is present, and that the absence is not reported as an error anywhere. There is no positive connection indication to rely on.
12. Revision history
| Rev | Date | Product version | Change |
|---|---|---|---|
| 1 | 2026-09-02 | 47b58fc | Initial issue. |