diff --git a/examples/ConfigurableFirmataDeviceDriver/ConfigurableFirmataDeviceDriver.ino b/examples/ConfigurableFirmataDeviceDriver/ConfigurableFirmataDeviceDriver.ino new file mode 100644 index 0000000..d82ae8e --- /dev/null +++ b/examples/ConfigurableFirmataDeviceDriver/ConfigurableFirmataDeviceDriver.ino @@ -0,0 +1,441 @@ +/* + Firmata is a generic protocol for communicating with microcontrollers + from software on a host computer. It is intended to work with + any host computer software package. + + To download a host software package, please clink on the following link + to open the download page in your default browser. + + https://github.com/firmata/ConfigurableFirmata#firmata-client-libraries + + Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. + Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. + Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. + Copyright (C) 2013 Norbert Truchsess. All rights reserved. + Copyright (C) 2014 Nicolas Panel. All rights reserved. + Copyright (C) 2009-2016 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. + + Last updated by Jeff Hoefs: January 23rd, 2016 +*/ + +/* + README + + This is an example use of ConfigurableFirmata. The easiest way to create a configuration is to + use http://firmatabuilder.com and select the communication transport and the firmata features + to include and an Arduino sketch (.ino) file will be generated and downloaded automatically. + + To manually configure a sketch, copy this file and follow the instructions in the + ETHERNET CONFIGURATION OPTION (if you want to use Ethernet instead of Serial/USB) and + FIRMATA FEATURE CONFIGURATION sections in this file. +*/ + +#include + +/*============================================================================== + * ETHERNET CONFIGURATION OPTION + * + * By default Firmata uses the Serial-port (over USB) of the Arduino. ConfigurableFirmata may also + * comunicate over ethernet using tcp/ip. To configure this sketch to use Ethernet instead of + * Serial, uncomment the approprate includes for your particular hardware. See STEPS 1 - 5 below. + * If you want to use Serial (over USB) then skip ahead to the FIRMATA FEATURE CONFIGURATION + * section further down in this file. + * + * If you enable Ethernet, you will need a Firmata client library with a network transport that can + * act as a server in order to establish a connection between ConfigurableFirmataEthernet and the + * Firmata host application (your application). + * + * To use ConfigurableFirmata with Ethernet you will need to have one of the following + * boards or shields: + * + * - Arduino Ethernet shield (or clone) + * - Arduino Ethernet board (or clone) + * - Arduino Yun + * + * If you are using an Arduino Ethernet shield you cannot use the following pins on + * the following boards. Firmata will ignore any requests to use these pins: + * + * - Arduino Uno or other ATMega328 boards: (D4, D10, D11, D12, D13) + * - Arduino Mega: (D4, D10, D50, D51, D52, D53) + * - Arduino Leonardo: (D4, D10) + * - Arduino Due: (D4, D10) + * - Arduino Zero: (D4, D10) + * + * If you are using an ArduinoEthernet board, the following pins cannot be used (same as Uno): + * - D4, D10, D11, D12, D13 + *============================================================================*/ + +// STEP 1 [REQUIRED] +// Uncomment / comment the appropriate set of includes for your hardware (OPTION A, B or C) + +/* + * OPTION A: Configure for Arduino Ethernet board or Arduino Ethernet shield (or clone) + * + * To configure ConfigurableFirmata to use the an Arduino Ethernet Shield or Arduino Ethernet + * Board (both use the same WIZ5100-based Ethernet controller), uncomment the SPI and Ethernet + * includes below. + */ +//#include +//#include + + +/* + * OPTION B: Configure for a board or shield using an ENC28J60-based Ethernet controller, + * uncomment out the UIPEthernet include below. + * + * The UIPEthernet-library can be downloaded + * from: https://github.com/ntruchsess/arduino_uip + */ +//#include + + +/* + * OPTION C: Configure for Arduino Yun + * + * The Ethernet port on the Arduino Yun board can be used with Firmata in this configuration. + * To execute StandardFirmataEthernet on Yun uncomment the Bridge and YunClient includes below. + * + * NOTE: in order to compile for the Yun you will also need to comment out some of the includes + * and declarations in the FIRMATA FEATURE CONFIGURATION section later in this file. Including all + * features exceeds the RAM and Flash memory of the Yun. Comment out anything you don't need. + * + * On Yun there's no need to configure local_ip and mac address as this is automatically + * configured on the linux-side of Yun. + * + * Establishing a connection with the Yun may take several seconds. + */ +//#include +//#include + +#if defined ethernet_h || defined UIPETHERNET_H || defined _YUN_CLIENT_H_ +#define NETWORK_FIRMATA + +// STEP 2 [REQUIRED for all boards and shields] +// replace with IP of the server you want to connect to, comment out if using 'remote_host' +#define remote_ip IPAddress(192, 168, 0, 1) +// OR replace with hostname of server you want to connect to, comment out if using 'remote_ip' +// #define remote_host "server.local" + +// STEP 3 [REQUIRED unless using Arduino Yun] +// Replace with the port that your server is listening on +#define remote_port 3030 + +// STEP 4 [REQUIRED unless using Arduino Yun OR if not using DHCP] +// Replace with your board or Ethernet shield's IP address +// Comment out if you want to use DHCP +#define local_ip IPAddress(192, 168, 0, 6) + +// STEP 5 [REQUIRED unless using Arduino Yun] +// replace with Ethernet shield mac. Must be unique for your network +const byte mac[] = {0x90, 0xA2, 0xDA, 0x0D, 0x07, 0x02}; +#endif + +/*============================================================================== + * FIRMATA FEATURE CONFIGURATION + * + * Comment out the include and declaration for any features that you do not need + * below. + * + * WARNING: Including all of the following features (especially if also using + * Ethernet) may exceed the Flash and/or RAM of lower memory boards such as the + * Arduino Uno or Leonardo. + *============================================================================*/ + +#include +DigitalInputFirmata digitalInput; + +#include +DigitalOutputFirmata digitalOutput; + +#include +AnalogInputFirmata analogInput; + +#include +AnalogOutputFirmata analogOutput; + +//#include +//#include +//ServoFirmata servo; +//// ServoFirmata depends on AnalogOutputFirmata +//#if defined ServoFirmata_h && ! defined AnalogOutputFirmata_h +//#error AnalogOutputFirmata must be included to use ServoFirmata +//#endif +// +//#include +//#include +//I2CFirmata i2c; + +//#include +//OneWireFirmata oneWire; + +//#include +//StepperFirmata stepper; + +//#include +//SerialFirmata serial; + +#include +FirmataExt firmataExt; + +//#include +//FirmataScheduler scheduler; + +// To add Encoder support you must first install the FirmataEncoder and Encoder libraries: +// https://github.com/firmata/FirmataEncoder +// https://www.pjrc.com/teensy/td_libs_Encoder.html +// #include +// #include +// FirmataEncoder encoder; + +#include "SelectedDeviceDrivers.h" +#include +DeviceFirmata deviceMgr; + +/*=================================================================================== + * END FEATURE CONFIGURATION - you should not need to change anything below this line + *==================================================================================*/ + +// dependencies. Do not comment out the following lines +#if defined AnalogOutputFirmata_h || defined ServoFirmata_h +#include +#endif + +#if defined AnalogInputFirmata_h || defined I2CFirmata_h || defined FirmataEncoder_h +#include +FirmataReporting reporting; +#endif + +// dependencies for Network Firmata. Do not comment out. +#ifdef NETWORK_FIRMATA +#if defined remote_ip && defined remote_host +#error "cannot define both remote_ip and remote_host at the same time!" +#endif +#include +#ifdef _YUN_CLIENT_H_ +YunClient client; +#else +EthernetClient client; +#endif +#if defined remote_ip && !defined remote_host +#ifdef local_ip +EthernetClientStream stream(client, local_ip, remote_ip, NULL, remote_port); +#else +EthernetClientStream stream(client, IPAddress(0, 0, 0, 0), remote_ip, NULL, remote_port); +#endif +#endif +#if !defined remote_ip && defined remote_host +#ifdef local_ip +EthernetClientStream stream(client, local_ip, IPAddress(0, 0, 0, 0), remote_host, remote_port); +#else +EthernetClientStream stream(client, IPAddress(0, 0, 0, 0), IPAddress(0, 0, 0, 0), remote_host, remote_port); +#endif +#endif +#endif + +/*============================================================================== + * FUNCTIONS + *============================================================================*/ + +void systemResetCallback() +{ + // initialize a default state + + // pins with analog capability default to analog input + // otherwise, pins default to digital output + for (byte i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_ANALOG(i)) { +#ifdef AnalogInputFirmata_h + // turns off pull-up, configures everything + Firmata.setPinMode(i, PIN_MODE_ANALOG); +#endif + } else if (IS_PIN_DIGITAL(i)) { +#ifdef DigitalOutputFirmata_h + // sets the output to 0, configures portConfigInputs + Firmata.setPinMode(i, OUTPUT); +#endif + } + } + +#ifdef FirmataExt_h + firmataExt.reset(); +#endif +} + +/*============================================================================== + * SETUP() + *============================================================================*/ + +void setup() +{ + /* + * ETHERNET SETUP + */ +#ifdef NETWORK_FIRMATA +#ifdef _YUN_CLIENT_H_ + Bridge.begin(); +#else +#ifdef local_ip + Ethernet.begin((uint8_t *)mac, local_ip); //start Ethernet +#else + Ethernet.begin((uint8_t *)mac); //start Ethernet using dhcp +#endif +#endif + delay(1000); +#endif + + /* + * FIRMATA SETUP + */ + Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION); + +#ifdef FirmataExt_h +#ifdef DigitalInputFirmata_h + firmataExt.addFeature(digitalInput); +#endif +#ifdef DigitalOutputFirmata_h + firmataExt.addFeature(digitalOutput); +#endif +#ifdef AnalogInputFirmata_h + firmataExt.addFeature(analogInput); +#endif +#ifdef AnalogOutputFirmata_h + firmataExt.addFeature(analogOutput); +#endif +#ifdef ServoFirmata_h + firmataExt.addFeature(servo); +#endif +#ifdef I2CFirmata_h + firmataExt.addFeature(i2c); +#endif +#ifdef OneWireFirmata_h + firmataExt.addFeature(oneWire); +#endif +#ifdef StepperFirmata_h + firmataExt.addFeature(stepper); +#endif +#ifdef SerialFirmata_h + firmataExt.addFeature(serial); +#endif +#ifdef FirmataReporting_h + firmataExt.addFeature(reporting); +#endif +#ifdef FirmataScheduler_h + firmataExt.addFeature(scheduler); +#endif +#ifdef FirmataEncoder_h + firmataExt.addFeature(encoder); +#endif +#ifdef DeviceFirmata_h + firmataExt.addFeature(deviceMgr); +#endif +#endif + /* systemResetCallback is declared here (in ConfigurableFirmata.ino) */ + Firmata.attach(SYSTEM_RESET, systemResetCallback); + + // Network Firmata communicates with Ethernet-shields over SPI. Therefor all + // SPI-pins must be set to PIN_MODE_IGNORE. Otherwise Firmata would break SPI-communication. + // add Pin 10 and configure pin 53 as output if using a MEGA with Ethernetshield. + // No need to ignore pin 10 on MEGA with ENC28J60, as here pin 53 should be connected to SS: +#ifdef NETWORK_FIRMATA + + #ifndef _YUN_CLIENT_H_ + // ignore SPI and pin 4 that is SS for SD-Card on Ethernet-shield + for (byte i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_SPI(i) + || 4 == i // SD Card on Ethernet shield uses pin 4 for SS + || 10 == i // Ethernet-shield uses pin 10 for SS + ) { + Firmata.setPinMode(i, PIN_MODE_IGNORE); + } + } + // pinMode(PIN_TO_DIGITAL(53), OUTPUT); configure hardware-SS as output on MEGA + pinMode(PIN_TO_DIGITAL(4), OUTPUT); // switch off SD-card bypassing Firmata + digitalWrite(PIN_TO_DIGITAL(4), HIGH); // SS is active low; + #endif + + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + pinMode(PIN_TO_DIGITAL(53), OUTPUT); // configure hardware SS as output on MEGA + #endif + + // start up Network Firmata: + Firmata.begin(stream); +#else + // Uncomment to save a couple of seconds by disabling the startup blink sequence. + // Firmata.disableBlinkVersion(); + + // start up the default Firmata using Serial interface: + Firmata.begin(57600); +#endif + systemResetCallback(); // reset to default config +} + +/*============================================================================== + * LOOP() + *============================================================================*/ +void loop() +{ +#ifdef DigitalInputFirmata_h + /* DIGITALREAD - as fast as possible, check for changes and output them to the + * stream buffer using Firmata.write() */ + digitalInput.report(); +#endif + + /* STREAMREAD - processing incoming message as soon as possible, while still + * checking digital inputs. */ + while (Firmata.available()) { + Firmata.processInput(); +#ifdef FirmataScheduler_h + if (!Firmata.isParsingMessage()) { + goto runtasks; + } + } + if (!Firmata.isParsingMessage()) { +runtasks: scheduler.runTasks(); +#endif + } + + /* SEND STREAM WRITE BUFFER - TO DO: make sure that the stream buffer doesn't go over + * 60 bytes. use a timer to sending an event character every 4 ms to + * trigger the buffer to dump. */ + +#ifdef FirmataReporting_h + if (reporting.elapsed()) { +#ifdef AnalogInputFirmata_h + /* ANALOGREAD - do all analogReads() at the configured sampling interval */ + analogInput.report(); +#endif +#ifdef I2CFirmata_h + // report i2c data for all device with read continuous mode enabled + i2c.report(); +#endif +#ifdef FirmataEncoder_h + // report encoders positions if reporting enabled. + encoder.report(); +#endif + } +#endif +#ifdef StepperFirmata_h + stepper.update(); +#endif +#ifdef SerialFirmata_h + serial.update(); +#endif + +#ifdef DeviceFirmata_h + deviceMgr.update(); +#endif + +#if defined NETWORK_FIRMATA && !defined local_ip &&!defined _YUN_CLIENT_H_ + // only necessary when using DHCP, ensures local IP is updated appropriately if it changes + if (Ethernet.maintain()) { + stream.maintain(Ethernet.localIP()); + } +#endif +} diff --git a/examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h b/examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h new file mode 100644 index 0000000..43b5ba0 --- /dev/null +++ b/examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h @@ -0,0 +1,22 @@ +#include + +// Device Drivers + +#include +#include +//#include +//#include + +//#include +//#include + +DeviceDriver *selectedDevices[] = { + new DDMeta("Meta",1), + new DDHello("Hello",1), +// new DDMCP9808("MCP9808",1,0x18), +// new DDServo("Servo",2), + +// new DDSensor("Chan",16), +// new DDStepper("Stepper",6), + 0}; + diff --git a/src/ConfigurableFirmata.h b/src/ConfigurableFirmata.h index de38800..a19bd7c 100644 --- a/src/ConfigurableFirmata.h +++ b/src/ConfigurableFirmata.h @@ -93,6 +93,11 @@ #define SYSEX_NON_REALTIME 0x7E // MIDI Reserved for non-realtime messages #define SYSEX_REALTIME 0x7F // MIDI Reserved for realtime messages +// DeviceFirmata commands + +#define DEVICE_QUERY 0x30 // message requesting action from a device driver (DeviceFirmata) +#define DEVICE_RESPONSE 0x31 // message providing the device driver response (DeviceFirmata) + // these are DEPRECATED to make the naming more consistent #define FIRMATA_STRING 0x71 // same as STRING_DATA #define SYSEX_I2C_REQUEST 0x76 // same as I2C_REQUEST diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp new file mode 100644 index 0000000..8d89bd7 --- /dev/null +++ b/src/DeviceFirmata.cpp @@ -0,0 +1,264 @@ +/* + DeviceFirmata.cpp - Firmata library +*/ + +#include "DeviceFirmata.h" +#include "utility/Boards.h" +#include + +DeviceTable *gDeviceTable; +extern DeviceDriver *selectedDevices[]; + +//---------------------------------------------------------------------------- + +DeviceFirmata::DeviceFirmata() { + gDeviceTable = new DeviceTable(selectedDevices,this); +} + +//--------------------------------------------------------------------------- + +void DeviceFirmata::reset() { + gDeviceTable->reset(); +} + +// The pins that are currently being used by device drivers are already +// marked as PIN_MODE_IGNORE, so we have no additional information to +// provide to the list of capabilities. + +void DeviceFirmata::handleCapability(byte pin) {} + +// Device driver capabilities do not necessarily map directly to Firmata pin +// modes, and so none of the Firmata modes are recognized and all pin mode +// requests are disavowed. + +boolean DeviceFirmata::handlePinMode(byte pin, int mode) { + return false; +} + +void DeviceFirmata::update() { + gDeviceTable->dispatchTimers(); +} + +//--------------------------------------------------------------------------- + +// The entire body of each device driver message is encoded in base-64 +// during transmission to and from this Firmata server. The first 9 bytes +// of the decoded message body form a prologue that contains slots for all +// the common query and request parameters. Following bytes in a query +// are used by the open() and write() methods; following bytes in a response +// are used by the read() method. + +// Note that the base64 library adds a null terminator after the decoded data. +// This means that the decode targets need to be at least one byte bigger than +// the actual data size. This also makes it possible to use the decoded data +// as a null-terminated string without having to add the null (eg, open()) + +boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { + if (command != DEVICE_QUERY) return false; + + if (argc < 12) { + reportError(EMSGSIZE); + return true; + } + byte parameterBlock[10]; // 9-byte beginning of every decoded DEVICE_QUERY message + byte *dataBlock = 0; // data from the client for use in open() and write() + byte *inputBuffer = 0; // data from the device in response to read() + + base64_decode((char *)parameterBlock, (char *)(argv), 12); + + int dataBlockLength = (argc == 12) ? 0 : base64_dec_len((char *)(argv + 12), argc - 12); + if (dataBlockLength > 0) { + dataBlock = new byte[dataBlockLength+1]; + if (dataBlock == 0) { + reportError(ENOMEM); + return true; + } + dataBlockLength = base64_decode((char *)dataBlock, (char *)(argv + 12), argc - 12); + } + + int action = (from8LEToHost(parameterBlock) & 0x0F); + int flags = (from8LEToHost(parameterBlock) & 0xF0) >> 4; + int handle = from16LEToHost(parameterBlock+1); + int openOpts = handle; + int reg = from16LEToHost(parameterBlock+3); + int count = from16LEToHost(parameterBlock+5); + + int status; + + switch (action) { + + case (int)DAC::OPEN: + if (dataBlockLength == 0) { + reportError(EINVAL); + } else { + status = gDeviceTable->open(openOpts, flags, (const char *)dataBlock); + reportOpen(status, openOpts, flags, dataBlock); + } + break; + + case (int)DAC::READ: + if (dataBlockLength != 0) { + reportError(EINVAL); + } else { + inputBuffer = new byte[count]; + if (inputBuffer == 0) { + reportError(ENOMEM); + } else { + status = gDeviceTable->read(handle, flags, reg, count, inputBuffer); + reportRead(status, handle, flags, reg, count, inputBuffer); + } + } + break; + + case (int)DAC::WRITE: + if (dataBlockLength != count) { + reportError(EINVAL); + } else { + status = gDeviceTable->write(handle, flags, reg, count, dataBlock); + reportWrite(status, handle, flags, reg, count); + } + break; + + case (int)DAC::CLOSE: + if (dataBlockLength != 0) { + reportError(EINVAL); + } else { + status = gDeviceTable->close(handle, flags); + reportClose(status, handle, flags); + } + break; + + default: + reportError(ENOTSUP); + break; + } + + delete dataBlock; + delete inputBuffer; + return true; +} + +//--------------------------------------------------------------------------- + +void DeviceFirmata::reportClaimPin(int pin) { + Firmata.setPinMode((byte)pin, PIN_MODE_IGNORE); +} + +void DeviceFirmata::reportReleasePin(int pin) { + Firmata.setPinMode((byte)pin, INPUT); +} + +void DeviceFirmata::reportOpen(int status, int openOpts, int flags, const byte *buf) { + sendDeviceResponse((int)DAC::OPEN, status, openOpts, flags, 0, 0,buf); +} +/** + * Translates a message from the DeviceDriver environment to a call to a Firmata-aware method. + * @param status The status code or actual byte count associated with this read. + * @param handle The handle of the unit doing the reply + * @param reg The register identifier associated with the read() being reported + * @param count The number of bytes that were requested. May be less than or + * equal to the byte count in status after a successful read. + * @param buf The byte[] result of the read(). + */ +void DeviceFirmata::reportRead(int status, int handle, int flags, int reg, int count, const byte *buf) { + sendDeviceResponse((int)DAC::READ, status, handle, flags, reg, count, buf); +} +/** + * Translates a message from the DeviceDriver environment to a call to a Firmata-aware method. + * @param status The status code or actual byte count associated with this write. + * @param handle The handle of the unit doing the reply + * @param reg The register identifier associated with the write() being reported + * @param count The number of bytes that were requested. May be less than or + * equal to the byte count in status after a successful write. + */ +void DeviceFirmata::reportWrite(int status, int handle, int flags, int reg, int count) { + sendDeviceResponse((int)DAC::WRITE, status, handle, flags, reg, count); +} + +void DeviceFirmata::reportClose(int status, int handle, int flags) { + sendDeviceResponse((int)DAC::CLOSE, status, handle, flags); +} + +void DeviceFirmata::reportError(int status) { + sendDeviceResponse((int)DAC::CLOSE, status); +} + +void DeviceFirmata::reportString(const byte *dataBytes) { + Firmata.sendString((char *)dataBytes); +} + + +//--------------------------------------------------------------------------- + +/** + * This method is called when there is a message to be sent back to the + * client. It may be in response to a DEVICE_REQUEST that was just + * processed, or it may be an asynchronous event such as a stepper motor in + * a new position or a continuous read data packet. + * + * dmB -> decoded message body + * emB -> encoded message body + * + * @param action The method identifier to use in the response. + * @param status Status value, new handle (open), or number of bytes actually read or written + * @param handle The handle identifying the device and unit number the message is coming from + * @param reg The register number associated with this message + * @param count The number of bytes specified originally by the caller + * @param dataBytes The raw data read from the device, if any + * + * Note: The base64 encoder adds a null at the end of the encoded data. Thus the encode + * target buffer needs to be one byte longer than the calculated data length. + */ +void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int flags, int reg, int count, + const byte *dataBytes) { + + byte dP[9]; // decoded (raw) message prologue + byte eP[12+1]; // encoded message prologue + byte *eD; // encoded data bytes + + Firmata.write(START_SYSEX); + Firmata.write(DEVICE_RESPONSE); + + dP[0] = (byte) ((flags & 0xF) << 4) | (action & 0xF); + dP[1] = (byte) lowByte(handle); + dP[2] = (byte) highByte(handle); + dP[3] = (byte) lowByte(reg); + dP[4] = (byte) highByte(reg); + dP[5] = (byte) lowByte(count); + dP[6] = (byte) highByte(count); + dP[7] = (byte) lowByte(status); + dP[8] = (byte) highByte(status); + + base64_encode((char *)eP, (char *)dP, 9); + + for (int idx = 0; idx < 12; idx++) { + Firmata.write(eP[idx]); + } + + int rawCount = 0; + int encCount = 0; + + if (dataBytes != 0) { + if (action == (int)DAC::OPEN) { + rawCount = strlen((const char *)dataBytes)+1; + } else if (action == (int)DAC::READ && status > 0) { + rawCount = status; + } + encCount = base64_enc_len(rawCount); + if (encCount > 0) { + eD = new byte[encCount+1]; + if (eD == 0) { + for (int idx = 0; idx < encCount; idx++) { + Firmata.write('/'); // Memory allocation error. This value will be decoded as 0x3F, ie, all 7 bits set. + } + } else { + base64_encode((char *)eD, (char *)dataBytes, rawCount); + for (int idx = 0; idx < encCount; idx++) { + Firmata.write(eD[idx]); // Success. These are the encoded data bytes. + } + } + delete eD; + } + } + Firmata.write(END_SYSEX); +} diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h new file mode 100644 index 0000000..c44171b --- /dev/null +++ b/src/DeviceFirmata.h @@ -0,0 +1,41 @@ + +#ifndef DeviceFirmata_h +#define DeviceFirmata_h + +#include +#include +#include +#include +#include + +class DeviceFirmata: public FirmataFeature, ClientReporter { +public: + DeviceFirmata(); + + // FirmataFeature + + void reset(); + void handleCapability(byte pin); + boolean handlePinMode(byte pin, int mode); + boolean handleSysex(byte command, byte argc, byte* argv); + + void update(); + + // ClientReporter + + void reportOpen(int status, int opts, int flags, const byte *buf); + void reportRead(int status, int handle, int flags, int reg, int count, const byte *dataBytes); + void reportWrite(int status, int handle, int flags, int reg, int count); + void reportClose(int status, int handle, int flags ); + void reportString(const byte *dataBytes); + void reportError(int status); + void reportClaimPin(int pin); + void reportReleasePin(int pin); + +private: + + void sendDeviceResponse(int action, int status, int handle = 0, int flags = 0, int reg = 0, int count = 0, const byte *dataBytes = 0); +}; + +#endif +