From b9a56d5749796a64a358ceeb13f46dd3bb9b5efe Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sat, 13 Feb 2016 21:26:35 -0800 Subject: [PATCH 01/34] Increment protocol and firmware version numbers for proposed DeviceFirmata change. --- src/ConfigurableFirmata.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ConfigurableFirmata.h b/src/ConfigurableFirmata.h index bb71f1d..e7e1a15 100644 --- a/src/ConfigurableFirmata.h +++ b/src/ConfigurableFirmata.h @@ -22,7 +22,8 @@ * Query using the REPORT_VERSION message. */ #define FIRMATA_PROTOCOL_MAJOR_VERSION 2 // for non-compatible changes -#define FIRMATA_PROTOCOL_MINOR_VERSION 5 // for backwards compatible changes +// #define FIRMATA_PROTOCOL_MINOR_VERSION 5 // for backwards compatible changes +#define FIRMATA_PROTOCOL_MINOR_VERSION 6 // DeviceFirmata pull request source #define FIRMATA_PROTOCOL_BUGFIX_VERSION 1 // for bugfix releases /* @@ -32,7 +33,8 @@ * Query using the REPORT_FIRMWARE message. */ #define FIRMATA_FIRMWARE_MAJOR_VERSION 2 // for non-compatible changes -#define FIRMATA_FIRMWARE_MINOR_VERSION 8 // for backwards compatible changes +// #define FIRMATA_FIRMWARE_MINOR_VERSION 8 // for backwards compatible changes +#define FIRMATA_FIRMWARE_MINOR_VERSION 9 // DeviceFirmata pull request source #define FIRMATA_FIRMWARE_BUGFIX_VERSION 1 // for bugfix releases // DEPRECATED as of ConfigurableFirmata v2.8.1. From 947e2350fbe580f77c4d873df9ac90d78d944987 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sat, 13 Feb 2016 21:49:09 -0800 Subject: [PATCH 02/34] Set target version numbers for protocol and firmware. --- src/ConfigurableFirmata.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ConfigurableFirmata.h b/src/ConfigurableFirmata.h index e7e1a15..c2526bc 100644 --- a/src/ConfigurableFirmata.h +++ b/src/ConfigurableFirmata.h @@ -23,8 +23,9 @@ */ #define FIRMATA_PROTOCOL_MAJOR_VERSION 2 // for non-compatible changes // #define FIRMATA_PROTOCOL_MINOR_VERSION 5 // for backwards compatible changes -#define FIRMATA_PROTOCOL_MINOR_VERSION 6 // DeviceFirmata pull request source -#define FIRMATA_PROTOCOL_BUGFIX_VERSION 1 // for bugfix releases +// #define FIRMATA_PROTOCOL_BUGFIX_VERSION 1 // for bugfix releases +#define FIRMATA_PROTOCOL_MINOR_VERSION 6 // DEVICE_QUERY/_RESPONSE is ... +#define FIRMATA_PROTOCOL_BUGFIX_VERSION 0 // ... proposed for Firmata 2.6 /* * Version numbers for the Firmata library. @@ -34,8 +35,9 @@ */ #define FIRMATA_FIRMWARE_MAJOR_VERSION 2 // for non-compatible changes // #define FIRMATA_FIRMWARE_MINOR_VERSION 8 // for backwards compatible changes -#define FIRMATA_FIRMWARE_MINOR_VERSION 9 // DeviceFirmata pull request source -#define FIRMATA_FIRMWARE_BUGFIX_VERSION 1 // for bugfix releases +// #define FIRMATA_FIRMWARE_BUGFIX_VERSION 1 // for bugfix releases +#define FIRMATA_FIRMWARE_MINOR_VERSION 9 // DeviceFirmata firmware update is ... +#define FIRMATA_FIRMWARE_BUGFIX_VERSION 0 // ... proposed for ConfigurableFirmata 2.9 // DEPRECATED as of ConfigurableFirmata v2.8.1. // Use FIRMATA_PROTOCOL_[MAJOR|MINOR|BUGFIX]_VERSION instead. From 43bf48a0d463e0281bd6cf29fd7d3a9a402afd92 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sat, 13 Feb 2016 23:20:38 -0800 Subject: [PATCH 03/34] Update library version to match the new 2.9 Firmware version. --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index a56d965..ad0417e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ConfigurableFirmata -version=2.8.1 +version=2.9.0 author=Firmata Developers maintainer=https://github.com/firmata/ConfigurableFirmata sentence=This library implements the Firmata protocol as a set of plugins that can be used to create applications to remotely interface with an Arduino board. From dd841d09926802743c01dc66f598d80f7e903385 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sun, 14 Feb 2016 23:32:55 -0800 Subject: [PATCH 04/34] Add DeviceFirmata from sketch repo. --- src/DeviceFirmata.cpp | 141 ++++++++++++++++++++++++++++++++++++++++++ src/DeviceFirmata.h | 41 ++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 src/DeviceFirmata.cpp create mode 100644 src/DeviceFirmata.h diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp new file mode 100644 index 0000000..fe31176 --- /dev/null +++ b/src/DeviceFirmata.cpp @@ -0,0 +1,141 @@ +/* + DeviceFirmata.cpp - Firmata library +*/ + +#include "DeviceFirmata.h" +#include + +extern DeviceDriver *selectedDevices[]; + +//---------------------------------------------------------------------------- + +DeviceFirmata::DeviceFirmata(const char *luRootName) { + dt = new DeviceTable(selectedDevices, luRootName); +} + +//--------------------------------------------------------------------------- + +void DeviceFirmata::reset(){} + +void DeviceFirmata::handleCapability(byte pin){} + +boolean DeviceFirmata::handlePinMode(byte pin, int mode) { + return false; +} + +void DeviceFirmata::update(unsigned long deltaMicros) { + dt->update(deltaMicros); +} + +void DeviceFirmata::report(unsigned long deltaMillis) { + dt->report(deltaMillis); +} + +// The first six bytes of argv for DEVICE_QUERY messages are: action, reserved, +// handle-low, handle-high, reserved, reserved. They are all constrained to +// 7-bit values.The bytes that follow, if any, are the parameter block. The +// parameter block is encoded with base-64 in the sysex message body during +// transmission to and from this Firmata server. + +// dpB -> decoded parameter block +// epB -> encoded parameter block + +boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { + + byte dpBlock[1 + MAX_DPB_LENGTH]; // decoded parameter block + + if (command != DEVICE_QUERY) { + return false; + } + + int action = argv[0]; + int handle = (argv[3] << 7) | argv[2]; + + int dpCount = base64_dec_len((char *)(argv + 6), argc - 6); + if (dpCount > MAX_DPB_LENGTH) { + sendDeviceResponse(handle, action, EMSGSIZE); + return true; + } + + if (dpCount > 0) { + dpCount = base64_decode((char *)dpBlock, (char *)(argv + 6), argc - 6); + } + + int status = dispatchDeviceAction(action, handle, dpCount, dpBlock); + + sendDeviceResponse(handle, action, status, dpBlock); + return true; +} + +//--------------------------------------------------------------------------- + +int DeviceFirmata::dispatchDeviceAction(int action, int handle, int dpCount, byte *dpBlock) { + int count = 0; + int reg = 0; + int flags = 0; + + switch (action) { + case DD_OPEN: + flags = handle; + return dt->open((char *)dpBlock, flags); + + case DD_STATUS: + count = from16LEToHost(dpBlock); + reg = from16LEToHost(dpBlock + 2); + return dt->status(handle,reg, count, dpBlock); + + case DD_CONTROL: + count = from16LEToHost(dpBlock); + reg = from16LEToHost(dpBlock + 2); + return dt->control(handle, reg, count, dpBlock + 4); + + case DD_READ: + count = from16LEToHost(dpBlock); + return dt->read(handle, count, dpBlock); + + case DD_WRITE: + count = from16LEToHost(dpBlock); + return dt->read(handle, count, dpBlock + 2); + + case DD_CLOSE: + return dt->close(handle); + + default: + return ENOSYS; + } +} + +// dpB -> decoded parameter block +// epB -> encoded parameter block + +void DeviceFirmata::sendDeviceResponse(int handle, int action, int status) { + sendDeviceResponse(handle, action, status, 0); +} + +void DeviceFirmata::sendDeviceResponse(int handle, int action, int status, const byte *dpB) { + byte epB[1 + ((MAX_DPB_LENGTH + 2) / 3) * 4]; + + Firmata.write(START_SYSEX); + Firmata.write(DEVICE_RESPONSE); + Firmata.write(action & 0x7F); + Firmata.write(0); + if (action == DD_OPEN) { + Firmata.write(0); + Firmata.write(0); + Firmata.write(status & 0x7F); // status is handle or error + Firmata.write((status >> 7) & 0x7F); + } else { + Firmata.write(handle & 0x7F); + Firmata.write((handle >> 7) & 0x7F); + Firmata.write(status & 0x7F); // status is bytecount or error + Firmata.write((status >> 7) & 0x7F); + + if (status > 0 && status <= MAX_DPB_LENGTH) { // status is bytecount + int epCount = base64_encode((char *)epB, (char *)dpB, status); + for (int idx = 0; idx < epCount; idx++) { + Firmata.write(epB[idx]); + } + } + } + Firmata.write(END_SYSEX); +} diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h new file mode 100644 index 0000000..53b8dff --- /dev/null +++ b/src/DeviceFirmata.h @@ -0,0 +1,41 @@ + +#ifndef DeviceFirmata_h +#define DeviceFirmata_h + +#include +#include +#include + +#define MAX_DPB_LENGTH 128 // decoded parameter block length (plain text) + +// Firmata coding of DeviceDriver methods + +#define DD_OPEN 0x00 +#define DD_STATUS 0x01 +#define DD_CONTROL 0x02 +#define DD_READ 0x03 +#define DD_WRITE 0x04 +#define DD_CLOSE 0x05 + +class DeviceFirmata: public FirmataFeature { +public: + DeviceFirmata(const char *luRootName = 0); + + void reset(); + void handleCapability(byte pin); + boolean handlePinMode(byte pin, int mode); + boolean handleSysex(byte command, byte argc, byte* argv); + + void update(unsigned long deltaMicros); + void report(unsigned long deltaMillis); + +private: + DeviceTable *dt; + + int dispatchDeviceAction(int act, int handle, int pc, byte *pv); + void sendDeviceResponse(int handle, int action, int status); + void sendDeviceResponse(int handle, int action, int status, const byte *dpBlock); +}; + +#endif + From 4ed2796be2459536d3ec6da1df5337e2f961e1be Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sun, 14 Feb 2016 23:47:35 -0800 Subject: [PATCH 05/34] Fit DeviceFirmata and its new environment together. --- src/ConfigurableFirmata.h | 5 +++++ src/DeviceFirmata.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/ConfigurableFirmata.h b/src/ConfigurableFirmata.h index c2526bc..cb1b616 100644 --- a/src/ConfigurableFirmata.h +++ b/src/ConfigurableFirmata.h @@ -97,6 +97,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.h b/src/DeviceFirmata.h index 53b8dff..6396aed 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -3,6 +3,7 @@ #define DeviceFirmata_h #include +#include #include #include From f4dec505f46a7f32e1a2acf065826812611b51f0 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Mon, 15 Feb 2016 15:04:12 -0800 Subject: [PATCH 06/34] Workaround for Servo.h / ConfigurableFirmata.h include order conflict. --- src/utility/Boards.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/utility/Boards.h b/src/utility/Boards.h index 276c523..25c18e1 100644 --- a/src/utility/Boards.h +++ b/src/utility/Boards.h @@ -29,9 +29,11 @@ // this file). If Servo.h wasn't included, this allows the code to still // compile, but without support for any Servos. Hopefully that's what the // user intended by not including Servo.h -#ifndef MAX_SERVOS -#define MAX_SERVOS 0 -#endif +// --> ConfigurableFirmata.h is included before Servo.h and so the following +// --> definition causes a compiler warning. +// #ifndef MAX_SERVOS +// #define MAX_SERVOS 0 +// #endif /* Firmata Hardware Abstraction Layer From aedb305d8a325c28dda8cb30ea8fd4bb30fb8e0f Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Mon, 15 Feb 2016 19:02:00 -0800 Subject: [PATCH 07/34] Clarify device driver call chain for main loop() processing. --- src/DeviceFirmata.cpp | 8 ++------ src/DeviceFirmata.h | 3 +-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index fe31176..9601f1d 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -23,12 +23,8 @@ boolean DeviceFirmata::handlePinMode(byte pin, int mode) { return false; } -void DeviceFirmata::update(unsigned long deltaMicros) { - dt->update(deltaMicros); -} - -void DeviceFirmata::report(unsigned long deltaMillis) { - dt->report(deltaMillis); +void DeviceFirmata::update() { + dt->dispatchTimers(); } // The first six bytes of argv for DEVICE_QUERY messages are: action, reserved, diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 6396aed..871f32a 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -27,8 +27,7 @@ class DeviceFirmata: public FirmataFeature { boolean handlePinMode(byte pin, int mode); boolean handleSysex(byte command, byte argc, byte* argv); - void update(unsigned long deltaMicros); - void report(unsigned long deltaMillis); + void update(); private: DeviceTable *dt; From 99a537a009b47ca7a3dba12fb446be60d8e47b51 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sun, 21 Feb 2016 22:16:33 -0800 Subject: [PATCH 08/34] Implement ClientReporter. --- src/DeviceFirmata.cpp | 134 ++++++++++++++++++++++++++++++------------ src/DeviceFirmata.h | 19 ++++-- 2 files changed, 110 insertions(+), 43 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 9601f1d..1b15ec4 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -15,16 +15,18 @@ DeviceFirmata::DeviceFirmata(const char *luRootName) { //--------------------------------------------------------------------------- -void DeviceFirmata::reset(){} +void DeviceFirmata::reset() { + dt->reset(); +} -void DeviceFirmata::handleCapability(byte pin){} +void DeviceFirmata::handleCapability(byte pin) {} boolean DeviceFirmata::handlePinMode(byte pin, int mode) { return false; } void DeviceFirmata::update() { - dt->dispatchTimers(); + dt->dispatchTimers((ClientReporter*)this); } // The first six bytes of argv for DEVICE_QUERY messages are: action, reserved, @@ -57,15 +59,14 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { dpCount = base64_decode((char *)dpBlock, (char *)(argv + 6), argc - 6); } - int status = dispatchDeviceAction(action, handle, dpCount, dpBlock); - - sendDeviceResponse(handle, action, status, dpBlock); + dispatchDeviceAction(action, handle, dpCount, dpBlock); return true; } //--------------------------------------------------------------------------- -int DeviceFirmata::dispatchDeviceAction(int action, int handle, int dpCount, byte *dpBlock) { +void DeviceFirmata::dispatchDeviceAction(int action, int handle, int dpCount, byte *dpBlock) { + int status = 0; int count = 0; int reg = 0; int flags = 0; @@ -73,65 +74,120 @@ int DeviceFirmata::dispatchDeviceAction(int action, int handle, int dpCount, byt switch (action) { case DD_OPEN: flags = handle; - return dt->open((char *)dpBlock, flags); + status = dt->open((char *)dpBlock, flags); + reportOpen(status); + break; case DD_STATUS: count = from16LEToHost(dpBlock); reg = from16LEToHost(dpBlock + 2); - return dt->status(handle,reg, count, dpBlock); + status = dt->status(handle, reg, count, dpBlock); + reportStatus(handle, status, dpBlock); + break; case DD_CONTROL: count = from16LEToHost(dpBlock); reg = from16LEToHost(dpBlock + 2); - return dt->control(handle, reg, count, dpBlock + 4); + status = dt->control(handle, reg, count, dpBlock + 4); + reportControl(handle, status); + break; case DD_READ: count = from16LEToHost(dpBlock); - return dt->read(handle, count, dpBlock); + status = dt->read(handle, count, dpBlock); + reportRead(handle, status, dpBlock); + break; case DD_WRITE: count = from16LEToHost(dpBlock); - return dt->read(handle, count, dpBlock + 2); + status = dt->read(handle, count, dpBlock + 2); + reportWrite(handle, status); + break; case DD_CLOSE: - return dt->close(handle); + status = dt->close(handle); + reportClose(handle, status); + break; default: - return ENOSYS; + status = ENOSYS; + break; } } +//--------------------------------------------------------------------------- + +/** + * 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. + * @param handle The 14-bit handle identifying the device and unit + * number the message is coming from + * @param action The method identifier to use in the response. + * @param status Status value to send or number of bytes in dpBlock to send + * @param dpBlock The decoded (raw) parameter block to send upwards. + */ +void DeviceFirmata::sendDeviceResponse(int action, int handle, int status, const byte *dpB) { + byte epB[1 + ((MAX_DPB_LENGTH + 2) / 3) * 4]; + byte header[8] = {START_SYSEX, DEVICE_RESPONSE}; + int epCount = 0; + + header[2] = (byte) action; + header[3] = (byte) 0; + header[4] = (byte)getUnitHandle(handle); + header[5] = (byte)getDeviceHandle(handle); + header[6] = (byte)(status & 0x7F); + header[7] = (byte)((status >> 7) & 0x7F); + + for (int idx = 0; idx < 8; idx++) { + Firmata.write(header[idx]); + } + // dpB -> decoded parameter block // epB -> encoded parameter block -void DeviceFirmata::sendDeviceResponse(int handle, int action, int status) { - sendDeviceResponse(handle, action, status, 0); + if (status > 0 && status <= MAX_DPB_LENGTH && dpB != 0) { + epCount = base64_encode((char *)epB, (char *)dpB, status); + } + + for (int idx = 0; idx < epCount; idx++) { + Firmata.write(epB[idx]); + } + Firmata.write(END_SYSEX); } -void DeviceFirmata::sendDeviceResponse(int handle, int action, int status, const byte *dpB) { - byte epB[1 + ((MAX_DPB_LENGTH + 2) / 3) * 4]; +//--------------------------------------------------------------------------- - Firmata.write(START_SYSEX); - Firmata.write(DEVICE_RESPONSE); - Firmata.write(action & 0x7F); - Firmata.write(0); - if (action == DD_OPEN) { - Firmata.write(0); - Firmata.write(0); - Firmata.write(status & 0x7F); // status is handle or error - Firmata.write((status >> 7) & 0x7F); +void DeviceFirmata::reportOpen(int status) { + sendDeviceResponse(DD_OPEN, 0, status); +} + +void DeviceFirmata::reportStatus(int handle, int status, const byte *dpB) { + if (status < 0) { + sendDeviceResponse(DD_STATUS, handle, status); } else { - Firmata.write(handle & 0x7F); - Firmata.write((handle >> 7) & 0x7F); - Firmata.write(status & 0x7F); // status is bytecount or error - Firmata.write((status >> 7) & 0x7F); - - if (status > 0 && status <= MAX_DPB_LENGTH) { // status is bytecount - int epCount = base64_encode((char *)epB, (char *)dpB, status); - for (int idx = 0; idx < epCount; idx++) { - Firmata.write(epB[idx]); - } - } + sendDeviceResponse(DD_STATUS, handle, status, dpB); } - Firmata.write(END_SYSEX); } + +void DeviceFirmata::reportRead(int handle, int status, const byte *dpB) { + if (status < 0) { + sendDeviceResponse(DD_READ, handle, status); + } else { + sendDeviceResponse(DD_READ, handle, status, dpB); + } +} + +void DeviceFirmata::reportControl(int handle, int status) { + sendDeviceResponse(DD_CONTROL, handle, status); +} + +void DeviceFirmata::reportWrite(int handle, int status) { + sendDeviceResponse(DD_WRITE, handle, status); +} + +void DeviceFirmata::reportClose(int handle, int status) { + sendDeviceResponse(DD_CLOSE, handle, status); +} + diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 871f32a..034be3f 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -6,6 +6,7 @@ #include #include #include +#include #define MAX_DPB_LENGTH 128 // decoded parameter block length (plain text) @@ -18,10 +19,12 @@ #define DD_WRITE 0x04 #define DD_CLOSE 0x05 -class DeviceFirmata: public FirmataFeature { +class DeviceFirmata: public FirmataFeature, ClientReporter { public: DeviceFirmata(const char *luRootName = 0); + // FirmataFeature + void reset(); void handleCapability(byte pin); boolean handlePinMode(byte pin, int mode); @@ -29,12 +32,20 @@ class DeviceFirmata: public FirmataFeature { void update(); + // ClientReporter + + void reportOpen(int status); + void reportStatus(int handle, int status, const byte *dpB); + void reportRead(int handle, int status, const byte *dpB); + void reportControl(int handle, int status); + void reportWrite(int handle, int status); + void reportClose(int handle, int status); + private: DeviceTable *dt; - int dispatchDeviceAction(int act, int handle, int pc, byte *pv); - void sendDeviceResponse(int handle, int action, int status); - void sendDeviceResponse(int handle, int action, int status, const byte *dpBlock); + void dispatchDeviceAction(int act, int handle, int pc, byte *pv); + void sendDeviceResponse(int action, int handle, int status, const byte *dpBlock = 0); }; #endif From 0b94bbdb2177ef678378c43a012047354910c14e Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Tue, 23 Feb 2016 00:14:06 -0800 Subject: [PATCH 09/34] Align the parameters of similar methods. --- src/DeviceFirmata.cpp | 6 +++--- src/DeviceFirmata.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 1b15ec4..192f05a 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -59,13 +59,13 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { dpCount = base64_decode((char *)dpBlock, (char *)(argv + 6), argc - 6); } - dispatchDeviceAction(action, handle, dpCount, dpBlock); + dispatchDeviceAction(handle, action, dpCount, dpBlock); return true; } //--------------------------------------------------------------------------- -void DeviceFirmata::dispatchDeviceAction(int action, int handle, int dpCount, byte *dpBlock) { +void DeviceFirmata::dispatchDeviceAction(int handle, int action, int dpCount, byte *dpBlock) { int status = 0; int count = 0; int reg = 0; @@ -128,7 +128,7 @@ void DeviceFirmata::dispatchDeviceAction(int action, int handle, int dpCount, by * @param status Status value to send or number of bytes in dpBlock to send * @param dpBlock The decoded (raw) parameter block to send upwards. */ -void DeviceFirmata::sendDeviceResponse(int action, int handle, int status, const byte *dpB) { +void DeviceFirmata::sendDeviceResponse(int handle, int action, int status, const byte *dpB) { byte epB[1 + ((MAX_DPB_LENGTH + 2) / 3) * 4]; byte header[8] = {START_SYSEX, DEVICE_RESPONSE}; int epCount = 0; diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 034be3f..368b0f4 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -44,8 +44,8 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { private: DeviceTable *dt; - void dispatchDeviceAction(int act, int handle, int pc, byte *pv); - void sendDeviceResponse(int action, int handle, int status, const byte *dpBlock = 0); + void dispatchDeviceAction(int handle, int act, int pc, byte *pv); + void sendDeviceResponse(int handle, int action, int status, const byte *dpBlock = 0); }; #endif From 3417d13a37e9b7ef03767960a73adee25fc6fda7 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Tue, 23 Feb 2016 19:47:36 -0800 Subject: [PATCH 10/34] Fix the bug in the previous commit... --- src/DeviceFirmata.cpp | 4 ++-- src/DeviceFirmata.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 192f05a..539015f 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -51,7 +51,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { int dpCount = base64_dec_len((char *)(argv + 6), argc - 6); if (dpCount > MAX_DPB_LENGTH) { - sendDeviceResponse(handle, action, EMSGSIZE); + sendDeviceResponse(action, handle, EMSGSIZE); return true; } @@ -128,7 +128,7 @@ void DeviceFirmata::dispatchDeviceAction(int handle, int action, int dpCount, by * @param status Status value to send or number of bytes in dpBlock to send * @param dpBlock The decoded (raw) parameter block to send upwards. */ -void DeviceFirmata::sendDeviceResponse(int handle, int action, int status, const byte *dpB) { +void DeviceFirmata::sendDeviceResponse(int action, int handle, int status, const byte *dpB) { byte epB[1 + ((MAX_DPB_LENGTH + 2) / 3) * 4]; byte header[8] = {START_SYSEX, DEVICE_RESPONSE}; int epCount = 0; diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 368b0f4..8f3a0a9 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -45,7 +45,7 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { DeviceTable *dt; void dispatchDeviceAction(int handle, int act, int pc, byte *pv); - void sendDeviceResponse(int handle, int action, int status, const byte *dpBlock = 0); + void sendDeviceResponse(int action, int handle, int status, const byte *dpBlock = 0); }; #endif From d3b78f4b5a9b052cf35def7cbf7aa9db4da86e91 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Fri, 26 Feb 2016 00:38:26 -0800 Subject: [PATCH 11/34] The timers return a status all the way back to the feature. --- src/DeviceFirmata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 539015f..2962f1b 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -26,7 +26,7 @@ boolean DeviceFirmata::handlePinMode(byte pin, int mode) { } void DeviceFirmata::update() { - dt->dispatchTimers((ClientReporter*)this); + int status = dt->dispatchTimers((ClientReporter*)this); } // The first six bytes of argv for DEVICE_QUERY messages are: action, reserved, From 8df3cac40c55b3044463e4f6225f87717a90b9eb Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sun, 28 Feb 2016 00:09:50 -0800 Subject: [PATCH 12/34] Handles are 14-bit, numbers are 7-bit. --- src/DeviceFirmata.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 2962f1b..25a1ce3 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -135,8 +135,8 @@ void DeviceFirmata::sendDeviceResponse(int action, int handle, int status, const header[2] = (byte) action; header[3] = (byte) 0; - header[4] = (byte)getUnitHandle(handle); - header[5] = (byte)getDeviceHandle(handle); + header[4] = (byte)getUnitNumber(handle); + header[5] = (byte)getDeviceNumber(handle); header[6] = (byte)(status & 0x7F); header[7] = (byte)((status >> 7) & 0x7F); From 071bc6c1f32a581bc61931f52578c34c027fa1f8 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Tue, 1 Mar 2016 00:34:30 -0800 Subject: [PATCH 13/34] Fold read/write into status/control methods. --- src/DeviceFirmata.cpp | 140 +++++++++++++++++++----------------------- src/DeviceFirmata.h | 19 +++--- 2 files changed, 70 insertions(+), 89 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 25a1ce3..fe75880 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -29,6 +29,8 @@ void DeviceFirmata::update() { int status = dt->dispatchTimers((ClientReporter*)this); } +//--------------------------------------------------------------------------- + // The first six bytes of argv for DEVICE_QUERY messages are: action, reserved, // handle-low, handle-high, reserved, reserved. They are all constrained to // 7-bit values.The bytes that follow, if any, are the parameter block. The @@ -51,7 +53,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { int dpCount = base64_dec_len((char *)(argv + 6), argc - 6); if (dpCount > MAX_DPB_LENGTH) { - sendDeviceResponse(action, handle, EMSGSIZE); + sendDeviceResponse(action, EMSGSIZE, handle); return true; } @@ -59,60 +61,79 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { dpCount = base64_decode((char *)dpBlock, (char *)(argv + 6), argc - 6); } - dispatchDeviceAction(handle, action, dpCount, dpBlock); - return true; -} - -//--------------------------------------------------------------------------- - -void DeviceFirmata::dispatchDeviceAction(int handle, int action, int dpCount, byte *dpBlock) { + int flags = 0; int status = 0; - int count = 0; int reg = 0; - int flags = 0; + int count = 0; switch (action) { + case DD_OPEN: flags = handle; status = dt->open((char *)dpBlock, flags); reportOpen(status); break; - case DD_STATUS: - count = from16LEToHost(dpBlock); - reg = from16LEToHost(dpBlock + 2); - status = dt->status(handle, reg, count, dpBlock); - reportStatus(handle, status, dpBlock); - break; - - case DD_CONTROL: - count = from16LEToHost(dpBlock); - reg = from16LEToHost(dpBlock + 2); - status = dt->control(handle, reg, count, dpBlock + 4); - reportControl(handle, status); + case DD_CLOSE: + status = dt->close(handle); + reportClose(status, handle); break; case DD_READ: - count = from16LEToHost(dpBlock); - status = dt->read(handle, count, dpBlock); - reportRead(handle, status, dpBlock); + reg = from8LEToHost(dpBlock); + count = from16LEToHost(dpBlock + 1); + status = dt->read(handle, reg, count, dpBlock + 3); + reportRead(status, handle, dpBlock); break; case DD_WRITE: - count = from16LEToHost(dpBlock); - status = dt->read(handle, count, dpBlock + 2); - reportWrite(handle, status); - break; - - case DD_CLOSE: - status = dt->close(handle); - reportClose(handle, status); + reg = from8LEToHost(dpBlock); + count = from16LEToHost(dpBlock + 1); + status = dt->write(handle, reg, count, dpBlock + 3); + reportWrite(status, handle, dpBlock); break; default: - status = ENOSYS; + sendDeviceResponse(action, ENOSYS, handle); break; + } + return true; +} + +//--------------------------------------------------------------------------- + + // in all cases, open and close don't return any bytes in the parameter buffer + // on error, both read and write return 3 bytes in the parameter buffer + // on read success, 3 bytes plus the data read are returned in the parameter buffer + // on write success, 3 bytes are returned in the parameter buffer + +void DeviceFirmata::reportOpen(int status) { + sendDeviceResponse(DD_OPEN, status, 0); +} + +void DeviceFirmata::reportClose(int status, int handle) { + sendDeviceResponse(DD_CLOSE, status, handle); +} +/** + * 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 14-bit handle of the unit doing the reply + * @param dpB The byte buffer holding the reg, requested byte count and the data that was read + */ + void DeviceFirmata::reportRead(int status, int handle, const byte *dpB) { + int dpCount = (status >= 0) ? status + 3 : 3; + sendDeviceResponse(DD_READ, status, handle, dpCount, dpB); +} +/** + * 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 14-bit handle of the unit doing the reply + * @param dpB The byte buffer holding the reg and requested byte count + */ +void DeviceFirmata::reportWrite(int status, int handle, const byte *dpB) { + int dpCount = 3; + sendDeviceResponse(DD_WRITE, status, handle, dpCount, dpB); } //--------------------------------------------------------------------------- @@ -122,14 +143,14 @@ void DeviceFirmata::dispatchDeviceAction(int handle, int action, int dpCount, by * 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. - * @param handle The 14-bit handle identifying the device and unit - * number the message is coming from * @param action The method identifier to use in the response. - * @param status Status value to send or number of bytes in dpBlock to send - * @param dpBlock The decoded (raw) parameter block to send upwards. + * @param status Status value to send or number of bytes actually read or written + * @param handle The 14-bit handle identifying the device and unit number the message is coming from + * @param dpCount The number of bytes to be encoded from the dpB and sent on + * @param dpBlock The decoded (raw) parameter block to send upwards after encoding */ -void DeviceFirmata::sendDeviceResponse(int action, int handle, int status, const byte *dpB) { - byte epB[1 + ((MAX_DPB_LENGTH + 2) / 3) * 4]; +void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int dpCount, const byte *dpB) { + byte epB[1 + 4 + ((MAX_DPB_LENGTH + 2) / 3) * 4]; byte header[8] = {START_SYSEX, DEVICE_RESPONSE}; int epCount = 0; @@ -147,8 +168,8 @@ void DeviceFirmata::sendDeviceResponse(int action, int handle, int status, const // dpB -> decoded parameter block // epB -> encoded parameter block - if (status > 0 && status <= MAX_DPB_LENGTH && dpB != 0) { - epCount = base64_encode((char *)epB, (char *)dpB, status); + if (dpCount > 0 && dpCount <= MAX_DPB_LENGTH && dpB != 0) { + epCount = base64_encode((char *)epB, (char *)dpB, dpCount); } for (int idx = 0; idx < epCount; idx++) { @@ -156,38 +177,3 @@ void DeviceFirmata::sendDeviceResponse(int action, int handle, int status, const } Firmata.write(END_SYSEX); } - -//--------------------------------------------------------------------------- - -void DeviceFirmata::reportOpen(int status) { - sendDeviceResponse(DD_OPEN, 0, status); -} - -void DeviceFirmata::reportStatus(int handle, int status, const byte *dpB) { - if (status < 0) { - sendDeviceResponse(DD_STATUS, handle, status); - } else { - sendDeviceResponse(DD_STATUS, handle, status, dpB); - } -} - -void DeviceFirmata::reportRead(int handle, int status, const byte *dpB) { - if (status < 0) { - sendDeviceResponse(DD_READ, handle, status); - } else { - sendDeviceResponse(DD_READ, handle, status, dpB); - } -} - -void DeviceFirmata::reportControl(int handle, int status) { - sendDeviceResponse(DD_CONTROL, handle, status); -} - -void DeviceFirmata::reportWrite(int handle, int status) { - sendDeviceResponse(DD_WRITE, handle, status); -} - -void DeviceFirmata::reportClose(int handle, int status) { - sendDeviceResponse(DD_CLOSE, handle, status); -} - diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 8f3a0a9..ca22d6d 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -13,11 +13,9 @@ // Firmata coding of DeviceDriver methods #define DD_OPEN 0x00 -#define DD_STATUS 0x01 -#define DD_CONTROL 0x02 -#define DD_READ 0x03 -#define DD_WRITE 0x04 -#define DD_CLOSE 0x05 +#define DD_READ 0x01 +#define DD_WRITE 0x02 +#define DD_CLOSE 0x03 class DeviceFirmata: public FirmataFeature, ClientReporter { public: @@ -35,17 +33,14 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { // ClientReporter void reportOpen(int status); - void reportStatus(int handle, int status, const byte *dpB); - void reportRead(int handle, int status, const byte *dpB); - void reportControl(int handle, int status); - void reportWrite(int handle, int status); - void reportClose(int handle, int status); + void reportClose(int status, int handle); + void reportRead(int status, int handle, const byte *dpB); + void reportWrite(int status, int handle, const byte *dpB); private: DeviceTable *dt; - void dispatchDeviceAction(int handle, int act, int pc, byte *pv); - void sendDeviceResponse(int action, int handle, int status, const byte *dpBlock = 0); + void sendDeviceResponse(int action, int status, int handle, int dpCount = 0, const byte *dpBlock = 0); }; #endif From 0fa9f1af19a631c713bab5410a965de272aa1529 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Tue, 1 Mar 2016 22:46:32 -0800 Subject: [PATCH 14/34] Fix register sign extension bug. --- src/DeviceFirmata.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index fe75880..9179fc4 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -41,6 +41,7 @@ void DeviceFirmata::update() { // epB -> encoded parameter block boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { + char *errString; byte dpBlock[1 + MAX_DPB_LENGTH]; // decoded parameter block @@ -74,25 +75,25 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { reportOpen(status); break; - case DD_CLOSE: - status = dt->close(handle); - reportClose(status, handle); - break; - case DD_READ: - reg = from8LEToHost(dpBlock); + reg = (int8_t)from8LEToHost(dpBlock); count = from16LEToHost(dpBlock + 1); status = dt->read(handle, reg, count, dpBlock + 3); reportRead(status, handle, dpBlock); break; case DD_WRITE: - reg = from8LEToHost(dpBlock); + reg = (int8_t)from8LEToHost(dpBlock); count = from16LEToHost(dpBlock + 1); status = dt->write(handle, reg, count, dpBlock + 3); reportWrite(status, handle, dpBlock); break; + case DD_CLOSE: + status = dt->close(handle); + reportClose(status, handle); + break; + default: sendDeviceResponse(action, ENOSYS, handle); break; @@ -111,10 +112,6 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { void DeviceFirmata::reportOpen(int status) { sendDeviceResponse(DD_OPEN, status, 0); } - -void DeviceFirmata::reportClose(int status, int handle) { - sendDeviceResponse(DD_CLOSE, status, handle); -} /** * 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. @@ -136,6 +133,10 @@ void DeviceFirmata::reportWrite(int status, int handle, const byte *dpB) { sendDeviceResponse(DD_WRITE, status, handle, dpCount, dpB); } +void DeviceFirmata::reportClose(int status, int handle) { + sendDeviceResponse(DD_CLOSE, status, handle); +} + //--------------------------------------------------------------------------- /** From dc8703aea99e3c175464b2cff378937cb5fc8968 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sat, 5 Mar 2016 22:52:25 -0800 Subject: [PATCH 15/34] Adapt to v0.7 message formatting. --- src/DeviceFirmata.cpp | 210 +++++++++++++++++++++++++----------------- src/DeviceFirmata.h | 9 +- 2 files changed, 131 insertions(+), 88 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 9179fc4..e4a7f1b 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -31,112 +31,134 @@ void DeviceFirmata::update() { //--------------------------------------------------------------------------- -// The first six bytes of argv for DEVICE_QUERY messages are: action, reserved, -// handle-low, handle-high, reserved, reserved. They are all constrained to -// 7-bit values.The bytes that follow, if any, are the parameter block. The -// parameter block is encoded with base-64 in the sysex message body during -// transmission to and from this Firmata server. - -// dpB -> decoded parameter block -// epB -> encoded parameter block +// 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. Any following bytes are used +// in the open() and write() methods. boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { - char *errString; - - byte dpBlock[1 + MAX_DPB_LENGTH]; // decoded parameter block - - if (command != DEVICE_QUERY) { - return false; - } + if (command != DEVICE_QUERY) return false; - int action = argv[0]; - int handle = (argv[3] << 7) | argv[2]; - - int dpCount = base64_dec_len((char *)(argv + 6), argc - 6); - if (dpCount > MAX_DPB_LENGTH) { - sendDeviceResponse(action, EMSGSIZE, handle); + if (argc < 12) { + reportError(EMSGSIZE); return true; } - - if (dpCount > 0) { - dpCount = base64_decode((char *)dpBlock, (char *)(argv + 6), argc - 6); + byte parameterBlock[9]; // 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]; + if (dataBlock == 0) { + reportError(ENOMEM); + return true; + } + base64_decode((char *)dataBlock, (char *)(argv + 12), argc - 12); } + int action = from8LEToHost(parameterBlock); + int handle = from16LEToHost(parameterBlock+1); + int reg = (int)from16LEToHost(parameterBlock+3); + int count = from16LEToHost(parameterBlock+5); + int flags = 0; int status = 0; - int reg = 0; - int count = 0; switch (action) { case DD_OPEN: - flags = handle; - status = dt->open((char *)dpBlock, flags); - reportOpen(status); + if (dataBlockLength == 0) { + reportError(EINVAL); + } else { + flags = handle; + status = dt->open((const char *)dataBlock, flags); + reportOpen(status); + } break; case DD_READ: - reg = (int8_t)from8LEToHost(dpBlock); - count = from16LEToHost(dpBlock + 1); - status = dt->read(handle, reg, count, dpBlock + 3); - reportRead(status, handle, dpBlock); + if (dataBlockLength != 0) { + reportError(EINVAL); + } else { + inputBuffer = new byte[count]; + if (inputBuffer == 0) { + reportError(ENOMEM); + } else { + status = dt->read(handle, reg, count, inputBuffer); + reportRead(status, handle, reg, count, inputBuffer); + } + } break; case DD_WRITE: - reg = (int8_t)from8LEToHost(dpBlock); - count = from16LEToHost(dpBlock + 1); - status = dt->write(handle, reg, count, dpBlock + 3); - reportWrite(status, handle, dpBlock); + if (dataBlockLength != count) { + reportError(EINVAL); + } else { + status = dt->write(handle, reg, count, dataBlock); + reportWrite(status, handle, reg, count); + } break; case DD_CLOSE: - status = dt->close(handle); - reportClose(status, handle); + if (dataBlockLength != 0) { + reportError(EINVAL); + } else { + status = dt->close(handle); + reportClose(status, handle); + } break; default: - sendDeviceResponse(action, ENOSYS, handle); + reportError(ENOTSUP); break; - } + + delete dataBlock; + delete inputBuffer; return true; } //--------------------------------------------------------------------------- - // in all cases, open and close don't return any bytes in the parameter buffer - // on error, both read and write return 3 bytes in the parameter buffer - // on read success, 3 bytes plus the data read are returned in the parameter buffer - // on write success, 3 bytes are returned in the parameter buffer - void DeviceFirmata::reportOpen(int status) { - sendDeviceResponse(DD_OPEN, status, 0); + sendDeviceResponse(DD_OPEN, status); } /** * 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 14-bit handle of the unit doing the reply - * @param dpB The byte buffer holding the reg, requested byte count and the data that was 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, const byte *dpB) { - int dpCount = (status >= 0) ? status + 3 : 3; - sendDeviceResponse(DD_READ, status, handle, dpCount, dpB); +void DeviceFirmata::reportRead(int status, int handle, int reg, int count, const byte *buf) { + sendDeviceResponse(DD_READ, status, handle, 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 14-bit handle of the unit doing the reply - * @param dpB The byte buffer holding the reg and requested byte count + * @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, const byte *dpB) { - int dpCount = 3; - sendDeviceResponse(DD_WRITE, status, handle, dpCount, dpB); +void DeviceFirmata::reportWrite(int status, int handle, int reg, int count) { + sendDeviceResponse(DD_WRITE, status, handle, reg, count); } void DeviceFirmata::reportClose(int status, int handle) { sendDeviceResponse(DD_CLOSE, status, handle); } +void DeviceFirmata::reportError(int status) { + sendDeviceResponse(DD_CLOSE, status); +} + //--------------------------------------------------------------------------- /** @@ -144,37 +166,57 @@ void DeviceFirmata::reportClose(int status, int handle) { * 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 to send or number of bytes actually read or written - * @param handle The 14-bit handle identifying the device and unit number the message is coming from - * @param dpCount The number of bytes to be encoded from the dpB and sent on - * @param dpBlock The decoded (raw) parameter block to send upwards after encoding + * @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 */ -void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int dpCount, const byte *dpB) { - byte epB[1 + 4 + ((MAX_DPB_LENGTH + 2) / 3) * 4]; - byte header[8] = {START_SYSEX, DEVICE_RESPONSE}; - int epCount = 0; - - header[2] = (byte) action; - header[3] = (byte) 0; - header[4] = (byte)getUnitNumber(handle); - header[5] = (byte)getDeviceNumber(handle); - header[6] = (byte)(status & 0x7F); - header[7] = (byte)((status >> 7) & 0x7F); - - for (int idx = 0; idx < 8; idx++) { - Firmata.write(header[idx]); - } - -// dpB -> decoded parameter block -// epB -> encoded parameter block - - if (dpCount > 0 && dpCount <= MAX_DPB_LENGTH && dpB != 0) { - epCount = base64_encode((char *)epB, (char *)dpB, dpCount); +void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int reg, int count, + const byte *dataBytes) { + + byte dP[9]; // decoded (raw) message prologue + byte eP[12]; // encoded message prologue + byte *eD; // encoded data bytes + + Firmata.write(START_SYSEX); + Firmata.write(DEVICE_RESPONSE); + + dP[0] = (byte) action; + 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]); } - for (int idx = 0; idx < epCount; idx++) { - Firmata.write(epB[idx]); + if (action == DD_READ && status > 0) { + int eDCount = base64_enc_len(status); + eD = new byte[eDCount]; + if (dataBytes == 0 || eD == 0) { + for (int idx = 0; idx < eDCount; idx++) { + Firmata.write('/'); // Error. This value will be decoded as 0x3F, ie, all 6 bits set. + } + } else { + base64_encode((char *)eD, (char *)dataBytes, status); + for (int idx = 0; idx < eDCount; 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 index ca22d6d..f40f182 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -8,7 +8,7 @@ #include #include -#define MAX_DPB_LENGTH 128 // decoded parameter block length (plain text) +#define MAX_READ_COUNT 150 // max number of bytes that can be read with one message // Firmata coding of DeviceDriver methods @@ -33,14 +33,15 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { // ClientReporter void reportOpen(int status); + void reportRead(int status, int handle, int reg, int count, const byte *dataBytes); + void reportWrite(int status, int handle, int reg, int count); void reportClose(int status, int handle); - void reportRead(int status, int handle, const byte *dpB); - void reportWrite(int status, int handle, const byte *dpB); + void reportError(int status); private: DeviceTable *dt; - void sendDeviceResponse(int action, int status, int handle, int dpCount = 0, const byte *dpBlock = 0); + void sendDeviceResponse(int action, int status, int handle = 0, int reg = 0, int count = 0, const byte *dataBytes = 0); }; #endif From f01f3a4dbae89d1f10de2d6d91f954f9d6fd3356 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sun, 6 Mar 2016 22:46:30 -0800 Subject: [PATCH 16/34] Adapt to v0.7 device message formats --- src/ConfigurableFirmata.h | 10 ++++++---- src/DeviceFirmata.cpp | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ConfigurableFirmata.h b/src/ConfigurableFirmata.h index cb1b616..f0d3127 100644 --- a/src/ConfigurableFirmata.h +++ b/src/ConfigurableFirmata.h @@ -24,8 +24,9 @@ #define FIRMATA_PROTOCOL_MAJOR_VERSION 2 // for non-compatible changes // #define FIRMATA_PROTOCOL_MINOR_VERSION 5 // for backwards compatible changes // #define FIRMATA_PROTOCOL_BUGFIX_VERSION 1 // for bugfix releases -#define FIRMATA_PROTOCOL_MINOR_VERSION 6 // DEVICE_QUERY/_RESPONSE is ... -#define FIRMATA_PROTOCOL_BUGFIX_VERSION 0 // ... proposed for Firmata 2.6 +// +#define FIRMATA_PROTOCOL_MINOR_VERSION 6 // Proposal for Firmata 2.6 with ... +#define FIRMATA_PROTOCOL_BUGFIX_VERSION 7 // ... DEVICE_QUERY/_RESPONSE v 0.7 /* * Version numbers for the Firmata library. @@ -36,8 +37,9 @@ #define FIRMATA_FIRMWARE_MAJOR_VERSION 2 // for non-compatible changes // #define FIRMATA_FIRMWARE_MINOR_VERSION 8 // for backwards compatible changes // #define FIRMATA_FIRMWARE_BUGFIX_VERSION 1 // for bugfix releases -#define FIRMATA_FIRMWARE_MINOR_VERSION 9 // DeviceFirmata firmware update is ... -#define FIRMATA_FIRMWARE_BUGFIX_VERSION 0 // ... proposed for ConfigurableFirmata 2.9 +// +#define FIRMATA_FIRMWARE_MINOR_VERSION 9 // DeviceFirmata feature is proposed ... +#define FIRMATA_FIRMWARE_BUGFIX_VERSION 0 // ... for ConfigurableFirmata 2.9 // DEPRECATED as of ConfigurableFirmata v2.8.1. // Use FIRMATA_PROTOCOL_[MAJOR|MINOR|BUGFIX]_VERSION instead. diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index e4a7f1b..ea6244d 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -61,9 +61,9 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { } int action = from8LEToHost(parameterBlock); - int handle = from16LEToHost(parameterBlock+1); + int handle = (int)from16LEToHost(parameterBlock+1); int reg = (int)from16LEToHost(parameterBlock+3); - int count = from16LEToHost(parameterBlock+5); + int count = (int)from16LEToHost(parameterBlock+5); int flags = 0; int status = 0; From defc77d6ff517f7cf260a37db9449b6ad0e540b3 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Tue, 8 Mar 2016 00:05:20 -0800 Subject: [PATCH 17/34] decode was overflowing buffer. --- src/DeviceFirmata.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index ea6244d..a21eb54 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -52,18 +52,18 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { int dataBlockLength = (argc == 12) ? 0 : base64_dec_len((char *)(argv + 12), argc - 12); if (dataBlockLength > 0) { - dataBlock = new byte[dataBlockLength]; + dataBlock = new byte[dataBlockLength+1]; if (dataBlock == 0) { reportError(ENOMEM); return true; } - base64_decode((char *)dataBlock, (char *)(argv + 12), argc - 12); + dataBlockLength = base64_decode((char *)dataBlock, (char *)(argv + 12), argc - 12); } int action = from8LEToHost(parameterBlock); - int handle = (int)from16LEToHost(parameterBlock+1); - int reg = (int)from16LEToHost(parameterBlock+3); - int count = (int)from16LEToHost(parameterBlock+5); + int handle = from16LEToHost(parameterBlock+1); + int reg = from16LEToHost(parameterBlock+3); + int count = from16LEToHost(parameterBlock+5); int flags = 0; int status = 0; From 69ab2acc00869754450b4f37ab0703b818e4883e Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Tue, 8 Mar 2016 08:58:20 -0800 Subject: [PATCH 18/34] Allow for extra null terminator in b64 encode/decode. --- src/DeviceFirmata.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index a21eb54..362ad83 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -37,6 +37,11 @@ void DeviceFirmata::update() { // the common query and request parameters. Any following bytes are used // in the open() and write() methods. +// 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; @@ -44,7 +49,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { reportError(EMSGSIZE); return true; } - byte parameterBlock[9]; // 9-byte beginning of every decoded DEVICE_QUERY message + 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() @@ -176,12 +181,15 @@ void DeviceFirmata::reportError(int status) { * @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 reg, int count, const byte *dataBytes) { - byte dP[9]; // decoded (raw) message prologue - byte eP[12]; // encoded message prologue + byte dP[9]; // source (raw) message prologue + byte eP[12+1]; // encoded message prologue byte *eD; // encoded data bytes Firmata.write(START_SYSEX); @@ -205,7 +213,7 @@ void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int r if (action == DD_READ && status > 0) { int eDCount = base64_enc_len(status); - eD = new byte[eDCount]; + eD = new byte[eDCount+1]; if (dataBytes == 0 || eD == 0) { for (int idx = 0; idx < eDCount; idx++) { Firmata.write('/'); // Error. This value will be decoded as 0x3F, ie, all 6 bits set. From cc0c86488033e14b6853dd283be6c75742fbe5da Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Tue, 15 Mar 2016 23:42:12 -0700 Subject: [PATCH 19/34] Fix IS_PIN_INTERRUPT macro. --- src/utility/Boards.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utility/Boards.h b/src/utility/Boards.h index 25c18e1..a1f7066 100644 --- a/src/utility/Boards.h +++ b/src/utility/Boards.h @@ -438,7 +438,7 @@ writePort(port, value, bitmask): Write an 8 bit port. #define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS) #define IS_PIN_I2C(p) ((p) == 2 || (p) == 3) #define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK) -#define IS_PIN_INTERRUPT(p) digitalPinToInterrupt(p) +#define IS_PIN_INTERRUPT(p) (digitalPinToInterrupt(p)!= -1) #define IS_PIN_SERIAL(p) ((p) == 0 || (p) == 1) #define PIN_TO_DIGITAL(p) (p) #define PIN_TO_ANALOG(p) (p) - 18 From 5353e23cf00dc6736f8c395187ceae3be98814ae Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Fri, 18 Mar 2016 02:07:36 -0700 Subject: [PATCH 20/34] Adapt to new Meta device driver. --- src/DeviceFirmata.cpp | 11 ++++++----- src/DeviceFirmata.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 362ad83..5cfe4d9 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -9,8 +9,8 @@ extern DeviceDriver *selectedDevices[]; //---------------------------------------------------------------------------- -DeviceFirmata::DeviceFirmata(const char *luRootName) { - dt = new DeviceTable(selectedDevices, luRootName); +DeviceFirmata::DeviceFirmata() { + dt = new DeviceTable(selectedDevices); } //--------------------------------------------------------------------------- @@ -26,7 +26,7 @@ boolean DeviceFirmata::handlePinMode(byte pin, int mode) { } void DeviceFirmata::update() { - int status = dt->dispatchTimers((ClientReporter*)this); + dt->dispatchTimers((ClientReporter*)this); } //--------------------------------------------------------------------------- @@ -34,8 +34,9 @@ void DeviceFirmata::update() { // 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. Any following bytes are used -// in the open() and write() methods. +// 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 diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index f40f182..710f8c6 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -19,7 +19,7 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { public: - DeviceFirmata(const char *luRootName = 0); + DeviceFirmata(); // FirmataFeature From ba946f02768a77e38ac4939893ba8a7a75a144d7 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Fri, 18 Mar 2016 23:45:03 -0700 Subject: [PATCH 21/34] Provide public access to the DeviceTable object pointer. --- src/DeviceFirmata.cpp | 21 ++++++++++++++------- src/DeviceFirmata.h | 1 - 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 5cfe4d9..010f79f 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -5,18 +5,25 @@ #include "DeviceFirmata.h" #include +// Create one globally visible pointer to the DeviceTable object for +// DeviceFirmata and others to use when making calls to the device +// drivers in the table. If LuniLib is being used without DeviceFirmata, +// then whatever class calls 'new DeviceTable(...)' should define the +// Device pointer instead. + +DeviceTable *Device; extern DeviceDriver *selectedDevices[]; //---------------------------------------------------------------------------- DeviceFirmata::DeviceFirmata() { - dt = new DeviceTable(selectedDevices); + Device = new DeviceTable(selectedDevices); } //--------------------------------------------------------------------------- void DeviceFirmata::reset() { - dt->reset(); + Device->reset(); } void DeviceFirmata::handleCapability(byte pin) {} @@ -26,7 +33,7 @@ boolean DeviceFirmata::handlePinMode(byte pin, int mode) { } void DeviceFirmata::update() { - dt->dispatchTimers((ClientReporter*)this); + Device->dispatchTimers((ClientReporter*)this); } //--------------------------------------------------------------------------- @@ -81,7 +88,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { reportError(EINVAL); } else { flags = handle; - status = dt->open((const char *)dataBlock, flags); + status = Device->open((const char *)dataBlock, flags); reportOpen(status); } break; @@ -94,7 +101,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (inputBuffer == 0) { reportError(ENOMEM); } else { - status = dt->read(handle, reg, count, inputBuffer); + status = Device->read(handle, reg, count, inputBuffer); reportRead(status, handle, reg, count, inputBuffer); } } @@ -104,7 +111,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (dataBlockLength != count) { reportError(EINVAL); } else { - status = dt->write(handle, reg, count, dataBlock); + status = Device->write(handle, reg, count, dataBlock); reportWrite(status, handle, reg, count); } break; @@ -113,7 +120,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (dataBlockLength != 0) { reportError(EINVAL); } else { - status = dt->close(handle); + status = Device->close(handle); reportClose(status, handle); } break; diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 710f8c6..be518d5 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -39,7 +39,6 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { void reportError(int status); private: - DeviceTable *dt; void sendDeviceResponse(int action, int status, int handle = 0, int reg = 0, int count = 0, const byte *dataBytes = 0); }; From 0e3c2388a143d6dc6ea2a154b80dc294ef4f37ae Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sat, 19 Mar 2016 23:02:51 -0700 Subject: [PATCH 22/34] DeviceTable now holds the client reporter pointer itself. --- src/DeviceFirmata.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 010f79f..283ac45 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -17,7 +17,7 @@ extern DeviceDriver *selectedDevices[]; //---------------------------------------------------------------------------- DeviceFirmata::DeviceFirmata() { - Device = new DeviceTable(selectedDevices); + Device = new DeviceTable(selectedDevices,this); } //--------------------------------------------------------------------------- @@ -33,7 +33,7 @@ boolean DeviceFirmata::handlePinMode(byte pin, int mode) { } void DeviceFirmata::update() { - Device->dispatchTimers((ClientReporter*)this); + Device->dispatchTimers(); } //--------------------------------------------------------------------------- From 48f15cd8399fc44e9025cdbedcd6474c26be975c Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sun, 3 Apr 2016 00:43:32 -0700 Subject: [PATCH 23/34] Add a little more info in an error message. --- src/FirmataExt.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/FirmataExt.cpp b/src/FirmataExt.cpp index 53bcb0c..d126f8b 100644 --- a/src/FirmataExt.cpp +++ b/src/FirmataExt.cpp @@ -30,8 +30,13 @@ void handleSetPinModeCallback(byte pin, int mode) void handleSysexCallback(byte command, byte argc, byte* argv) { + char buf[100]; if (!FirmataExtInstance->handleSysex(command, argc, argv)) { - Firmata.sendString("Unhandled sysex command"); + buf[0] = '\0'; + itoa(command,buf,16); + strlcat(buf," (hex), Unhandled SYSEX command.",100); + Firmata.sendString(buf); + // Firmata.sendString("Unhandled sysex command "+itoa()); } } From 881ce304384ac0446b154e260cc23bcdcccbdf1a Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Mon, 4 Apr 2016 11:59:05 -0700 Subject: [PATCH 24/34] Add unit name to Open response. --- src/DeviceFirmata.cpp | 42 ++++++++++++++++++++++++++---------------- src/DeviceFirmata.h | 2 +- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 283ac45..354005b 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -89,7 +89,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { } else { flags = handle; status = Device->open((const char *)dataBlock, flags); - reportOpen(status); + reportOpen(status,dataBlock); } break; @@ -137,8 +137,8 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { //--------------------------------------------------------------------------- -void DeviceFirmata::reportOpen(int status) { - sendDeviceResponse(DD_OPEN, status); +void DeviceFirmata::reportOpen(int status, const byte *buf) { + sendDeviceResponse(DD_OPEN, status, 0, 0, 0, buf); } /** * Translates a message from the DeviceDriver environment to a call to a Firmata-aware method. @@ -196,7 +196,7 @@ void DeviceFirmata::reportError(int status) { void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int reg, int count, const byte *dataBytes) { - byte dP[9]; // source (raw) message prologue + byte dP[9]; // decoded (raw) message prologue byte eP[12+1]; // encoded message prologue byte *eD; // encoded data bytes @@ -219,20 +219,30 @@ void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int r Firmata.write(eP[idx]); } - if (action == DD_READ && status > 0) { - int eDCount = base64_enc_len(status); - eD = new byte[eDCount+1]; - if (dataBytes == 0 || eD == 0) { - for (int idx = 0; idx < eDCount; idx++) { - Firmata.write('/'); // Error. This value will be decoded as 0x3F, ie, all 6 bits set. - } - } else { - base64_encode((char *)eD, (char *)dataBytes, status); - for (int idx = 0; idx < eDCount; idx++) { - Firmata.write(eD[idx]); // Success. These are the encoded data bytes. + int rawCount = 0; + int encCount = 0; + + if (dataBytes != 0) { + if (action == DD_OPEN) { + rawCount = strlen((const char *)dataBytes)+1; + } else if (action == DD_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 6 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; } - delete eD; } Firmata.write(END_SYSEX); } diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index be518d5..68eaa65 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -32,7 +32,7 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { // ClientReporter - void reportOpen(int status); + void reportOpen(int status, const byte *buf); void reportRead(int status, int handle, int reg, int count, const byte *dataBytes); void reportWrite(int status, int handle, int reg, int count); void reportClose(int status, int handle); From 732d35a21b462351db6f5127c6f57b0740713394 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sun, 17 Apr 2016 23:19:03 -0700 Subject: [PATCH 25/34] Add capability response to DeviceFirmata. --- src/DeviceFirmata.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 354005b..e88aa0f 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -3,6 +3,7 @@ */ #include "DeviceFirmata.h" +#include "utility/Boards.h" #include // Create one globally visible pointer to the DeviceTable object for @@ -26,7 +27,75 @@ void DeviceFirmata::reset() { Device->reset(); } -void DeviceFirmata::handleCapability(byte pin) {} +void DeviceFirmata::handleCapability(byte pin) { + +// Digital Input + + if (IS_PIN_DIGITAL(pin)) { + Firmata.write((byte)INPUT); + Firmata.write(1); + Firmata.write((byte)PIN_MODE_PULLUP); + Firmata.write(1); + } + +// Digital Output + + if (IS_PIN_DIGITAL(pin)) { + Firmata.write((byte)OUTPUT); + Firmata.write(1); + } + + // One Wire + + if (IS_PIN_DIGITAL(pin)) { + Firmata.write(PIN_MODE_ONEWIRE); + Firmata.write(1); + } + + // Analog Input + + if (IS_PIN_ANALOG(pin)) { + Firmata.write(PIN_MODE_ANALOG); + Firmata.write(10); // 10 = 10-bit resolution + } + + // Analog Output + + if (IS_PIN_PWM(pin)) { + Firmata.write(PIN_MODE_PWM); + Firmata.write(8); // 8 = 8-bit resolution + } + + // Servo Control + + #define MAX_SERVOS 10 + if (IS_PIN_SERVO(pin)) { + Firmata.write(PIN_MODE_SERVO); + Firmata.write(14); //14 bit resolution (Servo takes int as argument) + } + + // I2C + + if (IS_PIN_I2C(pin)) { + Firmata.write(PIN_MODE_I2C); + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA + } + + // Stepper Motor + + if (IS_PIN_DIGITAL(pin)) { + Firmata.write(PIN_MODE_STEPPER); + Firmata.write(21); //21 bits used for number of steps + } + + // Serial + + // if (IS_PIN_SERIAL(pin)) { + // Firmata.write(PIN_MODE_SERIAL); + // Firmata.write(getSerialPinType(pin)); + // } + +} boolean DeviceFirmata::handlePinMode(byte pin, int mode) { return false; From 6901e9373013874089a911f6060e018253d5dd9b Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Wed, 27 Apr 2016 20:43:26 -0700 Subject: [PATCH 26/34] Implement Device Action Flags along with Action Codes. --- src/DeviceFirmata.cpp | 44 +++++++++++++++++++++---------------------- src/DeviceFirmata.h | 10 +++++----- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index e88aa0f..a69b769 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -142,13 +142,14 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { dataBlockLength = base64_decode((char *)dataBlock, (char *)(argv + 12), argc - 12); } - int action = from8LEToHost(parameterBlock); + 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 flags = 0; - int status = 0; + int status; switch (action) { @@ -156,9 +157,8 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (dataBlockLength == 0) { reportError(EINVAL); } else { - flags = handle; - status = Device->open((const char *)dataBlock, flags); - reportOpen(status,dataBlock); + status = Device->open(openOpts, flags, (const char *)dataBlock); + reportOpen(status, openOpts, flags, dataBlock); } break; @@ -170,8 +170,8 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (inputBuffer == 0) { reportError(ENOMEM); } else { - status = Device->read(handle, reg, count, inputBuffer); - reportRead(status, handle, reg, count, inputBuffer); + status = Device->read(handle, flags, reg, count, inputBuffer); + reportRead(status, handle, flags, reg, count, inputBuffer); } } break; @@ -180,8 +180,8 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (dataBlockLength != count) { reportError(EINVAL); } else { - status = Device->write(handle, reg, count, dataBlock); - reportWrite(status, handle, reg, count); + status = Device->write(handle, flags, reg, count, dataBlock); + reportWrite(status, handle, flags, reg, count); } break; @@ -189,8 +189,8 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (dataBlockLength != 0) { reportError(EINVAL); } else { - status = Device->close(handle); - reportClose(status, handle); + status = Device->close(handle, flags); + reportClose(status, handle, flags); } break; @@ -206,8 +206,8 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { //--------------------------------------------------------------------------- -void DeviceFirmata::reportOpen(int status, const byte *buf) { - sendDeviceResponse(DD_OPEN, status, 0, 0, 0, buf); +void DeviceFirmata::reportOpen(int status, int openOpts, int flags, const byte *buf) { + sendDeviceResponse(DD_OPEN, status, openOpts, flags, 0, 0,buf); } /** * Translates a message from the DeviceDriver environment to a call to a Firmata-aware method. @@ -218,8 +218,8 @@ void DeviceFirmata::reportOpen(int status, const byte *buf) { * 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 reg, int count, const byte *buf) { - sendDeviceResponse(DD_READ, status, handle, reg, count, buf); +void DeviceFirmata::reportRead(int status, int handle, int flags, int reg, int count, const byte *buf) { + sendDeviceResponse(DD_READ, status, handle, flags, reg, count, buf); } /** * Translates a message from the DeviceDriver environment to a call to a Firmata-aware method. @@ -229,12 +229,12 @@ void DeviceFirmata::reportRead(int status, int handle, int reg, int count, const * @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 reg, int count) { - sendDeviceResponse(DD_WRITE, status, handle, reg, count); +void DeviceFirmata::reportWrite(int status, int handle, int flags, int reg, int count) { + sendDeviceResponse(DD_WRITE, status, handle, flags, reg, count); } -void DeviceFirmata::reportClose(int status, int handle) { - sendDeviceResponse(DD_CLOSE, status, handle); +void DeviceFirmata::reportClose(int status, int handle, int flags) { + sendDeviceResponse(DD_CLOSE, status, handle, flags); } void DeviceFirmata::reportError(int status) { @@ -262,7 +262,7 @@ void DeviceFirmata::reportError(int status) { * 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 reg, int count, +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 @@ -272,7 +272,7 @@ void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int r Firmata.write(START_SYSEX); Firmata.write(DEVICE_RESPONSE); - dP[0] = (byte) action; + dP[0] = (byte) ((flags & 0xF) << 4) | (action & 0xF); dP[1] = (byte) lowByte(handle); dP[2] = (byte) highByte(handle); dP[3] = (byte) lowByte(reg); diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 68eaa65..36f7f44 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -32,15 +32,15 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { // ClientReporter - void reportOpen(int status, const byte *buf); - void reportRead(int status, int handle, int reg, int count, const byte *dataBytes); - void reportWrite(int status, int handle, int reg, int count); - void reportClose(int status, int handle); + 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 reportError(int status); private: - void sendDeviceResponse(int action, int status, int handle = 0, int reg = 0, int count = 0, const byte *dataBytes = 0); + void sendDeviceResponse(int action, int status, int handle = 0, int flags = 0, int reg = 0, int count = 0, const byte *dataBytes = 0); }; #endif From 983ce5e6ab9b850b4e84b1bc8054884fb937c524 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Fri, 29 Apr 2016 23:58:29 -0700 Subject: [PATCH 27/34] Move action codes to DeviceDriver.h --- src/DeviceFirmata.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 36f7f44..6407837 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -8,15 +8,6 @@ #include #include -#define MAX_READ_COUNT 150 // max number of bytes that can be read with one message - -// Firmata coding of DeviceDriver methods - -#define DD_OPEN 0x00 -#define DD_READ 0x01 -#define DD_WRITE 0x02 -#define DD_CLOSE 0x03 - class DeviceFirmata: public FirmataFeature, ClientReporter { public: DeviceFirmata(); From 069c53ba963d71fe18eae8f7d978e7a92099d560 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Fri, 29 Apr 2016 23:59:19 -0700 Subject: [PATCH 28/34] Make device table and associated codes more available to device drivers. --- src/DeviceFirmata.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index a69b769..6aea2f4 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -12,19 +12,19 @@ // then whatever class calls 'new DeviceTable(...)' should define the // Device pointer instead. -DeviceTable *Device; +DeviceTable *globalDeviceTable; extern DeviceDriver *selectedDevices[]; //---------------------------------------------------------------------------- DeviceFirmata::DeviceFirmata() { - Device = new DeviceTable(selectedDevices,this); + globalDeviceTable = new DeviceTable(selectedDevices,this); } //--------------------------------------------------------------------------- void DeviceFirmata::reset() { - Device->reset(); + globalDeviceTable->reset(); } void DeviceFirmata::handleCapability(byte pin) { @@ -102,7 +102,7 @@ boolean DeviceFirmata::handlePinMode(byte pin, int mode) { } void DeviceFirmata::update() { - Device->dispatchTimers(); + globalDeviceTable->dispatchTimers(); } //--------------------------------------------------------------------------- @@ -153,16 +153,16 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { switch (action) { - case DD_OPEN: + case (int)DAC::OPEN: if (dataBlockLength == 0) { reportError(EINVAL); } else { - status = Device->open(openOpts, flags, (const char *)dataBlock); + status = globalDeviceTable->open(openOpts, flags, (const char *)dataBlock); reportOpen(status, openOpts, flags, dataBlock); } break; - case DD_READ: + case (int)DAC::READ: if (dataBlockLength != 0) { reportError(EINVAL); } else { @@ -170,26 +170,26 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (inputBuffer == 0) { reportError(ENOMEM); } else { - status = Device->read(handle, flags, reg, count, inputBuffer); + status = globalDeviceTable->read(handle, flags, reg, count, inputBuffer); reportRead(status, handle, flags, reg, count, inputBuffer); } } break; - case DD_WRITE: + case (int)DAC::WRITE: if (dataBlockLength != count) { reportError(EINVAL); } else { - status = Device->write(handle, flags, reg, count, dataBlock); + status = globalDeviceTable->write(handle, flags, reg, count, dataBlock); reportWrite(status, handle, flags, reg, count); } break; - case DD_CLOSE: + case (int)DAC::CLOSE: if (dataBlockLength != 0) { reportError(EINVAL); } else { - status = Device->close(handle, flags); + status = globalDeviceTable->close(handle, flags); reportClose(status, handle, flags); } break; @@ -207,7 +207,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { //--------------------------------------------------------------------------- void DeviceFirmata::reportOpen(int status, int openOpts, int flags, const byte *buf) { - sendDeviceResponse(DD_OPEN, status, openOpts, flags, 0, 0,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. @@ -219,7 +219,7 @@ void DeviceFirmata::reportOpen(int status, int openOpts, int flags, const byte * * @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(DD_READ, status, handle, flags, reg, count, 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. @@ -230,15 +230,15 @@ void DeviceFirmata::reportRead(int status, int handle, int flags, int reg, int c * 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(DD_WRITE, status, handle, flags, reg, count); + sendDeviceResponse((int)DAC::WRITE, status, handle, flags, reg, count); } void DeviceFirmata::reportClose(int status, int handle, int flags) { - sendDeviceResponse(DD_CLOSE, status, handle, flags); + sendDeviceResponse((int)DAC::CLOSE, status, handle, flags); } void DeviceFirmata::reportError(int status) { - sendDeviceResponse(DD_CLOSE, status); + sendDeviceResponse((int)DAC::CLOSE, status); } //--------------------------------------------------------------------------- @@ -292,9 +292,9 @@ void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int f int encCount = 0; if (dataBytes != 0) { - if (action == DD_OPEN) { + if (action == (int)DAC::OPEN) { rawCount = strlen((const char *)dataBytes)+1; - } else if (action == DD_READ && status > 0) { + } else if (action == (int)DAC::READ && status > 0) { rawCount = status; } encCount = base64_enc_len(rawCount); @@ -302,7 +302,7 @@ void DeviceFirmata::sendDeviceResponse(int action, int status, int handle, int f 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 6 bits set. + 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); From 9c68638dd4e653d4269bdaf9ce6b731678542cc4 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Mon, 9 May 2016 23:16:09 -0700 Subject: [PATCH 29/34] Add the new reporter method reportString(). --- src/DeviceFirmata.cpp | 5 +++++ src/DeviceFirmata.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 6aea2f4..5d219ad 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -241,6 +241,11 @@ void DeviceFirmata::reportError(int status) { sendDeviceResponse((int)DAC::CLOSE, status); } +void DeviceFirmata::reportString(const byte *dataBytes) { + Firmata.sendString((char *)dataBytes); +} + + //--------------------------------------------------------------------------- /** diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 6407837..4c7ac0f 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -27,6 +27,7 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { 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); private: From 3be3d1c8ad37469fb0d088c0f0faed760ad50a10 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Wed, 18 May 2016 00:05:59 -0700 Subject: [PATCH 30/34] Begin to replace capability request response with pin locking. --- src/DeviceFirmata.cpp | 104 ++++++++++-------------------------------- src/DeviceFirmata.h | 2 + 2 files changed, 25 insertions(+), 81 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 5d219ad..645ebd0 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -6,103 +6,37 @@ #include "utility/Boards.h" #include -// Create one globally visible pointer to the DeviceTable object for -// DeviceFirmata and others to use when making calls to the device -// drivers in the table. If LuniLib is being used without DeviceFirmata, -// then whatever class calls 'new DeviceTable(...)' should define the -// Device pointer instead. - -DeviceTable *globalDeviceTable; +DeviceTable *gDeviceTable; extern DeviceDriver *selectedDevices[]; //---------------------------------------------------------------------------- DeviceFirmata::DeviceFirmata() { - globalDeviceTable = new DeviceTable(selectedDevices,this); + gDeviceTable = new DeviceTable(selectedDevices,this); } //--------------------------------------------------------------------------- void DeviceFirmata::reset() { - globalDeviceTable->reset(); + gDeviceTable->reset(); } -void DeviceFirmata::handleCapability(byte pin) { - -// Digital Input - - if (IS_PIN_DIGITAL(pin)) { - Firmata.write((byte)INPUT); - Firmata.write(1); - Firmata.write((byte)PIN_MODE_PULLUP); - Firmata.write(1); - } - -// Digital Output - - if (IS_PIN_DIGITAL(pin)) { - Firmata.write((byte)OUTPUT); - Firmata.write(1); - } - - // One Wire +// 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. - if (IS_PIN_DIGITAL(pin)) { - Firmata.write(PIN_MODE_ONEWIRE); - Firmata.write(1); - } - - // Analog Input - - if (IS_PIN_ANALOG(pin)) { - Firmata.write(PIN_MODE_ANALOG); - Firmata.write(10); // 10 = 10-bit resolution - } +void DeviceFirmata::handleCapability(byte pin) {} - // Analog Output - - if (IS_PIN_PWM(pin)) { - Firmata.write(PIN_MODE_PWM); - Firmata.write(8); // 8 = 8-bit resolution - } - - // Servo Control - - #define MAX_SERVOS 10 - if (IS_PIN_SERVO(pin)) { - Firmata.write(PIN_MODE_SERVO); - Firmata.write(14); //14 bit resolution (Servo takes int as argument) - } - - // I2C - - if (IS_PIN_I2C(pin)) { - Firmata.write(PIN_MODE_I2C); - Firmata.write(1); // TODO: could assign a number to map to SCL or SDA - } - - // Stepper Motor - - if (IS_PIN_DIGITAL(pin)) { - Firmata.write(PIN_MODE_STEPPER); - Firmata.write(21); //21 bits used for number of steps - } - - // Serial - - // if (IS_PIN_SERIAL(pin)) { - // Firmata.write(PIN_MODE_SERIAL); - // Firmata.write(getSerialPinType(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() { - globalDeviceTable->dispatchTimers(); + gDeviceTable->dispatchTimers(); } //--------------------------------------------------------------------------- @@ -157,7 +91,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (dataBlockLength == 0) { reportError(EINVAL); } else { - status = globalDeviceTable->open(openOpts, flags, (const char *)dataBlock); + status = gDeviceTable->open(openOpts, flags, (const char *)dataBlock); reportOpen(status, openOpts, flags, dataBlock); } break; @@ -170,7 +104,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (inputBuffer == 0) { reportError(ENOMEM); } else { - status = globalDeviceTable->read(handle, flags, reg, count, inputBuffer); + status = gDeviceTable->read(handle, flags, reg, count, inputBuffer); reportRead(status, handle, flags, reg, count, inputBuffer); } } @@ -180,7 +114,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (dataBlockLength != count) { reportError(EINVAL); } else { - status = globalDeviceTable->write(handle, flags, reg, count, dataBlock); + status = gDeviceTable->write(handle, flags, reg, count, dataBlock); reportWrite(status, handle, flags, reg, count); } break; @@ -189,7 +123,7 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { if (dataBlockLength != 0) { reportError(EINVAL); } else { - status = globalDeviceTable->close(handle, flags); + status = gDeviceTable->close(handle, flags); reportClose(status, handle, flags); } break; @@ -206,6 +140,14 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { //--------------------------------------------------------------------------- +void reportPinClaim(int pin) { + Firmata.setPinMode((byte)pin, PIN_MODE_IGNORE); +} + +void reportPinRelease(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); } diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index 4c7ac0f..f52cd3b 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -29,6 +29,8 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { void reportClose(int status, int handle, int flags ); void reportString(const byte *dataBytes); void reportError(int status); + void reportPinClaim(int pin); + void reportPinRelease(int pin); private: From da48938cd128de64bc5e79081058a498b4df2c4e Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Wed, 18 May 2016 09:14:05 -0700 Subject: [PATCH 31/34] Track change to ClientReporter interface. --- src/DeviceFirmata.cpp | 4 ++-- src/DeviceFirmata.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DeviceFirmata.cpp b/src/DeviceFirmata.cpp index 645ebd0..8d89bd7 100644 --- a/src/DeviceFirmata.cpp +++ b/src/DeviceFirmata.cpp @@ -140,11 +140,11 @@ boolean DeviceFirmata::handleSysex(byte command, byte argc, byte *argv) { //--------------------------------------------------------------------------- -void reportPinClaim(int pin) { +void DeviceFirmata::reportClaimPin(int pin) { Firmata.setPinMode((byte)pin, PIN_MODE_IGNORE); } -void reportPinRelease(int pin) { +void DeviceFirmata::reportReleasePin(int pin) { Firmata.setPinMode((byte)pin, INPUT); } diff --git a/src/DeviceFirmata.h b/src/DeviceFirmata.h index f52cd3b..c44171b 100644 --- a/src/DeviceFirmata.h +++ b/src/DeviceFirmata.h @@ -29,8 +29,8 @@ class DeviceFirmata: public FirmataFeature, ClientReporter { void reportClose(int status, int handle, int flags ); void reportString(const byte *dataBytes); void reportError(int status); - void reportPinClaim(int pin); - void reportPinRelease(int pin); + void reportClaimPin(int pin); + void reportReleasePin(int pin); private: From f90ed74d01b305ab0643920ce9d61e203d942347 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Wed, 18 May 2016 16:34:07 -0700 Subject: [PATCH 32/34] Put main program changes in with the Device Firmata update. --- .../ConfigurableFirmataDeviceDriver.ino | 441 ++++++++++++++++++ .../SelectedDeviceDrivers.h | 20 + 2 files changed, 461 insertions(+) create mode 100644 examples/ConfigurableFirmataDeviceDriver/ConfigurableFirmataDeviceDriver.ino create mode 100644 examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h 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..950827b --- /dev/null +++ b/examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h @@ -0,0 +1,20 @@ +#include + +// Device Drivers + +//#include +#include +#include +//#include +//#include +//#include + +DeviceDriver *selectedDevices[] = { +// new DDHello("Hello",1), + new DDMCP9808("MCP9808",1,0x18), + new DDMeta("Meta",1), +// new DDSensor("Chan",16), +// new DDServo("Servo",2), +// new DDStepper("Stepper",6), + 0}; + From 8b4e215afcdf2f9c883102b89fe195b8223c7139 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Thu, 19 May 2016 20:35:26 -0700 Subject: [PATCH 33/34] Change the example set of device drivers. --- .../SelectedDeviceDrivers.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h b/examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h index 950827b..43b5ba0 100644 --- a/examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h +++ b/examples/ConfigurableFirmataDeviceDriver/SelectedDeviceDrivers.h @@ -2,19 +2,21 @@ // Device Drivers -//#include -#include #include -//#include +#include +//#include //#include + +//#include //#include DeviceDriver *selectedDevices[] = { -// new DDHello("Hello",1), - new DDMCP9808("MCP9808",1,0x18), new DDMeta("Meta",1), -// new DDSensor("Chan",16), + new DDHello("Hello",1), +// new DDMCP9808("MCP9808",1,0x18), // new DDServo("Servo",2), + +// new DDSensor("Chan",16), // new DDStepper("Stepper",6), 0}; From aa8179b6edaa798716800151664a9d23ae3a5889 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Mon, 6 Jun 2016 19:37:24 -0700 Subject: [PATCH 34/34] Trim to only include changes that are absolutely needed. --- library.properties | 4 ++-- src/ConfigurableFirmata.h | 13 ++++--------- src/FirmataExt.cpp | 7 +------ src/utility/Boards.h | 10 ++++------ 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/library.properties b/library.properties index 401348f..bc3bf68 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=ConfigurableFirmata -version=2.9.0 -author=Firmata Developers (2.9 Proposal by Doug Johnson, based on 2.8.2) +version=2.8.2 +author=Firmata Developers maintainer=https://github.com/firmata/ConfigurableFirmata sentence=This library implements the Firmata protocol as a set of plugins that can be used to create applications to remotely interface with an Arduino board. paragraph=ConfigurableFirmata is an implementation of the Firmata protocol that breaks features such as Digital Input, Digital Output, Analog Input, Analog Output, I2C, etc into individual classes making it easier to mix and match standard features with custom features. diff --git a/src/ConfigurableFirmata.h b/src/ConfigurableFirmata.h index df2a1b0..a19bd7c 100644 --- a/src/ConfigurableFirmata.h +++ b/src/ConfigurableFirmata.h @@ -22,11 +22,8 @@ * Query using the REPORT_VERSION message. */ #define FIRMATA_PROTOCOL_MAJOR_VERSION 2 // for non-compatible changes -// #define FIRMATA_PROTOCOL_MINOR_VERSION 5 // for backwards compatible changes -// #define FIRMATA_PROTOCOL_BUGFIX_VERSION 1 // for bugfix releases -// -#define FIRMATA_PROTOCOL_MINOR_VERSION 6 // Proposal for Firmata 2.6 with ... -#define FIRMATA_PROTOCOL_BUGFIX_VERSION 7 // ... DEVICE_QUERY/_RESPONSE v 0.7 +#define FIRMATA_PROTOCOL_MINOR_VERSION 5 // for backwards compatible changes +#define FIRMATA_PROTOCOL_BUGFIX_VERSION 1 // for bugfix releases /* * Version numbers for the Firmata library. @@ -35,10 +32,8 @@ * Query using the REPORT_FIRMWARE message. */ #define FIRMATA_FIRMWARE_MAJOR_VERSION 2 // for non-compatible changes -// #define FIRMATA_FIRMWARE_MINOR_VERSION 8 // for backwards compatible changes -// #define FIRMATA_FIRMWARE_BUGFIX_VERSION 2 // for bugfix releases -#define FIRMATA_FIRMWARE_MINOR_VERSION 9 // DeviceFirmata feature is proposed ... -#define FIRMATA_FIRMWARE_BUGFIX_VERSION 0 // ... for ConfigurableFirmata 2.9 +#define FIRMATA_FIRMWARE_MINOR_VERSION 8 // for backwards compatible changes +#define FIRMATA_FIRMWARE_BUGFIX_VERSION 2 // for bugfix releases // DEPRECATED as of ConfigurableFirmata v2.8.1. // Use FIRMATA_PROTOCOL_[MAJOR|MINOR|BUGFIX]_VERSION instead. diff --git a/src/FirmataExt.cpp b/src/FirmataExt.cpp index d126f8b..53bcb0c 100644 --- a/src/FirmataExt.cpp +++ b/src/FirmataExt.cpp @@ -30,13 +30,8 @@ void handleSetPinModeCallback(byte pin, int mode) void handleSysexCallback(byte command, byte argc, byte* argv) { - char buf[100]; if (!FirmataExtInstance->handleSysex(command, argc, argv)) { - buf[0] = '\0'; - itoa(command,buf,16); - strlcat(buf," (hex), Unhandled SYSEX command.",100); - Firmata.sendString(buf); - // Firmata.sendString("Unhandled sysex command "+itoa()); + Firmata.sendString("Unhandled sysex command"); } } diff --git a/src/utility/Boards.h b/src/utility/Boards.h index a1f7066..276c523 100644 --- a/src/utility/Boards.h +++ b/src/utility/Boards.h @@ -29,11 +29,9 @@ // this file). If Servo.h wasn't included, this allows the code to still // compile, but without support for any Servos. Hopefully that's what the // user intended by not including Servo.h -// --> ConfigurableFirmata.h is included before Servo.h and so the following -// --> definition causes a compiler warning. -// #ifndef MAX_SERVOS -// #define MAX_SERVOS 0 -// #endif +#ifndef MAX_SERVOS +#define MAX_SERVOS 0 +#endif /* Firmata Hardware Abstraction Layer @@ -438,7 +436,7 @@ writePort(port, value, bitmask): Write an 8 bit port. #define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS) #define IS_PIN_I2C(p) ((p) == 2 || (p) == 3) #define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK) -#define IS_PIN_INTERRUPT(p) (digitalPinToInterrupt(p)!= -1) +#define IS_PIN_INTERRUPT(p) digitalPinToInterrupt(p) #define IS_PIN_SERIAL(p) ((p) == 0 || (p) == 1) #define PIN_TO_DIGITAL(p) (p) #define PIN_TO_ANALOG(p) (p) - 18