17
17
18
18
#include " FirmataParser.h"
19
19
20
+ #include " FirmataConstants.h"
21
+
20
22
// ******************************************************************************
21
23
// * Constructors
22
24
// ******************************************************************************
23
25
24
26
/* *
25
- * The Firmata class.
26
- * An instance named "Firmata" is created automatically for the user.
27
+ * The FirmataParser class.
28
+ * @param dataBuffer A pointer to an external buffer used to store parsed data
29
+ * @param dataBufferSize The size of the external buffer
27
30
*/
28
- FirmataParser::FirmataParser ()
31
+ FirmataParser::FirmataParser (uint8_t * const dataBuffer, size_t dataBufferSize )
29
32
:
30
- executeMultiByteCommand(0 ),
31
- multiByteChannel(0 ),
32
- waitForData(0 ),
33
- parsingSysex(false ),
34
- sysexBytesRead(0 ),
35
- currentAnalogCallback((callbackFunction)NULL),
36
- currentDigitalCallback((callbackFunction)NULL),
37
- currentReportAnalogCallback((callbackFunction)NULL),
38
- currentReportDigitalCallback((callbackFunction)NULL),
39
- currentPinModeCallback((callbackFunction)NULL),
40
- currentPinValueCallback((callbackFunction)NULL),
41
- currentReportFirmwareCallback((systemCallbackFunction)NULL),
42
- currentReportVersionCallback((systemCallbackFunction)NULL),
43
- currentSystemResetCallback((systemCallbackFunction)NULL),
44
- currentStringCallback((stringCallbackFunction)NULL),
45
- currentSysexCallback((sysexCallbackFunction)NULL)
33
+ dataBuffer(dataBuffer),
34
+ dataBufferSize(dataBufferSize),
35
+ executeMultiByteCommand(0 ),
36
+ multiByteChannel(0 ),
37
+ waitForData(0 ),
38
+ parsingSysex(false ),
39
+ sysexBytesRead(0 ),
40
+ currentDataBufferOverflowCallbackContext((void *)NULL),
41
+ currentAnalogCallback((callbackFunction)NULL),
42
+ currentDigitalCallback((callbackFunction)NULL),
43
+ currentReportAnalogCallback((callbackFunction)NULL),
44
+ currentReportDigitalCallback((callbackFunction)NULL),
45
+ currentPinModeCallback((callbackFunction)NULL),
46
+ currentPinValueCallback((callbackFunction)NULL),
47
+ currentReportFirmwareCallback((systemCallbackFunction)NULL),
48
+ currentReportVersionCallback((systemCallbackFunction)NULL),
49
+ currentSystemResetCallback((systemCallbackFunction)NULL),
50
+ currentStringCallback((stringCallbackFunction)NULL),
51
+ currentSysexCallback((sysexCallbackFunction)NULL),
52
+ currentDataBufferOverflowCallback((dataBufferOverflowCallbackFunction)NULL)
46
53
{
54
+ allowBufferUpdate = ((uint8_t *)NULL == dataBuffer);
47
55
}
48
56
49
57
// ******************************************************************************
@@ -53,47 +61,6 @@ FirmataParser::FirmataParser()
53
61
// ------------------------------------------------------------------------------
54
62
// Serial Receive Handling
55
63
56
- /* *
57
- * Process incoming sysex messages. Handles REPORT_FIRMWARE and STRING_DATA internally.
58
- * Calls callback function for STRING_DATA and all other sysex messages.
59
- * @private
60
- */
61
- void FirmataParser::processSysexMessage (void )
62
- {
63
- switch (storedInputData[0 ]) { // first byte in buffer is command
64
- case REPORT_FIRMWARE:
65
- if (currentReportFirmwareCallback)
66
- (*currentReportFirmwareCallback)();
67
- break ;
68
- case STRING_DATA:
69
- if (currentStringCallback) {
70
- size_t bufferLength = (sysexBytesRead - 1 ) / 2 ;
71
- size_t i = 1 ;
72
- size_t j = 0 ;
73
- while (j < bufferLength) {
74
- // The string length will only be at most half the size of the
75
- // stored input buffer so we can decode the string within the buffer.
76
- storedInputData[j] = storedInputData[i];
77
- i++;
78
- storedInputData[j] += (storedInputData[i] << 7 );
79
- i++;
80
- j++;
81
- }
82
- // Make sure string is null terminated. This may be the case for data
83
- // coming from client libraries in languages that don't null terminate
84
- // strings.
85
- if (storedInputData[j - 1 ] != ' \0 ' ) {
86
- storedInputData[j] = ' \0 ' ;
87
- }
88
- (*currentStringCallback)((char *)&storedInputData[0 ]);
89
- }
90
- break ;
91
- default :
92
- if (currentSysexCallback)
93
- (*currentSysexCallback)(storedInputData[0 ], sysexBytesRead - 1 , storedInputData + 1 );
94
- }
95
- }
96
-
97
64
/* *
98
65
* Parse data from the input stream.
99
66
* @param inputData A single byte to be added to the parser.
@@ -110,43 +77,43 @@ void FirmataParser::parse(uint8_t inputData)
110
77
processSysexMessage ();
111
78
} else {
112
79
// normal data byte - add to buffer
113
- storedInputData[sysexBytesRead] = inputData;
114
- sysexBytesRead++ ;
80
+ bufferDataAtPosition ( inputData, sysexBytesRead) ;
81
+ ++sysexBytesRead ;
115
82
}
116
83
} else if ( (waitForData > 0 ) && (inputData < 128 ) ) {
117
- waitForData-- ;
118
- storedInputData[waitForData] = inputData;
84
+ --waitForData ;
85
+ bufferDataAtPosition ( inputData, waitForData) ;
119
86
if ( (waitForData == 0 ) && executeMultiByteCommand ) { // got the whole message
120
87
switch (executeMultiByteCommand) {
121
88
case ANALOG_MESSAGE:
122
89
if (currentAnalogCallback) {
123
90
(*currentAnalogCallback)(multiByteChannel,
124
- (storedInputData [0 ] << 7 )
125
- + storedInputData [1 ]);
91
+ (dataBuffer [0 ] << 7 )
92
+ + dataBuffer [1 ]);
126
93
}
127
94
break ;
128
95
case DIGITAL_MESSAGE:
129
96
if (currentDigitalCallback) {
130
97
(*currentDigitalCallback)(multiByteChannel,
131
- (storedInputData [0 ] << 7 )
132
- + storedInputData [1 ]);
98
+ (dataBuffer [0 ] << 7 )
99
+ + dataBuffer [1 ]);
133
100
}
134
101
break ;
135
102
case SET_PIN_MODE:
136
103
if (currentPinModeCallback)
137
- (*currentPinModeCallback)(storedInputData [1 ], storedInputData [0 ]);
104
+ (*currentPinModeCallback)(dataBuffer [1 ], dataBuffer [0 ]);
138
105
break ;
139
106
case SET_DIGITAL_PIN_VALUE:
140
107
if (currentPinValueCallback)
141
- (*currentPinValueCallback)(storedInputData [1 ], storedInputData [0 ]);
108
+ (*currentPinValueCallback)(dataBuffer [1 ], dataBuffer [0 ]);
142
109
break ;
143
110
case REPORT_ANALOG:
144
111
if (currentReportAnalogCallback)
145
- (*currentReportAnalogCallback)(multiByteChannel, storedInputData [0 ]);
112
+ (*currentReportAnalogCallback)(multiByteChannel, dataBuffer [0 ]);
146
113
break ;
147
114
case REPORT_DIGITAL:
148
115
if (currentReportDigitalCallback)
149
- (*currentReportDigitalCallback)(multiByteChannel, storedInputData [0 ]);
116
+ (*currentReportDigitalCallback)(multiByteChannel, dataBuffer [0 ]);
150
117
break ;
151
118
}
152
119
executeMultiByteCommand = 0 ;
@@ -197,6 +164,31 @@ const
197
164
return (waitForData > 0 || parsingSysex);
198
165
}
199
166
167
+ /* *
168
+ * Provides a mechanism to either set or update the working buffer of the parser.
169
+ * The method will be enabled when no buffer has been provided, or an overflow
170
+ * condition exists.
171
+ * @param dataBuffer A pointer to an external buffer used to store parsed data
172
+ * @param dataBufferSize The size of the external buffer
173
+ */
174
+ int FirmataParser::setDataBufferOfSize (uint8_t * dataBuffer, size_t dataBufferSize)
175
+ {
176
+ int result;
177
+
178
+ if ( !allowBufferUpdate ) {
179
+ result = __LINE__;
180
+ } else if ((uint8_t *)NULL == dataBuffer) {
181
+ result = __LINE__;
182
+ } else {
183
+ this ->dataBuffer = dataBuffer;
184
+ this ->dataBufferSize = dataBufferSize;
185
+ allowBufferUpdate = false ;
186
+ result = 0 ;
187
+ }
188
+
189
+ return result;
190
+ }
191
+
200
192
/* *
201
193
* Attach a generic sysex callback function to a command (options are: ANALOG_MESSAGE,
202
194
* DIGITAL_MESSAGE, REPORT_ANALOG, REPORT DIGITAL, SET_PIN_MODE and SET_DIGITAL_PIN_VALUE).
@@ -249,9 +241,21 @@ void FirmataParser::attach(uint8_t command, stringCallbackFunction newFunction)
249
241
*/
250
242
void FirmataParser::attach (uint8_t command, sysexCallbackFunction newFunction)
251
243
{
244
+ (void )command;
252
245
currentSysexCallback = newFunction;
253
246
}
254
247
248
+ /* *
249
+ * Attach a buffer overflow callback
250
+ * @param newFunction A reference to the buffer overflow callback function to attach.
251
+ * @param context The context supplied by the end-user, and provided during the execution of the callback
252
+ */
253
+ void FirmataParser::attach (dataBufferOverflowCallbackFunction newFunction, void * context)
254
+ {
255
+ currentDataBufferOverflowCallback = newFunction;
256
+ currentDataBufferOverflowCallbackContext = context;
257
+ }
258
+
255
259
/* *
256
260
* Detach a callback function for a specified command (such as SYSTEM_RESET, STRING_DATA,
257
261
* ANALOG_MESSAGE, DIGITAL_MESSAGE, etc).
@@ -272,10 +276,91 @@ void FirmataParser::detach(uint8_t command)
272
276
}
273
277
}
274
278
279
+ /* *
280
+ * Detach the buffer overflow callback
281
+ * @param <unused> Any pointer of type dataBufferOverflowCallbackFunction.
282
+ */
283
+ void FirmataParser::detach (dataBufferOverflowCallbackFunction)
284
+ {
285
+ currentDataBufferOverflowCallback = (dataBufferOverflowCallbackFunction)NULL ;
286
+ currentDataBufferOverflowCallbackContext = (void *)NULL ;
287
+ }
288
+
275
289
// ******************************************************************************
276
290
// * Private Methods
277
291
// ******************************************************************************
278
292
293
+ /* *
294
+ * Buffer abstraction to prevent memory corruption
295
+ * @param data The byte to put into the buffer
296
+ * @param pos The position to insert the byte into the buffer
297
+ * @return writeError A boolean to indicate if an error occured
298
+ * @private
299
+ */
300
+ bool FirmataParser::bufferDataAtPosition (const uint8_t data, const size_t pos)
301
+ {
302
+ bool bufferOverflow = (pos >= dataBufferSize);
303
+
304
+ // Notify of overflow condition
305
+ if ( bufferOverflow
306
+ && ((dataBufferOverflowCallbackFunction)NULL != currentDataBufferOverflowCallback) )
307
+ {
308
+ allowBufferUpdate = true ;
309
+ currentDataBufferOverflowCallback (currentDataBufferOverflowCallbackContext);
310
+ // Check if overflow was resolved during callback
311
+ bufferOverflow = (pos >= dataBufferSize);
312
+ }
313
+
314
+ // Write data to buffer if no overflow condition persist
315
+ if ( !bufferOverflow )
316
+ {
317
+ dataBuffer[pos] = data;
318
+ }
319
+
320
+ return bufferOverflow;
321
+ }
322
+
323
+ /* *
324
+ * Process incoming sysex messages. Handles REPORT_FIRMWARE and STRING_DATA internally.
325
+ * Calls callback function for STRING_DATA and all other sysex messages.
326
+ * @private
327
+ */
328
+ void FirmataParser::processSysexMessage (void )
329
+ {
330
+ switch (dataBuffer[0 ]) { // first byte in buffer is command
331
+ case REPORT_FIRMWARE:
332
+ if (currentReportFirmwareCallback)
333
+ (*currentReportFirmwareCallback)();
334
+ break ;
335
+ case STRING_DATA:
336
+ if (currentStringCallback) {
337
+ size_t bufferLength = (sysexBytesRead - 1 ) / 2 ;
338
+ size_t i = 1 ;
339
+ size_t j = 0 ;
340
+ while (j < bufferLength) {
341
+ // The string length will only be at most half the size of the
342
+ // stored input buffer so we can decode the string within the buffer.
343
+ bufferDataAtPosition (dataBuffer[i], j);
344
+ ++i;
345
+ bufferDataAtPosition ((dataBuffer[j] + (dataBuffer[i] << 7 )), j);
346
+ ++i;
347
+ ++j;
348
+ }
349
+ // Make sure string is null terminated. This may be the case for data
350
+ // coming from client libraries in languages that don't null terminate
351
+ // strings.
352
+ if (dataBuffer[j - 1 ] != ' \0 ' ) {
353
+ bufferDataAtPosition (' \0 ' , j);
354
+ }
355
+ (*currentStringCallback)((char *)&dataBuffer[0 ]);
356
+ }
357
+ break ;
358
+ default :
359
+ if (currentSysexCallback)
360
+ (*currentSysexCallback)(dataBuffer[0 ], sysexBytesRead - 1 , dataBuffer + 1 );
361
+ }
362
+ }
363
+
279
364
/* *
280
365
* Resets the system state upon a SYSTEM_RESET message from the host software.
281
366
* @private
@@ -288,8 +373,8 @@ void FirmataParser::systemReset(void)
288
373
executeMultiByteCommand = 0 ; // execute this after getting multi-byte data
289
374
multiByteChannel = 0 ; // channel data for multiByteCommands
290
375
291
- for (i = 0 ; i < MAX_DATA_BYTES; i++ ) {
292
- storedInputData [i] = 0 ;
376
+ for (i = 0 ; i < dataBufferSize; ++i ) {
377
+ dataBuffer [i] = 0 ;
293
378
}
294
379
295
380
parsingSysex = false ;
@@ -298,4 +383,3 @@ void FirmataParser::systemReset(void)
298
383
if (currentSystemResetCallback)
299
384
(*currentSystemResetCallback)();
300
385
}
301
-
0 commit comments