Complete specification for the On-Body Haptics OSC (Open Sound Control) protocol.
The On-Body Haptics system uses OSC (Open Sound Control) for communication between control applications and haptic devices. This protocol is platform-agnostic and works with both Arduino and Raspberry Pi implementations.
- Protocol: UDP (User Datagram Protocol)
- Default Port: 9999 (configurable)
- Message Format: OSC 1.0 specification
- Address Pattern:
/onbody/...
/onbody/[belt_id]/pattern [pattern_name]
- belt_id: Belt identifier (1-6) or
*for all belts - pattern_name: String (see Pattern Names below)
Examples:
/onbody/1/pattern CW # Clockwise rotation on belt 1
/onbody/2/pattern CCW # Counter-clockwise on belt 2
/onbody/*/pattern PULSE # Pulse all belts
/onbody/3/pattern CUSTOM1 # Custom pattern 1 on belt 3
| Pattern | Description |
|---|---|
CW |
Clockwise rotation around belt |
CCW |
Counter-clockwise rotation |
PULSE |
Pulse all motors simultaneously |
WAVE |
Wave pattern (intensity ramp) |
CUSTOM1 to CUSTOM7 |
Custom user-defined patterns |
OFF |
Turn off all motors |
/onbody/effect/[effect_id] [channel]
- effect_id: DRV2605L effect number (1-123)
- channel: Channel number (0-7) or omit for all channels
Examples:
/onbody/effect/14 0 # Strong click on channel 0
/onbody/effect/14 # Strong click on all channels
/onbody/effect/33 3 # Pulsing on channel 3
/onbody/channel/[channel]/effect [effect_id]
Example:
/onbody/channel/5/effect 14 # Strong click on channel 5
The DRV2605L provides 123 haptic effects. Common effects:
14- Strong Click 100%15- Sharp Click 100%16- Soft Click 100%
18- Soft Bump 100%19- Medium Bump 100%
20- Double Click 100%21- Double Click 60%
30- Triple Click 100%31- Triple Click 60%
33- Pulsing Strong 1 (100%)34- Pulsing Strong 2 (60%)35- Pulsing Medium 1 (100%)
64- Ramp Up Long Heavy65- Ramp Up Medium Heavy70- Ramp Down Long Heavy
See DRV2605L datasheet for complete effect library.
The Arduino implementation supports regex-based addressing for controlling multiple belts:
/onbody/*/pattern CW # All belts
/onbody/[12]/pattern PULSE # Belts 1 and 2
/onbody/[1-3]/pattern CCW # Belts 1, 2, and 3
Future versions may support intensity parameters:
/onbody/1/pattern CW 0.5 # Half intensity (planned)
/onbody/effect/14 0 0.75 # 75% intensity (planned)
OSC Address Pattern: /onbody/...
OSC Type Tags: Varies by message
OSC Arguments: Strings, integers, floats
Arduino Pattern (as OSC bundle):
Address: /onbody/1/pattern
Type Tag: ,s (string)
Argument: "CW"
Raspberry Pi Effect:
Address: /onbody/effect/14
Type Tag: ,i (integer)
Argument: 0
from pythonosc import udp_client
# Connect to device
client = udp_client.SimpleUDPClient("192.168.1.100", 9999)
# Arduino: Send pattern
client.send_message("/onbody/1/pattern", "CW")
client.send_message("/onbody/*/pattern", "PULSE")
# Raspberry Pi: Send effect
client.send_message("/onbody/effect/14", 0) # Channel 0
client.send_message("/onbody/effect/33", 5) # Channel 5
# Send to all channels (omit argument)
client.send_message("/onbody/effect/14")const OSC = require('osc-js');
const osc = new OSC();
osc.open({ port: 9999 });
// Arduino: Send pattern
osc.send(new OSC.Message('/onbody/1/pattern', 'CW'));
osc.send(new OSC.Message('/onbody/2/pattern', 'CCW'));
// Raspberry Pi: Send effect
osc.send(new OSC.Message('/onbody/effect/14', 0));[udpsend 192.168.1.100 9999]
|
[prepend /onbody/1/pattern]
|
[CW( [CCW( [PULSE(
# In Execute DAT
def onValueChange(channel, sampleIndex, val, prev):
# Send OSC message
op('oscout1').sendOSC('/onbody/1/pattern', ['CW'])[udpsend]
|
[send 192.168.1.100 9999(
|
[/onbody/1/pattern CW(
using UnityEngine;
using OscCore;
public class HapticController : MonoBehaviour
{
OscClient client;
void Start()
{
client = new OscClient("192.168.1.100", 9999);
}
public void SendPattern(int belt, string pattern)
{
client.Send($"/onbody/{belt}/pattern", pattern);
}
public void SendEffect(int effect, int channel)
{
client.Send($"/onbody/effect/{effect}", channel);
}
}- Receive Port: 9999 (OSC messages to device)
- Send Port: 3333 (OSC responses from device, optional)
Allow UDP traffic on port 9999:
# Linux (ufw)
sudo ufw allow 9999/udp
# Linux (iptables)
sudo iptables -A INPUT -p udp --dport 9999 -j ACCEPT
# macOS
# System Preferences > Security & Privacy > Firewall > Firewall Options
# Windows
# Windows Defender Firewall > Advanced Settings > Inbound RulesThe system performs minimal validation:
- Invalid patterns are ignored (Arduino)
- Invalid effect IDs are clamped to 1-123 (Raspberry Pi)
- Invalid channels are ignored (Raspberry Pi)
- Lost packets: OSC uses UDP, packets may be lost
- No acknowledgment: Devices don't send ACK by default
- No retry: Clients should implement retry logic if needed
# Monitor serial output
# Arduino IDE > Tools > Serial Monitor (57600 baud)# Enable verbose logging in config.py
VERBOSE_OSC = True
LOG_LEVEL = "DEBUG"
# View logs
sudo journalctl -u octopulse-server -f-
Arduino + Bluetooth: 50-100ms total latency
- Network to server: 1-10ms
- Bluetooth transmission: 40-80ms
- Arduino processing: 1-5ms
-
Raspberry Pi + I2C: 10-30ms total latency
- Network transmission: 1-10ms
- OSC parsing: 1-5ms
- I2C communication: 5-10ms
- DRV2605L processing: 1-5ms
- Arduino: ~10 patterns/second per belt
- Raspberry Pi: ~30 effects/second per channel
No built-in rate limiting. Implement in client:
import time
def send_pattern_throttled(client, belt, pattern, min_interval=0.1):
"""Send pattern with minimum interval between calls"""
client.send_message(f"/onbody/{belt}/pattern", pattern)
time.sleep(min_interval)- Run on trusted networks only
- Use firewall rules to restrict access
- Consider VPN or SSH tunneling for remote access
- See SECURITY.md for detailed security guidance
You can extend the protocol with custom namespaces:
/onbody/custom/myapp/...
Just ensure they don't conflict with standard messages.
Planned for future versions:
/onbody/intensity- Global intensity control/onbody/status- Device status queries/onbody/calibrate- Calibration commands/onbody/config- Runtime configuration updates
- Max/MSP 8.x
- TouchDesigner 2022.x
- Pure Data 0.52.x
- Unity 2021.x/2022.x
- Python 3.7+
- Node.js 14+
- OSC 1.0 specification
- UDP/IP protocol
- Network byte order (big-endian)
Questions about the OSC protocol? Ask in GitHub Discussions.