forked from firmata/arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FirmataMarshaller.cpp
431 lines (391 loc) · 15.6 KB
/
FirmataMarshaller.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
/*
FirmataMarshaller.cpp
Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
*/
//******************************************************************************
//* Includes
//******************************************************************************
#include "FirmataMarshaller.h"
#if defined(__cplusplus) && !defined(ARDUINO)
#include <cstring>
#else
#include <string.h>
#endif
#include "FirmataConstants.h"
using namespace firmata;
//******************************************************************************
//* Support Functions
//******************************************************************************
/**
* Request or halt a stream of analog readings from the Firmata host application. The range of pins is
* limited to [0..15] when using the REPORT_ANALOG. The maximum result of the REPORT_ANALOG is limited to 14 bits
* (16384). To increase the pin range or value, see the documentation for the EXTENDED_ANALOG
* message.
* @param pin The analog pin for which to request the value (limited to pins 0 - 15).
* @param stream_enable A zero value will disable the stream, a non-zero will enable the stream
* @note The maximum resulting value is 14-bits (16384).
*/
void FirmataMarshaller::reportAnalog(uint8_t pin, bool stream_enable)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
// pin can only be 0-15, so chop higher bits
FirmataStream->write(REPORT_ANALOG | (pin & 0xF));
FirmataStream->write(stream_enable);
}
/**
* Request or halt an 8-bit port stream from the Firmata host application (protocol v2 and later).
* Send 14-bits in a single digital message (protocol v1).
* @param portNumber The port number for which to request the value. Note that this is not the same as a "port" on the
* physical microcontroller. Ports are defined in order per every 8 pins in ascending order
* of the Arduino digital pin numbering scheme. Port 0 = pins D0 - D7, port 1 = pins D8 - D15, etc.
* @param stream_enable A zero value will disable the stream, a non-zero will enable the stream
*/
void FirmataMarshaller::reportDigitalPort(uint8_t portNumber, bool stream_enable)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(REPORT_DIGITAL | (portNumber & 0xF));
FirmataStream->write(stream_enable);
}
/**
* An alternative to the normal analog message, this extended version allows addressing beyond
* pin 15 and supports sending analog values with any number of bits.
* @param pin The analog pin to which the value is sent.
* @param bytec The size of the storage for the analog value
* @param bytev The pointer to the location of the analog value
*/
void FirmataMarshaller::sendExtendedAnalog(uint8_t pin, size_t bytec, uint8_t * bytev)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(START_SYSEX);
FirmataStream->write(EXTENDED_ANALOG);
FirmataStream->write(pin);
transformByteStreamToMessageBytes(bytec, bytev, bytec);
FirmataStream->write(END_SYSEX);
}
/**
* Transform 8-bit stream into 7-bit message
* @param bytec The number of data bytes in the message.
* @param bytev A pointer to the array of data bytes to send in the message.
* @param max_bytes Force message to be n bytes, regardless of data bits.
*/
void FirmataMarshaller::transformByteStreamToMessageBytes (size_t bytec, uint8_t * bytev, size_t max_bytes)
const
{
static const size_t transmit_bits = 7;
static const uint8_t transmit_mask = ((1 << transmit_bits) - 1);
size_t bytes_sent = 0;
size_t outstanding_bits = 0;
uint8_t outstanding_bit_cache = *bytev;
if ( !max_bytes ) { max_bytes = static_cast<size_t>(-1); }
for (size_t i = 0 ; (i < bytec) && (bytes_sent < max_bytes) ; ++i) {
uint8_t transmit_byte = (outstanding_bit_cache|(bytev[i] << outstanding_bits));
FirmataStream->write(transmit_mask & transmit_byte);
++bytes_sent;
outstanding_bit_cache = (bytev[i] >> (transmit_bits - outstanding_bits));
outstanding_bits = (outstanding_bits + (8 - transmit_bits));
for ( ; (outstanding_bits >= transmit_bits) && (bytes_sent < max_bytes) ; ) {
transmit_byte = outstanding_bit_cache;
FirmataStream->write(transmit_mask & transmit_byte);
++bytes_sent;
outstanding_bit_cache >>= transmit_bits;
outstanding_bits -= transmit_bits;
}
}
if ( outstanding_bits && (bytes_sent < max_bytes) ) {
FirmataStream->write(static_cast<uint8_t>((1 << outstanding_bits) - 1) & outstanding_bit_cache);
}
}
//******************************************************************************
//* Constructors
//******************************************************************************
/**
* The FirmataMarshaller class.
*/
FirmataMarshaller::FirmataMarshaller()
:
FirmataStream((Stream *)NULL)
{
}
//******************************************************************************
//* Public Methods
//******************************************************************************
/**
* Reassign the Firmata stream transport.
* @param s A reference to the Stream transport object. This can be any type of
* transport that implements the Stream interface. Some examples include Ethernet, WiFi
* and other UARTs on the board (Serial1, Serial2, etc).
*/
void FirmataMarshaller::begin(Stream &s)
{
FirmataStream = &s;
}
/**
* Closes the FirmataMarshaller stream by setting its stream reference to `(Stream *)NULL`
*/
void FirmataMarshaller::end(void)
{
FirmataStream = (Stream *)NULL;
}
//******************************************************************************
//* Output Stream Handling
//******************************************************************************
/**
* Query the target's firmware name and version
*/
void FirmataMarshaller::queryFirmwareVersion(void)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(START_SYSEX);
FirmataStream->write(REPORT_FIRMWARE);
FirmataStream->write(END_SYSEX);
}
/**
* Query the target's Firmata protocol version
*/
void FirmataMarshaller::queryVersion(void)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(REPORT_VERSION);
}
/**
* Halt the stream of analog readings from the Firmata host application. The range of pins is
* limited to [0..15] when using the REPORT_ANALOG. The maximum result of the REPORT_ANALOG is limited to 14 bits
* (16384). To increase the pin range or value, see the documentation for the EXTENDED_ANALOG
* message.
* @param pin The analog pin for which to request the value (limited to pins 0 - 15).
*/
void FirmataMarshaller::reportAnalogDisable(uint8_t pin)
const
{
reportAnalog(pin, false);
}
/**
* Request a stream of analog readings from the Firmata host application. The range of pins is
* limited to [0..15] when using the REPORT_ANALOG. The maximum result of the REPORT_ANALOG is limited to 14 bits
* (16384). To increase the pin range or value, see the documentation for the EXTENDED_ANALOG
* message.
* @param pin The analog pin for which to request the value (limited to pins 0 - 15).
*/
void FirmataMarshaller::reportAnalogEnable(uint8_t pin)
const
{
reportAnalog(pin, true);
}
/**
* Halt an 8-bit port stream from the Firmata host application (protocol v2 and later).
* Send 14-bits in a single digital message (protocol v1).
* @param portNumber The port number for which to request the value. Note that this is not the same as a "port" on the
* physical microcontroller. Ports are defined in order per every 8 pins in ascending order
* of the Arduino digital pin numbering scheme. Port 0 = pins D0 - D7, port 1 = pins D8 - D15, etc.
*/
void FirmataMarshaller::reportDigitalPortDisable(uint8_t portNumber)
const
{
reportDigitalPort(portNumber, false);
}
/**
* Request an 8-bit port stream from the Firmata host application (protocol v2 and later).
* Send 14-bits in a single digital message (protocol v1).
* @param portNumber The port number for which to request the value. Note that this is not the same as a "port" on the
* physical microcontroller. Ports are defined in order per every 8 pins in ascending order
* of the Arduino digital pin numbering scheme. Port 0 = pins D0 - D7, port 1 = pins D8 - D15, etc.
*/
void FirmataMarshaller::reportDigitalPortEnable(uint8_t portNumber)
const
{
reportDigitalPort(portNumber, true);
}
/**
* Send an analog message to the Firmata host application. The range of pins is limited to [0..15]
* when using the ANALOG_MESSAGE. The maximum value of the ANALOG_MESSAGE is limited to 14 bits
* (16384). To increase the pin range or value, see the documentation for the EXTENDED_ANALOG
* message.
* @param pin The analog pin to which the value is sent.
* @param value The value of the analog pin (0 - 1024 for 10-bit analog, 0 - 4096 for 12-bit, etc).
* @note The maximum value is 14-bits (16384).
*/
void FirmataMarshaller::sendAnalog(uint8_t pin, uint16_t value)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
if ( (0xF >= pin) && (0x3FFF >= value) ) {
FirmataStream->write(ANALOG_MESSAGE|pin);
transformByteStreamToMessageBytes(sizeof(value), reinterpret_cast<uint8_t *>(&value), sizeof(value));
} else {
sendExtendedAnalog(pin, sizeof(value), reinterpret_cast<uint8_t *>(&value));
}
}
/**
* Send an analog mapping query to the Firmata host application. The resulting sysex message will
* have an ANALOG_MAPPING_RESPONSE command byte, followed by a list of pins [0-n]; where each
* pin will specify its corresponding analog pin number or 0x7F (127) if not applicable.
*/
void FirmataMarshaller::sendAnalogMappingQuery(void)
const
{
sendSysex(ANALOG_MAPPING_QUERY, 0, NULL);
}
/**
* Send a capability query to the Firmata host application. The resulting sysex message will have
* a CAPABILITY_RESPONSE command byte, followed by a list of byte tuples (mode and mode resolution)
* for each pin; where each pin list is terminated by 0x7F (127).
*/
void FirmataMarshaller::sendCapabilityQuery(void)
const
{
sendSysex(CAPABILITY_QUERY, 0, NULL);
}
/**
* Send a single digital pin value to the Firmata host application.
* @param pin The digital pin to send the value of.
* @param value The value of the pin.
*/
void FirmataMarshaller::sendDigital(uint8_t pin, uint8_t value)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(SET_DIGITAL_PIN_VALUE);
FirmataStream->write(pin & 0x7F);
FirmataStream->write(value != 0);
}
/**
* Send an 8-bit port in a single digital message (protocol v2 and later).
* Send 14-bits in a single digital message (protocol v1).
* @param portNumber The port number to send. Note that this is not the same as a "port" on the
* physical microcontroller. Ports are defined in order per every 8 pins in ascending order
* of the Arduino digital pin numbering scheme. Port 0 = pins D0 - D7, port 1 = pins D8 - D15, etc.
* @param portData The value of the port. The value of each pin in the port is represented by a bit.
*/
void FirmataMarshaller::sendDigitalPort(uint8_t portNumber, uint16_t portData)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(DIGITAL_MESSAGE | (portNumber & 0xF));
// Tx bits 0-6 (protocol v1 and higher)
// Tx bits 7-13 (bit 7 only for protocol v2 and higher)
transformByteStreamToMessageBytes(sizeof(portData), reinterpret_cast<uint8_t *>(&portData), sizeof(portData));
}
/**
* Sends the firmware name and version to the Firmata host application.
* @param major The major verison number
* @param minor The minor version number
* @param bytec The length of the firmware name
* @param bytev The firmware name array
*/
void FirmataMarshaller::sendFirmwareVersion(uint8_t major, uint8_t minor, size_t bytec, uint8_t *bytev)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
size_t i;
FirmataStream->write(START_SYSEX);
FirmataStream->write(REPORT_FIRMWARE);
FirmataStream->write(major);
FirmataStream->write(minor);
for (i = 0; i < bytec; ++i) {
transformByteStreamToMessageBytes(sizeof(bytev[i]), reinterpret_cast<uint8_t *>(&bytev[i]), sizeof(bytev[i]));
}
FirmataStream->write(END_SYSEX);
}
/**
* Send the Firmata protocol version to the Firmata host application.
* @param major The major verison number
* @param minor The minor version number
*/
void FirmataMarshaller::sendVersion(uint8_t major, uint8_t minor)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(REPORT_VERSION);
FirmataStream->write(major);
FirmataStream->write(minor);
}
/**
* Send the pin mode/configuration. The pin configuration (or mode) in Firmata represents the
* current function of the pin. Examples are digital input or output, analog input, pwm, i2c,
* serial (uart), etc.
* @param pin The pin to configure.
* @param config The configuration value for the specified pin.
*/
void FirmataMarshaller::sendPinMode(uint8_t pin, uint8_t config)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(SET_PIN_MODE);
FirmataStream->write(pin);
FirmataStream->write(config);
}
/**
* Send a pin state query to the Firmata host application. The resulting sysex message will have
* a PIN_STATE_RESPONSE command byte, followed by the pin number, the pin mode and a stream of
* bits to indicate any *data* written to the pin (pin state).
* @param pin The pin to query
* @note The pin state is any data written to the pin (i.e. pin state != pin value)
*/
void FirmataMarshaller::sendPinStateQuery(uint8_t pin)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(START_SYSEX);
FirmataStream->write(PIN_STATE_QUERY);
FirmataStream->write(pin);
FirmataStream->write(END_SYSEX);
}
/**
* Send a sysex message where all values after the command byte are packet as 2 7-bit bytes
* (this is not always the case so this function is not always used to send sysex messages).
* @param command The sysex command byte.
* @param bytec The number of data bytes in the message (excludes start, command and end bytes).
* @param bytev A pointer to the array of data bytes to send in the message.
*/
void FirmataMarshaller::sendSysex(uint8_t command, size_t bytec, uint8_t *bytev)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
size_t i;
FirmataStream->write(START_SYSEX);
FirmataStream->write(command);
for (i = 0; i < bytec; ++i) {
transformByteStreamToMessageBytes(sizeof(bytev[i]), reinterpret_cast<uint8_t *>(&bytev[i]), sizeof(bytev[i]));
}
FirmataStream->write(END_SYSEX);
}
/**
* Send a string to the Firmata host application.
* @param string A pointer to the char string
*/
void FirmataMarshaller::sendString(const char *string)
const
{
sendSysex(STRING_DATA, strlen(string), reinterpret_cast<uint8_t *>(const_cast<char *>(string)));
}
/**
* The sampling interval sets how often analog data and i2c data is reported to the client.
* @param interval_ms The interval (in milliseconds) at which to sample
* @note The default sampling interval is 19ms
*/
void FirmataMarshaller::setSamplingInterval(uint16_t interval_ms)
const
{
sendSysex(SAMPLING_INTERVAL, sizeof(interval_ms), reinterpret_cast<uint8_t *>(&interval_ms));
}
/**
* Perform a software reset on the target. For example, StandardFirmata.ino will initialize
* everything to a known state and reset the parsing buffer.
*/
void FirmataMarshaller::systemReset(void)
const
{
if ( (Stream *)NULL == FirmataStream ) { return; }
FirmataStream->write(SYSTEM_RESET);
}