Updating to Firmata-2.1beta1 (rev 23).

This commit is contained in:
David A. Mellis 2009-07-14 21:32:55 +00:00
parent 17dd739777
commit 3f5220e454
10 changed files with 683 additions and 369 deletions

View File

@ -29,18 +29,18 @@ extern "C" {
void sendValueAsTwo7bitBytes(int value)
{
Serial.print(value & B01111111, BYTE); // LSB
Serial.print(value >> 7 & B01111111, BYTE); // MSB
Serial.print(value & B01111111, BYTE); // LSB
Serial.print(value >> 7 & B01111111, BYTE); // MSB
}
void startSysex(void)
{
Serial.print(START_SYSEX, BYTE);
Serial.print(START_SYSEX, BYTE);
}
void endSysex(void)
{
Serial.print(END_SYSEX, BYTE);
Serial.print(END_SYSEX, BYTE);
}
//******************************************************************************
@ -49,8 +49,8 @@ void endSysex(void)
FirmataClass::FirmataClass(void)
{
firmwareVersionCount = 0;
systemReset();
firmwareVersionCount = 0;
systemReset();
}
//******************************************************************************
@ -60,83 +60,83 @@ FirmataClass::FirmataClass(void)
/* begin method for overriding default serial bitrate */
void FirmataClass::begin(void)
{
Serial.begin(115200);
blinkVersion();
delay(300);
printVersion();
Serial.begin(57600);
blinkVersion();
delay(300);
printVersion();
}
/* begin method for overriding default serial bitrate */
void FirmataClass::begin(long speed)
{
blinkVersion();
blinkVersion();
#if defined(__AVR_ATmega128__) // Wiring
Serial.begin((uint32_t)speed);
Serial.begin((uint32_t)speed);
#else
Serial.begin(speed);
Serial.begin(speed);
#endif
delay(300);
printVersion();
printFirmwareVersion();
delay(300);
printVersion();
printFirmwareVersion();
}
// output the protocol version message to the serial port
void FirmataClass::printVersion(void) {
Serial.print(REPORT_VERSION, BYTE);
Serial.print(FIRMATA_MAJOR_VERSION, BYTE);
Serial.print(FIRMATA_MINOR_VERSION, BYTE);
Serial.print(REPORT_VERSION, BYTE);
Serial.print(FIRMATA_MAJOR_VERSION, BYTE);
Serial.print(FIRMATA_MINOR_VERSION, BYTE);
}
void FirmataClass::blinkVersion(void)
{
// flash the pin with the protocol version
pinMode(VERSION_BLINK_PIN,OUTPUT);
pin13strobe(FIRMATA_MAJOR_VERSION, 200, 400);
delay(300);
pin13strobe(2,1,4); // separator, a quick burst
delay(300);
pin13strobe(FIRMATA_MINOR_VERSION, 200, 400);
// flash the pin with the protocol version
pinMode(VERSION_BLINK_PIN,OUTPUT);
pin13strobe(FIRMATA_MAJOR_VERSION, 200, 400);
delay(300);
pin13strobe(2,1,4); // separator, a quick burst
delay(300);
pin13strobe(FIRMATA_MINOR_VERSION, 200, 400);
}
void FirmataClass::printFirmwareVersion(void)
{
byte i;
byte i;
if(firmwareVersionCount) { // make sure that the name has been set before reporting
startSysex();
Serial.print(REPORT_FIRMWARE, BYTE);
Serial.print(firmwareVersionVector[0]); // major version number
Serial.print(firmwareVersionVector[1]); // minor version number
for(i=2; i<firmwareVersionCount; ++i) {
sendValueAsTwo7bitBytes(firmwareVersionVector[i]);
}
endSysex();
if(firmwareVersionCount) { // make sure that the name has been set before reporting
startSysex();
Serial.print(REPORT_FIRMWARE, BYTE);
Serial.print(firmwareVersionVector[0]); // major version number
Serial.print(firmwareVersionVector[1]); // minor version number
for(i=2; i<firmwareVersionCount; ++i) {
sendValueAsTwo7bitBytes(firmwareVersionVector[i]);
}
endSysex();
}
}
void FirmataClass::setFirmwareNameAndVersion(const char *name, byte major, byte minor)
{
const char *filename;
char *extension;
const char *filename;
char *extension;
// parse out ".cpp" and "applet/" that comes from using __FILE__
extension = strstr(name, ".cpp");
filename = strrchr(name, '/') + 1; //points to slash, +1 gets to start of filename
// add two bytes for version numbers
if(extension && filename) {
firmwareVersionCount = extension - filename + 2;
} else {
firmwareVersionCount = strlen(name) + 2;
filename = name;
}
firmwareVersionVector = (byte *) malloc(firmwareVersionCount);
firmwareVersionVector[firmwareVersionCount] = 0;
firmwareVersionVector[0] = major;
firmwareVersionVector[1] = minor;
strncpy((char*)firmwareVersionVector + 2, filename, firmwareVersionCount - 2);
// alas, no snprintf on Arduino
// snprintf(firmwareVersionVector, MAX_DATA_BYTES, "%c%c%s",
// (char)major, (char)minor, firmwareVersionVector);
// parse out ".cpp" and "applet/" that comes from using __FILE__
extension = strstr(name, ".cpp");
filename = strrchr(name, '/') + 1; //points to slash, +1 gets to start of filename
// add two bytes for version numbers
if(extension && filename) {
firmwareVersionCount = extension - filename + 2;
} else {
firmwareVersionCount = strlen(name) + 2;
filename = name;
}
firmwareVersionVector = (byte *) malloc(firmwareVersionCount);
firmwareVersionVector[firmwareVersionCount] = 0;
firmwareVersionVector[0] = major;
firmwareVersionVector[1] = minor;
strncpy((char*)firmwareVersionVector + 2, filename, firmwareVersionCount - 2);
// alas, no snprintf on Arduino
// snprintf(firmwareVersionVector, MAX_DATA_BYTES, "%c%c%s",
// (char)major, (char)minor, firmwareVersionVector);
}
//------------------------------------------------------------------------------
@ -144,123 +144,123 @@ void FirmataClass::setFirmwareNameAndVersion(const char *name, byte major, byte
int FirmataClass::available(void)
{
return Serial.available();
return Serial.available();
}
void FirmataClass::processSysexMessage(void)
{
switch(storedInputData[0]) { //first byte in buffer is command
case REPORT_FIRMWARE:
printFirmwareVersion();
break;
case FIRMATA_STRING:
if(currentStringCallback) {
byte bufferLength = (sysexBytesRead - 1) / 2;
char *buffer = (char*)malloc(bufferLength * sizeof(char));
byte i = 1;
byte j = 0;
while(j < bufferLength) {
buffer[j] = (char)storedInputData[i];
i++;
buffer[j] += (char)(storedInputData[i] << 7);
i++;
j++;
}
(*currentStringCallback)(buffer);
}
break;
default:
if(currentSysexCallback)
(*currentSysexCallback)(storedInputData[0], sysexBytesRead - 1, storedInputData + 1);
switch(storedInputData[0]) { //first byte in buffer is command
case REPORT_FIRMWARE:
printFirmwareVersion();
break;
case STRING_DATA:
if(currentStringCallback) {
byte bufferLength = (sysexBytesRead - 1) / 2;
char *buffer = (char*)malloc(bufferLength * sizeof(char));
byte i = 1;
byte j = 0;
while(j < bufferLength) {
buffer[j] = (char)storedInputData[i];
i++;
buffer[j] += (char)(storedInputData[i] << 7);
i++;
j++;
}
(*currentStringCallback)(buffer);
}
break;
default:
if(currentSysexCallback)
(*currentSysexCallback)(storedInputData[0], sysexBytesRead - 1, storedInputData + 1);
}
}
void FirmataClass::processInput(void)
{
int inputData = Serial.read(); // this is 'int' to handle -1 when no data
int command;
int inputData = Serial.read(); // this is 'int' to handle -1 when no data
int command;
// TODO make sure it handles -1 properly
// TODO make sure it handles -1 properly
if (parsingSysex) {
if(inputData == END_SYSEX) {
//stop sysex byte
parsingSysex = false;
//fire off handler function
processSysexMessage();
} else {
//normal data byte - add to buffer
storedInputData[sysexBytesRead] = inputData;
sysexBytesRead++;
}
} else if( (waitForData > 0) && (inputData < 128) ) {
waitForData--;
storedInputData[waitForData] = inputData;
if( (waitForData==0) && executeMultiByteCommand ) { // got the whole message
switch(executeMultiByteCommand) {
case ANALOG_MESSAGE:
if(currentAnalogCallback) {
(*currentAnalogCallback)(multiByteChannel,
(storedInputData[0] << 7)
+ storedInputData[1]);
}
break;
case DIGITAL_MESSAGE:
if(currentDigitalCallback) {
(*currentDigitalCallback)(multiByteChannel,
(storedInputData[0] << 7)
+ storedInputData[1]);
}
break;
case SET_PIN_MODE:
if(currentPinModeCallback)
(*currentPinModeCallback)(storedInputData[1], storedInputData[0]);
break;
case REPORT_ANALOG:
if(currentReportAnalogCallback)
(*currentReportAnalogCallback)(multiByteChannel,storedInputData[0]);
break;
case REPORT_DIGITAL:
if(currentReportDigitalCallback)
(*currentReportDigitalCallback)(multiByteChannel,storedInputData[0]);
break;
}
executeMultiByteCommand = 0;
}
if (parsingSysex) {
if(inputData == END_SYSEX) {
//stop sysex byte
parsingSysex = false;
//fire off handler function
processSysexMessage();
} else {
// remove channel info from command byte if less than 0xF0
if(inputData < 0xF0) {
command = inputData & 0xF0;
multiByteChannel = inputData & 0x0F;
} else {
command = inputData;
// commands in the 0xF* range don't use channel data
}
switch (command) {
case ANALOG_MESSAGE:
case DIGITAL_MESSAGE:
case SET_PIN_MODE:
waitForData = 2; // two data bytes needed
executeMultiByteCommand = command;
break;
case REPORT_ANALOG:
case REPORT_DIGITAL:
waitForData = 1; // two data bytes needed
executeMultiByteCommand = command;
break;
case START_SYSEX:
parsingSysex = true;
sysexBytesRead = 0;
break;
case SYSTEM_RESET:
systemReset();
break;
case REPORT_VERSION:
Firmata.printVersion();
break;
}
//normal data byte - add to buffer
storedInputData[sysexBytesRead] = inputData;
sysexBytesRead++;
}
} else if( (waitForData > 0) && (inputData < 128) ) {
waitForData--;
storedInputData[waitForData] = inputData;
if( (waitForData==0) && executeMultiByteCommand ) { // got the whole message
switch(executeMultiByteCommand) {
case ANALOG_MESSAGE:
if(currentAnalogCallback) {
(*currentAnalogCallback)(multiByteChannel,
(storedInputData[0] << 7)
+ storedInputData[1]);
}
break;
case DIGITAL_MESSAGE:
if(currentDigitalCallback) {
(*currentDigitalCallback)(multiByteChannel,
(storedInputData[0] << 7)
+ storedInputData[1]);
}
break;
case SET_PIN_MODE:
if(currentPinModeCallback)
(*currentPinModeCallback)(storedInputData[1], storedInputData[0]);
break;
case REPORT_ANALOG:
if(currentReportAnalogCallback)
(*currentReportAnalogCallback)(multiByteChannel,storedInputData[0]);
break;
case REPORT_DIGITAL:
if(currentReportDigitalCallback)
(*currentReportDigitalCallback)(multiByteChannel,storedInputData[0]);
break;
}
executeMultiByteCommand = 0;
}
} else {
// remove channel info from command byte if less than 0xF0
if(inputData < 0xF0) {
command = inputData & 0xF0;
multiByteChannel = inputData & 0x0F;
} else {
command = inputData;
// commands in the 0xF* range don't use channel data
}
switch (command) {
case ANALOG_MESSAGE:
case DIGITAL_MESSAGE:
case SET_PIN_MODE:
waitForData = 2; // two data bytes needed
executeMultiByteCommand = command;
break;
case REPORT_ANALOG:
case REPORT_DIGITAL:
waitForData = 1; // two data bytes needed
executeMultiByteCommand = command;
break;
case START_SYSEX:
parsingSysex = true;
sysexBytesRead = 0;
break;
case SYSTEM_RESET:
systemReset();
break;
case REPORT_VERSION:
Firmata.printVersion();
break;
}
}
}
//------------------------------------------------------------------------------
@ -269,31 +269,31 @@ void FirmataClass::processInput(void)
// send an analog message
void FirmataClass::sendAnalog(byte pin, int value)
{
// pin can only be 0-15, so chop higher bits
Serial.print(ANALOG_MESSAGE | (pin & 0xF), BYTE);
sendValueAsTwo7bitBytes(value);
// pin can only be 0-15, so chop higher bits
Serial.print(ANALOG_MESSAGE | (pin & 0xF), BYTE);
sendValueAsTwo7bitBytes(value);
}
// send a single digital pin in a digital message
void FirmataClass::sendDigital(byte pin, int value)
{
/* TODO add single pin digital messages to the protocol, this needs to
* track the last digital data sent so that it can be sure to change just
* one bit in the packet. This is complicated by the fact that the
* numbering of the pins will probably differ on Arduino, Wiring, and
* other boards. The DIGITAL_MESSAGE sends 14 bits at a time, but it is
* probably easier to send 8 bit ports for any board with more than 14
* digital pins.
*/
/* TODO add single pin digital messages to the protocol, this needs to
* track the last digital data sent so that it can be sure to change just
* one bit in the packet. This is complicated by the fact that the
* numbering of the pins will probably differ on Arduino, Wiring, and
* other boards. The DIGITAL_MESSAGE sends 14 bits at a time, but it is
* probably easier to send 8 bit ports for any board with more than 14
* digital pins.
*/
// TODO: the digital message should not be sent on the serial port every
// time sendDigital() is called. Instead, it should add it to an int
// which will be sent on a schedule. If a pin changes more than once
// before the digital message is sent on the serial port, it should send a
// digital message for each change.
// TODO: the digital message should not be sent on the serial port every
// time sendDigital() is called. Instead, it should add it to an int
// which will be sent on a schedule. If a pin changes more than once
// before the digital message is sent on the serial port, it should send a
// digital message for each change.
// if(value == 0)
// sendDigitalPortPair();
// if(value == 0)
// sendDigitalPortPair();
}
@ -301,33 +301,33 @@ void FirmataClass::sendDigital(byte pin, int value)
// send an 8-bit port in a single digital message (protocol v2)
void FirmataClass::sendDigitalPort(byte portNumber, int portData)
{
Serial.print(DIGITAL_MESSAGE | (portNumber & 0xF),BYTE);
Serial.print(portData % 128, BYTE); // Tx bits 0-6
Serial.print(portData >> 7, BYTE); // Tx bits 7-13
Serial.print(DIGITAL_MESSAGE | (portNumber & 0xF),BYTE);
Serial.print(portData % 128, BYTE); // Tx bits 0-6
Serial.print(portData >> 7, BYTE); // Tx bits 7-13
}
void FirmataClass::sendSysex(byte command, byte bytec, byte* bytev)
{
byte i;
startSysex();
Serial.print(command, BYTE);
for(i=0; i<bytec; i++) {
sendValueAsTwo7bitBytes(bytev[i]);
}
endSysex();
byte i;
startSysex();
Serial.print(command, BYTE);
for(i=0; i<bytec; i++) {
sendValueAsTwo7bitBytes(bytev[i]);
}
endSysex();
}
void FirmataClass::sendString(byte command, const char* string)
{
sendSysex(command, strlen(string), (byte *)string);
sendSysex(command, strlen(string), (byte *)string);
}
// send a string as the protocol string type
void FirmataClass::sendString(const char* string)
{
sendString(FIRMATA_STRING, string);
sendString(STRING_DATA, string);
}
@ -336,43 +336,43 @@ void FirmataClass::sendString(const char* string)
// generic callbacks
void FirmataClass::attach(byte command, callbackFunction newFunction)
{
switch(command) {
case ANALOG_MESSAGE: currentAnalogCallback = newFunction; break;
case DIGITAL_MESSAGE: currentDigitalCallback = newFunction; break;
case REPORT_ANALOG: currentReportAnalogCallback = newFunction; break;
case REPORT_DIGITAL: currentReportDigitalCallback = newFunction; break;
case SET_PIN_MODE: currentPinModeCallback = newFunction; break;
}
switch(command) {
case ANALOG_MESSAGE: currentAnalogCallback = newFunction; break;
case DIGITAL_MESSAGE: currentDigitalCallback = newFunction; break;
case REPORT_ANALOG: currentReportAnalogCallback = newFunction; break;
case REPORT_DIGITAL: currentReportDigitalCallback = newFunction; break;
case SET_PIN_MODE: currentPinModeCallback = newFunction; break;
}
}
void FirmataClass::attach(byte command, systemResetCallbackFunction newFunction)
{
switch(command) {
case SYSTEM_RESET: currentSystemResetCallback = newFunction; break;
}
switch(command) {
case SYSTEM_RESET: currentSystemResetCallback = newFunction; break;
}
}
void FirmataClass::attach(byte command, stringCallbackFunction newFunction)
{
switch(command) {
case FIRMATA_STRING: currentStringCallback = newFunction; break;
}
switch(command) {
case STRING_DATA: currentStringCallback = newFunction; break;
}
}
void FirmataClass::attach(byte command, sysexCallbackFunction newFunction)
{
currentSysexCallback = newFunction;
currentSysexCallback = newFunction;
}
void FirmataClass::detach(byte command)
{
switch(command) {
case SYSTEM_RESET: currentSystemResetCallback = NULL; break;
case FIRMATA_STRING: currentStringCallback = NULL; break;
case START_SYSEX: currentSysexCallback = NULL; break;
default:
attach(command, (callbackFunction)NULL);
}
switch(command) {
case SYSTEM_RESET: currentSystemResetCallback = NULL; break;
case STRING_DATA: currentStringCallback = NULL; break;
case START_SYSEX: currentSysexCallback = NULL; break;
default:
attach(command, (callbackFunction)NULL);
}
}
// sysex callbacks
@ -402,24 +402,24 @@ void FirmataClass::detach(byte command)
// resets the system state upon a SYSTEM_RESET message from the host software
void FirmataClass::systemReset(void)
{
byte i;
byte i;
waitForData = 0; // this flag says the next serial input will be data
executeMultiByteCommand = 0; // execute this after getting multi-byte data
multiByteChannel = 0; // channel data for multiByteCommands
waitForData = 0; // this flag says the next serial input will be data
executeMultiByteCommand = 0; // execute this after getting multi-byte data
multiByteChannel = 0; // channel data for multiByteCommands
for(i=0; i<MAX_DATA_BYTES; i++) {
storedInputData[i] = 0;
}
for(i=0; i<MAX_DATA_BYTES; i++) {
storedInputData[i] = 0;
}
parsingSysex = false;
sysexBytesRead = 0;
parsingSysex = false;
sysexBytesRead = 0;
if(currentSystemResetCallback)
(*currentSystemResetCallback)();
if(currentSystemResetCallback)
(*currentSystemResetCallback)();
//flush(); //TODO uncomment when Firmata is a subclass of HardwareSerial
//flush(); //TODO uncomment when Firmata is a subclass of HardwareSerial
}
@ -428,14 +428,14 @@ void FirmataClass::systemReset(void)
// used for flashing the pin for the version number
void FirmataClass::pin13strobe(int count, int onInterval, int offInterval)
{
byte i;
pinMode(VERSION_BLINK_PIN, OUTPUT);
for(i=0; i<count; i++) {
delay(offInterval);
digitalWrite(VERSION_BLINK_PIN, HIGH);
delay(onInterval);
digitalWrite(VERSION_BLINK_PIN, LOW);
}
byte i;
pinMode(VERSION_BLINK_PIN, OUTPUT);
for(i=0; i<count; i++) {
delay(offInterval);
digitalWrite(VERSION_BLINK_PIN, HIGH);
delay(onInterval);
digitalWrite(VERSION_BLINK_PIN, LOW);
}
}

View File

@ -22,7 +22,7 @@
* software can test whether it will be compatible with the currently
* installed firmware. */
#define FIRMATA_MAJOR_VERSION 2 // for non-compatible changes
#define FIRMATA_MINOR_VERSION 0 // for backwards compatible changes
#define FIRMATA_MINOR_VERSION 1 // for backwards compatible changes
#define VERSION_BLINK_PIN 13 // digital pin to blink version on
#define MAX_DATA_BYTES 32 // max number of data bytes in non-Sysex messages
@ -42,12 +42,22 @@
#define END_SYSEX 0xF7 // end a MIDI Sysex message
// extended command set using sysex (0-127/0x00-0x7F)
/* 0x00-0x0F reserved for custom commands */
/* 0x00-0x0F reserved for user-defined commands */
#define SERVO_CONFIG 0x70 // set max angle, minPulse, maxPulse, freq
#define FIRMATA_STRING 0x71 // a string message with 14-bits per char
#define STRING_DATA 0x71 // a string message with 14-bits per char
#define SHIFT_DATA 0x75 // a bitstream to/from a shift register
#define I2C_REQUEST 0x76 // send an I2C read/write request
#define I2C_REPLY 0x77 // a reply to an I2C read request
#define I2C_CONFIG 0x78 // config I2C settings such as delay times and power pins
#define REPORT_FIRMWARE 0x79 // report name and version of the firmware
#define SAMPLING_INTERVAL 0x7A // set the poll rate of the main loop
#define SYSEX_NON_REALTIME 0x7E // MIDI Reserved for non-realtime messages
#define SYSEX_REALTIME 0x7F // MIDI Reserved for realtime messages
// 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
#define SYSEX_I2C_REPLY 0x77 // same as I2C_REPLY
#define SYSEX_SAMPLING_INTERVAL 0x7A // same as SAMPLING_INTERVAL
// pin modes
//#define INPUT 0x00 // defined in wiring.h
@ -55,7 +65,8 @@
#define ANALOG 0x02 // analog pin in analogInput mode
#define PWM 0x03 // digital pin in PWM output mode
#define SERVO 0x04 // digital pin in Servo output mode
#define SHIFT 0x05 // shiftIn/shiftOut mode
#define I2C 0x06 // pin included in I2C setup
extern "C" {
// callback function types
@ -151,6 +162,11 @@ extern FirmataClass Firmata;
#define TOTAL_DIGITAL_PINS 20 // 14 digital + 6 analog
#define TOTAL_PORTS 3 // total number of ports for the board
#define ANALOG_PORT 2 // port# of analog used as digital
#elif defined(__AVR_ATmega1280__)// Arduino Mega
#define TOTAL_ANALOG_PINS 16
#define TOTAL_DIGITAL_PINS 54
#define TOTAL_PORTS 8 // total number of ports for the board
#define ANALOG_PORT 2 // port# of analog used as digital
#elif defined(__AVR_ATmega128__)// Wiring
#define TOTAL_ANALOG_PINS 8
#define TOTAL_DIGITAL_PINS 51

View File

@ -61,7 +61,7 @@ void setup()
servo9.attach(9);
servo10.attach(10);
Firmata.begin();
Firmata.begin(57600);
}
/*==============================================================================

View File

@ -25,9 +25,9 @@ void sysexCallback(byte command, byte argc, byte*argv)
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(FIRMATA_STRING, stringCallback);
Firmata.attach(STRING_DATA, stringCallback);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.begin();
Firmata.begin(57600);
}
void loop()

View File

@ -0,0 +1,220 @@
/*
Copyright (C) 2009 Jeff Hoefs. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. 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.
*/
#include <Wire.h>
#include <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 MAX_QUERIES 8
unsigned long currentMillis; // store the current value from millis()
unsigned long nextExecuteMillis; // for comparison with currentMillis
unsigned int samplingInterval = 32; // default sampling interval is 33ms
unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom()
unsigned int powerPinsEnabled = 0; // use as boolean to prevent enablePowerPins from being called more than once
#define MINIMUM_SAMPLING_INTERVAL 10
#define REGISTER_NOT_SPECIFIED -1
struct i2c_device_info {
byte addr;
byte reg;
byte bytes;
};
i2c_device_info query[MAX_QUERIES];
byte i2cRxData[32];
boolean readingContinuously = false;
byte queryIndex = 0;
void readAndReportData(byte address, int theRegister, byte numBytes)
{
if (theRegister != REGISTER_NOT_SPECIFIED) {
Wire.beginTransmission(address);
Wire.send((byte)theRegister);
Wire.endTransmission();
delayMicroseconds(i2cReadDelayTime); // delay is necessary for some devices such as WiiNunchuck
}
else {
theRegister = 0; // fill the register with a dummy value
}
Wire.requestFrom(address, numBytes);
// check to be sure correct number of bytes were returned by slave
if(numBytes == Wire.available()) {
i2cRxData[0] = address;
i2cRxData[1] = theRegister;
for (int i = 0; i < numBytes; i++) {
i2cRxData[2 + i] = Wire.receive();
}
// send slave address, register and received bytes
Firmata.sendSysex(I2C_REPLY, numBytes + 2, i2cRxData);
}
else {
if(numBytes > Wire.available()) {
Firmata.sendString("I2C Read Error: Too many bytes received");
} else {
Firmata.sendString("I2C Read Error: Too few bytes received");
}
}
}
void sysexCallback(byte command, byte argc, byte *argv)
{
byte mode;
byte slaveAddress;
byte slaveRegister;
byte data;
int delayTime;
if (command == I2C_REQUEST) {
mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
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);
Wire.send(data);
}
Wire.endTransmission();
delayMicroseconds(70); // TODO is this needed?
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
readAndReportData(slaveAddress, (int)slaveRegister, data);
}
else {
// a slave register is NOT specified
data = argv[2] + (argv[3] << 7); // bytes to read
readAndReportData(slaveAddress, (int)REGISTER_NOT_SPECIFIED, data);
}
break;
case I2C_READ_CONTINUOUSLY:
if ((queryIndex + 1) >= MAX_QUERIES) {
// too many queries, just ignore
Firmata.sendString("too many queries");
break;
}
query[queryIndex].addr = slaveAddress;
query[queryIndex].reg = argv[2] + (argv[3] << 7);
query[queryIndex].bytes = argv[4] + (argv[5] << 7);
readingContinuously = true;
queryIndex++;
break;
case I2C_STOP_READING:
readingContinuously = false;
queryIndex = 0;
break;
default:
break;
}
}
else if (command == SAMPLING_INTERVAL) {
samplingInterval = argv[0] + (argv[1] << 7);
if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) {
samplingInterval = MINIMUM_SAMPLING_INTERVAL;
}
samplingInterval -= 1;
Firmata.sendString("sampling interval");
}
else if (command == I2C_CONFIG) {
delayTime = (argv[4] + (argv[5] << 7)); // MSB
delayTime = (delayTime << 8) + (argv[2] + (argv[3] << 7)); // add LSB
if((argv[0] + (argv[1] << 7)) > 0) {
enablePowerPins(PC3, PC2);
}
if(delayTime > 0) {
i2cReadDelayTime = delayTime;
}
if(argc > 6) {
// If you extend I2C_Config, handle your data here
}
}
}
void systemResetCallback()
{
readingContinuously = false;
queryIndex = 0;
}
/* reference: BlinkM_funcs.h by Tod E. Kurt, ThingM, http://thingm.com/ */
// Enables Pins A2 and A3 to be used as GND and Power
// so that I2C devices can be plugged directly
// into Arduino header (pins A2 - A5)
static void enablePowerPins(byte pwrpin, byte gndpin)
{
if(powerPinsEnabled == 0) {
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |= _BV(pwrpin);
powerPinsEnabled = 1;
Firmata.sendString("Power pins enabled");
delay(100);
}
}
void setup()
{
Firmata.setFirmwareVersion(2, 0);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.attach(SYSTEM_RESET, systemResetCallback);
for (int i = 0; i < TOTAL_DIGITAL_PINS; ++i) {
pinMode(i, OUTPUT);
}
/* I2C data is not reliable at higher baud rates, you'll need to change the
baud rate on the host computer as well. To get a firmware running with
minimal effort, you can try using the default baud rate (115200) */
Firmata.begin(38400);
Wire.begin();
}
void loop()
{
while (Firmata.available()) {
Firmata.processInput();
}
currentMillis = millis();
if (currentMillis > nextExecuteMillis) {
nextExecuteMillis = currentMillis + samplingInterval;
for (byte i = 0; i < queryIndex; i++) {
readAndReportData(query[i].addr, query[i].reg, query[i].bytes);
}
}
}

View File

@ -28,7 +28,7 @@ void setup()
servo9.attach(9);
servo10.attach(10);
Firmata.begin();
Firmata.begin(57600);
}
void loop()

View File

@ -16,7 +16,7 @@ void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.begin();
Firmata.begin(57600);
}
void loop()

View File

@ -45,7 +45,7 @@ void setup()
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.begin();
Firmata.begin(57600);
}
void loop()

View File

@ -50,14 +50,20 @@ TARGET := $(shell pwd | sed 's|.*/\(.*\)|\1|')
ARDUINO = /Applications/arduino
ARDUINO_SRC = $(ARDUINO)/hardware/cores/arduino
ARDUINO_LIB_SRC = $(ARDUINO)/hardware/libraries
ARDUINO_TOOLS = $(ARDUINO)/hardware/tools
INCLUDE = -I$(ARDUINO_SRC) -I$(ARDUINO)/hardware/tools/avr/avr/include \
-I$(ARDUINO_LIB_SRC)/EEPROM \
-I$(ARDUINO_LIB_SRC)/Firmata \
-I$(ARDUINO_LIB_SRC)/Matrix \
-I$(ARDUINO_LIB_SRC)/Servo \
-I$(ARDUINO_LIB_SRC)/Wire \
-I$(ARDUINO_LIB_SRC)
SRC = $(wildcard $(ARDUINO_SRC)/*.c)
CXXSRC = applet/$(TARGET).cpp $(ARDUINO_SRC)/HardwareSerial.cpp \
$(ARDUINO_LIB_SRC)/EEPROM/EEPROM.cpp \
$(ARDUINO_LIB_SRC)/Firmata/Firmata.cpp \
$(ARDUINO_LIB_SRC)/Servo/Servo.cpp \
$(ARDUINO_SRC)/Print.cpp \
$(ARDUINO_SRC)/WMath.cpp
HEADERS = $(wildcard $(ARDUINO_SRC)/*.h) $(wildcard $(ARDUINO_LIB_SRC)/*/*.h)
@ -106,12 +112,14 @@ AVRDUDE_FLAGS = -F -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \
-b $(UPLOAD_RATE) -q -V
# Program settings
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
ARDUINO_AVR_BIN = $(ARDUINO_TOOLS)/avr/bin
CC = $(ARDUINO_AVR_BIN)/avr-gcc
CXX = $(ARDUINO_AVR_BIN)/avr-g++
OBJCOPY = $(ARDUINO_AVR_BIN)/avr-objcopy
OBJDUMP = $(ARDUINO_AVR_BIN)/avr-objdump
SIZE = $(ARDUINO_AVR_BIN)/avr-size
NM = $(ARDUINO_AVR_BIN)/avr-nm
#AVRDUDE = $(ARDUINO_AVR_BIN)/avrdude
AVRDUDE = avrdude
REMOVE = rm -f
MV = mv -f
@ -204,7 +212,8 @@ applet/$(TARGET).cpp: $(TARGET).pde
# Link: create ELF output file from object files.
applet/$(TARGET).elf: applet/$(TARGET).cpp $(OBJ)
$(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS)
$(CC) $(ALL_CFLAGS) $(OBJ) -lm --output $@ $(LDFLAGS)
# $(CC) $(ALL_CFLAGS) $(OBJ) $(ARDUINO_TOOLS)/avr/avr/lib/avr5/crtm168.o --output $@ $(LDFLAGS)
pd_close_serial:
echo 'close;' | /Applications/Pd-extended.app/Contents/Resources/bin/pdsend 34567 || true
@ -258,4 +267,7 @@ etags_MINGW:
# etags -a /usr/include/*.h /usr/include/sys/*.h
path:
echo $(PATH)
echo $$PATH

View File

@ -1,21 +1,24 @@
/*
Copyright (C) 2006-2008 Hans-Christoph Steiner. 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
*/
/*
* TODO: add Servo support using setPinMode(pin, SERVO);
* TODO: add Servo support using setPinModeCallback(pin, SERVO);
* TODO: use Program Control to load stored profiles from EEPROM
*/
#include <EEPROM.h>
#include <Firmata.h>
#include <Servo.h>
/*==============================================================================
* GLOBAL VARIABLES
@ -34,37 +37,45 @@ byte portStatus[TOTAL_PORTS];
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long nextExecuteMillis; // for comparison with currentMillis
int samplingInterval = 19; // how often to run the main loop (in ms)
Servo servos[2]; // the servo library can control servos on pins 9 and 10 only
/*==============================================================================
* FUNCTIONS
* FUNCTIONS
*============================================================================*/
void outputPort(byte portNumber, byte portValue)
{
portValue = portValue &~ portStatus[portNumber];
if(previousPINs[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
Firmata.sendDigitalPort(portNumber, portValue);
}
Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
Firmata.sendDigitalPort(portNumber, portValue);
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using Serial.print() */
void checkDigitalInputs(void)
void checkDigitalInputs(void)
{
byte i, tmp;
for(i=0; i < TOTAL_PORTS; i++) {
if(reportPINs[i]) {
switch(i) {
case 0: outputPort(0, PIND &~ B00000011); break; // ignore Rx/Tx 0/1
case 1: outputPort(1, PINB); break;
case ANALOG_PORT: outputPort(ANALOG_PORT, PINC); break;
}
}
byte i, tmp;
for(i=0; i < TOTAL_PORTS; i++) {
if(reportPINs[i]) {
switch(i) {
case 0:
outputPort(0, PIND &~ B00000011); // ignore Rx/Tx 0/1
break;
case 1:
outputPort(1, PINB);
break;
case ANALOG_PORT:
outputPort(ANALOG_PORT, PINC);
break;
}
}
}
}
// -----------------------------------------------------------------------------
@ -72,61 +83,87 @@ void checkDigitalInputs(void)
* two bit-arrays that track Digital I/O and PWM status
*/
void setPinModeCallback(byte pin, int mode) {
byte port = 0;
byte offset = 0;
byte port = 0;
byte offset = 0;
if (pin < 8) {
port = 0;
offset = 0;
} else if (pin < 14) {
port = 1;
offset = 8;
} else if (pin < 22) {
port = 2;
offset = 14;
}
if(pin > 1) { // ignore RxTx (pins 0 and 1)
// TODO: abstract for different boards
if (pin < 8) {
port = 0;
offset = 0;
} else if (pin < 14) {
port = 1;
offset = 8;
} else if (pin < 22) {
port = 2;
offset = 14;
}
if(pin > 1) { // ignore RxTx (pins 0 and 1)
reportAnalogCallback(pin - 14, mode == ANALOG ? 1 : 0); // turn on/off reporting
switch(mode) {
case ANALOG:
digitalWrite(pin, LOW); // disable internal pull-ups and fall thru to 'case INPUT:'
case INPUT:
pinStatus[pin] = mode;
pinMode(pin, INPUT);
portStatus[port] = portStatus[port] &~ (1 << (pin - offset));
break;
case OUTPUT:
digitalWrite(pin, LOW); // disable PWM and fall thru to 'case PWM:'
case PWM:
pinStatus[pin] = mode;
pinMode(pin, OUTPUT);
portStatus[port] = portStatus[port] | (1 << (pin - offset));
break;
case SERVO:
if((pin == 9 || pin == 10))
pinStatus[pin] = mode;
switch(mode) {
case INPUT:
pinMode(pin, INPUT);
portStatus[port] = portStatus[port] &~ (1 << (pin - offset));
break;
case OUTPUT:
digitalWrite(pin, LOW); // disable PWM
case PWM:
pinMode(pin, OUTPUT);
portStatus[port] = portStatus[port] | (1 << (pin - offset));
break;
//case ANALOG: // TODO figure this out
default:
Firmata.sendString("");
}
// TODO: save status to EEPROM here, if changed
else
Firmata.sendString("Servo only on pins 9 and 10");
break;
case I2C:
pinStatus[pin] = mode;
Firmata.sendString("I2C mode not yet supported");
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)
{
setPinModeCallback(pin,PWM);
switch(pinStatus[pin]) {
case SERVO:
if(pin == 9) servos[0].write(value);
if(pin == 10) servos[1].write(value);
break;
case PWM:
analogWrite(pin, value);
break;
}
}
void digitalWriteCallback(byte port, int value)
{
switch(port) {
case 0: // pins 2-7 (don't change Rx/Tx, pins 0 and 1)
// 0xFF03 == B1111111100000011 0x03 == B00000011
PORTD = (value &~ 0xFF03) | (PORTD & 0x03);
break;
case 1: // pins 8-13 (14,15 are disabled for the crystal)
PORTB = (byte)value;
break;
case 2: // analog pins used as digital
PORTC = (byte)value;
break;
}
switch(port) {
case 0: // pins 2-7 (don't change Rx/Tx, pins 0 and 1)
// 0xFF03 == B1111111100000011 0x03 == B00000011
PORTD = (value &~ 0xFF03) | (PORTD & 0x03);
break;
case 1: // pins 8-13 (14,15 are disabled for the crystal)
PORTB = (byte)value;
break;
case 2: // analog pins used as digital
byte pin;
byte pinModeMask;
for(pin=0; pin<8; pin++)
if(pinStatus[pin] == OUTPUT)
pinModeMask += 1 << pin;
PORTC = (byte)value & pinModeMask;
break;
}
}
// -----------------------------------------------------------------------------
@ -136,64 +173,93 @@ void digitalWriteCallback(byte port, int value)
//}
void reportAnalogCallback(byte pin, int value)
{
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << pin);
}
else { // everything but 0 enables reporting of that pin
analogInputsToReport = analogInputsToReport | (1 << pin);
}
// TODO: save status to EEPROM here, if changed
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << pin);
}
else { // everything but 0 enables reporting of that pin
analogInputsToReport = analogInputsToReport | (1 << pin);
setPinModeCallback(pin, ANALOG);
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
reportPINs[port] = (byte)value;
if(port == ANALOG_PORT) // turn off analog reporting when used as digital
analogInputsToReport = 0;
reportPINs[port] = (byte)value;
if(port == ANALOG_PORT) // turn off analog reporting when used as digital
analogInputsToReport = 0;
}
/*==============================================================================
* SYSEX-BASED commands
*============================================================================*/
void sysexCallback(byte command, byte argc, byte *argv)
{
switch(command) {
case SERVO_CONFIG:
if(argc > 4) {
// these vars are here for clarity, they'll optimized away by the compiler
byte pin = argv[0] - 9; // servos are pins 9 and 10, so offset for array
int minPulse = argv[1] + (argv[2] << 7);
int maxPulse = argv[3] + (argv[4] << 7);
servos[pin].attach(argv[0], minPulse, maxPulse);
// TODO does the Servo have to be detach()ed before reconfiguring?
setPinModeCallback(pin, SERVO);
}
break;
case SAMPLING_INTERVAL:
if (argc > 1)
samplingInterval = argv[0] + (argv[1] << 7);
else
Firmata.sendString("Not enough data");
break;
}
}
/*==============================================================================
* SETUP()
*============================================================================*/
void setup()
{
byte i;
byte i;
Firmata.setFirmwareVersion(2, 0);
Firmata.setFirmwareVersion(2, 1);
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(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);
portStatus[0] = B00000011; // ignore Tx/RX pins
portStatus[1] = B11000000; // ignore 14/15 pins
portStatus[2] = B00000000;
portStatus[0] = B00000011; // ignore Tx/RX pins
portStatus[1] = B11000000; // ignore 14/15 pins
portStatus[2] = B00000000;
// for(i=0; i<TOTAL_DIGITAL_PINS; ++i) { // TODO make this work with analogs
for(i=0; i<14; ++i) {
setPinModeCallback(i,OUTPUT);
}
// set all outputs to 0 to make sure internal pull-up resistors are off
PORTB = 0; // pins 8-15
PORTC = 0; // analog port
PORTD = 0; // pins 0-7
for(i=0; i<TOTAL_DIGITAL_PINS; ++i) { // TODO make this work with analogs
setPinModeCallback(i,OUTPUT);
}
// set all outputs to 0 to make sure internal pull-up resistors are off
PORTB = 0; // pins 8-15
PORTC = 0; // analog port
PORTD = 0; // pins 0-7
// TODO rethink the init, perhaps it should report analog on default
for(i=0; i<TOTAL_PORTS; ++i) {
reportPINs[i] = false;
}
// TODO: load state from EEPROM here
// TODO rethink the init, perhaps it should report analog on default
for(i=0; i<TOTAL_PORTS; ++i) {
reportPINs[i] = false;
}
// TODO: load state from EEPROM here
/* send digital inputs here, if enabled, to set the initial state on the
* host computer, since once in the loop(), this firmware will only send
* digital data on change. */
if(reportPINs[0]) outputPort(0, PIND &~ B00000011); // ignore Rx/Tx 0/1
if(reportPINs[1]) outputPort(1, PINB);
if(reportPINs[ANALOG_PORT]) outputPort(ANALOG_PORT, PINC);
/* send digital inputs here, if enabled, to set the initial state on the
* host computer, since once in the loop(), this firmware will only send
* digital data on change. */
if(reportPINs[0]) outputPort(0, PIND &~ B00000011); // ignore Rx/Tx 0/1
if(reportPINs[1]) outputPort(1, PINB);
if(reportPINs[ANALOG_PORT]) outputPort(ANALOG_PORT, PINC);
Firmata.begin();
Firmata.begin(57600);
}
/*==============================================================================
@ -201,26 +267,26 @@ void setup()
*============================================================================*/
void loop()
{
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* FTDI buffer using Serial.print() */
checkDigitalInputs();
currentMillis = millis();
if(currentMillis > nextExecuteMillis) {
nextExecuteMillis = currentMillis + 19; // run this every 20ms
/* SERIALREAD - Serial.read() uses a 128 byte circular buffer, so handle
* all serialReads at once, i.e. empty the buffer */
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. */
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* FTDI buffer using Serial.print() */
checkDigitalInputs();
currentMillis = millis();
if(currentMillis > nextExecuteMillis) {
nextExecuteMillis = currentMillis + samplingInterval;
/* SERIALREAD - Serial.read() uses a 128 byte circular buffer, so handle
* all serialReads at once, i.e. empty the buffer */
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. */
/* ANALOGREAD - right after the event character, do all of the
* analogReads(). These only need to be done every 4ms. */
for(analogPin=0;analogPin<TOTAL_ANALOG_PINS;analogPin++) {
if( analogInputsToReport & (1 << analogPin) ) {
Firmata.sendAnalog(analogPin, analogRead(analogPin));
}
}
/* ANALOGREAD - right after the event character, do all of the
* analogReads(). These only need to be done every 4ms. */
for(analogPin=0;analogPin<TOTAL_ANALOG_PINS;analogPin++) {
if( analogInputsToReport & (1 << analogPin) ) {
Firmata.sendAnalog(analogPin, analogRead(analogPin));
}
}
}
}