-
Notifications
You must be signed in to change notification settings - Fork 516
Complete base API #350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Complete base API #350
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,14 +66,57 @@ const | |
} | ||
|
||
/** | ||
* Split a 16-bit integer into two 7-bit values and write each value. | ||
* @param value The 16-bit value to be split and written separately. | ||
* 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::sendValueAsTwo7bitBytes(uint16_t value) | ||
void FirmataMarshaller::sendExtendedAnalog(uint8_t pin, size_t bytec, uint8_t * bytev) | ||
const | ||
{ | ||
FirmataStream->write(value & 0x7F); // LSB | ||
FirmataStream->write(value >> 7 & 0x7F); // MSB | ||
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); | ||
} | ||
} | ||
|
||
//****************************************************************************** | ||
|
@@ -116,6 +159,28 @@ void FirmataMarshaller::end(void) | |
//* 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add this back in with the updated max. I assume it's 32 bits. However different firmata client libraries may interpret this in different ways (I think some only expect a max of 16 bits), but that shouldn't break anything unless a library looks for a specific number of bytes rather than accepting all bytes up to END_SYSEX (but that would be the fault of the client library). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is, in fact, still 14-bits. I removed it when I thought it would be changing. I have replaced the note. I amended the last commit because it was aptly named. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's limited to 14 bits when using ANALOG_MESSAGE, but sendAnalog now calls sendExtendedAnalog so larger values should be allowed. No cap is defined in the protocol documentation but I think 32-bit is probably reasonable (although not sure I've ever seen hardware that supports that resolution). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here's the equivalent in firmata.js for example: https://github.com/firmata/firmata.js/blob/master/lib/firmata.js#L776-L806 |
||
{ | ||
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 | ||
|
@@ -173,17 +238,20 @@ const | |
* 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 send the value of (limited to pins 0 - 15). | ||
* @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). | ||
* The maximum value is 14-bits (16384). | ||
* @note The maximum value is 14-bits (16384). | ||
*/ | ||
void FirmataMarshaller::sendAnalog(uint8_t pin, uint16_t value) | ||
const | ||
{ | ||
if ( (Stream *)NULL == FirmataStream ) { return; } | ||
// pin can only be 0-15, so chop higher bits | ||
FirmataStream->write(ANALOG_MESSAGE | (pin & 0xF)); | ||
sendValueAsTwo7bitBytes(value); | ||
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)); | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -236,8 +304,45 @@ const | |
{ | ||
if ( (Stream *)NULL == FirmataStream ) { return; } | ||
FirmataStream->write(DIGITAL_MESSAGE | (portNumber & 0xF)); | ||
FirmataStream->write((uint8_t)portData % 128); // Tx bits 0-6 (protocol v1 and higher) | ||
FirmataStream->write(portData >> 7); // Tx bits 7-13 (bit 7 only for protocol v2 and higher) | ||
// 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); | ||
} | ||
|
||
/** | ||
|
@@ -256,6 +361,23 @@ const | |
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). | ||
|
@@ -271,7 +393,7 @@ const | |
FirmataStream->write(START_SYSEX); | ||
FirmataStream->write(command); | ||
for (i = 0; i < bytec; ++i) { | ||
sendValueAsTwo7bitBytes(bytev[i]); | ||
transformByteStreamToMessageBytes(sizeof(bytev[i]), reinterpret_cast<uint8_t *>(&bytev[i]), sizeof(bytev[i])); | ||
} | ||
FirmataStream->write(END_SYSEX); | ||
} | ||
|
@@ -283,5 +405,27 @@ const | |
void FirmataMarshaller::sendString(const char *string) | ||
const | ||
{ | ||
sendSysex(STRING_DATA, strlen(string), (uint8_t *)string); | ||
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); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a similar Encoder7Bit class in ConfigurableFirmata that both reads and writes: https://github.com/firmata/ConfigurableFirmata/blob/master/src/Encoder7Bit.cpp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and example use to read and write
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, I wish I had seen it before! ;-) However, just looking at it (and mulling it over) briefly, it looks like another dependency that will need to be managed.
Since this is a self-contained, private function, we will be able to refactor it at any time, without impacting the external API. For now, I would like to move forward using the current implementation, at least until ConfigurableFirmata becomes a little more mainstream (or Encoder7Bit is refactored to consume/extend the FirmataMarshaller API) and gets pulled under the
firmata
namespace.