Skip to Content
This documentation is provided with the HEAT environment and describes this HEAT instance.

Interface control document

1. Identification

ItemValue
DocumentICD-CMEMOTIBIT-001 rev 1
ProductEmotiBit module (Emotibit in the module registry, settings section HEAT.EmotibitModule)
Product versions covered47b58fc
PeerEmotiBit device on the local network, reached through the bundled EmotibitBridge library
HostHEAT Capture, HEAT.Capture.ModuleSDK 1.0.141
Data modelSimulationFrame carrying one Entity with BiometricSensorComponent readings
Date2026-09-02
OwnerVRAI 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

IDNameDirectionTransportPeer
IF-CMEMOTIBIT-01Bridge interop surfaceBidirectionalIn-process managed calls and callbacksEmotibitBridge assembly
IF-CMEMOTIBIT-02Bridge configurationModule to bridgeEmotibitConfig instance passed at constructionEmotibitBridge assembly
IF-CMEMOTIBIT-03Frame publicationModule to hostModule SDK publication pathHEAT 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.

ItemValue
Assembly nameEmotibitBridge
Assembly version1.0.0.0
Informational version1.0.0+5bfae5821a494c0f2458bea5fd144ef907f6ffe6
Target framework.NETCoreApp,Version=v9.0
Build configurationDebug
Namespaces usedEmotibit.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

MemberUse 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).

ParameterContent
sensorNameThe two-character EmotiBit type tag the callback was registered for.
valuesOne or more samples for that tag. Several samples can arrive in one callback when the device batches them into a single packet.
headerEmotibitHeader, 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

PhaseAction
InitialiseModuleCopies 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....
StartCaptureLogs Mapped Emotibit Sensor Callbacks for collection and registers one callback per whitelisted tag. Returns false if the manager is null.
HandleCaptureCalls Update(0), then publishes a frame if any reading accumulated, then resets the entity state.
EndCaptureUnregisters 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

ConditionBehaviour
No EmotiBit found or reachableNo 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 startStartCapture returns false; the SDK treats the start as failed.
Malformed or empty packetHandled inside the bridge and never surfaced to the module.
Exception in a sensor callbackPropagates on the bridge’s dispatch path; the module installs no handler.
Exception in HandleCaptureLogged by the SDK as “Error in module” and the capture loop continues.
Capture restarted in the same loaded moduleSee 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.

SettingDefaultEffect on the interface
SendAdvertisingInterval1000Milliseconds between advertising blasts.
CheckAdvertisingInterval100Milliseconds between checks for advertising replies.
AdvertisingThreadSleep1000Microseconds slept between advertising thread loops.
DataThreadSleep1000Microseconds slept between data thread loops.
EnableBroadcasttrueEnables broadcast discovery.
EnableUnicasttrueEnables unicast scanning.
DebugLogsfalseAlso passed as the manager’s debug constructor argument.
CollectMaxNumberOfPackets0Packets collected per pass; 0 or less means all available.
UnicastIpRangeLowerBound2First host octet scanned.
UnicastIpRangeUpperBound254Last host octet scanned.
NUnicastIpsPerLoop1Addresses probed per loop.
UnicastMinLoopDelay3Minimum delay between unicast loops.
LocalIPs192.168.1.188, 192.168.1.167Local addresses offered to the bridge for discovery.
NetworkIncludeList*.*.*.*Networks eligible for discovery.
NetworkExcludeList0.0.0.0Networks excluded from discovery.
SensorWhiteListthe 20 tags in 7.1The 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

ItemValue
Entity id1234, a compile-time constant
Entity nameNot set
ComponentsOne BiometricSensorComponent per callback received in the window
Reading nameThe raw two-character type tag, unmapped
Reading valueThe float as delivered by the bridge
Capture timeUTC 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

ConceptConvention
Reading valuesPassed through unconverted, unscaled and unvalidated, in whatever unit the device reports
Reading identityRaw EmotiBit type tag string
Time baseHost wall clock, UTC Unix milliseconds, applied at frame build
Device timeThe 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

  1. Set LocalIPs to the capture workstation’s actual address before the first run. The default values are development-network addresses.
  2. 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... and Mapped Emotibit Sensor Callbacks for collection.
  3. Confirm one entity with id 1234 appears in captured data, and that readings appear under the raw tag names in 7.1.
  4. 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.
  5. Set DebugLogs to true and confirm bridge-level discovery logging appears; return it to false for normal operation.
  6. 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.
  7. 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

RevDateProduct versionChange
12026-09-0247b58fcInitial issue.
Opens your browser's print dialog. Choose "Save as PDF" as the destination.