Skip to content

Commit bdd3916

Browse files
committed
Split parsing functionality into new lib
1 parent 39c8e70 commit bdd3916

File tree

2 files changed

+458
-0
lines changed

2 files changed

+458
-0
lines changed

FirmataParser.cpp

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
/*
2+
Firmata.cpp - Firmata library v2.5.4 - 2016-10-23
3+
Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved.
4+
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
5+
6+
This library is free software; you can redistribute it and/or
7+
modify it under the terms of the GNU Lesser General Public
8+
License as published by the Free Software Foundation; either
9+
version 2.1 of the License, or (at your option) any later version.
10+
11+
See file LICENSE.txt for further informations on licensing terms.
12+
*/
13+
14+
//******************************************************************************
15+
//* Includes
16+
//******************************************************************************
17+
18+
#include "FirmataParser.h"
19+
20+
//******************************************************************************
21+
//* Constructors
22+
//******************************************************************************
23+
24+
/**
25+
* The Firmata class.
26+
* An instance named "Firmata" is created automatically for the user.
27+
*/
28+
FirmataParser::FirmataParser()
29+
:
30+
executeMultiByteCommand(0),
31+
multiByteChannel(0),
32+
storedInputData{0},
33+
waitForData(0),
34+
parsingSysex(false),
35+
sysexBytesRead(0),
36+
currentAnalogCallback((callbackFunction)NULL),
37+
currentDigitalCallback((callbackFunction)NULL),
38+
currentReportAnalogCallback((callbackFunction)NULL),
39+
currentReportDigitalCallback((callbackFunction)NULL),
40+
currentPinModeCallback((callbackFunction)NULL),
41+
currentPinValueCallback((callbackFunction)NULL),
42+
currentReportFirmwareCallback((systemCallbackFunction)NULL),
43+
currentReportVersionCallback((systemCallbackFunction)NULL),
44+
currentSystemResetCallback((systemCallbackFunction)NULL),
45+
currentStringCallback((stringCallbackFunction)NULL),
46+
currentSysexCallback((sysexCallbackFunction)NULL)
47+
{
48+
}
49+
50+
//******************************************************************************
51+
//* Public Methods
52+
//******************************************************************************
53+
54+
//------------------------------------------------------------------------------
55+
// Serial Receive Handling
56+
57+
/**
58+
* Process incoming sysex messages. Handles REPORT_FIRMWARE and STRING_DATA internally.
59+
* Calls callback function for STRING_DATA and all other sysex messages.
60+
* @private
61+
*/
62+
void FirmataParser::processSysexMessage(void)
63+
{
64+
switch (storedInputData[0]) { //first byte in buffer is command
65+
case REPORT_FIRMWARE:
66+
if (currentReportFirmwareCallback)
67+
(*currentReportFirmwareCallback)();
68+
break;
69+
case STRING_DATA:
70+
if (currentStringCallback) {
71+
size_t bufferLength = (sysexBytesRead - 1) / 2;
72+
size_t i = 1;
73+
size_t j = 0;
74+
while (j < bufferLength) {
75+
// The string length will only be at most half the size of the
76+
// stored input buffer so we can decode the string within the buffer.
77+
storedInputData[j] = storedInputData[i];
78+
i++;
79+
storedInputData[j] += (storedInputData[i] << 7);
80+
i++;
81+
j++;
82+
}
83+
// Make sure string is null terminated. This may be the case for data
84+
// coming from client libraries in languages that don't null terminate
85+
// strings.
86+
if (storedInputData[j - 1] != '\0') {
87+
storedInputData[j] = '\0';
88+
}
89+
(*currentStringCallback)((char *)&storedInputData[0]);
90+
}
91+
break;
92+
default:
93+
if (currentSysexCallback)
94+
(*currentSysexCallback)(storedInputData[0], sysexBytesRead - 1, storedInputData + 1);
95+
}
96+
}
97+
98+
/**
99+
* Parse data from the input stream.
100+
* @param inputData A single byte to be added to the parser.
101+
*/
102+
void FirmataParser::parse(uint8_t inputData)
103+
{
104+
uint8_t command;
105+
106+
if (parsingSysex) {
107+
if (inputData == END_SYSEX) {
108+
//stop sysex byte
109+
parsingSysex = false;
110+
//fire off handler function
111+
processSysexMessage();
112+
} else {
113+
//normal data byte - add to buffer
114+
storedInputData[sysexBytesRead] = inputData;
115+
sysexBytesRead++;
116+
}
117+
} else if ( (waitForData > 0) && (inputData < 128) ) {
118+
waitForData--;
119+
storedInputData[waitForData] = inputData;
120+
if ( (waitForData == 0) && executeMultiByteCommand ) { // got the whole message
121+
switch (executeMultiByteCommand) {
122+
case ANALOG_MESSAGE:
123+
if (currentAnalogCallback) {
124+
(*currentAnalogCallback)(multiByteChannel,
125+
(storedInputData[0] << 7)
126+
+ storedInputData[1]);
127+
}
128+
break;
129+
case DIGITAL_MESSAGE:
130+
if (currentDigitalCallback) {
131+
(*currentDigitalCallback)(multiByteChannel,
132+
(storedInputData[0] << 7)
133+
+ storedInputData[1]);
134+
}
135+
break;
136+
case SET_PIN_MODE:
137+
if (currentPinModeCallback)
138+
(*currentPinModeCallback)(storedInputData[1], storedInputData[0]);
139+
break;
140+
case SET_DIGITAL_PIN_VALUE:
141+
if (currentPinValueCallback)
142+
(*currentPinValueCallback)(storedInputData[1], storedInputData[0]);
143+
break;
144+
case REPORT_ANALOG:
145+
if (currentReportAnalogCallback)
146+
(*currentReportAnalogCallback)(multiByteChannel, storedInputData[0]);
147+
break;
148+
case REPORT_DIGITAL:
149+
if (currentReportDigitalCallback)
150+
(*currentReportDigitalCallback)(multiByteChannel, storedInputData[0]);
151+
break;
152+
}
153+
executeMultiByteCommand = 0;
154+
}
155+
} else {
156+
// remove channel info from command byte if less than 0xF0
157+
if (inputData < 0xF0) {
158+
command = inputData & 0xF0;
159+
multiByteChannel = inputData & 0x0F;
160+
} else {
161+
command = inputData;
162+
// commands in the 0xF* range don't use channel data
163+
}
164+
switch (command) {
165+
case ANALOG_MESSAGE:
166+
case DIGITAL_MESSAGE:
167+
case SET_PIN_MODE:
168+
case SET_DIGITAL_PIN_VALUE:
169+
waitForData = 2; // two data bytes needed
170+
executeMultiByteCommand = command;
171+
break;
172+
case REPORT_ANALOG:
173+
case REPORT_DIGITAL:
174+
waitForData = 1; // one data byte needed
175+
executeMultiByteCommand = command;
176+
break;
177+
case START_SYSEX:
178+
parsingSysex = true;
179+
sysexBytesRead = 0;
180+
break;
181+
case SYSTEM_RESET:
182+
systemReset();
183+
break;
184+
case REPORT_VERSION:
185+
if (currentReportVersionCallback)
186+
(*currentReportVersionCallback)();
187+
break;
188+
}
189+
}
190+
}
191+
192+
/**
193+
* @return Returns true if the parser is actively parsing data.
194+
*/
195+
bool FirmataParser::isParsingMessage(void)
196+
{
197+
return (waitForData > 0 || parsingSysex);
198+
}
199+
200+
/**
201+
* Attach a generic sysex callback function to a command (options are: ANALOG_MESSAGE,
202+
* DIGITAL_MESSAGE, REPORT_ANALOG, REPORT DIGITAL, SET_PIN_MODE and SET_DIGITAL_PIN_VALUE).
203+
* @param command The ID of the command to attach a callback function to.
204+
* @param newFunction A reference to the callback function to attach.
205+
*/
206+
void FirmataParser::attach(uint8_t command, callbackFunction newFunction)
207+
{
208+
switch (command) {
209+
case ANALOG_MESSAGE: currentAnalogCallback = newFunction; break;
210+
case DIGITAL_MESSAGE: currentDigitalCallback = newFunction; break;
211+
case REPORT_ANALOG: currentReportAnalogCallback = newFunction; break;
212+
case REPORT_DIGITAL: currentReportDigitalCallback = newFunction; break;
213+
case SET_PIN_MODE: currentPinModeCallback = newFunction; break;
214+
case SET_DIGITAL_PIN_VALUE: currentPinValueCallback = newFunction; break;
215+
}
216+
}
217+
218+
/**
219+
* Attach a system callback function (options are: REPORT_FIRMWARE, REPORT_VERSION
220+
* and SYSTEM_RESET).
221+
* @param command The ID of the command to attach a callback function to.
222+
* @param newFunction A reference to the callback function to attach.
223+
*/
224+
void FirmataParser::attach(uint8_t command, systemCallbackFunction newFunction)
225+
{
226+
switch (command) {
227+
case REPORT_FIRMWARE: currentReportFirmwareCallback = newFunction; break;
228+
case REPORT_VERSION: currentReportVersionCallback = newFunction; break;
229+
case SYSTEM_RESET: currentSystemResetCallback = newFunction; break;
230+
}
231+
}
232+
233+
/**
234+
* Attach a callback function for the STRING_DATA command.
235+
* @param command Must be set to STRING_DATA or it will be ignored.
236+
* @param newFunction A reference to the string callback function to attach.
237+
*/
238+
void FirmataParser::attach(uint8_t command, stringCallbackFunction newFunction)
239+
{
240+
switch (command) {
241+
case STRING_DATA: currentStringCallback = newFunction; break;
242+
}
243+
}
244+
245+
/**
246+
* Attach a generic sysex callback function to sysex command.
247+
* @param command The ID of the command to attach a callback function to.
248+
* @param newFunction A reference to the sysex callback function to attach.
249+
*/
250+
void FirmataParser::attach(uint8_t command, sysexCallbackFunction newFunction)
251+
{
252+
currentSysexCallback = newFunction;
253+
}
254+
255+
/**
256+
* Detach a callback function for a specified command (such as SYSTEM_RESET, STRING_DATA,
257+
* ANALOG_MESSAGE, DIGITAL_MESSAGE, etc).
258+
* @param command The ID of the command to detatch the callback function from.
259+
*/
260+
void FirmataParser::detach(uint8_t command)
261+
{
262+
switch (command) {
263+
case REPORT_FIRMWARE:
264+
case REPORT_VERSION:
265+
case SYSTEM_RESET:
266+
attach(command, (systemCallbackFunction)NULL);
267+
break;
268+
case STRING_DATA: currentStringCallback = (stringCallbackFunction)NULL; break;
269+
case START_SYSEX: currentSysexCallback = (sysexCallbackFunction)NULL; break;
270+
default:
271+
attach(command, (callbackFunction)NULL);
272+
}
273+
}
274+
275+
//******************************************************************************
276+
//* Private Methods
277+
//******************************************************************************
278+
279+
/**
280+
* Resets the system state upon a SYSTEM_RESET message from the host software.
281+
* @private
282+
*/
283+
void FirmataParser::systemReset(void)
284+
{
285+
size_t i;
286+
287+
waitForData = 0; // this flag says the next serial input will be data
288+
executeMultiByteCommand = 0; // execute this after getting multi-byte data
289+
multiByteChannel = 0; // channel data for multiByteCommands
290+
291+
for (i = 0; i < MAX_DATA_BYTES; i++) {
292+
storedInputData[i] = 0;
293+
}
294+
295+
parsingSysex = false;
296+
sysexBytesRead = 0;
297+
298+
if (currentSystemResetCallback)
299+
(*currentSystemResetCallback)();
300+
}
301+

0 commit comments

Comments
 (0)