0
# SysEx Messaging
1
2
Custom system exclusive messaging for extended Arduino functionality beyond standard Firmata operations, enabling custom protocols and advanced device communication.
3
4
## Capabilities
5
6
### SysEx Message Transmission
7
8
Send custom SysEx messages to Arduino for extended functionality and custom protocols.
9
10
```javascript { .api }
11
/**
12
* Send SysEx (System Exclusive) message to Arduino
13
* Enables custom protocols and extended functionality beyond standard Firmata
14
* @param command - SysEx command byte (0-127, automatically masked to 7-bit)
15
* @param data - Optional array of data bytes (each automatically masked to 7-bit)
16
* @param callback - Optional callback function called when message is sent
17
*/
18
sysex(command: number, data?: number[], callback?: Function): void;
19
```
20
21
**Parameters:**
22
- **command**: Custom command byte (0-127)
23
- **data**: Array of data bytes (each 0-127, automatically masked to 7-bit values)
24
- **callback**: Optional function called after message transmission
25
26
**Usage Examples:**
27
28
```javascript
29
// Send simple command without data
30
arduino.sysex(0x01);
31
32
// Send command with data array
33
arduino.sysex(0x01, [13, 5, 2]); // Command 0x01 with three data bytes
34
35
// With callback
36
arduino.sysex(0x01, [13, 5, 2], function() {
37
console.log('SysEx message sent');
38
});
39
40
// Send empty data array explicitly
41
arduino.sysex(0x05, [], function() {
42
console.log('Command 0x05 sent without data');
43
});
44
```
45
46
### SysEx Message Reception
47
48
Handle incoming SysEx messages from Arduino for bidirectional custom communication.
49
50
```javascript { .api }
51
// Event: sysex
52
// Fired when a SysEx message is received from Arduino
53
// Event object properties:
54
interface SysexEvent {
55
command: number; // SysEx command byte (0-127)
56
data: number[]; // Array of data bytes received
57
}
58
```
59
60
**Usage Example:**
61
62
```javascript
63
arduino.on('sysex', function(event) {
64
console.log(`SysEx received - Command: 0x${event.command.toString(16)}`);
65
console.log('Data bytes:', event.data);
66
67
// Handle specific commands
68
switch(event.command) {
69
case 0x01:
70
console.log('LED blink command response received');
71
break;
72
case 0x02:
73
if (event.data.length >= 2) {
74
const sensorId = event.data[0];
75
const sensorValue = event.data[1];
76
console.log(`Sensor ${sensorId} reading: ${sensorValue}`);
77
}
78
break;
79
default:
80
console.log('Unknown SysEx command:', event.command);
81
}
82
});
83
```
84
85
### Bidirectional SysEx Communication
86
87
Complete example showing both sending and receiving SysEx messages.
88
89
```javascript
90
const ArduinoFirmata = require('arduino-firmata');
91
const arduino = new ArduinoFirmata();
92
93
arduino.connect();
94
95
arduino.on('connect', function() {
96
console.log('Arduino connected - SysEx communication ready');
97
98
// Set up SysEx message handler
99
arduino.on('sysex', function(event) {
100
console.log(`Received SysEx command: 0x${event.command.toString(16)}`);
101
console.log('Data:', event.data);
102
103
// Echo protocol - respond to command 0x10
104
if (event.command === 0x10) {
105
arduino.sysex(0x11, event.data); // Echo back with command 0x11
106
console.log('Echoed SysEx message');
107
}
108
});
109
110
// Send periodic SysEx commands
111
setInterval(function() {
112
arduino.sysex(0x01, [13, 3, 10]); // LED blink: pin 13, 3 times, 1000ms interval
113
}, 5000);
114
});
115
```
116
117
### Custom Protocol Example
118
119
Implement a custom sensor reading protocol using SysEx.
120
121
```javascript
122
class CustomSensorProtocol {
123
constructor(arduino) {
124
this.arduino = arduino;
125
this.sensorReadings = {};
126
127
// Set up SysEx handler for sensor responses
128
this.arduino.on('sysex', (event) => {
129
this.handleSysexMessage(event);
130
});
131
}
132
133
// Request sensor reading
134
requestSensor(sensorId) {
135
console.log(`Requesting reading from sensor ${sensorId}`);
136
this.arduino.sysex(0x20, [sensorId]); // Command 0x20 = read sensor
137
}
138
139
// Request all sensors
140
requestAllSensors() {
141
console.log('Requesting all sensor readings');
142
this.arduino.sysex(0x21); // Command 0x21 = read all sensors
143
}
144
145
// Handle incoming SysEx messages
146
handleSysexMessage(event) {
147
switch(event.command) {
148
case 0x30: // Sensor reading response
149
if (event.data.length >= 3) {
150
const sensorId = event.data[0];
151
const valueHigh = event.data[1];
152
const valueLow = event.data[2];
153
const value = (valueHigh << 7) | valueLow; // Reconstruct 14-bit value
154
155
this.sensorReadings[sensorId] = value;
156
console.log(`Sensor ${sensorId}: ${value}`);
157
}
158
break;
159
160
case 0x31: // All sensors response
161
console.log('All sensors data received');
162
for (let i = 0; i < event.data.length; i += 3) {
163
if (i + 2 < event.data.length) {
164
const sensorId = event.data[i];
165
const valueHigh = event.data[i + 1];
166
const valueLow = event.data[i + 2];
167
const value = (valueHigh << 7) | valueLow;
168
this.sensorReadings[sensorId] = value;
169
}
170
}
171
break;
172
}
173
}
174
175
// Get cached sensor reading
176
getSensorReading(sensorId) {
177
return this.sensorReadings[sensorId] || null;
178
}
179
}
180
181
// Usage
182
arduino.on('connect', function() {
183
const sensorProtocol = new CustomSensorProtocol(arduino);
184
185
// Request individual sensor
186
sensorProtocol.requestSensor(1);
187
188
// Request all sensors periodically
189
setInterval(() => {
190
sensorProtocol.requestAllSensors();
191
}, 2000);
192
});
193
```
194
195
### LED Control via SysEx
196
197
Example implementation matching the sample code from the repository.
198
199
```javascript
200
// LED blink control using SysEx (matches StandardFirmataWithLedBlink)
201
arduino.on('connect', function() {
202
console.log('LED blink controller ready');
203
204
// Blink LED on pin 13: 5 times with 200ms intervals
205
arduino.sysex(0x01, [13, 5, 2]); // pin, count, interval (200ms = 2 * 100ms)
206
207
// Blink LED on pin 12: 3 times with 1000ms intervals
208
arduino.sysex(0x01, [12, 3, 10]); // pin, count, interval (1000ms = 10 * 100ms)
209
210
// Handle blink completion notifications
211
arduino.on('sysex', function(event) {
212
if (event.command === 0x01) {
213
console.log('LED blink sequence completed');
214
console.log('Response data:', event.data);
215
}
216
});
217
});
218
```
219
220
### Data Encoding for SysEx
221
222
SysEx messages use 7-bit data encoding, requiring special handling for larger values.
223
224
```javascript
225
// Helper functions for encoding/decoding multi-byte values
226
function encode14bit(value) {
227
// Split 14-bit value into two 7-bit bytes
228
const high = (value >> 7) & 0x7F;
229
const low = value & 0x7F;
230
return [high, low];
231
}
232
233
function decode14bit(high, low) {
234
// Reconstruct 14-bit value from two 7-bit bytes
235
return (high << 7) | low;
236
}
237
238
// Send 14-bit sensor threshold value
239
const threshold = 5000;
240
const [thresholdHigh, thresholdLow] = encode14bit(threshold);
241
arduino.sysex(0x40, [1, thresholdHigh, thresholdLow]); // Sensor 1 threshold
242
243
// Receive and decode 14-bit values
244
arduino.on('sysex', function(event) {
245
if (event.command === 0x41 && event.data.length >= 3) {
246
const sensorId = event.data[0];
247
const value = decode14bit(event.data[1], event.data[2]);
248
console.log(`14-bit sensor ${sensorId} reading: ${value}`);
249
}
250
});
251
```
252
253
### Protocol Documentation Example
254
255
When implementing custom SysEx protocols, document the command structure:
256
257
```javascript
258
/*
259
* Custom SysEx Protocol Documentation
260
*
261
* Command 0x01 - LED Blink Control
262
* Data: [pin, count, interval]
263
* - pin: LED pin number (0-13)
264
* - count: Number of blinks (1-127)
265
* - interval: Interval in 100ms units (1-127)
266
* Response: Command 0x01 with completion status
267
*
268
* Command 0x20 - Request Sensor Reading
269
* Data: [sensor_id]
270
* - sensor_id: Sensor identifier (0-127)
271
* Response: Command 0x30 with sensor data
272
*
273
* Command 0x30 - Sensor Reading Response
274
* Data: [sensor_id, value_high, value_low]
275
* - sensor_id: Sensor identifier (0-127)
276
* - value_high: Upper 7 bits of 14-bit value
277
* - value_low: Lower 7 bits of 14-bit value
278
*/
279
280
const SYSEX_COMMANDS = {
281
LED_BLINK: 0x01,
282
REQUEST_SENSOR: 0x20,
283
SENSOR_RESPONSE: 0x30,
284
SET_CONFIG: 0x40,
285
GET_STATUS: 0x50
286
};
287
288
// Use named constants for better code maintenance
289
arduino.sysex(SYSEX_COMMANDS.LED_BLINK, [13, 5, 2]);
290
```
291
292
## Constants
293
294
```javascript { .api }
295
// SysEx protocol constants
296
ArduinoFirmata.START_SYSEX = 0xF0; // Start of SysEx message
297
ArduinoFirmata.END_SYSEX = 0xF7; // End of SysEx message
298
```
299
300
## SysEx Protocol Specifications
301
302
### Message Format
303
SysEx messages follow the MIDI SysEx format:
304
- **Start byte**: `0xF0` (START_SYSEX)
305
- **Command byte**: Custom command (0-127, 7-bit)
306
- **Data bytes**: Optional data (each 0-127, 7-bit)
307
- **End byte**: `0xF7` (END_SYSEX)
308
309
### Data Constraints
310
- All data bytes are automatically masked to 7 bits (0-127)
311
- Values above 127 are truncated: `value & 0x7F`
312
- For values > 127, use multi-byte encoding (see encoding examples above)
313
314
### Arduino-Side Implementation
315
To use SysEx messaging, your Arduino must be running firmware that handles custom SysEx commands:
316
317
```cpp
318
// Example Arduino SysEx handler (C++ code for Arduino)
319
void sysexCallback(byte command, byte argc, byte* argv) {
320
switch(command) {
321
case 0x01: // LED blink
322
if (argc >= 3) {
323
int pin = argv[0];
324
int count = argv[1];
325
int interval = argv[2] * 100; // Convert to milliseconds
326
blinkLED(pin, count, interval);
327
}
328
break;
329
}
330
}
331
```
332
333
## Use Cases
334
335
### Custom Sensors
336
- Multi-sensor data collection with timestamps
337
- Calibration parameter transmission
338
- Sensor configuration and threshold settings
339
340
### Advanced Motor Control
341
- Stepper motor control with acceleration profiles
342
- Multi-axis coordination commands
343
- Complex motion sequences
344
345
### Communication Protocols
346
- I2C device configuration via Arduino
347
- SPI device control and data transfer
348
- Custom serial protocol bridging
349
350
### Real-time Data Streaming
351
- High-frequency sensor data with custom formatting
352
- Compressed data transmission
353
- Synchronized multi-device communication
354
355
## Best Practices
356
357
### Protocol Design
358
1. **Document commands**: Maintain clear protocol documentation
359
2. **Use command constants**: Define named constants for command values
360
3. **Validate data**: Check data array lengths before processing
361
4. **Handle errors**: Implement error responses for invalid commands
362
5. **Version compatibility**: Include version information in protocols
363
364
### Performance Considerations
365
```javascript
366
// Batch SysEx messages to avoid flooding the Arduino
367
const messageQueue = [];
368
let sending = false;
369
370
function queueSysex(command, data, callback) {
371
messageQueue.push({ command, data, callback });
372
processSysexQueue();
373
}
374
375
function processSysexQueue() {
376
if (sending || messageQueue.length === 0) return;
377
378
sending = true;
379
const { command, data, callback } = messageQueue.shift();
380
381
arduino.sysex(command, data, function() {
382
sending = false;
383
if (callback) callback();
384
setTimeout(processSysexQueue, 10); // Small delay between messages
385
});
386
}
387
```
388
389
### Error Handling
390
```javascript
391
// Implement timeout for SysEx responses
392
function sysexWithTimeout(command, data, timeout = 5000) {
393
return new Promise((resolve, reject) => {
394
let responseReceived = false;
395
396
const timeoutId = setTimeout(() => {
397
if (!responseReceived) {
398
reject(new Error(`SysEx command 0x${command.toString(16)} timed out`));
399
}
400
}, timeout);
401
402
const responseHandler = (event) => {
403
if (event.command === command + 1) { // Assuming response is command + 1
404
responseReceived = true;
405
clearTimeout(timeoutId);
406
arduino.removeListener('sysex', responseHandler);
407
resolve(event.data);
408
}
409
};
410
411
arduino.on('sysex', responseHandler);
412
arduino.sysex(command, data);
413
});
414
}
415
416
// Usage with async/await
417
try {
418
const response = await sysexWithTimeout(0x20, [1]);
419
console.log('Sensor response:', response);
420
} catch (error) {
421
console.error('SysEx error:', error.message);
422
}
423
```