555 lines
18 KiB
C++
555 lines
18 KiB
C++
/*
|
|
Firmata.cpp - Firmata library v2.5.8 - 2018-04-15
|
|
Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved.
|
|
Copyright (C) 2009-2017 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 "Firmata.h"
|
|
#include "HardwareSerial.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
using namespace firmata;
|
|
|
|
//******************************************************************************
|
|
//* Static Members
|
|
//******************************************************************************
|
|
// make one instance for the user to use
|
|
FirmataClass Firmata;
|
|
|
|
/* callback functions */
|
|
callbackFunction FirmataClass::currentAnalogCallback = (callbackFunction)NULL;
|
|
callbackFunction FirmataClass::currentDigitalCallback = (callbackFunction)NULL;
|
|
callbackFunction FirmataClass::currentPinModeCallback = (callbackFunction)NULL;
|
|
callbackFunction FirmataClass::currentPinValueCallback = (callbackFunction)NULL;
|
|
callbackFunction FirmataClass::currentReportAnalogCallback = (callbackFunction)NULL;
|
|
callbackFunction FirmataClass::currentReportDigitalCallback = (callbackFunction)NULL;
|
|
stringCallbackFunction FirmataClass::currentStringCallback = (stringCallbackFunction)NULL;
|
|
sysexCallbackFunction FirmataClass::currentSysexCallback = (sysexCallbackFunction)NULL;
|
|
systemCallbackFunction FirmataClass::currentSystemResetCallback = (systemCallbackFunction)NULL;
|
|
|
|
//******************************************************************************
|
|
//* Support Functions
|
|
//******************************************************************************
|
|
|
|
/**
|
|
* Split a 16-bit byte into two 7-bit values and write each value.
|
|
* @param value The 16-bit value to be split and written separately.
|
|
*/
|
|
void FirmataClass::sendValueAsTwo7bitBytes(int value)
|
|
{
|
|
marshaller.encodeByteStream(sizeof(value), reinterpret_cast<uint8_t *>(&value), sizeof(value));
|
|
}
|
|
|
|
/**
|
|
* A helper method to write the beginning of a Sysex message transmission.
|
|
*/
|
|
void FirmataClass::startSysex(void)
|
|
{
|
|
FirmataStream->write(START_SYSEX);
|
|
}
|
|
|
|
/**
|
|
* A helper method to write the end of a Sysex message transmission.
|
|
*/
|
|
void FirmataClass::endSysex(void)
|
|
{
|
|
FirmataStream->write(END_SYSEX);
|
|
}
|
|
|
|
//******************************************************************************
|
|
//* Constructors
|
|
//******************************************************************************
|
|
|
|
/**
|
|
* The Firmata class.
|
|
* An instance named "Firmata" is created automatically for the user.
|
|
*/
|
|
FirmataClass::FirmataClass()
|
|
:
|
|
parser(FirmataParser(parserBuffer, MAX_DATA_BYTES))
|
|
{
|
|
firmwareVersionCount = 0;
|
|
firmwareVersionVector = 0;
|
|
blinkVersionDisabled = false;
|
|
|
|
// Establish callback translation to parser callbacks
|
|
parser.attach(ANALOG_MESSAGE, (FirmataParser::callbackFunction)staticAnalogCallback, (void *)NULL);
|
|
parser.attach(DIGITAL_MESSAGE, (FirmataParser::callbackFunction)staticDigitalCallback, (void *)NULL);
|
|
parser.attach(REPORT_ANALOG, (FirmataParser::callbackFunction)staticReportAnalogCallback, (void *)NULL);
|
|
parser.attach(REPORT_DIGITAL, (FirmataParser::callbackFunction)staticReportDigitalCallback, (void *)NULL);
|
|
parser.attach(SET_PIN_MODE, (FirmataParser::callbackFunction)staticPinModeCallback, (void *)NULL);
|
|
parser.attach(SET_DIGITAL_PIN_VALUE, (FirmataParser::callbackFunction)staticPinValueCallback, (void *)NULL);
|
|
parser.attach(STRING_DATA, (FirmataParser::stringCallbackFunction)staticStringCallback, (void *)NULL);
|
|
parser.attach(START_SYSEX, (FirmataParser::sysexCallbackFunction)staticSysexCallback, (void *)NULL);
|
|
parser.attach(REPORT_FIRMWARE, (FirmataParser::versionCallbackFunction)staticReportFirmwareCallback, this);
|
|
parser.attach(REPORT_VERSION, (FirmataParser::systemCallbackFunction)staticReportVersionCallback, this);
|
|
parser.attach(SYSTEM_RESET, (FirmataParser::systemCallbackFunction)staticSystemResetCallback, (void *)NULL);
|
|
}
|
|
|
|
//******************************************************************************
|
|
//* Public Methods
|
|
//******************************************************************************
|
|
|
|
/**
|
|
* Initialize the default Serial transport at the default baud of 57600.
|
|
*/
|
|
void FirmataClass::begin(void)
|
|
{
|
|
begin(57600);
|
|
}
|
|
|
|
/**
|
|
* Initialize the default Serial transport and override the default baud.
|
|
* Sends the protocol version to the host application followed by the firmware version and name.
|
|
* blinkVersion is also called. To skip the call to blinkVersion, call Firmata.disableBlinkVersion()
|
|
* before calling Firmata.begin(baud).
|
|
* @param speed The baud to use. 57600 baud is the default value.
|
|
*/
|
|
void FirmataClass::begin(long speed)
|
|
{
|
|
Serial.begin(speed);
|
|
blinkVersion();
|
|
begin(Serial);
|
|
}
|
|
|
|
/**
|
|
* 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 FirmataClass::begin(Stream &s)
|
|
{
|
|
FirmataStream = &s;
|
|
marshaller.begin(s);
|
|
// do not call blinkVersion() here because some hardware such as the
|
|
// Ethernet shield use pin 13
|
|
printVersion(); // send the protocol version
|
|
printFirmwareVersion(); // send the firmware name and version
|
|
}
|
|
|
|
/**
|
|
* Send the Firmata protocol version to the Firmata host application.
|
|
*/
|
|
void FirmataClass::printVersion(void)
|
|
{
|
|
marshaller.sendVersion(FIRMATA_PROTOCOL_MAJOR_VERSION, FIRMATA_PROTOCOL_MINOR_VERSION);
|
|
}
|
|
|
|
/**
|
|
* Blink the Firmata protocol version to the onboard LEDs (if the board has an onboard LED).
|
|
* If VERSION_BLINK_PIN is not defined in Boards.h for a particular board, then this method
|
|
* does nothing.
|
|
* The first series of flashes indicates the firmware major version (2 flashes = 2).
|
|
* The second series of flashes indicates the firmware minor version (5 flashes = 5).
|
|
*/
|
|
void FirmataClass::blinkVersion(void)
|
|
{
|
|
#if defined(VERSION_BLINK_PIN)
|
|
if (blinkVersionDisabled) return;
|
|
// flash the pin with the protocol version
|
|
pinMode(VERSION_BLINK_PIN, OUTPUT);
|
|
strobeBlinkPin(VERSION_BLINK_PIN, FIRMATA_FIRMWARE_MAJOR_VERSION, 40, 210);
|
|
delay(250);
|
|
strobeBlinkPin(VERSION_BLINK_PIN, FIRMATA_FIRMWARE_MINOR_VERSION, 40, 210);
|
|
delay(125);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Provides a means to disable the version blink sequence on the onboard LED, trimming startup
|
|
* time by a couple of seconds.
|
|
* Call this before Firmata.begin(). It only applies when using the default Serial transport.
|
|
*/
|
|
void FirmataClass::disableBlinkVersion()
|
|
{
|
|
blinkVersionDisabled = true;
|
|
}
|
|
|
|
/**
|
|
* Sends the firmware name and version to the Firmata host application. The major and minor version
|
|
* numbers are the first 2 bytes in the message. The following bytes are the characters of the
|
|
* firmware name.
|
|
*/
|
|
void FirmataClass::printFirmwareVersion(void)
|
|
{
|
|
if (firmwareVersionCount) { // make sure that the name has been set before reporting
|
|
marshaller.sendFirmwareVersion(static_cast<uint8_t>(firmwareVersionVector[0]), static_cast<uint8_t>(firmwareVersionVector[1]), (firmwareVersionCount - 2), reinterpret_cast<uint8_t *>(&firmwareVersionVector[2]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the name and version of the firmware. This is not the same version as the Firmata protocol
|
|
* (although at times the firmware version and protocol version may be the same number).
|
|
* @param name A pointer to the name char array
|
|
* @param major The major version number
|
|
* @param minor The minor version number
|
|
*/
|
|
void FirmataClass::setFirmwareNameAndVersion(const char *name, byte major, byte minor)
|
|
{
|
|
const char *firmwareName;
|
|
const char *extension;
|
|
|
|
// parse out ".cpp" and "applet/" that comes from using __FILE__
|
|
extension = strstr(name, ".cpp");
|
|
firmwareName = strrchr(name, '/');
|
|
|
|
if (!firmwareName) {
|
|
// windows
|
|
firmwareName = strrchr(name, '\\');
|
|
}
|
|
if (!firmwareName) {
|
|
// user passed firmware name
|
|
firmwareName = name;
|
|
} else {
|
|
firmwareName ++;
|
|
}
|
|
|
|
if (!extension) {
|
|
firmwareVersionCount = strlen(firmwareName) + 2;
|
|
} else {
|
|
firmwareVersionCount = extension - firmwareName + 2;
|
|
}
|
|
|
|
// in case anyone calls setFirmwareNameAndVersion more than once
|
|
free(firmwareVersionVector);
|
|
|
|
firmwareVersionVector = (byte *) malloc(firmwareVersionCount + 1);
|
|
firmwareVersionVector[firmwareVersionCount] = 0;
|
|
firmwareVersionVector[0] = major;
|
|
firmwareVersionVector[1] = minor;
|
|
strncpy((char *)firmwareVersionVector + 2, firmwareName, firmwareVersionCount - 2);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Serial Receive Handling
|
|
|
|
/**
|
|
* A wrapper for Stream::available()
|
|
* @return The number of bytes remaining in the input stream buffer.
|
|
*/
|
|
int FirmataClass::available(void)
|
|
{
|
|
return FirmataStream->available();
|
|
}
|
|
|
|
/**
|
|
* Read a single int from the input stream. If the value is not = -1, pass it on to parse(byte)
|
|
*/
|
|
void FirmataClass::processInput(void)
|
|
{
|
|
int inputData = FirmataStream->read(); // this is 'int' to handle -1 when no data
|
|
if (inputData != -1) {
|
|
parser.parse(inputData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse data from the input stream.
|
|
* @param inputData A single byte to be added to the parser.
|
|
*/
|
|
void FirmataClass::parse(byte inputData)
|
|
{
|
|
parser.parse(inputData);
|
|
}
|
|
|
|
/**
|
|
* @return Returns true if the parser is actively parsing data.
|
|
*/
|
|
boolean FirmataClass::isParsingMessage(void)
|
|
{
|
|
return parser.isParsingMessage();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Output Stream Handling
|
|
|
|
/**
|
|
* 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 send the value of (limited to pins 0 - 15).
|
|
* @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).
|
|
*/
|
|
void FirmataClass::sendAnalog(byte pin, int value)
|
|
{
|
|
marshaller.sendAnalog(pin, value);
|
|
}
|
|
|
|
/* (intentionally left out asterix here)
|
|
* STUB - NOT IMPLEMENTED
|
|
* 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 FirmataClass::sendDigital(byte pin, int value)
|
|
{
|
|
(void)pin;
|
|
(void)value;
|
|
/* TODO add single pin digital messages to the protocol, this needs to
|
|
* track the last digital data sent so that it can be sure to change just
|
|
* one bit in the packet. This is complicated by the fact that the
|
|
* numbering of the pins will probably differ on Arduino, Wiring, and
|
|
* other boards.
|
|
*/
|
|
|
|
// TODO: the digital message should not be sent on the serial port every
|
|
// time sendDigital() is called. Instead, it should add it to an int
|
|
// which will be sent on a schedule. If a pin changes more than once
|
|
// before the digital message is sent on the serial port, it should send a
|
|
// digital message for each change.
|
|
|
|
// if(value == 0)
|
|
// sendDigitalPortPair();
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 FirmataClass::sendDigitalPort(byte portNumber, int portData)
|
|
{
|
|
marshaller.sendDigitalPort(portNumber, portData);
|
|
}
|
|
|
|
/**
|
|
* 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 FirmataClass::sendSysex(byte command, byte bytec, byte *bytev)
|
|
{
|
|
marshaller.sendSysex(command, bytec, bytev);
|
|
}
|
|
|
|
/**
|
|
* Send a string to the Firmata host application.
|
|
* @param command Must be STRING_DATA
|
|
* @param string A pointer to the char string
|
|
*/
|
|
void FirmataClass::sendString(byte command, const char *string)
|
|
{
|
|
if (command == STRING_DATA) {
|
|
marshaller.sendString(string);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a string to the Firmata host application.
|
|
* @param string A pointer to the char string
|
|
*/
|
|
void FirmataClass::sendString(const char *string)
|
|
{
|
|
marshaller.sendString(string);
|
|
}
|
|
|
|
/**
|
|
* A wrapper for Stream::available().
|
|
* Write a single byte to the output stream.
|
|
* @param c The byte to be written.
|
|
*/
|
|
void FirmataClass::write(byte c)
|
|
{
|
|
FirmataStream->write(c);
|
|
}
|
|
|
|
/**
|
|
* Attach a generic sysex callback function to a command (options are: ANALOG_MESSAGE,
|
|
* DIGITAL_MESSAGE, REPORT_ANALOG, REPORT DIGITAL, SET_PIN_MODE and SET_DIGITAL_PIN_VALUE).
|
|
* @param command The ID of the command to attach a callback function to.
|
|
* @param newFunction A reference to the callback function to attach.
|
|
*/
|
|
void FirmataClass::attach(uint8_t command, ::callbackFunction newFunction)
|
|
{
|
|
switch (command) {
|
|
case ANALOG_MESSAGE:
|
|
currentAnalogCallback = newFunction;
|
|
break;
|
|
case DIGITAL_MESSAGE:
|
|
currentDigitalCallback = newFunction;
|
|
break;
|
|
case REPORT_ANALOG:
|
|
currentReportAnalogCallback = newFunction;
|
|
break;
|
|
case REPORT_DIGITAL:
|
|
currentReportDigitalCallback = newFunction;
|
|
break;
|
|
case SET_PIN_MODE:
|
|
currentPinModeCallback = newFunction;
|
|
break;
|
|
case SET_DIGITAL_PIN_VALUE:
|
|
currentPinValueCallback = newFunction;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attach a callback function for the SYSTEM_RESET command.
|
|
* @param command Must be set to SYSTEM_RESET or it will be ignored.
|
|
* @param newFunction A reference to the system reset callback function to attach.
|
|
*/
|
|
void FirmataClass::attach(uint8_t command, systemCallbackFunction newFunction)
|
|
{
|
|
switch (command) {
|
|
case SYSTEM_RESET:
|
|
currentSystemResetCallback = newFunction;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attach a callback function for the STRING_DATA command.
|
|
* @param command Must be set to STRING_DATA or it will be ignored.
|
|
* @param newFunction A reference to the string callback function to attach.
|
|
*/
|
|
void FirmataClass::attach(uint8_t command, stringCallbackFunction newFunction)
|
|
{
|
|
switch (command) {
|
|
case STRING_DATA:
|
|
currentStringCallback = newFunction;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attach a generic sysex callback function to sysex command.
|
|
* @param command The ID of the command to attach a callback function to.
|
|
* @param newFunction A reference to the sysex callback function to attach.
|
|
*/
|
|
void FirmataClass::attach(uint8_t command, sysexCallbackFunction newFunction)
|
|
{
|
|
(void)command;
|
|
currentSysexCallback = newFunction;
|
|
}
|
|
|
|
/**
|
|
* Detach a callback function for a specified command (such as SYSTEM_RESET, STRING_DATA,
|
|
* ANALOG_MESSAGE, DIGITAL_MESSAGE, etc).
|
|
* @param command The ID of the command to detatch the callback function from.
|
|
*/
|
|
void FirmataClass::detach(uint8_t command)
|
|
{
|
|
switch (command) {
|
|
case SYSTEM_RESET:
|
|
attach(command, (systemCallbackFunction)NULL);
|
|
break;
|
|
case STRING_DATA:
|
|
attach(command, (stringCallbackFunction)NULL);
|
|
break;
|
|
case START_SYSEX:
|
|
attach(command, (sysexCallbackFunction)NULL);
|
|
break;
|
|
default:
|
|
attach(command, (callbackFunction)NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param pin The pin to get the configuration of.
|
|
* @return The configuration of the specified pin.
|
|
*/
|
|
byte FirmataClass::getPinMode(byte pin)
|
|
{
|
|
return pinConfig[pin];
|
|
}
|
|
|
|
/**
|
|
* Set 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 FirmataClass::setPinMode(byte pin, byte config)
|
|
{
|
|
if (pinConfig[pin] == PIN_MODE_IGNORE)
|
|
return;
|
|
|
|
pinConfig[pin] = config;
|
|
}
|
|
|
|
/**
|
|
* @param pin The pin to get the state of.
|
|
* @return The state of the specified pin.
|
|
*/
|
|
int FirmataClass::getPinState(byte pin)
|
|
{
|
|
return pinState[pin];
|
|
}
|
|
|
|
/**
|
|
* Set the pin state. The pin state of an output pin is the pin value. The state of an
|
|
* input pin is 0, unless the pin has it's internal pull up resistor enabled, then the value is 1.
|
|
* @param pin The pin to set the state of
|
|
* @param state Set the state of the specified pin
|
|
*/
|
|
void FirmataClass::setPinState(byte pin, int state)
|
|
{
|
|
pinState[pin] = state;
|
|
}
|
|
|
|
// sysex callbacks
|
|
/*
|
|
* this is too complicated for analogReceive, but maybe for Sysex?
|
|
void FirmataClass::attachSysex(sysexFunction newFunction)
|
|
{
|
|
byte i;
|
|
byte tmpCount = analogReceiveFunctionCount;
|
|
analogReceiveFunction* tmpArray = analogReceiveFunctionArray;
|
|
analogReceiveFunctionCount++;
|
|
analogReceiveFunctionArray = (analogReceiveFunction*) calloc(analogReceiveFunctionCount, sizeof(analogReceiveFunction));
|
|
for(i = 0; i < tmpCount; i++) {
|
|
analogReceiveFunctionArray[i] = tmpArray[i];
|
|
}
|
|
analogReceiveFunctionArray[tmpCount] = newFunction;
|
|
free(tmpArray);
|
|
}
|
|
*/
|
|
|
|
//******************************************************************************
|
|
//* Private Methods
|
|
//******************************************************************************
|
|
|
|
/**
|
|
* Flashing the pin for the version number
|
|
* @private
|
|
* @param pin The pin the LED is attached to.
|
|
* @param count The number of times to flash the LED.
|
|
* @param onInterval The number of milliseconds for the LED to be ON during each interval.
|
|
* @param offInterval The number of milliseconds for the LED to be OFF during each interval.
|
|
*/
|
|
void FirmataClass::strobeBlinkPin(byte pin, int count, int onInterval, int offInterval)
|
|
{
|
|
byte i;
|
|
for (i = 0; i < count; i++) {
|
|
delay(offInterval);
|
|
digitalWrite(pin, HIGH);
|
|
delay(onInterval);
|
|
digitalWrite(pin, LOW);
|
|
}
|
|
}
|
|
|