diff --git a/Firmata.cpp b/Firmata.cpp index 99d04f4a..20cee89a 100644 --- a/Firmata.cpp +++ b/Firmata.cpp @@ -1,5 +1,5 @@ /* - Firmata.cpp - Firmata library v2.4.2 - 2015-3-16 + Firmata.cpp - Firmata library v2.4.3 - 2015-4-11 Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. This library is free software; you can redistribute it and/or diff --git a/Firmata.h b/Firmata.h index e286b3c6..9c788e13 100644 --- a/Firmata.h +++ b/Firmata.h @@ -1,5 +1,5 @@ /* - Firmata.h - Firmata library v2.4.2 - 2015-3-16 + Firmata.h - Firmata library v2.4.3 - 2015-4-11 Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. This library is free software; you can redistribute it and/or @@ -21,7 +21,7 @@ * installed firmware. */ #define FIRMATA_MAJOR_VERSION 2 // for non-compatible changes #define FIRMATA_MINOR_VERSION 4 // for backwards compatible changes -#define FIRMATA_BUGFIX_VERSION 2 // for bugfix releases +#define FIRMATA_BUGFIX_VERSION 3 // for bugfix releases #define MAX_DATA_BYTES 64 // max number of data bytes in incoming messages diff --git a/examples/StandardFirmata/StandardFirmata.ino b/examples/StandardFirmata/StandardFirmata.ino index 49da4abb..9dfb8c4f 100644 --- a/examples/StandardFirmata/StandardFirmata.ino +++ b/examples/StandardFirmata/StandardFirmata.ino @@ -1,19 +1,17 @@ /* - * 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. - * - * http://firmata.org/wiki/Download - */ + 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/arduino#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) 2009-2014 Jeff Hoefs. All rights reserved. + Copyright (C) 2009-2015 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 @@ -22,29 +20,25 @@ See file LICENSE.txt for further informations on licensing terms. - formatted using the GNU C formatting and indenting + Last updated by Jeff Hoefs: April 11, 2015 */ -/* - * TODO: use Program Control to load stored profiles from EEPROM - */ - #include #include #include -// move the following defines to Firmata.h? -#define I2C_WRITE B00000000 -#define I2C_READ B00001000 -#define I2C_READ_CONTINUOUSLY B00010000 -#define I2C_STOP_READING B00011000 -#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 #define I2C_10BIT_ADDRESS_MODE_MASK B00100000 +#define MAX_QUERIES 8 +#define REGISTER_NOT_SPECIFIED -1 -#define MAX_QUERIES 8 +// the minimum interval for sampling analog input #define MINIMUM_SAMPLING_INTERVAL 10 -#define REGISTER_NOT_SPECIFIED -1 /*============================================================================== * GLOBAL VARIABLES @@ -65,7 +59,7 @@ int pinState[TOTAL_PINS]; // any value that has been written /* timer variables */ unsigned long currentMillis; // store the current value from millis() unsigned long previousMillis; // for comparison with currentMillis -unsigned int samplingInterval = 19; // how often to run the main loop (in ms) +unsigned int samplingInterval = 19; // how often to run the main loop (in ms) /* i2c data */ struct i2c_device_info { @@ -77,12 +71,11 @@ struct i2c_device_info { /* for i2c read continuous more */ i2c_device_info query[MAX_QUERIES]; -boolean isResetting = false; - byte i2cRxData[32]; boolean isI2CEnabled = false; signed char queryIndex = -1; -unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom() +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; Servo servos[MAX_SERVOS]; byte servoPinMap[TOTAL_PINS]; @@ -90,6 +83,26 @@ byte detachedServos[MAX_SERVOS]; byte detachedServoCount = 0; byte servoCount = 0; +boolean isResetting = false; + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} /*============================================================================== * FUNCTIONS @@ -139,11 +152,7 @@ void readAndReportData(byte address, int theRegister, byte numBytes) { // do not always require the register read so upon interrupt you call Wire.requestFrom() if (theRegister != REGISTER_NOT_SPECIFIED) { Wire.beginTransmission(address); -#if ARDUINO >= 100 - Wire.write((byte)theRegister); -#else - Wire.send((byte)theRegister); -#endif + wireWrite((byte)theRegister); Wire.endTransmission(); // do not set a value of 0 if (i2cReadDelayTime > 0) { @@ -158,20 +167,16 @@ void readAndReportData(byte address, int theRegister, byte numBytes) { // check to be sure correct number of bytes were returned by slave if (numBytes < Wire.available()) { - Firmata.sendString("I2C Read Error: Too many bytes received"); + Firmata.sendString("I2C: Too many bytes received"); } else if (numBytes > Wire.available()) { - Firmata.sendString("I2C Read Error: Too few bytes received"); + Firmata.sendString("I2C: Too few bytes received"); } i2cRxData[0] = address; i2cRxData[1] = theRegister; for (int i = 0; i < numBytes && Wire.available(); i++) { -#if ARDUINO >= 100 - i2cRxData[2 + i] = Wire.read(); -#else - i2cRxData[2 + i] = Wire.receive(); -#endif + i2cRxData[2 + i] = wireRead(); } // send slave address, register and received bytes @@ -221,6 +226,9 @@ void checkDigitalInputs(void) */ void setPinModeCallback(byte pin, int mode) { + if (pinConfig[pin] == IGNORE) + return; + if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) { // disable i2c so pins can be used for other functions // the following if statements should reconfigure the pins properly @@ -246,7 +254,7 @@ void setPinModeCallback(byte pin, int mode) case ANALOG: if (IS_PIN_ANALOG(pin)) { if (IS_PIN_DIGITAL(pin)) { - pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups } pinConfig[pin] = ANALOG; @@ -254,7 +262,7 @@ void setPinModeCallback(byte pin, int mode) break; case INPUT: if (IS_PIN_DIGITAL(pin)) { - pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups pinConfig[pin] = INPUT; } @@ -409,11 +417,7 @@ void sysexCallback(byte command, byte argc, byte *argv) Wire.beginTransmission(slaveAddress); for (byte i = 2; i < argc; i += 2) { data = argv[i] + (argv[i + 1] << 7); -#if ARDUINO >= 100 - Wire.write(data); -#else - Wire.send(data); -#endif + wireWrite(data); } Wire.endTransmission(); delayMicroseconds(70); @@ -541,11 +545,11 @@ void sysexCallback(byte command, byte argc, byte *argv) } if (IS_PIN_ANALOG(pin)) { Firmata.write(ANALOG); - Firmata.write(10); + Firmata.write(10); // 10 = 10-bit resolution } if (IS_PIN_PWM(pin)) { Firmata.write(PWM); - Firmata.write(8); + Firmata.write(8); // 8 = 8-bit resolution } if (IS_PIN_DIGITAL(pin)) { Firmata.write(SERVO); @@ -553,7 +557,7 @@ void sysexCallback(byte command, byte argc, byte *argv) } if (IS_PIN_I2C(pin)) { Firmata.write(I2C); - Firmata.write(1); // to do: determine appropriate value + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA } Firmata.write(127); } @@ -599,7 +603,6 @@ void enableI2CPins() isI2CEnabled = true; - // is there enough time before the first I2C request to call this here? Wire.begin(); } @@ -617,14 +620,16 @@ void disableI2CPins() { void systemResetCallback() { isResetting = true; + // initialize a defalt state // TODO: option to load config from EEPROM instead of default + if (isI2CEnabled) { disableI2CPins(); } for (byte i = 0; i < TOTAL_PORTS; i++) { - reportPINs[i] = false; // by default, reporting off + reportPINs[i] = false; // by default, reporting off portConfigInputs[i] = 0; // until activated previousPINs[i] = 0; } @@ -687,14 +692,12 @@ void loop() * FTDI buffer using Serial.print() */ checkDigitalInputs(); - /* SERIALREAD - processing incoming messagse as soon as possible, while still + /* STREAMREAD - processing incoming messagse as soon as possible, while still * checking digital inputs. */ while (Firmata.available()) Firmata.processInput(); - /* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over - * 60 bytes. use a timer to sending an event character every 4 ms to - * trigger the buffer to dump. */ + // TODO - ensure that Stream buffer doesn't go over 60 bytes currentMillis = millis(); if (currentMillis - previousMillis > samplingInterval) { diff --git a/examples/StandardFirmataEthernet/StandardFirmataEthernet.ino b/examples/StandardFirmataEthernet/StandardFirmataEthernet.ino new file mode 100644 index 00000000..cc5fdb0f --- /dev/null +++ b/examples/StandardFirmataEthernet/StandardFirmataEthernet.ino @@ -0,0 +1,883 @@ +/* + 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 list of Firmata client libraries your default browser. + + https://github.com/firmata/arduino#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) 2009-2015 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: April 11, 2015 +*/ + +/* + README + + To use StandardFirmataEthernet you will need to have one of the following + boards or shields: + + - Arduino Ethernet shield (or clone) + - Arduino Ethernet board (or clone) + - Arduino Yun + + Follow the instructions in the NETWORK CONFIGURATION section below to + configure your particular hardware. + + NOTE: 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) +*/ + +#include +#include +#include + +//#define SERIAL_DEBUG +#include "utility/firmataDebug.h" + +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_10BIT_ADDRESS_MODE_MASK B00100000 +#define I2C_MAX_QUERIES 8 +#define I2C_REGISTER_NOT_SPECIFIED -1 + +// the minimum interval for sampling analog input +#define MINIMUM_SAMPLING_INTERVAL 10 + + +/*============================================================================== + * NETWORK CONFIGURATION + * + * You must configure your particular hardware. Follow the steps below. + *============================================================================*/ + +// STEP 1 [REQUIRED] +// Uncomment / comment the appropriate set of includes for your hardware (OPTION A or B) +// Option A is enabled by default. + +/* + * OPTION A: Configure for Arduino Ethernet board or shield + * + * To configure StandardFirmataEthernet to use the original WIZ5100-based + * ethernet shield or Arduino Ethernet uncomment the includes of 'SPI.h' and 'Ethernet.h': + */ + +#include +#include + +/* + * OPTION B: Configure for Arduin Yun + * + * To execute StandardFirmataEthernet on Yun uncomment Bridge.h and YunClient.h. + * Do not include Ethernet.h or SPI.h above in this case. + * On Yun there's no need to configure local_ip and mac in the sketch + * as this is configured on the linux-side of Yun. + */ + +// #include +// #include + + +// 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(10, 0, 0, 3) +// *** REMOTE HOST IS NOT YET WORKING *** +// 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 Arduin 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(10, 0, 0, 15) + +// STEP 5 [REQUIRED unless using Arduino Yun] +// replace with ethernet shield mac. Must be unique for your network +const byte mac[] = {0x90, 0xA2, 0xDA, 0x00, 0x53, 0xE5}; + +#if !defined ethernet_h && !defined _YUN_CLIENT_H_ +#error "you must uncomment the includes for your board configuration. See OPTIONS A and B in the NETWORK CONFIGURATION SECTION" +#endif + +#if defined remote_ip && defined remote_host +#error "cannot define both remote_ip and remote_host at the same time!" +#endif + + +/*============================================================================== + * GLOBAL VARIABLES + *============================================================================*/ + +/* network */ + +#include "utility/EthernetClientStream.h" + +#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 + +/* analog inputs */ +int analogInputsToReport = 0; // bitwise array to store pin reporting + +/* digital input ports */ +byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence +byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent + +/* pins configuration */ +byte pinConfig[TOTAL_PINS]; // configuration of every pin +byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else +int pinState[TOTAL_PINS]; // any value that has been written + +/* timer variables */ +unsigned long currentMillis; // store the current value from millis() +unsigned long previousMillis; // for comparison with currentMillis +unsigned int samplingInterval = 19; // how often to sample analog inputs (in ms) + +/* i2c data */ +struct i2c_device_info { + byte addr; + int reg; + byte bytes; +}; + +/* for i2c read continuous mode */ +i2c_device_info query[I2C_MAX_QUERIES]; + +byte i2cRxData[32]; +boolean isI2CEnabled = false; +signed char queryIndex = -1; +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; + +Servo servos[MAX_SERVOS]; +byte servoPinMap[TOTAL_PINS]; +byte detachedServos[MAX_SERVOS]; +byte detachedServoCount = 0; +byte servoCount = 0; + +boolean isResetting = false; + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + +/*============================================================================== + * FUNCTIONS + *============================================================================*/ + +void attachServo(byte pin, int minPulse, int maxPulse) +{ + if (servoCount < MAX_SERVOS) { + // reuse indexes of detached servos until all have been reallocated + if (detachedServoCount > 0) { + servoPinMap[pin] = detachedServos[detachedServoCount - 1]; + if (detachedServoCount > 0) detachedServoCount--; + } else { + servoPinMap[pin] = servoCount; + servoCount++; + } + if (minPulse > 0 && maxPulse > 0) { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); + } else { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); + } + } else { + Firmata.sendString("Max servos attached"); + } +} + +void detachServo(byte pin) +{ + servos[servoPinMap[pin]].detach(); + // if we're detaching the last servo, decrement the count + // otherwise store the index of the detached servo + if (servoPinMap[pin] == servoCount && servoCount > 0) { + servoCount--; + } else if (servoCount > 0) { + // keep track of detached servos because we want to reuse their indexes + // before incrementing the count of attached servos + detachedServoCount++; + detachedServos[detachedServoCount - 1] = servoPinMap[pin]; + } + + servoPinMap[pin] = 255; +} + +void readAndReportData(byte address, int theRegister, byte numBytes) { + // allow I2C requests that don't require a register read + // for example, some devices using an interrupt pin to signify new data available + // do not always require the register read so upon interrupt you call Wire.requestFrom() + if (theRegister != I2C_REGISTER_NOT_SPECIFIED) { + Wire.beginTransmission(address); + wireWrite((byte)theRegister); + Wire.endTransmission(); + // do not set a value of 0 + if (i2cReadDelayTime > 0) { + // delay is necessary for some devices such as WiiNunchuck + delayMicroseconds(i2cReadDelayTime); + } + } else { + theRegister = 0; // fill the register with a dummy value + } + + Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom + + // check to be sure correct number of bytes were returned by slave + if (numBytes < Wire.available()) { + Firmata.sendString("I2C: Too many bytes received"); + } else if (numBytes > Wire.available()) { + Firmata.sendString("I2C: Too few bytes received"); + } + + i2cRxData[0] = address; + i2cRxData[1] = theRegister; + + for (int i = 0; i < numBytes && Wire.available(); i++) { + i2cRxData[2 + i] = wireRead(); + } + + // send slave address, register and received bytes + Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); +} + +void outputPort(byte portNumber, byte portValue, byte forceSend) +{ + // pins not configured as INPUT are cleared to zeros + portValue = portValue & portConfigInputs[portNumber]; + // only send if the value is different than previously sent + if (forceSend || previousPINs[portNumber] != portValue) { + Firmata.sendDigitalPort(portNumber, portValue); + previousPINs[portNumber] = portValue; + } +} + +/* ----------------------------------------------------------------------------- + * check all the active digital inputs for change of state, then add any events + * to the Stream output queue using Stream.write() */ +void checkDigitalInputs(void) +{ + /* Using non-looping code allows constants to be given to readPort(). + * The compiler will apply substantial optimizations if the inputs + * to readPort() are compile-time constants. */ + if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false); + if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false); + if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false); + if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false); + if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false); + if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false); + if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false); + if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false); + if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false); + if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false); + if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false); + if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false); + if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false); + if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false); + if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false); + if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false); +} + +// ----------------------------------------------------------------------------- +/* sets the pin mode to the correct state and sets the relevant bits in the + * two bit-arrays that track Digital I/O and PWM status + */ +void setPinModeCallback(byte pin, int mode) +{ + if (pinConfig[pin] == IGNORE) + return; + + if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) { + // disable i2c so pins can be used for other functions + // the following if statements should reconfigure the pins properly + disableI2CPins(); + } + if (IS_PIN_DIGITAL(pin) && mode != SERVO) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + } + if (IS_PIN_ANALOG(pin)) { + reportAnalogCallback(PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting + } + if (IS_PIN_DIGITAL(pin)) { + if (mode == INPUT) { + portConfigInputs[pin / 8] |= (1 << (pin & 7)); + } else { + portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); + } + } + pinState[pin] = 0; + switch (mode) { + case ANALOG: + if (IS_PIN_ANALOG(pin)) { + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups + } + pinConfig[pin] = ANALOG; + } + break; + case INPUT: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups + pinConfig[pin] = INPUT; + } + break; + case OUTPUT: + if (IS_PIN_DIGITAL(pin)) { + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM + pinMode(PIN_TO_DIGITAL(pin), OUTPUT); + pinConfig[pin] = OUTPUT; + } + break; + case PWM: + if (IS_PIN_PWM(pin)) { + pinMode(PIN_TO_PWM(pin), OUTPUT); + analogWrite(PIN_TO_PWM(pin), 0); + pinConfig[pin] = PWM; + } + break; + case SERVO: + if (IS_PIN_DIGITAL(pin)) { + pinConfig[pin] = SERVO; + if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) { + // pass -1 for min and max pulse values to use default values set + // by Servo library + attachServo(pin, -1, -1); + } + } + break; + case I2C: + if (IS_PIN_I2C(pin)) { + // mark the pin as i2c + // the user must call I2C_CONFIG to enable I2C for a device + pinConfig[pin] = I2C; + } + break; + default: + Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM + } + // TODO: save status to EEPROM here, if changed +} + +void analogWriteCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS) { + switch (pinConfig[pin]) { + case SERVO: + if (IS_PIN_DIGITAL(pin)) + servos[servoPinMap[pin]].write(value); + pinState[pin] = value; + break; + case PWM: + if (IS_PIN_PWM(pin)) + analogWrite(PIN_TO_PWM(pin), value); + pinState[pin] = value; + break; + } + } +} + +void digitalWriteCallback(byte port, int value) +{ + byte pin, lastPin, mask = 1, pinWriteMask = 0; + + if (port < TOTAL_PORTS) { + // create a mask of the pins on this port that are writable. + lastPin = port * 8 + 8; + if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS; + for (pin = port * 8; pin < lastPin; pin++) { + // do not disturb non-digital pins (eg, Rx & Tx) + if (IS_PIN_DIGITAL(pin)) { + // only write to OUTPUT and INPUT (enables pullup) + // do not touch pins in PWM, ANALOG, SERVO or other modes + if (pinConfig[pin] == OUTPUT || pinConfig[pin] == INPUT) { + pinWriteMask |= mask; + pinState[pin] = ((byte)value & mask) ? 1 : 0; + } + } + mask = mask << 1; + } + writePort(port, (byte)value, pinWriteMask); + } +} + + +// ----------------------------------------------------------------------------- +/* sets bits in a bit array (int) to toggle the reporting of the analogIns + */ +//void FirmataClass::setAnalogPinReporting(byte pin, byte state) { +//} +void reportAnalogCallback(byte analogPin, int value) +{ + if (analogPin < TOTAL_ANALOG_PINS) { + if (value == 0) { + analogInputsToReport = analogInputsToReport & ~ (1 << analogPin); + } else { + analogInputsToReport = analogInputsToReport | (1 << analogPin); + // prevent during system reset or all analog pin values will be reported + // which may report noise for unconnected analog pins + if (!isResetting) { + // Send pin value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // TODO: save status to EEPROM here, if changed +} + +void reportDigitalCallback(byte port, int value) +{ + if (port < TOTAL_PORTS) { + reportPINs[port] = (byte)value; + // Send port value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + if (value) outputPort(port, readPort(port, portConfigInputs[port]), true); + } + // do not disable analog reporting on these 8 pins, to allow some + // pins used for digital, others analog. Instead, allow both types + // of reporting to be enabled, but check if the pin is configured + // as analog when sampling the analog inputs. Likewise, while + // scanning digital pins, portConfigInputs will mask off values from any + // pins configured as analog +} + +/*============================================================================== + * SYSEX-BASED commands + *============================================================================*/ + +void sysexCallback(byte command, byte argc, byte *argv) +{ + byte mode; + byte slaveAddress; + byte data; + int slaveRegister; + unsigned int delayTime; + + switch (command) { + case I2C_REQUEST: + mode = argv[1] & I2C_READ_WRITE_MODE_MASK; + if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { + Firmata.sendString("10-bit addressing not supported"); + return; + } + else { + slaveAddress = argv[0]; + } + + switch (mode) { + case I2C_WRITE: + Wire.beginTransmission(slaveAddress); + for (byte i = 2; i < argc; i += 2) { + data = argv[i] + (argv[i + 1] << 7); + wireWrite(data); + } + Wire.endTransmission(); + delayMicroseconds(70); + break; + case I2C_READ: + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + readAndReportData(slaveAddress, (int)slaveRegister, data); + break; + case I2C_READ_CONTINUOUSLY: + if ((queryIndex + 1) >= I2C_MAX_QUERIES) { + // too many queries, just ignore + Firmata.sendString("too many I2C queries"); + break; + } + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = (int)I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + queryIndex++; + query[queryIndex].addr = slaveAddress; + query[queryIndex].reg = slaveRegister; + query[queryIndex].bytes = data; + break; + case I2C_STOP_READING: + byte queryIndexToSkip; + // if read continuous mode is enabled for only 1 i2c device, disable + // read continuous reporting for that device + if (queryIndex <= 0) { + queryIndex = -1; + } else { + // if read continuous mode is enabled for multiple devices, + // determine which device to stop reading and remove it's data from + // the array, shifiting other array data to fill the space + for (byte i = 0; i < queryIndex + 1; i++) { + if (query[i].addr == slaveAddress) { + queryIndexToSkip = i; + break; + } + } + + for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { + if (i < I2C_MAX_QUERIES) { + query[i].addr = query[i + 1].addr; + query[i].reg = query[i + 1].reg; + query[i].bytes = query[i + 1].bytes; + } + } + queryIndex--; + } + break; + default: + break; + } + break; + case I2C_CONFIG: + delayTime = (argv[0] + (argv[1] << 7)); + + if (delayTime > 0) { + i2cReadDelayTime = delayTime; + } + + if (!isI2CEnabled) { + enableI2CPins(); + } + + break; + case SERVO_CONFIG: + if (argc > 4) { + // these vars are here for clarity, they'll optimized away by the compiler + byte pin = argv[0]; + int minPulse = argv[1] + (argv[2] << 7); + int maxPulse = argv[3] + (argv[4] << 7); + + if (IS_PIN_DIGITAL(pin)) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + attachServo(pin, minPulse, maxPulse); + setPinModeCallback(pin, SERVO); + } + } + break; + case SAMPLING_INTERVAL: + if (argc > 1) { + samplingInterval = argv[0] + (argv[1] << 7); + if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { + samplingInterval = MINIMUM_SAMPLING_INTERVAL; + } + } else { + //Firmata.sendString("Not enough data"); + } + break; + case EXTENDED_ANALOG: + if (argc > 1) { + int val = argv[1]; + if (argc > 2) val |= (argv[2] << 7); + if (argc > 3) val |= (argv[3] << 14); + analogWriteCallback(argv[0], val); + } + break; + case CAPABILITY_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(CAPABILITY_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_DIGITAL(pin)) { + Firmata.write((byte)INPUT); + Firmata.write(1); + Firmata.write((byte)OUTPUT); + Firmata.write(1); + } + if (IS_PIN_ANALOG(pin)) { + Firmata.write(ANALOG); + Firmata.write(10); // 10 = 10-bit resolution + } + if (IS_PIN_PWM(pin)) { + Firmata.write(PWM); + Firmata.write(8); // 8 = 8-bit resolution + } + if (IS_PIN_DIGITAL(pin)) { + Firmata.write(SERVO); + Firmata.write(14); + } + if (IS_PIN_I2C(pin)) { + Firmata.write(I2C); + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA + } + Firmata.write(127); + } + Firmata.write(END_SYSEX); + break; + case PIN_STATE_QUERY: + if (argc > 0) { + byte pin = argv[0]; + Firmata.write(START_SYSEX); + Firmata.write(PIN_STATE_RESPONSE); + Firmata.write(pin); + if (pin < TOTAL_PINS) { + Firmata.write((byte)pinConfig[pin]); + Firmata.write((byte)pinState[pin] & 0x7F); + if (pinState[pin] & 0xFF80) Firmata.write((byte)(pinState[pin] >> 7) & 0x7F); + if (pinState[pin] & 0xC000) Firmata.write((byte)(pinState[pin] >> 14) & 0x7F); + } + Firmata.write(END_SYSEX); + } + break; + case ANALOG_MAPPING_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(ANALOG_MAPPING_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127); + } + Firmata.write(END_SYSEX); + break; + } +} + +void enableI2CPins() +{ + byte i; + // is there a faster way to do this? would probaby require importing + // Arduino.h to get SCL and SDA pins + for (i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_I2C(i)) { + // mark pins as i2c so they are ignore in non i2c data requests + setPinModeCallback(i, I2C); + } + } + + isI2CEnabled = true; + + Wire.begin(); +} + +/* disable the i2c pins so they can be used for other functions */ +void disableI2CPins() { + isI2CEnabled = false; + // disable read continuous mode for all devices + queryIndex = -1; +} + +/*============================================================================== + * SETUP() + *============================================================================*/ + +void systemResetCallback() +{ + isResetting = true; + + // initialize a defalt state + // TODO: option to load config from EEPROM instead of default + + if (isI2CEnabled) { + disableI2CPins(); + } + + for (byte i = 0; i < TOTAL_PORTS; i++) { + reportPINs[i] = false; // by default, reporting off + portConfigInputs[i] = 0; // until activated + previousPINs[i] = 0; + } + + for (byte i = 0; i < TOTAL_PINS; i++) { + // pins with analog capability default to analog input + // otherwise, pins default to digital output + if (IS_PIN_ANALOG(i)) { + // turns off pullup, configures everything + setPinModeCallback(i, ANALOG); + } else { + // sets the output to 0, configures portConfigInputs + setPinModeCallback(i, OUTPUT); + } + + servoPinMap[i] = 255; + } + // by default, do not report any analog inputs + analogInputsToReport = 0; + + detachedServoCount = 0; + servoCount = 0; + + /* send digital inputs to set the initial state on the host computer, + * since once in the loop(), this firmware will only send on change */ + /* + TODO: this can never execute, since no pins default to digital input + but it will be needed when/if we support EEPROM stored config + for (byte i=0; i < TOTAL_PORTS; i++) { + outputPort(i, readPort(i, portConfigInputs[i]), true); + } + */ + isResetting = false; +} + +void setup() +{ + DEBUG_BEGIN(9600); + +#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 + + DEBUG_PRINTLN("connecting..."); + + Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); + + Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); + Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); + Firmata.attach(REPORT_ANALOG, reportAnalogCallback); + Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); + Firmata.attach(SET_PIN_MODE, setPinModeCallback); + Firmata.attach(START_SYSEX, sysexCallback); + Firmata.attach(SYSTEM_RESET, systemResetCallback); + + // StandardFirmataEthernet communicates with Ethernet shields over SPI. Therefor all + // SPI pins must be set to IGNORE. Otherwise Firmata would break SPI communication. + // add Pin 10 and configure pin 53 as output if using a MEGA with an Ethernet shield. + + // 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-shiedl uses pin 4 for SS + || 10 == i // Ethernet-shield uses pin 10 for SS +#if defined(__AVR_ATmega32U4__) + || 24 == i // On Leonardo, pin 24 maps to D4 and pin 28 maps to D10 + || 28 == i +#endif + ) { + pinConfig[i] = IGNORE; + } + } + + // Arduino Ethernet, Arduino EthernetShield and Arduino Yun all have SD SS wired to D4 + pinMode(PIN_TO_DIGITAL(4), OUTPUT); // switch off SD card bypassing Firmata + digitalWrite(PIN_TO_DIGITAL(4), HIGH); // SS is active low; + +#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); + systemResetCallback(); // reset to default config +} + +/*============================================================================== + * LOOP() + *============================================================================*/ +void loop() +{ + byte pin, analogPin; + + /* DIGITALREAD - as fast as possible, check for changes and output them to the + * Stream buffer using Stream.write() */ + checkDigitalInputs(); + + /* STREAMREAD - processing incoming messagse as soon as possible, while still + * checking digital inputs. */ + while (Firmata.available()) + Firmata.processInput(); + + // TODO - ensure that Stream buffer doesn't go over 60 bytes + + currentMillis = millis(); + if (currentMillis - previousMillis > samplingInterval) { + previousMillis += samplingInterval; + /* ANALOGREAD - do all analogReads() at the configured sampling interval */ + for (pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) { + analogPin = PIN_TO_ANALOG(pin); + if (analogInputsToReport & (1 << analogPin)) { + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // report i2c data for all device with read continuous mode enabled + if (queryIndex > -1) { + for (byte i = 0; i < queryIndex + 1; i++) { + readAndReportData(query[i].addr, query[i].reg, query[i].bytes); + } + } + } + +#if !defined local_ip && !defined _YUN_CLIENT_H_ + if (Ethernet.maintain()) + { + stream.maintain(Ethernet.localIP()); + } +#endif + +} diff --git a/examples/StandardFirmataYun/StandardFirmataYun.ino b/examples/StandardFirmataYun/StandardFirmataYun.ino index b2e671f0..9391fb93 100644 --- a/examples/StandardFirmataYun/StandardFirmataYun.ino +++ b/examples/StandardFirmataYun/StandardFirmataYun.ino @@ -1,15 +1,13 @@ /* - * 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. - * - * http://firmata.org/wiki/Download - */ + 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/arduino#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. @@ -23,29 +21,36 @@ See file LICENSE.txt for further informations on licensing terms. - formatted using the GNU C formatting and indenting + Last updated by Jeff Hoefs: April 11, 2015 */ /* - * TODO: use Program Control to load stored profiles from EEPROM - */ + README + + The Arduino Yun has both an Arduino (Atmega32u4) environment and a + Linux (Linino) environment. + + StandardFirmataYun enables Firmata running on the Arduino environment + to communicate with a Firmata client application running on the Linux + environment. +*/ #include #include #include -// move the following defines to Firmata.h? -#define I2C_WRITE B00000000 -#define I2C_READ B00001000 -#define I2C_READ_CONTINUOUSLY B00010000 -#define I2C_STOP_READING B00011000 -#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 #define I2C_10BIT_ADDRESS_MODE_MASK B00100000 +#define MAX_QUERIES 8 +#define REGISTER_NOT_SPECIFIED -1 -#define MAX_QUERIES 8 +// the minimum interval for sampling analog input #define MINIMUM_SAMPLING_INTERVAL 10 -#define REGISTER_NOT_SPECIFIED -1 /*============================================================================== * GLOBAL VARIABLES @@ -66,7 +71,7 @@ int pinState[TOTAL_PINS]; // any value that has been written /* timer variables */ unsigned long currentMillis; // store the current value from millis() unsigned long previousMillis; // for comparison with currentMillis -unsigned int samplingInterval = 19; // how often to run the main loop (in ms) +unsigned int samplingInterval = 19; // how often to run the main loop (in ms) /* i2c data */ struct i2c_device_info { @@ -78,12 +83,11 @@ struct i2c_device_info { /* for i2c read continuous more */ i2c_device_info query[MAX_QUERIES]; -boolean isResetting = false; - byte i2cRxData[32]; boolean isI2CEnabled = false; signed char queryIndex = -1; -unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom() +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; Servo servos[MAX_SERVOS]; byte servoPinMap[TOTAL_PINS]; @@ -91,6 +95,26 @@ byte detachedServos[MAX_SERVOS]; byte detachedServoCount = 0; byte servoCount = 0; +boolean isResetting = false; + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} /*============================================================================== * FUNCTIONS @@ -103,19 +127,16 @@ void attachServo(byte pin, int minPulse, int maxPulse) if (detachedServoCount > 0) { servoPinMap[pin] = detachedServos[detachedServoCount - 1]; if (detachedServoCount > 0) detachedServoCount--; - } - else { + } else { servoPinMap[pin] = servoCount; servoCount++; } if (minPulse > 0 && maxPulse > 0) { servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); - } - else { + } else { servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); } - } - else { + } else { Firmata.sendString("Max servos attached"); } } @@ -127,8 +148,7 @@ void detachServo(byte pin) // otherwise store the index of the detached servo if (servoPinMap[pin] == servoCount && servoCount > 0) { servoCount--; - } - else if (servoCount > 0) { + } else if (servoCount > 0) { // keep track of detached servos because we want to reuse their indexes // before incrementing the count of attached servos detachedServoCount++; @@ -144,19 +164,14 @@ void readAndReportData(byte address, int theRegister, byte numBytes) { // do not always require the register read so upon interrupt you call Wire.requestFrom() if (theRegister != REGISTER_NOT_SPECIFIED) { Wire.beginTransmission(address); -#if ARDUINO >= 100 - Wire.write((byte)theRegister); -#else - Wire.send((byte)theRegister); -#endif + wireWrite((byte)theRegister); Wire.endTransmission(); // do not set a value of 0 if (i2cReadDelayTime > 0) { // delay is necessary for some devices such as WiiNunchuck delayMicroseconds(i2cReadDelayTime); } - } - else { + } else { theRegister = 0; // fill the register with a dummy value } @@ -164,21 +179,16 @@ void readAndReportData(byte address, int theRegister, byte numBytes) { // check to be sure correct number of bytes were returned by slave if (numBytes < Wire.available()) { - Firmata.sendString("I2C Read Error: Too many bytes received"); - } - else if (numBytes > Wire.available()) { - Firmata.sendString("I2C Read Error: Too few bytes received"); + Firmata.sendString("I2C: Too many bytes received"); + } else if (numBytes > Wire.available()) { + Firmata.sendString("I2C: Too few bytes received"); } i2cRxData[0] = address; i2cRxData[1] = theRegister; for (int i = 0; i < numBytes && Wire.available(); i++) { -#if ARDUINO >= 100 - i2cRxData[2 + i] = Wire.read(); -#else - i2cRxData[2 + i] = Wire.receive(); -#endif + i2cRxData[2 + i] = wireRead(); } // send slave address, register and received bytes @@ -228,6 +238,9 @@ void checkDigitalInputs(void) */ void setPinModeCallback(byte pin, int mode) { + if (pinConfig[pin] == IGNORE) + return; + if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) { // disable i2c so pins can be used for other functions // the following if statements should reconfigure the pins properly @@ -244,8 +257,7 @@ void setPinModeCallback(byte pin, int mode) if (IS_PIN_DIGITAL(pin)) { if (mode == INPUT) { portConfigInputs[pin / 8] |= (1 << (pin & 7)); - } - else { + } else { portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); } } @@ -254,7 +266,7 @@ void setPinModeCallback(byte pin, int mode) case ANALOG: if (IS_PIN_ANALOG(pin)) { if (IS_PIN_DIGITAL(pin)) { - pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups } pinConfig[pin] = ANALOG; @@ -262,7 +274,7 @@ void setPinModeCallback(byte pin, int mode) break; case INPUT: if (IS_PIN_DIGITAL(pin)) { - pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups pinConfig[pin] = INPUT; } @@ -417,11 +429,7 @@ void sysexCallback(byte command, byte argc, byte *argv) Wire.beginTransmission(slaveAddress); for (byte i = 2; i < argc; i += 2) { data = argv[i] + (argv[i + 1] << 7); -#if ARDUINO >= 100 - Wire.write(data); -#else - Wire.send(data); -#endif + wireWrite(data); } Wire.endTransmission(); delayMicroseconds(70); @@ -466,8 +474,7 @@ void sysexCallback(byte command, byte argc, byte *argv) // read continuous reporting for that device if (queryIndex <= 0) { queryIndex = -1; - } - else { + } else { // if read continuous mode is enabled for multiple devices, // determine which device to stop reading and remove it's data from // the array, shifiting other array data to fill the space @@ -526,8 +533,7 @@ void sysexCallback(byte command, byte argc, byte *argv) if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { samplingInterval = MINIMUM_SAMPLING_INTERVAL; } - } - else { + } else { //Firmata.sendString("Not enough data"); } break; @@ -551,11 +557,11 @@ void sysexCallback(byte command, byte argc, byte *argv) } if (IS_PIN_ANALOG(pin)) { Firmata.write(ANALOG); - Firmata.write(10); + Firmata.write(10); // 10 = 10-bit resolution } if (IS_PIN_PWM(pin)) { Firmata.write(PWM); - Firmata.write(8); + Firmata.write(8); // 8 = 8-bit resolution } if (IS_PIN_DIGITAL(pin)) { Firmata.write(SERVO); @@ -563,7 +569,7 @@ void sysexCallback(byte command, byte argc, byte *argv) } if (IS_PIN_I2C(pin)) { Firmata.write(I2C); - Firmata.write(1); // to do: determine appropriate value + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA } Firmata.write(127); } @@ -609,7 +615,6 @@ void enableI2CPins() isI2CEnabled = true; - // is there enough time before the first I2C request to call this here? Wire.begin(); } @@ -627,14 +632,16 @@ void disableI2CPins() { void systemResetCallback() { isResetting = true; + // initialize a defalt state // TODO: option to load config from EEPROM instead of default + if (isI2CEnabled) { disableI2CPins(); } for (byte i = 0; i < TOTAL_PORTS; i++) { - reportPINs[i] = false; // by default, reporting off + reportPINs[i] = false; // by default, reporting off portConfigInputs[i] = 0; // until activated previousPINs[i] = 0; } @@ -645,8 +652,7 @@ void systemResetCallback() if (IS_PIN_ANALOG(i)) { // turns off pullup, configures everything setPinModeCallback(i, ANALOG); - } - else { + } else { // sets the output to 0, configures portConfigInputs setPinModeCallback(i, OUTPUT); } @@ -663,11 +669,11 @@ void systemResetCallback() * since once in the loop(), this firmware will only send on change */ /* TODO: this can never execute, since no pins default to digital input - but it will be needed when/if we support EEPROM stored config - for (byte i=0; i < TOTAL_PORTS; i++) { - outputPort(i, readPort(i, portConfigInputs[i]), true); - } - */ + but it will be needed when/if we support EEPROM stored config + for (byte i=0; i < TOTAL_PORTS; i++) { + outputPort(i, readPort(i, portConfigInputs[i]), true); + } + */ isResetting = false; } @@ -710,14 +716,12 @@ void loop() * FTDI buffer using Serial.print() */ checkDigitalInputs(); - /* SERIALREAD - processing incoming messagse as soon as possible, while still + /* STREAMREAD - processing incoming messagse as soon as possible, while still * checking digital inputs. */ while (Firmata.available()) Firmata.processInput(); - /* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over - * 60 bytes. use a timer to sending an event character every 4 ms to - * trigger the buffer to dump. */ + // TODO - ensure that Stream buffer doesn't go over 60 bytes currentMillis = millis(); if (currentMillis - previousMillis > samplingInterval) { @@ -739,4 +743,3 @@ void loop() } } } - diff --git a/extras/revisions.txt b/extras/revisions.txt index 66f77da3..9a933d24 100644 --- a/extras/revisions.txt +++ b/extras/revisions.txt @@ -1,3 +1,13 @@ +FIRMATA 2.4.3 - Apr 11, 2015 + +[core library] +* Added debug macros (utility/firmataDebug.h) +* Added Norbert Truchsess' EthernetClientStream lib from the configurable branch + +[examples] +* Added StandardFirmataEthernet to enable Firmata over Ethernet +* Minor updates to StandardFirmata and StandardFirmataYun + FIRMATA 2.4.2 - Mar 16, 2015 [core library] diff --git a/library.properties b/library.properties index d9205c12..fd9eea05 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Firmata -version=2.4.2 +version=2.4.3 author=Firmata Developers maintainer=https://github.com/firmata/arduino sentence=Enables the communication with computer apps using a standard serial protocol. For all Arduino boards. diff --git a/readme.md b/readme.md index a7388662..22e0c171 100644 --- a/readme.md +++ b/readme.md @@ -58,7 +58,7 @@ Most of the time you will be interacting with arduino with a client library on t 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. ##Updating Firmata in the Arduino IDE -The version of firmata in the Arduino IDE contains an outdated version of Firmata. To update Firmata, download the latest [release](https://github.com/firmata/arduino/releases/tag/v2.4.2) (for Arduino 1.0.x or Arduino 1.5.6 or higher) and replace the existing Firmata folder in your Arduino application. See the instructions below for your platform. +The version of firmata in the Arduino IDE contains an outdated version of Firmata. To update Firmata, download the latest [release](https://github.com/firmata/arduino/releases/tag/v2.4.3) (for Arduino 1.0.x or Arduino 1.5.6 or higher) and replace the existing Firmata folder in your Arduino application. See the instructions below for your platform. *Note that Arduino 1.5.0 - 1.5.5 are not supported. Please use Arduino 1.5.6 or higher (or Arduino 1.0.5 or 1.0.6).* @@ -69,8 +69,8 @@ The Firmata library is contained within the Arduino package. 1. Navigate to the Arduino application 2. Right click on the application icon and select `Show Package Contents` 3. Navigate to: `/Contents/Resources/Java/libraries/` and replace the existing -`Firmata` folder with latest [Firmata release](https://github.com/firmata/arduino/releases/tag/v2.4.2) (note there is a different download -for Arduino 1.0.x vs 1.5.x) +`Firmata` folder with latest [Firmata release](https://github.com/firmata/arduino/releases/tag/v2.4.3) (note there is a different download +for Arduino 1.0.x vs 1.6.x) 4. Restart the Arduino application and the latest version of Firmata will be available. If you are using the Java 7 version of Arduino 1.5.7 or higher, the file path @@ -79,8 +79,8 @@ will differ slightly: `Contents/Java/libraries/Firmata` (no Resources directory) ###Windows: 1. Navigate to `c:/Program\ Files/arduino-1.x/libraries/` and replace the existing -`Firmata` folder with the latest [Firmata release](https://github.com/firmata/arduino/releases/tag/v2.4.2) (note there is a different download -for Arduino 1.0.x vs 1.5.x). +`Firmata` folder with the latest [Firmata release](https://github.com/firmata/arduino/releases/tag/v2.4.3) (note there is a different download +for Arduino 1.0.x vs 1.6.x). 2. Restart the Arduino application and the latest version of Firmata will be available. *Update the path and arduino version as necessary* @@ -88,8 +88,8 @@ for Arduino 1.0.x vs 1.5.x). ###Linux: 1. Navigate to `~/arduino-1.x/libraries/` and replace the existing -`Firmata` folder with the latest [Firmata release](https://github.com/firmata/arduino/releases/tag/v2.4.2) (note there is a different download -for Arduino 1.0.x vs 1.5.x). +`Firmata` folder with the latest [Firmata release](https://github.com/firmata/arduino/releases/tag/v2.4.3) (note there is a different download +for Arduino 1.0.x vs 1.6.x). 2. Restart the Arduino application and the latest version of Firmata will be available. *Update the path and arduino version as necessary* @@ -97,7 +97,7 @@ for Arduino 1.0.x vs 1.5.x). ###Using the Source code rather than release archive Clone this repo directly into the core Arduino libraries directory. If you are using -Arduino 1.5.x, the repo directory structure will not match the Arduino +Arduino 1.5.x or 1.6.x, the repo directory structure will not match the Arduino library format, however it should still compile as long as you are using Arduino 1.5.7 or higher. @@ -111,7 +111,7 @@ $ git clone git@github.com:firmata/arduino.git /Applications/Arduino.app/Content *Update paths if you're using Windows or Linux* -To generate properly formatted versions of Firmata (for Arduino 1.0.x and Arduino 1.5.x), run the +To generate properly formatted versions of Firmata (for Arduino 1.0.x and Arduino 1.6.x), run the `release.sh` script. diff --git a/release.sh b/release.sh index 9d5cc146..49c43802 100644 --- a/release.sh +++ b/release.sh @@ -6,6 +6,7 @@ mkdir -p temp/Firmata cp -r examples temp/Firmata cp -r extras temp/Firmata +cp -r utility temp/Firmata cp Boards.h temp/Firmata cp Firmata.cpp temp/Firmata cp Firmata.h temp/Firmata @@ -15,7 +16,7 @@ cd temp find . -name "*.DS_Store" -type f -delete zip -r Firmata.zip ./Firmata/ cd .. -mv ./temp/Firmata.zip Firmata-2.4.2.zip +mv ./temp/Firmata.zip Firmata-2.4.3.zip #package for Arduino 1.6.x cp library.properties temp/Firmata @@ -25,9 +26,10 @@ mkdir src mv Boards.h ./src/ mv Firmata.cpp ./src/ mv Firmata.h ./src/ +mv utility ./src/ cd .. find . -name "*.DS_Store" -type f -delete zip -r Firmata.zip ./Firmata/ cd .. -mv ./temp/Firmata.zip Arduino-1.6.x-Firmata-2.4.2.zip +mv ./temp/Firmata.zip Arduino-1.6.x-Firmata-2.4.3.zip rm -r ./temp diff --git a/utility/EthernetClientStream.cpp b/utility/EthernetClientStream.cpp new file mode 100644 index 00000000..81ccf839 --- /dev/null +++ b/utility/EthernetClientStream.cpp @@ -0,0 +1,108 @@ +/* + EthernetClientStream.cpp + An Arduino-Stream that wraps an instance of Client reconnecting to + the remote-ip in a transparent way. A disconnected client may be + recognized by the returnvalues -1 from calls to peek or read and + a 0 from calls to write. + + Copyright (C) 2013 Norbert Truchsess. 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. + + formatted using the GNU C formatting and indenting + */ + +#include "EthernetClientStream.h" +#include + +//#define SERIAL_DEBUG +#include "firmataDebug.h" + +#define MILLIS_RECONNECT 5000 + +EthernetClientStream::EthernetClientStream(Client &client, IPAddress localip, IPAddress ip, const char* host, uint16_t port) +: ip(ip), + host(host), + port(port), + connected(false), + client(client), + localip(localip) +{ +} + +int +EthernetClientStream::available() +{ + return maintain() ? client.available() : 0; +} + +int +EthernetClientStream::read() +{ + return maintain() ? client.read() : -1; +} + +int +EthernetClientStream::peek() +{ + return maintain() ? client.peek() : -1; +} + +void EthernetClientStream::flush() +{ + if (maintain()) + client.flush(); +} + +size_t +EthernetClientStream::write(uint8_t c) +{ + return maintain() ? client.write(c) : 0; +} + +void +EthernetClientStream::maintain(IPAddress localip) +{ + if (this->localip!=localip) + { + this->localip = localip; + if (connected) + stop(); + } +} + +void +EthernetClientStream::stop() +{ + client.stop(); + connected = false; + time_connect = millis(); +} + +bool +EthernetClientStream::maintain() +{ + if (client && client.connected()) + return true; + + if (connected) + { + stop(); + } + else if (millis()-time_connect >= MILLIS_RECONNECT) + { + connected = host ? client.connect(host,port) : client.connect(ip,port); + if (!connected) { + time_connect = millis(); + DEBUG_PRINTLN("connection failed"); + } else { + DEBUG_PRINTLN("connected"); + } + } + return connected; +} diff --git a/utility/EthernetClientStream.h b/utility/EthernetClientStream.h new file mode 100644 index 00000000..c0d34e54 --- /dev/null +++ b/utility/EthernetClientStream.h @@ -0,0 +1,52 @@ +/* + EthernetClientStream.h + An Arduino-Stream that wraps an instance of Client reconnecting to + the remote-ip in a transparent way. A disconnected client may be + recognized by the returnvalues -1 from calls to peek or read and + a 0 from calls to write. + + Copyright (C) 2013 Norbert Truchsess. 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. + + formatted using the GNU C formatting and indenting + */ + +#ifndef ETHERNETCLIENTSTREAM_H +#define ETHERNETCLIENTSTREAM_H + +#include +#include +#include +#include +#include + +class EthernetClientStream : public Stream +{ +public: + EthernetClientStream(Client &client, IPAddress localip, IPAddress ip, const char* host, uint16_t port); + int available(); + int read(); + int peek(); + void flush(); + size_t write(uint8_t); + void maintain(IPAddress localip); + +private: + IPAddress localip; + IPAddress ip; + const char* host; + uint16_t port; + Client &client; + bool connected; + uint32_t time_connect; + bool maintain(); + void stop(); +}; + +#endif diff --git a/utility/firmataDebug.h b/utility/firmataDebug.h new file mode 100644 index 00000000..6e364b0c --- /dev/null +++ b/utility/firmataDebug.h @@ -0,0 +1,14 @@ +#ifndef FIRMATA_DEBUG_H +#define FIRMATA_DEBUG_H + +#ifdef SERIAL_DEBUG + #define DEBUG_BEGIN(baud) Serial.begin(baud); while(!Serial) {;} + #define DEBUG_PRINTLN(x) Serial.println (x) + #define DEBUG_PRINT(x) Serial.print (x) +#else + #define DEBUG_BEGIN(baud) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINT(x) +#endif + +#endif /* FIRMATA_DEBUG_H */