diff --git a/Boards.h b/Boards.h index 8ee1c28d..1d92cddd 100644 --- a/Boards.h +++ b/Boards.h @@ -141,6 +141,13 @@ writePort(port, value, bitmask): Write an 8 bit port. #define digitalPinHasPWM(p) IS_PIN_DIGITAL(p) #endif +#undef IS_PIN_INTERRUPT +#if defined(digitalPinToInterrupt) && defined(NOT_AN_INTERRUPT) +#define IS_PIN_INTERRUPT(p) (digitalPinToInterrupt(p) > NOT_AN_INTERRUPT) +#else +#define IS_PIN_INTERRUPT(p) (0) +#endif + // Arduino Duemilanove, Diecimila, and NG #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) #if defined(NUM_ANALOG_INPUTS) && NUM_ANALOG_INPUTS == 6 @@ -219,6 +226,20 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) #define PIN_TO_SERVO(p) ((p) - 2) +#elif defined(AVR_NANO_EVERY) || defined(ARDUINO_NANO_EVERY) || defined(ARDUINO_AVR_NANO_EVERY) +#define TOTAL_ANALOG_PINS 8 +#define TOTAL_PINS 24 // 14 digital + 8 analog + 2 i2c +#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) <= 21) // TBD if pins 0 and 1 are usable +#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) < 14 + TOTAL_ANALOG_PINS) +#define IS_PIN_PWM(p) digitalPinHasPWM(p) +#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS) // deprecated since v2.4 +#define IS_PIN_I2C(p) ((p) == PIN_WIRE_SDA || (p) == PIN_WIRE_SCL) // SDA = 22, SCL = 23 +#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK) +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_ANALOG(p) ((p) - 14) +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) +#define PIN_TO_SERVO(p) (p) // deprecated since v2.4 + // Arduino UNO WiFi rev2 (ATMega 4809) #elif defined(__AVR_ATmega4809__) #define TOTAL_ANALOG_PINS 6 @@ -261,6 +282,40 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_SERVO(p) ((p) - 2) +// Arduino Nano 33 IoT +#elif defined(ARDUINO_SAMD_NANO_33_IOT) +#define TOTAL_ANALOG_PINS 8 +#define TOTAL_PINS 22 // 14 Digital + 8 Analog +#define IS_PIN_DIGITAL(p) ((p) < TOTAL_PINS) +#define IS_PIN_ANALOG(p) ((p) > 13 && (p) < 14 + TOTAL_ANALOG_PINS) +#define IS_PIN_PWM(p) digitalPinHasPWM(p) +#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS) // deprecated since v2.4 +#define IS_PIN_I2C(p) ((p) == PIN_WIRE_SDA || (p) == PIN_WIRE_SCL) +#define IS_PIN_SPI(p) ((p) == PIN_SPI_SS || (p) == PIN_SPI_MOSI || (p) == PIN_SPI_MISO || (p) == PIN_SPI_SCK) +#define IS_PIN_SERIAL(p) ((p) == PIN_SERIAL1_RX || (p) == PIN_SERIAL1_TX) //defined in variant.h RX = 0 TX = 1 +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_ANALOG(p) ((p) - 14) +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) +#define PIN_TO_SERVO(p) (p) // deprecated since v2.4 + + +// Arduino Nano 33 BLE +#elif defined(ARDUINO_ARDUINO_NANO33BLE) +#define TOTAL_ANALOG_PINS 8 +#define TOTAL_PINS 22 // 14 Digital + 8 Analog +#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS) +#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) < 14 + TOTAL_ANALOG_PINS) +#define IS_PIN_PWM(p) digitalPinHasPWM(p) +#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS) // deprecated since v2.4 +#define IS_PIN_I2C(p) ((p) == PIN_WIRE_SDA || (p) == PIN_WIRE_SCL) // SDA = 18, SCL = 19 +#define IS_PIN_SPI(p) ((p) == PIN_SPI_SS || (p) == PIN_SPI_MOSI || (p) == PIN_SPI_MISO || (p) == PIN_SPI_SCK) +#define IS_PIN_SERIAL(p) ((p) == PIN_SERIAL_RX || (p) == PIN_SERIAL_TX) //defined in pins_arduino.h RX = 1 TX = 0 +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_ANALOG(p) ((p) - 14) +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) +#define PIN_TO_SERVO(p) (p) // deprecated since v2.4 + + // Arduino/Genuino MKR1000 or MKR1010 #elif defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010) #define TOTAL_ANALOG_PINS 7 @@ -399,6 +454,25 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) #define PIN_TO_SERVO(p) (p) // deprecated since v2.4 +// Arduino UNO R4 Minima and Wifi +// The pinout is the same as for the classical UNO R3 +#elif defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI) +#if defined(NUM_ANALOG_INPUTS) && NUM_ANALOG_INPUTS == 6 +#define TOTAL_ANALOG_PINS 6 +#define TOTAL_PINS 20 // 14 digital + 6 analog +#else +#define TOTAL_ANALOG_PINS 8 +#define TOTAL_PINS 22 // 14 digital + 8 analog +#endif +#define VERSION_BLINK_PIN 13 +#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) <= 19) +#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) - 2 < MAX_SERVOS) +#define IS_PIN_I2C(p) ((p) == 18 || (p) == 19) +#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK) +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_ANALOG(p) ((p) - 14) +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) +#define PIN_TO_SERVO(p) ((p) - 2) // Teensy 1.0 #elif defined(__AVR_AT90USB162__) @@ -536,6 +610,99 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) #define PIN_TO_SERVO(p) (p) +// Teensy 4.0 and Teensy 4.1 +#elif defined(__IMXRT1062__) +#if !defined(ARDUINO_TEENSY40) && !defined(ARDUINO_TEENSY41) + #warning Assuming ARDUINO_TEENSY40. Please #define ARDUINO_TEENSY40 or ARDUINO_TEENSY41. + #define ARDUINO_TEENSY40 +#endif +#if defined(ARDUINO_TEENSY40) + #define TOTAL_PINS 40 + #define TOTAL_ANALOG_PINS 14 + #define IS_PIN_ANALOG(p) ((p) >= 14 && (p) <= 27) + #define PIN_TO_ANALOG(p) ((p) - 14) +#elif defined(ARDUINO_TEENSY41) + #define TOTAL_PINS 55 + #define TOTAL_ANALOG_PINS 18 + #define IS_PIN_ANALOG(p) (((p) >= 14 && (p) <= 27) || ((p) >= 38 && (p) <= 41)) + #define PIN_TO_ANALOG(p) (((p) <= 27) ? ((p) - 14 ) : ((p) - 24)) +#endif +#define VERSION_BLINK_PIN 13 +#define PIN_SERIAL1_RX 0 +#define PIN_SERIAL1_TX 1 +#define PIN_SERIAL2_RX 7 +#define PIN_SERIAL2_TX 8 +#define PIN_SERIAL3_RX 15 +#define PIN_SERIAL3_TX 14 +#define PIN_SERIAL4_RX 16 +#define PIN_SERIAL4_TX 17 +#define PIN_SERIAL5_RX 21 +#define PIN_SERIAL5_TX 20 +#define PIN_SERIAL6_RX 25 +#define PIN_SERIAL6_TX 24 +#define PIN_SERIAL7_RX 28 +#define PIN_SERIAL7_TX 29 +#if defined(ARDUINO_TEENSY40) + #define IS_PIN_SERIAL(p) (((p) == PIN_SERIAL1_RX) || \ + ((p) == PIN_SERIAL1_TX) || \ + ((p) == PIN_SERIAL2_RX) || \ + ((p) == PIN_SERIAL2_TX) || \ + ((p) == PIN_SERIAL3_RX) || \ + ((p) == PIN_SERIAL3_TX) || \ + ((p) == PIN_SERIAL4_RX) || \ + ((p) == PIN_SERIAL4_TX) || \ + ((p) == PIN_SERIAL5_RX) || \ + ((p) == PIN_SERIAL5_TX) || \ + ((p) == PIN_SERIAL6_RX) || \ + ((p) == PIN_SERIAL6_TX) || \ + ((p) == PIN_SERIAL7_RX) || \ + ((p) == PIN_SERIAL7_TX)) + #define IS_PIN_PWM(p) (((p) >= 0 && (p) <= 16) || \ + ((p) == 18) || \ + ((p) == 19) || \ + ((p) >= 22 && (p) <= 25) || \ + ((p) == 28) || \ + ((p) == 29) || \ + ((p) >= 33 && (p) <= 39)) +#elif defined(ARDUINO_TEENSY41) + #define PIN_SERIAL8_RX 34 + #define PIN_SERIAL8_TX 35 + #define IS_PIN_SERIAL(p) (((p) == PIN_SERIAL1_RX) || \ + ((p) == PIN_SERIAL1_TX) || \ + ((p) == PIN_SERIAL2_RX) || \ + ((p) == PIN_SERIAL2_TX) || \ + ((p) == PIN_SERIAL3_RX) || \ + ((p) == PIN_SERIAL3_TX) || \ + ((p) == PIN_SERIAL4_RX) || \ + ((p) == PIN_SERIAL4_TX) || \ + ((p) == PIN_SERIAL5_RX) || \ + ((p) == PIN_SERIAL5_TX) || \ + ((p) == PIN_SERIAL6_RX) || \ + ((p) == PIN_SERIAL6_TX) || \ + ((p) == PIN_SERIAL7_RX) || \ + ((p) == PIN_SERIAL7_TX) ||\ + ((p) == PIN_SERIAL8_RX) || \ + ((p) == PIN_SERIAL8_TX)) + #define IS_PIN_PWM(p) (((p) >= 0 && (p) <= 15) || \ + ((p) == 18) || \ + ((p) == 19) || \ + ((p) >= 22 && (p) <= 25) || \ + ((p) == 28) || \ + ((p) == 29) || \ + ((p) == 33) || \ + ((p) == 36) || \ + ((p) == 37) || \ + ((p) >= 42 && (p) <= 47) || \ + ((p) == 51) || \ + ((p) == 54)) +#endif +#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS) +#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS) +#define IS_PIN_I2C(p) ((p) == 18 || (p) == 19) +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_PWM(p) (p) +#define PIN_TO_SERVO(p) (p) + // Leonardo #elif defined(__AVR_ATmega32U4__) @@ -611,7 +778,7 @@ writePort(port, value, bitmask): Write an 8 bit port. // Sanguino/Melzi, e.g. Creality Ender-3 #elif defined(__AVR_ATmega1284P__) #define TOTAL_ANALOG_PINS 8 -#define TOTAL_PINS 32 +#define TOTAL_PINS 32 #define VERSION_BLINK_PIN 13 #define PIN_SERIAL1_RX 8 //PD0 #define PIN_SERIAL1_TX 9 //PD1 @@ -935,6 +1102,31 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) #define PIN_TO_SERVO(p) (p) // deprecated since v2.4 +//Arduino Uno Wifi Rev2 +#elif defined(__AVR_ATmega4809__) +#define TOTAL_ANALOG_PINS NUM_ANALOG_INPUTS //6 +#define TOTAL_PINS 41 // 14 digital + 6 analog + 6 reserved + 10 internal used + 2 I2C + 3 SPI +#define TOTAL_PORTS 3 +#define VERSION_BLINK_PIN LED_BUILTIN //25 +#define PIN_SERIAL1_RX 0 +#define PIN_SERIAL1_TX 1 +#define PIN_SERIAL2_RX 23 +#define PIN_SERIAL2_TX 24 +#define PIN_SERIAL0_RX 26 +#define PIN_SERIAL0_TX 27 +#define IS_PIN_DIGITAL(p) (((p) >= 0 && (p) < 20) || (p) == 25) +#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) < 19) +#define IS_PIN_PWM(p) digitalPinHasPWM(p) +#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS) +#define IS_PIN_I2C(p) ((p) == 20 || (p) == 21) +#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK) +#define IS_PIN_SERIAL(p) ((p) == 23 || (p) == 24 || (p) == 26 || (p) == 27) +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_ANALOG(p) ((p) - 14) +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) +#define PIN_TO_SERVO(p) (p) + + // anything else #else #error "Please edit Boards.h with a hardware abstraction for this board" diff --git a/examples/StandardFirmataBLE/bleConfig.h b/examples/StandardFirmataBLE/bleConfig.h index a783ce9b..def71f17 100644 --- a/examples/StandardFirmataBLE/bleConfig.h +++ b/examples/StandardFirmataBLE/bleConfig.h @@ -1,15 +1,15 @@ /*================================================================================================== * BLE CONFIGURATION * - * If you are using an Arduino 101, you do not need to make any changes to this file (unless you - * need a unique ble local name (see below). If you are using another supported BLE board or shield, - * follow the instructions for the specific board or shield below. - * - * Make sure you have the Intel Curie Boards package v2.0.2 or higher installed via the Arduino - * Boards Manager. + * If you are using a device supported by the ArduinoBLE library or an Arduino 101, you do not need + * to make any changes to this file (unless you need a unique ble local name (see below)). If you + * are using another supported BLE board or shield, follow the instructions for the specific board + * or shield below. * * Supported boards and shields: - * - Arduino 101 (recommended) + * - Devices supported by the ArduinoBLE library, including the Arduino MKR WiFi 1010, + * Arduino UNO WiFi Rev.2, Arduino Nano 33 IoT, and Arduino Nano 33 BLE + * - Arduino 101 * - RedBearLab BLE Shield (v2) ** to be verified ** * - RedBearLab BLE Nano ** works with modifications ** * - Adafruit Feather M0 Bluefruit LE @@ -20,6 +20,25 @@ // within the same physical space #define FIRMATA_BLE_LOCAL_NAME "FIRMATA" +/* + * ArduinoBLE devices + * + * Be sure to install the ArduinoBLE library via the Arduino Library Manager. + * + */ +#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_ARDUINO_NANO33BLE) +#define ARDUINO_BLE + +// Value is specified in units of 0.625 ms +#define FIRMATA_BLE_ADVERTISING_INTERVAL 32 // 20ms (20 / 0.625) + +// These values are specified in units of 1.25 ms and must be between +// 0x0006 (7.5ms) and 0x0c80 (4s). +#define FIRMATA_BLE_MIN_INTERVAL 0x000c // 15ms (15 / 1.25) +#define FIRMATA_BLE_MAX_INTERVAL 0x0018 // 30ms (30 / 1.25) +#endif + + /* * Arduino 101 * @@ -107,6 +126,12 @@ * END BLE CONFIGURATION - you should not need to change anything below this line *================================================================================================*/ +#ifdef ARDUINO_BLE +#include "utility/ArduinoBLE_UART_Stream.h" +ArduinoBLE_UART_Stream stream; +#endif + + #ifdef _VARIANT_ARDUINO_101_X_ #include "utility/BLEStream.h" BLEStream stream; diff --git a/library.properties b/library.properties index 0dc9a348..6e27f0b8 100644 --- a/library.properties +++ b/library.properties @@ -1,7 +1,7 @@ name=Firmata -version=2.5.8 +version=2.5.9 author=Firmata Developers -maintainer=https://github.com/firmata/arduino +maintainer=Firmata team sentence=Enables the communication with computer apps using a standard serial protocol. For all Arduino/Genuino boards. paragraph=The Firmata library implements the Firmata protocol for communicating with software on the host computer. This allows you to write custom firmware without having to create your own protocol and objects for the programming environment that you are using. category=Device Control diff --git a/readme.md b/readme.md index 3632b1f6..cde48641 100644 --- a/readme.md +++ b/readme.md @@ -28,7 +28,6 @@ Most of the time you will be interacting with Arduino with a client library on t * processing * [https://github.com/firmata/processing](https://github.com/firmata/processing) - * [http://funnel.cc](http://funnel.cc) * python * [https://github.com/MrYsLab/pymata4](https://github.com/MrYsLab/pymata4) * [https://github.com/MrYsLab/pymata-express](https://github.com/MrYsLab/pymata-express) @@ -63,7 +62,7 @@ Most of the time you will be interacting with Arduino with a client library on t * Pharo * [https://github.com/pharo-iot/Firmata](https://github.com/pharo-iot/Firmata) * PHP - * [https://github.com/ThomasWeinert/carica-firmata]() + * [https://github.com/ThomasWeinert/carica-firmata](https://github.com/ThomasWeinert/carica-firmata) * [https://github.com/oasynnoum/phpmake_firmata](https://github.com/oasynnoum/phpmake_firmata) * Haskell * [http://hackage.haskell.org/package/hArduino](http://hackage.haskell.org/package/hArduino) @@ -73,18 +72,25 @@ Most of the time you will be interacting with Arduino with a client library on t * [https://github.com/nfrancois/firmata](https://github.com/nfrancois/firmata) * Max/MSP * [http://www.maxuino.org/](http://www.maxuino.org/) + * [https://github.com/NullMember/MaxFirmata](https://github.com/NullMember/MaxFirmata) * Elixir * [https://github.com/kfatehi/firmata](https://github.com/kfatehi/firmata) * Modelica * [https://www.wolfram.com/system-modeler/libraries/model-plug/](https://www.wolfram.com/system-modeler/libraries/model-plug/) * Go * [https://github.com/kraman/go-firmata](https://github.com/kraman/go-firmata) -* vvvv - * [https://vvvv.org/blog/arduino-second-service](https://vvvv.org/blog/arduino-second-service) +* vvvv/VL + * [https://github.com/vvvv/VL.IO.Firmata](https://github.com/vvvv/VL.IO.Firmata) * openFrameworks * [http://openframeworks.cc/documentation/communication/ofArduino/](http://openframeworks.cc/documentation/communication/ofArduino/) * Rust * [https://github.com/zankich/rust-firmata](https://github.com/zankich/rust-firmata) +* Pure Data + * [https://github.com/NullMember/PDFirmata](https://github.com/NullMember/PDFirmata) +* Common Lisp + * [https://github.com/cjfuller/cl-firmata](https://github.com/cjfuller/cl-firmata) +* Linux Kernel Module + * [https://github.com/logicog/firmata_mod] Note: The above libraries may support various versions of the Firmata protocol and therefore may not support all features of the latest Firmata spec nor all Arduino and Arduino-compatible boards. Refer to the respective projects for details. diff --git a/release.sh b/release.sh index 0c47db8d..089f34e3 100644 --- a/release.sh +++ b/release.sh @@ -1,6 +1,6 @@ #!/bin/sh -# use this script to package Firmata for distribution +# [Optional] use this script to package Firmata for distribution # package for Arduino 1.0.x mkdir -p temp/Firmata @@ -15,7 +15,7 @@ cd temp find . -name "*.DS_Store" -type f -delete zip -r Firmata.zip ./Firmata/ cd .. -mv ./temp/Firmata.zip Arduino-1.0.x-Firmata-2.5.8.zip +mv ./temp/Firmata.zip Arduino-1.0.x-Firmata-2.5.9.zip #package for Arduino 1.6.x and 1.8.x cp library.properties temp/Firmata @@ -29,5 +29,5 @@ cd .. find . -name "*.DS_Store" -type f -delete zip -r Firmata.zip ./Firmata/ cd .. -mv ./temp/Firmata.zip Firmata-2.5.8.zip +mv ./temp/Firmata.zip Firmata-2.5.9.zip rm -r ./temp diff --git a/test/firmata_test/firmata_test.ino b/test/firmata_test/firmata_test.ino deleted file mode 100644 index db058535..00000000 --- a/test/firmata_test/firmata_test.ino +++ /dev/null @@ -1,172 +0,0 @@ -/* - * To run this test suite, you must first install the ArduinoUnit library - * to your Arduino/libraries/ directory. - * You can get ArduinoUnit here: https://github.com/mmurdoch/arduinounit - * Download version 2.0 or greater or install it via the Arduino library manager. - */ - -#include -#include - -void setup() -{ - Serial.begin(9600); -} - -void loop() -{ - Test::run(); -} - -test(beginPrintsVersion) -{ - FakeStream stream; - - Firmata.begin(stream); - - char expected[] = { - REPORT_VERSION, - FIRMATA_PROTOCOL_MAJOR_VERSION, - FIRMATA_PROTOCOL_MINOR_VERSION, - 0 - }; - assertEqual(expected, stream.bytesWritten()); -} - -void processMessage(const byte *message, size_t length) -{ - FakeStream stream; - Firmata.begin(stream); - - for (size_t i = 0; i < length; i++) { - stream.nextByte(message[i]); - Firmata.processInput(); - } -} - -byte _digitalPort; -int _digitalPortValue; -void writeToDigitalPort(byte port, int value) -{ - _digitalPort = port; - _digitalPortValue = value; -} - -void setupDigitalPort() -{ - _digitalPort = 0; - _digitalPortValue = 0; -} - -char * _receivedString; -void handleStringCallback(char *str) -{ - _receivedString = str; -} - -test(processWriteDigital_0) -{ - setupDigitalPort(); - Firmata.attach(DIGITAL_MESSAGE, writeToDigitalPort); - - byte message[] = { DIGITAL_MESSAGE, 0, 0 }; - processMessage(message, 3); - - assertEqual(0, _digitalPortValue); -} - -test(processWriteDigital_127) -{ - setupDigitalPort(); - Firmata.attach(DIGITAL_MESSAGE, writeToDigitalPort); - - byte message[] = { DIGITAL_MESSAGE, 127, 0 }; - processMessage(message, 3); - - assertEqual(127, _digitalPortValue); -} - -test(processWriteDigital_128) -{ - setupDigitalPort(); - Firmata.attach(DIGITAL_MESSAGE, writeToDigitalPort); - - byte message[] = { DIGITAL_MESSAGE, 0, 1 }; - processMessage(message, 3); - - assertEqual(128, _digitalPortValue); -} - -test(processWriteLargestDigitalValue) -{ - setupDigitalPort(); - Firmata.attach(DIGITAL_MESSAGE, writeToDigitalPort); - - byte message[] = { DIGITAL_MESSAGE, 0x7F, 0x7F }; - processMessage(message, 3); - - // Maximum of 14 bits can be set (B0011111111111111) - assertEqual(0x3FFF, _digitalPortValue); -} - -test(defaultDigitalWritePortIsZero) -{ - setupDigitalPort(); - Firmata.attach(DIGITAL_MESSAGE, writeToDigitalPort); - - byte message[] = { DIGITAL_MESSAGE, 0, 0 }; - processMessage(message, 3); - - assertEqual(0, _digitalPort); -} - -test(specifiedDigitalWritePort) -{ - setupDigitalPort(); - Firmata.attach(DIGITAL_MESSAGE, writeToDigitalPort); - - byte message[] = { DIGITAL_MESSAGE + 1, 0, 0 }; - processMessage(message, 3); - - assertEqual(1, _digitalPort); -} - -test(setFirmwareVersionDoesNotLeakMemory) -{ - Firmata.setFirmwareVersion(1, 0); - int initialMemory = freeMemory(); - - Firmata.setFirmwareVersion(1, 0); - - assertEqual(0, initialMemory - freeMemory()); -} - -test(sendStringShouldEncode2BytesPerChar) -{ - FakeStream stream; - Firmata.begin(stream); - // reset the buffer because the firmware name string will be sent on Firmata.begin - stream.reset(); - - char testString[] = "hi!"; - Firmata.sendString(testString); - - byte expected[] = { START_SYSEX, STRING_DATA, 'h', 0, 'i', 0, '!', 0, END_SYSEX }; - - int len = stream.bytesWritten().length(); - assertEqual(sizeof(expected), len); - for (byte i = 0; i < len; i++) { - assertEqual(expected[i], (byte)stream.bytesWritten().charAt(i)); - } -} - -test(receivedStringShouldDecodeFrom2BytesPerChar) -{ - Firmata.attach(STRING_DATA, handleStringCallback); - - byte message[] = { START_SYSEX, STRING_DATA, 'b', 0, 'y', 0, 'e', 0, '!', 0, END_SYSEX }; - processMessage(message, 11); - - assertEqual("bye!", _receivedString); -} - diff --git a/test/readme.md b/test/readme.md deleted file mode 100644 index ab8f8c3d..00000000 --- a/test/readme.md +++ /dev/null @@ -1,13 +0,0 @@ -# Testing Firmata - -Tests tests are written using the [ArduinoUnit](https://github.com/mmurdoch/arduinounit) library (version 2.0). - -Follow the instructions in the [ArduinoUnit readme](https://github.com/mmurdoch/arduinounit/blob/master/readme.md) to install the library. - -Compile and upload the test sketch as you would any other sketch. Then open the -Serial Monitor to view the test results. - -If you make changes to Firmata.cpp, run the tests in /test/ to ensure -that your changes have not produced any unexpected errors. - -You should also perform manual tests against actual hardware. diff --git a/utility/ArduinoBLE_UART_Stream.cpp b/utility/ArduinoBLE_UART_Stream.cpp new file mode 100644 index 00000000..747fa84e --- /dev/null +++ b/utility/ArduinoBLE_UART_Stream.cpp @@ -0,0 +1,4 @@ +/* + * Implementation is in ArduinoBLE_UART_Stream.h to avoid making ArduinoBLE a + * build-time dependency for all projects that use the Firmata library. + */ diff --git a/utility/ArduinoBLE_UART_Stream.h b/utility/ArduinoBLE_UART_Stream.h new file mode 100644 index 00000000..e87c4669 --- /dev/null +++ b/utility/ArduinoBLE_UART_Stream.h @@ -0,0 +1,256 @@ +/* + ArduinoBLE_UART_Stream.h + + Based on BLEStream.h and the HardwareBLESerial library: + https://github.com/Uberi/Arduino-HardwareBLESerial + */ + +#ifndef _ARDUINO_BLE_UART_STREAM_H_ +#define _ARDUINO_BLE_UART_STREAM_H_ + +#include + +#define BLE_ATTRIBUTE_MAX_VALUE_LENGTH 20 + + +class ArduinoBLE_UART_Stream : public Stream +{ + public: + ArduinoBLE_UART_Stream(); + + void setLocalName(const char *localName); + void setAdvertisingInterval(unsigned short advertisingInterval); + void setConnectionInterval(unsigned short minConnInterval, unsigned short maxConnInterval); + void setFlushInterval(int flushInterval); + + void begin(); + bool poll(); + void end(); + + // Print overrides + size_t write(uint8_t byte); + using Print::write; // Expose other write variants + + // Stream overrides + int available(); + int read(); + int peek(); + void flush(); + + private: + void dataReceived(const unsigned char *data, size_t size); + + static void connectedHandler(BLEDevice central); + static void disconnectedHandler(BLEDevice central); + + static void rxWrittenHandler(BLEDevice central, BLECharacteristic characteristic); + + static void txSubscribedHandler(BLEDevice central, BLECharacteristic characteristic); + static void txUnsubscribedHandler(BLEDevice central, BLECharacteristic characteristic); + + BLEService uartService; + BLECharacteristic rxCharacteristic; + BLECharacteristic txCharacteristic; + + String localName; + unsigned short advertisingInterval; + unsigned short minConnInterval; + unsigned short maxConnInterval; + int flushInterval; + + bool connected; + + unsigned char rxBuffer[256]; + size_t rxHead; + size_t rxTail; + + bool txSubscribed; + unsigned char txBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH]; + size_t txCount; + unsigned long lastFlushTime; + + static ArduinoBLE_UART_Stream *instance; +}; + + +ArduinoBLE_UART_Stream::ArduinoBLE_UART_Stream() : + uartService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"), + rxCharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse | BLEWrite, BLE_ATTRIBUTE_MAX_VALUE_LENGTH), + txCharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, BLE_ATTRIBUTE_MAX_VALUE_LENGTH), + advertisingInterval(0), + minConnInterval(0), + maxConnInterval(0), + flushInterval(100), // Default flush interval is 100ms + connected(false), + rxHead(0), + rxTail(0), + txSubscribed(false), + txCount(0), + lastFlushTime(0) +{ + instance = this; +} + +void ArduinoBLE_UART_Stream::setLocalName(const char *localName) +{ + this->localName = localName; +} + +void ArduinoBLE_UART_Stream::setAdvertisingInterval(unsigned short advertisingInterval) +{ + this->advertisingInterval = advertisingInterval; +} + +void ArduinoBLE_UART_Stream::setConnectionInterval(unsigned short minConnInterval, unsigned short maxConnInterval) +{ + this->minConnInterval = minConnInterval; + this->maxConnInterval = maxConnInterval; +} + +void ArduinoBLE_UART_Stream::setFlushInterval(int flushInterval) +{ + // The minimum allowed connection interval is 7.5ms, so don't try to flush + // more frequently than that + this->flushInterval = max(flushInterval, 8); +} + +void ArduinoBLE_UART_Stream::begin() +{ + BLE.begin(); + + if (localName.length() > 0) { + BLE.setLocalName(localName.c_str()); + } + if (advertisingInterval > 0) { + BLE.setAdvertisingInterval(advertisingInterval); + } + if (minConnInterval > 0 && maxConnInterval > 0) { + BLE.setConnectionInterval(minConnInterval, maxConnInterval); + } + + BLE.setEventHandler(BLEConnected, connectedHandler); + BLE.setEventHandler(BLEDisconnected, disconnectedHandler); + + rxCharacteristic.setEventHandler(BLEWritten, rxWrittenHandler); + uartService.addCharacteristic(rxCharacteristic); + + txCharacteristic.setEventHandler(BLESubscribed, txSubscribedHandler); + txCharacteristic.setEventHandler(BLEUnsubscribed, txUnsubscribedHandler); + uartService.addCharacteristic(txCharacteristic); + + BLE.addService(uartService); + BLE.setAdvertisedService(uartService); + BLE.advertise(); +} + +bool ArduinoBLE_UART_Stream::poll() +{ + if (millis() - lastFlushTime > flushInterval) { + flush(); // Always calls BLE.poll() + } else { + BLE.poll(); + } + return connected; +} + +void ArduinoBLE_UART_Stream::end() +{ + flush(); + txCharacteristic.setEventHandler(BLEUnsubscribed, NULL); + txCharacteristic.setEventHandler(BLESubscribed, NULL); + txSubscribed = false; + + rxCharacteristic.setEventHandler(BLEWritten, NULL); + rxHead = 0; + rxTail = 0; + + BLE.setEventHandler(BLEDisconnected, NULL); + BLE.setEventHandler(BLEConnected, NULL); + connected = false; + + BLE.end(); +} + +size_t ArduinoBLE_UART_Stream::write(uint8_t byte) +{ + if (!txSubscribed) { + return 0; + } + txBuffer[txCount] = byte; + txCount++; + if (txCount == sizeof(txBuffer)) { + flush(); + } + return 1; +} + +int ArduinoBLE_UART_Stream::available() +{ + return (rxHead - rxTail + sizeof(rxBuffer)) % sizeof(rxBuffer); +} + +int ArduinoBLE_UART_Stream::read() +{ + if (rxTail == rxHead) { + return -1; + } + uint8_t byte = rxBuffer[rxTail]; + rxTail = (rxTail + 1) % sizeof(rxBuffer); + return byte; +} + +int ArduinoBLE_UART_Stream::peek() +{ + if (rxTail == rxHead) { + return -1; + } + return rxBuffer[rxTail]; +} + +void ArduinoBLE_UART_Stream::flush() +{ + if (txCount > 0) { + txCharacteristic.setValue(txBuffer, txCount); + txCount = 0; + } + lastFlushTime = millis(); + BLE.poll(); +} + +void ArduinoBLE_UART_Stream::dataReceived(const unsigned char *data, size_t size) +{ + for (size_t i = 0; i < size; i++) { + rxBuffer[rxHead] = data[i]; + rxHead = (rxHead + 1) % sizeof(rxBuffer); + } +} + +void ArduinoBLE_UART_Stream::connectedHandler(BLEDevice central) +{ + instance->connected = true; +} + +void ArduinoBLE_UART_Stream::disconnectedHandler(BLEDevice central) +{ + instance->connected = false; +} + +void ArduinoBLE_UART_Stream::rxWrittenHandler(BLEDevice central, BLECharacteristic characteristic) +{ + instance->dataReceived(characteristic.value(), characteristic.valueLength()); +} + +void ArduinoBLE_UART_Stream::txSubscribedHandler(BLEDevice central, BLECharacteristic characteristic) +{ + instance->txSubscribed = true; +} + +void ArduinoBLE_UART_Stream::txUnsubscribedHandler(BLEDevice central, BLECharacteristic characteristic) +{ + instance->txSubscribed = false; +} + +ArduinoBLE_UART_Stream * ArduinoBLE_UART_Stream::instance = NULL; + + +#endif // _ARDUINO_BLE_UART_STREAM_H_ diff --git a/utility/BluefruitLE_SPI_Stream.cpp b/utility/BluefruitLE_SPI_Stream.cpp index 93953e96..86d2788c 100644 --- a/utility/BluefruitLE_SPI_Stream.cpp +++ b/utility/BluefruitLE_SPI_Stream.cpp @@ -1,3 +1,5 @@ /* - * Implementation is in BluefruitLE_SPI_Stream.h to avoid linker issues. + * Implementation is in BluefruitLE_SPI_Stream.h to avoid making + * Adafruit_BluefruitLE_nRF51 a build-time dependency for all projects that use + * the Firmata library. */ diff --git a/utility/SerialFirmata.h b/utility/SerialFirmata.h index a05b761a..eb969da1 100644 --- a/utility/SerialFirmata.h +++ b/utility/SerialFirmata.h @@ -48,13 +48,12 @@ // a) continuous streaming at higher baud rates: enable but set to 0 (receive buffer store & forward) // b) messages: set to a value below min. inter message delay (message store & forward) // c) continuous streaming at lower baud rates or random characters: undefine or set to -1 (disable) -// 3) Smaller delays may not have the desired effect, especially with less powerful CPUs, +// 3) Smaller delays may not have the desired effect, especially with less powerful CPUs, // if set to a value near or below the average Firmata main loop duration. -// 4) The Firmata stream write buffer size must be equal or greater than the max. +// 4) The Firmata stream write buffer size must be equal or greater than the max. // serial buffer/message size and the Firmata frame size (4 bytes) to prevent fragmentation // on the transport layer. //#define FIRMATA_SERIAL_RX_DELAY 50 // [ms] -#define FIRMATA_SERIAL_RX_DELAY 50 #define FIRMATA_SERIAL_FEATURE @@ -66,7 +65,7 @@ #define HW_SERIAL4 0x04 #define HW_SERIAL5 0x05 #define HW_SERIAL6 0x06 -// extensible up to 0x07 +#define HW_SERIAL7 0x07 #define SW_SERIAL0 0x08 #define SW_SERIAL1 0x09 @@ -79,6 +78,8 @@ #define SERIAL_READ_ARR_LEN 12 // map configuration query response resolution value to serial pin type +#define RES_RX0 0x00 +#define RES_TX0 0x01 #define RES_RX1 0x02 #define RES_TX1 0x03 #define RES_RX2 0x04 @@ -91,6 +92,8 @@ #define RES_TX5 0x0b #define RES_RX6 0x0c #define RES_TX6 0x0d +#define RES_RX7 0x0e +#define RES_TX7 0x0f // Serial command bytes #define SERIAL_CONFIG 0x10 @@ -120,6 +123,10 @@ namespace { #if defined(PIN_SERIAL_RX) // TODO when use of HW_SERIAL0 is enabled #endif + #if defined(PIN_SERIAL0_RX) + if (pin == PIN_SERIAL0_RX) return RES_RX0; + if (pin == PIN_SERIAL0_TX) return RES_TX0; + #endif #if defined(PIN_SERIAL1_RX) if (pin == PIN_SERIAL1_RX) return RES_RX1; if (pin == PIN_SERIAL1_TX) return RES_TX1; @@ -143,6 +150,10 @@ namespace { #if defined(PIN_SERIAL6_RX) if (pin == PIN_SERIAL6_RX) return RES_RX6; if (pin == PIN_SERIAL6_TX) return RES_TX6; + #endif + #if defined(PIN_SERIAL7_RX) + if (pin == PIN_SERIAL7_RX) return RES_RX7; + if (pin == PIN_SERIAL7_TX) return RES_TX7; #endif return 0; } @@ -158,6 +169,12 @@ namespace { // // TODO when use of HW_SERIAL0 is enabled // break; #endif + #if defined(PIN_SERIAL0_RX) + case HW_SERIAL0: + pins.rx = PIN_SERIAL0_RX; + pins.tx = PIN_SERIAL0_TX; + break; + #endif #if defined(PIN_SERIAL1_RX) case HW_SERIAL1: pins.rx = PIN_SERIAL1_RX; @@ -193,6 +210,12 @@ namespace { pins.rx = PIN_SERIAL6_RX; pins.tx = PIN_SERIAL6_TX; break; + #endif + #if defined(PIN_SERIAL7_RX) + case HW_SERIAL7: + pins.rx = PIN_SERIAL7_RX; + pins.tx = PIN_SERIAL7_TX; + break; #endif default: pins.rx = 0;