Skip to content

Commit 44e9026

Browse files
Merge pull request firmata#350 from zfields/mp_complete
Complete base API
2 parents bf3f08f + 9433b27 commit 44e9026

File tree

4 files changed

+171
-30
lines changed

4 files changed

+171
-30
lines changed

Firmata.cpp

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ systemCallbackFunction FirmataClass::currentSystemResetCallback = (systemCallbac
5050
*/
5151
void FirmataClass::sendValueAsTwo7bitBytes(int value)
5252
{
53-
marshaller.sendValueAsTwo7bitBytes(value);
53+
marshaller.transformByteStreamToMessageBytes(sizeof(value), reinterpret_cast<uint8_t *>(&value), sizeof(value));
5454
}
5555

5656
/**
@@ -146,9 +146,7 @@ void FirmataClass::begin(Stream &s)
146146
*/
147147
void FirmataClass::printVersion(void)
148148
{
149-
FirmataStream->write(REPORT_VERSION);
150-
FirmataStream->write(FIRMATA_PROTOCOL_MAJOR_VERSION);
151-
FirmataStream->write(FIRMATA_PROTOCOL_MINOR_VERSION);
149+
marshaller.sendVersion(FIRMATA_PROTOCOL_MAJOR_VERSION, FIRMATA_PROTOCOL_MINOR_VERSION);
152150
}
153151

154152
/**
@@ -188,17 +186,8 @@ void FirmataClass::disableBlinkVersion()
188186
*/
189187
void FirmataClass::printFirmwareVersion(void)
190188
{
191-
byte i;
192-
193189
if (firmwareVersionCount) { // make sure that the name has been set before reporting
194-
startSysex();
195-
FirmataStream->write(REPORT_FIRMWARE);
196-
FirmataStream->write(firmwareVersionVector[0]); // major version number
197-
FirmataStream->write(firmwareVersionVector[1]); // minor version number
198-
for (i = 2; i < firmwareVersionCount; ++i) {
199-
marshaller.sendValueAsTwo7bitBytes(firmwareVersionVector[i]);
200-
}
201-
endSysex();
190+
marshaller.sendFirmwareVersion(static_cast<uint8_t>(firmwareVersionVector[0]), static_cast<uint8_t>(firmwareVersionVector[1]), (firmwareVersionCount - 2), reinterpret_cast<uint8_t *>(&firmwareVersionVector[2]));
202191
}
203192
}
204193

Firmata.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class FirmataClass
128128

129129
/* private methods ------------------------------ */
130130
void strobeBlinkPin(byte pin, int count, int onInterval, int offInterval);
131-
friend void FirmataMarshaller::sendValueAsTwo7bitBytes(uint16_t value) const;
131+
friend void FirmataMarshaller::transformByteStreamToMessageBytes (size_t bytec, uint8_t * bytev, size_t max_bytes = 0) const;
132132

133133
/* callback functions */
134134
static callbackFunction currentAnalogCallback;

FirmataMarshaller.cpp

Lines changed: 158 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,57 @@ const
6666
}
6767

6868
/**
69-
* Split a 16-bit integer into two 7-bit values and write each value.
70-
* @param value The 16-bit value to be split and written separately.
69+
* An alternative to the normal analog message, this extended version allows addressing beyond
70+
* pin 15 and supports sending analog values with any number of bits.
71+
* @param pin The analog pin to which the value is sent.
72+
* @param bytec The size of the storage for the analog value
73+
* @param bytev The pointer to the location of the analog value
7174
*/
72-
void FirmataMarshaller::sendValueAsTwo7bitBytes(uint16_t value)
75+
void FirmataMarshaller::sendExtendedAnalog(uint8_t pin, size_t bytec, uint8_t * bytev)
7376
const
7477
{
75-
FirmataStream->write(value & 0x7F); // LSB
76-
FirmataStream->write(value >> 7 & 0x7F); // MSB
78+
if ( (Stream *)NULL == FirmataStream ) { return; }
79+
FirmataStream->write(START_SYSEX);
80+
FirmataStream->write(EXTENDED_ANALOG);
81+
FirmataStream->write(pin);
82+
transformByteStreamToMessageBytes(bytec, bytev, bytec);
83+
FirmataStream->write(END_SYSEX);
84+
}
85+
86+
/**
87+
* Transform 8-bit stream into 7-bit message
88+
* @param bytec The number of data bytes in the message.
89+
* @param bytev A pointer to the array of data bytes to send in the message.
90+
* @param max_bytes Force message to be n bytes, regardless of data bits.
91+
*/
92+
void FirmataMarshaller::transformByteStreamToMessageBytes (size_t bytec, uint8_t * bytev, size_t max_bytes)
93+
const
94+
{
95+
static const size_t transmit_bits = 7;
96+
static const uint8_t transmit_mask = ((1 << transmit_bits) - 1);
97+
98+
size_t bytes_sent = 0;
99+
size_t outstanding_bits = 0;
100+
uint8_t outstanding_bit_cache = *bytev;
101+
102+
if ( !max_bytes ) { max_bytes = static_cast<size_t>(-1); }
103+
for (size_t i = 0 ; (i < bytec) && (bytes_sent < max_bytes) ; ++i) {
104+
uint8_t transmit_byte = (outstanding_bit_cache|(bytev[i] << outstanding_bits));
105+
FirmataStream->write(transmit_mask & transmit_byte);
106+
++bytes_sent;
107+
outstanding_bit_cache = (bytev[i] >> (transmit_bits - outstanding_bits));
108+
outstanding_bits = (outstanding_bits + (8 - transmit_bits));
109+
for ( ; (outstanding_bits >= transmit_bits) && (bytes_sent < max_bytes) ; ) {
110+
transmit_byte = outstanding_bit_cache;
111+
FirmataStream->write(transmit_mask & transmit_byte);
112+
++bytes_sent;
113+
outstanding_bit_cache >>= transmit_bits;
114+
outstanding_bits -= transmit_bits;
115+
}
116+
}
117+
if ( outstanding_bits && (bytes_sent < max_bytes) ) {
118+
FirmataStream->write(static_cast<uint8_t>((1 << outstanding_bits) - 1) & outstanding_bit_cache);
119+
}
77120
}
78121

79122
//******************************************************************************
@@ -116,6 +159,28 @@ void FirmataMarshaller::end(void)
116159
//* Output Stream Handling
117160
//******************************************************************************
118161

162+
/**
163+
* Query the target's firmware name and version
164+
*/
165+
void FirmataMarshaller::queryFirmwareVersion(void)
166+
const
167+
{
168+
if ( (Stream *)NULL == FirmataStream ) { return; }
169+
FirmataStream->write(START_SYSEX);
170+
FirmataStream->write(REPORT_FIRMWARE);
171+
FirmataStream->write(END_SYSEX);
172+
}
173+
174+
/**
175+
* Query the target's Firmata protocol version
176+
*/
177+
void FirmataMarshaller::queryVersion(void)
178+
const
179+
{
180+
if ( (Stream *)NULL == FirmataStream ) { return; }
181+
FirmataStream->write(REPORT_VERSION);
182+
}
183+
119184
/**
120185
* Halt the stream of analog readings from the Firmata host application. The range of pins is
121186
* limited to [0..15] when using the REPORT_ANALOG. The maximum result of the REPORT_ANALOG is limited to 14 bits
@@ -173,17 +238,20 @@ const
173238
* when using the ANALOG_MESSAGE. The maximum value of the ANALOG_MESSAGE is limited to 14 bits
174239
* (16384). To increase the pin range or value, see the documentation for the EXTENDED_ANALOG
175240
* message.
176-
* @param pin The analog pin to send the value of (limited to pins 0 - 15).
241+
* @param pin The analog pin to which the value is sent.
177242
* @param value The value of the analog pin (0 - 1024 for 10-bit analog, 0 - 4096 for 12-bit, etc).
178-
* The maximum value is 14-bits (16384).
243+
* @note The maximum value is 14-bits (16384).
179244
*/
180245
void FirmataMarshaller::sendAnalog(uint8_t pin, uint16_t value)
181246
const
182247
{
183248
if ( (Stream *)NULL == FirmataStream ) { return; }
184-
// pin can only be 0-15, so chop higher bits
185-
FirmataStream->write(ANALOG_MESSAGE | (pin & 0xF));
186-
sendValueAsTwo7bitBytes(value);
249+
if ( (0xF >= pin) && (0x3FFF >= value) ) {
250+
FirmataStream->write(ANALOG_MESSAGE|pin);
251+
transformByteStreamToMessageBytes(sizeof(value), reinterpret_cast<uint8_t *>(&value), sizeof(value));
252+
} else {
253+
sendExtendedAnalog(pin, sizeof(value), reinterpret_cast<uint8_t *>(&value));
254+
}
187255
}
188256

189257
/**
@@ -236,8 +304,45 @@ const
236304
{
237305
if ( (Stream *)NULL == FirmataStream ) { return; }
238306
FirmataStream->write(DIGITAL_MESSAGE | (portNumber & 0xF));
239-
FirmataStream->write((uint8_t)portData % 128); // Tx bits 0-6 (protocol v1 and higher)
240-
FirmataStream->write(portData >> 7); // Tx bits 7-13 (bit 7 only for protocol v2 and higher)
307+
// Tx bits 0-6 (protocol v1 and higher)
308+
// Tx bits 7-13 (bit 7 only for protocol v2 and higher)
309+
transformByteStreamToMessageBytes(sizeof(portData), reinterpret_cast<uint8_t *>(&portData), sizeof(portData));
310+
}
311+
312+
/**
313+
* Sends the firmware name and version to the Firmata host application.
314+
* @param major The major verison number
315+
* @param minor The minor version number
316+
* @param bytec The length of the firmware name
317+
* @param bytev The firmware name array
318+
*/
319+
void FirmataMarshaller::sendFirmwareVersion(uint8_t major, uint8_t minor, size_t bytec, uint8_t *bytev)
320+
const
321+
{
322+
if ( (Stream *)NULL == FirmataStream ) { return; }
323+
size_t i;
324+
FirmataStream->write(START_SYSEX);
325+
FirmataStream->write(REPORT_FIRMWARE);
326+
FirmataStream->write(major);
327+
FirmataStream->write(minor);
328+
for (i = 0; i < bytec; ++i) {
329+
transformByteStreamToMessageBytes(sizeof(bytev[i]), reinterpret_cast<uint8_t *>(&bytev[i]), sizeof(bytev[i]));
330+
}
331+
FirmataStream->write(END_SYSEX);
332+
}
333+
334+
/**
335+
* Send the Firmata protocol version to the Firmata host application.
336+
* @param major The major verison number
337+
* @param minor The minor version number
338+
*/
339+
void FirmataMarshaller::sendVersion(uint8_t major, uint8_t minor)
340+
const
341+
{
342+
if ( (Stream *)NULL == FirmataStream ) { return; }
343+
FirmataStream->write(REPORT_VERSION);
344+
FirmataStream->write(major);
345+
FirmataStream->write(minor);
241346
}
242347

243348
/**
@@ -256,6 +361,23 @@ const
256361
FirmataStream->write(config);
257362
}
258363

364+
/**
365+
* Send a pin state query to the Firmata host application. The resulting sysex message will have
366+
* a PIN_STATE_RESPONSE command byte, followed by the pin number, the pin mode and a stream of
367+
* bits to indicate any *data* written to the pin (pin state).
368+
* @param pin The pin to query
369+
* @note The pin state is any data written to the pin (i.e. pin state != pin value)
370+
*/
371+
void FirmataMarshaller::sendPinStateQuery(uint8_t pin)
372+
const
373+
{
374+
if ( (Stream *)NULL == FirmataStream ) { return; }
375+
FirmataStream->write(START_SYSEX);
376+
FirmataStream->write(PIN_STATE_QUERY);
377+
FirmataStream->write(pin);
378+
FirmataStream->write(END_SYSEX);
379+
}
380+
259381
/**
260382
* Send a sysex message where all values after the command byte are packet as 2 7-bit bytes
261383
* (this is not always the case so this function is not always used to send sysex messages).
@@ -271,7 +393,7 @@ const
271393
FirmataStream->write(START_SYSEX);
272394
FirmataStream->write(command);
273395
for (i = 0; i < bytec; ++i) {
274-
sendValueAsTwo7bitBytes(bytev[i]);
396+
transformByteStreamToMessageBytes(sizeof(bytev[i]), reinterpret_cast<uint8_t *>(&bytev[i]), sizeof(bytev[i]));
275397
}
276398
FirmataStream->write(END_SYSEX);
277399
}
@@ -283,5 +405,27 @@ const
283405
void FirmataMarshaller::sendString(const char *string)
284406
const
285407
{
286-
sendSysex(STRING_DATA, strlen(string), (uint8_t *)string);
408+
sendSysex(STRING_DATA, strlen(string), reinterpret_cast<uint8_t *>(const_cast<char *>(string)));
409+
}
410+
411+
/**
412+
* The sampling interval sets how often analog data and i2c data is reported to the client.
413+
* @param interval_ms The interval (in milliseconds) at which to sample
414+
* @note The default sampling interval is 19ms
415+
*/
416+
void FirmataMarshaller::setSamplingInterval(uint16_t interval_ms)
417+
const
418+
{
419+
sendSysex(SAMPLING_INTERVAL, sizeof(interval_ms), reinterpret_cast<uint8_t *>(&interval_ms));
420+
}
421+
422+
/**
423+
* Perform a software reset on the target. For example, StandardFirmata.ino will initialize
424+
* everything to a known state and reset the parsing buffer.
425+
*/
426+
void FirmataMarshaller::systemReset(void)
427+
const
428+
{
429+
if ( (Stream *)NULL == FirmataStream ) { return; }
430+
FirmataStream->write(SYSTEM_RESET);
287431
}

FirmataMarshaller.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class FirmataMarshaller
3939
void end();
4040

4141
/* serial send handling */
42+
void queryFirmwareVersion(void) const;
43+
void queryVersion(void) const;
4244
void reportAnalogDisable(uint8_t pin) const;
4345
void reportAnalogEnable(uint8_t pin) const;
4446
void reportDigitalPortDisable(uint8_t portNumber) const;
@@ -48,15 +50,21 @@ class FirmataMarshaller
4850
void sendCapabilityQuery(void) const;
4951
void sendDigital(uint8_t pin, uint8_t value) const;
5052
void sendDigitalPort(uint8_t portNumber, uint16_t portData) const;
53+
void sendFirmwareVersion(uint8_t major, uint8_t minor, size_t bytec, uint8_t *bytev) const;
54+
void sendVersion(uint8_t major, uint8_t minor) const;
5155
void sendPinMode(uint8_t pin, uint8_t config) const;
56+
void sendPinStateQuery(uint8_t pin) const;
5257
void sendString(const char *string) const;
5358
void sendSysex(uint8_t command, size_t bytec, uint8_t *bytev) const;
59+
void setSamplingInterval(uint16_t interval_ms) const;
60+
void systemReset(void) const;
5461

5562
private:
5663
/* utility methods */
5764
void reportAnalog(uint8_t pin, bool stream_enable) const;
5865
void reportDigitalPort(uint8_t portNumber, bool stream_enable) const;
59-
void sendValueAsTwo7bitBytes(uint16_t value) const;
66+
void sendExtendedAnalog(uint8_t pin, size_t bytec, uint8_t * bytev) const;
67+
void transformByteStreamToMessageBytes (size_t bytec, uint8_t * bytev, size_t max_bytes = 0) const;
6068

6169
Stream * FirmataStream;
6270
};

0 commit comments

Comments
 (0)