initial commit

This commit is contained in:
2018-08-08 08:33:26 +02:00
commit ada8fdbdf9
368 changed files with 65878 additions and 0 deletions

View File

@@ -0,0 +1,509 @@
/*
Firmata.cpp - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Modified for Adafruit_BLE_Uart by Limor Fried/Kevin Townsend for
Adafruit Industries, 2014
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.
*/
//******************************************************************************
//* Includes
//******************************************************************************
#include "Adafruit_BLE_Firmata.h"
extern "C" {
#include <string.h>
#include <stdlib.h>
}
//******************************************************************************
//* Support Functions
//******************************************************************************
void Adafruit_BLE_FirmataClass::sendValueAsTwo7bitBytes(int value)
{
FirmataSerial.write(value & B01111111); // LSB
FirmataSerial.write(value >> 7 & B01111111); // MSB
}
void Adafruit_BLE_FirmataClass::startSysex(void)
{
FirmataSerial.write(START_SYSEX);
}
void Adafruit_BLE_FirmataClass::endSysex(void)
{
FirmataSerial.write(END_SYSEX);
}
//******************************************************************************
//* Constructors
//******************************************************************************
Adafruit_BLE_FirmataClass::Adafruit_BLE_FirmataClass(Stream &s) : FirmataSerial(s)
{
firmwareVersionCount = 0;
systemReset();
}
//******************************************************************************
//* Public Methods
//******************************************************************************
/* begin method for overriding default serial bitrate */
void Adafruit_BLE_FirmataClass::begin(void)
{
printVersion();
printFirmwareVersion();
}
void Adafruit_BLE_FirmataClass::begin(Stream &s)
{
FirmataSerial = s;
systemReset();
printVersion();
printFirmwareVersion();
}
// output the protocol version message to the serial port
void Adafruit_BLE_FirmataClass::printVersion(void) {
FirmataSerial.write(REPORT_VERSION);
FirmataSerial.write(FIRMATA_MAJOR_VERSION);
FirmataSerial.write(FIRMATA_MINOR_VERSION);
}
void Adafruit_BLE_FirmataClass::printFirmwareVersion(void)
{
byte i;
if(firmwareVersionCount) { // make sure that the name has been set before reporting
startSysex();
FirmataSerial.write(REPORT_FIRMWARE);
FirmataSerial.write(firmwareVersionVector[0]); // major version number
FirmataSerial.write(firmwareVersionVector[1]); // minor version number
for(i=2; i<firmwareVersionCount; ++i) {
sendValueAsTwo7bitBytes(firmwareVersionVector[i]);
}
endSysex();
}
}
void Adafruit_BLE_FirmataClass::setFirmwareNameAndVersion(const char *name, byte major, byte minor)
{
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);
}
//------------------------------------------------------------------------------
// Serial Receive Handling
int Adafruit_BLE_FirmataClass::available(void)
{
return FirmataSerial.available();
}
void Adafruit_BLE_FirmataClass::processSysexMessage(void)
{
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);
}
}
int Adafruit_BLE_FirmataClass::processInput(void)
{
int inputData = FirmataSerial.read(); // this is 'int' to handle -1 when no data
int command;
if (inputData == -1) return -1;
//Serial.print(F(" 0x")); Serial.print(inputData, HEX);
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;
#ifdef BLE_DEBUG
Serial.print(F(" 0x")); Serial.print(inputData, HEX);
#endif
if( (waitForData==0) && executeMultiByteCommand ) { // got the whole message
#ifdef BLE_DEBUG
Serial.println();
#endif
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 {
#ifdef BLE_DEBUG
Serial.print(F("\tReceived 0x")); Serial.print(inputData, HEX);
#endif
// 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:
printVersion();
break;
}
}
return inputData;
}
//------------------------------------------------------------------------------
// Serial Send Handling
// send an analog message
void Adafruit_BLE_FirmataClass::sendAnalog(byte pin, int value)
{
// create a three byte buffer
uint8_t sendbuffer[3];
// pin can only be 0-15, so chop higher bits
//FirmataSerial.write(ANALOG_MESSAGE | (pin & 0xF));
sendbuffer[0] = ANALOG_MESSAGE | (pin & 0xF);
//sendValueAsTwo7bitBytes(value);
sendbuffer[1] = value % 128; // Tx bits 0-6
sendbuffer[2] = (value >> 7) &0x7F; // Tx bits 7-13
FirmataSerial.write(sendbuffer, 3);
}
// send a single digital pin in a digital message
void Adafruit_BLE_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: 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();
}
// send 14-bits in a single digital message (protocol v1)
// send an 8-bit port in a single digital message (protocol v2)
void Adafruit_BLE_FirmataClass::sendDigitalPort(byte portNumber, int portData)
{
// create a three byte buffer
uint8_t sendbuffer[3];
sendbuffer[0] = DIGITAL_MESSAGE | (portNumber & 0xF);
sendbuffer[1] = (byte)portData % 128; // Tx bits 0-6
sendbuffer[2] = portData >> 7; // Tx bits 7-13
FirmataSerial.write(sendbuffer, 3);
}
void Adafruit_BLE_FirmataClass::sendSysex(byte command, byte bytec, byte* bytev)
{
byte i;
startSysex();
FirmataSerial.write(command);
for(i=0; i<bytec; i++) {
sendValueAsTwo7bitBytes(bytev[i]);
}
endSysex();
}
void Adafruit_BLE_FirmataClass::sendString(byte command, const char* string)
{
sendSysex(command, strlen(string), (byte *)string);
}
// send a string as the protocol string type
void Adafruit_BLE_FirmataClass::sendString(const char* string)
{
sendString(STRING_DATA, string);
}
// Internal Actions/////////////////////////////////////////////////////////////
// generic callbacks
void Adafruit_BLE_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;
}
}
void Adafruit_BLE_FirmataClass::attach(byte command, systemResetCallbackFunction newFunction)
{
switch(command) {
case SYSTEM_RESET: currentSystemResetCallback = newFunction; break;
}
}
void Adafruit_BLE_FirmataClass::attach(byte command, stringCallbackFunction newFunction)
{
switch(command) {
case STRING_DATA: currentStringCallback = newFunction; break;
}
}
void Adafruit_BLE_FirmataClass::attach(byte command, sysexCallbackFunction newFunction)
{
currentSysexCallback = newFunction;
}
void Adafruit_BLE_FirmataClass::detach(byte command)
{
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
/*
* this is too complicated for analogReceive, but maybe for Sysex?
void Adafruit_BLE_FirmataClass::attachSysex(sysexFunction newFunction)
{
byte i;
byte tmpCount = analogReceiveFunctionCount;
analogReceiveFunction* tmpArray = analogReceiveFunctionArray;
analogReceiveFunctionCount++;
analogReceiveFunctionArray = (analogReceiveFunction*) calloc(analogReceiveFunctionCount, sizeof(analogReceiveFunction));
for(i = 0; i < tmpCount; i++) {
analogReceiveFunctionArray[i] = tmpArray[i];
}
analogReceiveFunctionArray[tmpCount] = newFunction;
free(tmpArray);
}
*/
//******************************************************************************
//* Private Methods
//******************************************************************************/
// resets the system state upon a SYSTEM_RESET message from the host software
void Adafruit_BLE_FirmataClass::systemReset(void)
{
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
for(i=0; i<MAX_DATA_BYTES; i++) {
storedInputData[i] = 0;
}
parsingSysex = false;
sysexBytesRead = 0;
if(currentSystemResetCallback)
(*currentSystemResetCallback)();
//flush(); //TODO uncomment when Firmata is a subclass of HardwareSerial
}
void Adafruit_BLE_FirmataClass::setUsablePins(uint8_t *digitaliopins, uint8_t num_digitaliopins,
uint8_t *analogiopins, uint8_t num_analogiopins,
uint8_t *pwmpins, uint8_t num_pwmpins,
uint8_t *servopins, uint8_t num_servopins,
uint8_t sdapin, uint8_t sclpin)
{
_digitaliopins = digitaliopins;
_num_digitaliopins = num_digitaliopins;
_analogiopins = analogiopins;
_num_analogiopins = num_analogiopins;
_pwmpins = servopins;
_num_pwmpins = num_pwmpins;
_servopins = servopins;
_num_servopins = num_servopins;
_sdapin = sdapin;
_sclpin = sclpin;
}
boolean Adafruit_BLE_FirmataClass::contains(uint8_t *set, uint8_t num, uint8_t test) {
for (uint8_t i=0; i<num; i++) {
if (set[i] == test) return true;
}
return false;
}
uint8_t Adafruit_BLE_FirmataClass::location(uint8_t *set, uint8_t num, uint8_t test) {
for (uint8_t i=0; i<num; i++) {
if (set[i] == test) return i;
}
return 255;
}
uint8_t Adafruit_BLE_FirmataClass::PIN_TO_ANALOG(uint8_t p) {
return location(_analogiopins, _num_analogiopins, p);
}
/*==============================================================================
* readPort() - Read an 8 bit port
*============================================================================*/
unsigned char Adafruit_BLE_FirmataClass::readPort(byte port, byte bitmask)
{
unsigned char out=0, pin=port*8;
if (IS_PIN_DIGITAL(pin+0) && (bitmask & 0x01) && digitalRead(PIN_TO_DIGITAL(pin+0))) out |= 0x01;
if (IS_PIN_DIGITAL(pin+1) && (bitmask & 0x02) && digitalRead(PIN_TO_DIGITAL(pin+1))) out |= 0x02;
if (IS_PIN_DIGITAL(pin+2) && (bitmask & 0x04) && digitalRead(PIN_TO_DIGITAL(pin+2))) out |= 0x04;
if (IS_PIN_DIGITAL(pin+3) && (bitmask & 0x08) && digitalRead(PIN_TO_DIGITAL(pin+3))) out |= 0x08;
if (IS_PIN_DIGITAL(pin+4) && (bitmask & 0x10) && digitalRead(PIN_TO_DIGITAL(pin+4))) out |= 0x10;
if (IS_PIN_DIGITAL(pin+5) && (bitmask & 0x20) && digitalRead(PIN_TO_DIGITAL(pin+5))) out |= 0x20;
if (IS_PIN_DIGITAL(pin+6) && (bitmask & 0x40) && digitalRead(PIN_TO_DIGITAL(pin+6))) out |= 0x40;
if (IS_PIN_DIGITAL(pin+7) && (bitmask & 0x80) && digitalRead(PIN_TO_DIGITAL(pin+7))) out |= 0x80;
return out;
}
/*==============================================================================
* writePort() - Write an 8 bit port, only touch pins specified by a bitmask
*============================================================================*/
unsigned char Adafruit_BLE_FirmataClass::writePort(byte port, byte value, byte bitmask)
{
byte pin=port*8;
for (uint8_t i=0; i<8; i++) {
if (bitmask & (1 << i)) {
// dont touch non-digital pins
if (IS_PIN_DIGITAL(pin+i))
digitalWrite(PIN_TO_DIGITAL(pin+i), (value & (1 << i)));
}
}
}

View File

@@ -0,0 +1,219 @@
/*
Firmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Modified for Adafruit_BLE_Uart by Limor Fried/Kevin Townsend for
Adafruit Industries, 2014
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.
*/
#ifndef Adafruit_BLE_Firmata_h
#define Adafruit_BLE_Firmata_h
#include <Arduino.h>
//#include "Boards.h" /* Hardware Abstraction Layer + Wiring/Arduino */
//#define BLE_DEBUG
// 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_10BIT_ADDRESS_MODE_MASK B00100000
#define MAX_QUERIES 8
#define MINIMUM_SAMPLING_INTERVAL 10
#define REGISTER_NOT_SPECIFIED -1
/* Version numbers for the protocol. The protocol is still changing, so these
* version numbers are important. This number can be queried so that host
* 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 3 // for backwards compatible changes
#define FIRMATA_BUGFIX_VERSION 1 // for bugfix releases
#define MAX_DATA_BYTES 32 // max number of data bytes in non-Sysex messages
// message command bytes (128-255/0x80-0xFF)
#define DIGITAL_MESSAGE 0x90 // send data for a digital pin
#define ANALOG_MESSAGE 0xE0 // send data for an analog pin (or PWM)
#define REPORT_ANALOG 0xC0 // enable analog input by pin #
#define REPORT_DIGITAL 0xD0 // enable digital input by port pair
//
#define SET_PIN_MODE 0xF4 // set a pin to INPUT/OUTPUT/PWM/etc
//
#define REPORT_VERSION 0xF9 // report protocol version
#define SYSTEM_RESET 0xFF // reset from MIDI
//
#define START_SYSEX 0xF0 // start a MIDI Sysex message
#define END_SYSEX 0xF7 // end a MIDI Sysex message
// extended command set using sysex (0-127/0x00-0x7F)
/* 0x00-0x0F reserved for user-defined commands */
#define SERVO_CONFIG 0x70 // set max angle, minPulse, maxPulse, freq
#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 EXTENDED_ANALOG 0x6F // analog write (PWM, Servo, etc) to any pin
#define PIN_STATE_QUERY 0x6D // ask for a pin's current mode and value
#define PIN_STATE_RESPONSE 0x6E // reply with pin's current mode and value
#define CAPABILITY_QUERY 0x6B // ask for supported modes and resolution of all pins
#define CAPABILITY_RESPONSE 0x6C // reply with supported modes and resolution
#define ANALOG_MAPPING_QUERY 0x69 // ask for mapping of analog to pin numbers
#define ANALOG_MAPPING_RESPONSE 0x6A // reply with mapping info
#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
//#define OUTPUT 0x01 // defined in wiring.h
#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
#define TOTAL_PIN_MODES 7
extern "C" {
// callback function types
typedef void (*callbackFunction)(byte, int);
typedef void (*systemResetCallbackFunction)(void);
typedef void (*stringCallbackFunction)(char*);
typedef void (*sysexCallbackFunction)(byte command, byte argc, byte*argv);
}
// TODO make it a subclass of a generic Serial/Stream base class
class Adafruit_BLE_FirmataClass
{
public:
Adafruit_BLE_FirmataClass(Stream &s);
/* Arduino constructors */
void begin();
void begin(Stream &s);
/* querying functions */
void printVersion(void);
void blinkVersion(void);
void printFirmwareVersion(void);
//void setFirmwareVersion(byte major, byte minor); // see macro below
void setFirmwareNameAndVersion(const char *name, byte major, byte minor);
/* serial receive handling */
int available(void);
int processInput(void);
/* serial send handling */
void sendAnalog(byte pin, int value);
void sendDigital(byte pin, int value); // TODO implement this
void sendDigitalPort(byte portNumber, int portData);
void sendString(const char* string);
void sendString(byte command, const char* string);
void sendSysex(byte command, byte bytec, byte* bytev);
/* attach & detach callback functions to messages */
void attach(byte command, callbackFunction newFunction);
void attach(byte command, systemResetCallbackFunction newFunction);
void attach(byte command, stringCallbackFunction newFunction);
void attach(byte command, sysexCallbackFunction newFunction);
void detach(byte command);
/* board details */
void setUsablePins(uint8_t *digitaliopins, uint8_t num_digitaliopins,
uint8_t *analogiopins, uint8_t num_analogiopins,
uint8_t *pwmpins, uint8_t num_pwmpins,
uint8_t *servopins, uint8_t num_servopins,
uint8_t sdapin, uint8_t sclpin);
boolean IS_PIN_DIGITAL(uint8_t p) { return contains(_digitaliopins,_num_digitaliopins, p); }
uint8_t PIN_TO_DIGITAL(uint8_t p) { return p; }
boolean IS_PIN_ANALOG(uint8_t p) { return contains(_analogiopins, _num_analogiopins, p); }
uint8_t PIN_TO_ANALOG(uint8_t p);
boolean IS_PIN_PWM(uint8_t p) { return contains(_pwmpins, _num_pwmpins, p); }
uint8_t PIN_TO_PWM(uint8_t p) { return p; }
boolean IS_PIN_SERVO(uint8_t p) { return contains(_servopins, _num_servopins, p); }
uint8_t PIN_TO_SERVO(uint8_t p) { return p-2;}
boolean IS_PIN_I2C(uint8_t p) { return (p == _sdapin) || (p == _sclpin); }
unsigned char readPort(byte port, byte bitmask);
unsigned char writePort(byte port, byte value, byte bitmask);
uint8_t _num_analogiopins;
private:
Stream &FirmataSerial;
uint8_t *_digitaliopins, _num_digitaliopins;
uint8_t *_pwmpins, _num_pwmpins;
uint8_t *_analogiopins;
uint8_t *_servopins, _num_servopins;
uint8_t _sdapin, _sclpin;
boolean contains(uint8_t *set, uint8_t num, uint8_t test);
uint8_t location(uint8_t *set, uint8_t num, uint8_t test);
/* firmware name and version */
byte firmwareVersionCount;
byte *firmwareVersionVector;
/* input message handling */
byte waitForData; // this flag says the next serial input will be data
byte executeMultiByteCommand; // execute this after getting multi-byte data
byte multiByteChannel; // channel data for multiByteCommands
byte storedInputData[MAX_DATA_BYTES]; // multi-byte data
/* sysex */
boolean parsingSysex;
int sysexBytesRead;
/* callback functions */
callbackFunction currentAnalogCallback;
callbackFunction currentDigitalCallback;
callbackFunction currentReportAnalogCallback;
callbackFunction currentReportDigitalCallback;
callbackFunction currentPinModeCallback;
systemResetCallbackFunction currentSystemResetCallback;
stringCallbackFunction currentStringCallback;
sysexCallbackFunction currentSysexCallback;
/* private methods ------------------------------ */
void processSysexMessage(void);
void systemReset(void);
void pin13strobe(int count, int onInterval, int offInterval);
void sendValueAsTwo7bitBytes(int value);
void startSysex(void);
void endSysex(void);
};
extern Adafruit_BLE_FirmataClass BLE_Firmata;
/*==============================================================================
* MACROS
*============================================================================*/
/* shortcut for setFirmwareNameAndVersion() that uses __FILE__ to set the
* firmware name. It needs to be a macro so that __FILE__ is included in the
* firmware source file rather than the library source file.
*/
#define setFirmwareVersion(x, y) setFirmwareNameAndVersion(__FILE__, x, y)
#endif /* BLE_Firmata_h */

View File

@@ -0,0 +1,135 @@
/* Boards.h - Hardware Abstraction Layer for Firmata library */
#ifndef BLE_Firmata_Boards_h
#define BLE_Firmata_Boards_h
#include <inttypes.h>
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h" // for digitalRead, digitalWrite, etc
#else
#include "WProgram.h"
#endif
// Normally Servo.h must be included before Firmata.h (which then includes
// this file). If Servo.h wasn't included, this allows the code to still
// compile, but without support for any Servos. Hopefully that's what the
// user intended by not including Servo.h
#ifndef MAX_SERVOS
#define MAX_SERVOS 0
#endif
/*
Firmata Hardware Abstraction Layer
Firmata is built on top of the hardware abstraction functions of Arduino,
specifically digitalWrite, digitalRead, analogWrite, analogRead, and
pinMode. While these functions offer simple integer pin numbers, Firmata
needs more information than is provided by Arduino. This file provides
all other hardware specific details. To make Firmata support a new board,
only this file should require editing.
The key concept is every "pin" implemented by Firmata may be mapped to
any pin as implemented by Arduino. Usually a simple 1-to-1 mapping is
best, but such mapping should not be assumed. This hardware abstraction
layer allows Firmata to implement any number of pins which map onto the
Arduino implemented pins in almost any arbitrary way.
General Constants:
These constants provide basic information Firmata requires.
TOTAL_PINS: The total number of pins Firmata implemented by Firmata.
Usually this will match the number of pins the Arduino functions
implement, including any pins pins capable of analog or digital.
However, Firmata may implement any number of pins. For example,
on Arduino Mini with 8 analog inputs, 6 of these may be used
for digital functions, and 2 are analog only. On such boards,
Firmata can implement more pins than Arduino's pinMode()
function, in order to accommodate those special pins. The
Firmata protocol supports a maximum of 128 pins, so this
constant must not exceed 128.
TOTAL_ANALOG_PINS: The total number of analog input pins implemented.
The Firmata protocol allows up to 16 analog inputs, accessed
using offsets 0 to 15. Because Firmata presents the analog
inputs using different offsets than the actual pin numbers
(a legacy of Arduino's analogRead function, and the way the
analog input capable pins are physically labeled on all
Arduino boards), the total number of analog input signals
must be specified. 16 is the maximum.
VERSION_BLINK_PIN: When Firmata starts up, it will blink the version
number. This constant is the Arduino pin number where a
LED is connected.
Pin Mapping Macros:
These macros provide the mapping between pins as implemented by
Firmata protocol and the actual pin numbers used by the Arduino
functions. Even though such mappings are often simple, pin
numbers received by Firmata protocol should always be used as
input to these macros, and the result of the macro should be
used with with any Arduino function.
When Firmata is extended to support a new pin mode or feature,
a pair of macros should be added and used for all hardware
access. For simple 1:1 mapping, these macros add no actual
overhead, yet their consistent use allows source code which
uses them consistently to be easily adapted to all other boards
with different requirements.
IS_PIN_XXXX(pin): The IS_PIN macros resolve to true or non-zero
if a pin as implemented by Firmata corresponds to a pin
that actually implements the named feature.
PIN_TO_XXXX(pin): The PIN_TO macros translate pin numbers as
implemented by Firmata to the pin numbers needed as inputs
to the Arduino functions. The corresponding IS_PIN macro
should always be tested before using a PIN_TO macro, so
these macros only need to handle valid Firmata pin
numbers for the named feature.
Port Access Inline Funtions:
For efficiency, Firmata protocol provides access to digital
input and output pins grouped by 8 bit ports. When these
groups of 8 correspond to actual 8 bit ports as implemented
by the hardware, these inline functions can provide high
speed direct port access. Otherwise, a default implementation
using 8 calls to digitalWrite or digitalRead is used.
When porting Firmata to a new board, it is recommended to
use the default functions first and focus only on the constants
and macros above. When those are working, if optimized port
access is desired, these inline functions may be extended.
The recommended approach defines a symbol indicating which
optimization to use, and then conditional complication is
used within these functions.
readPort(port, bitmask): Read an 8 bit port, returning the value.
port: The port number, Firmata pins port*8 to port*8+7
bitmask: The actual pins to read, indicated by 1 bits.
writePort(port, value, bitmask): Write an 8 bit port.
port: The port number, Firmata pins port*8 to port*8+7
value: The 8 bit value to write
bitmask: The actual pins to write, indicated by 1 bits.
*/
/*==============================================================================
* Board Specific Configuration
*============================================================================*/
#define VERSION_BLINK_PIN 99
#define ARDUINO_PINOUT_OPTIMIZE 0
#endif /* BLE_Firmata_Boards_h */

View File

@@ -0,0 +1,458 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

View File

@@ -0,0 +1,125 @@
#Firmata
Firmata is a protocol for communicating with microcontrollers from software on a host computer. The [protocol](http://firmata.org/wiki/Protocol) can be implemented in firmware on any microcontroller architecture as well as software on any host computer software package. The arduino repository described here is a Firmata library for Arduino and Arduino-compatible devices. See the [firmata wiki](http://firmata.org/wiki/Main_Page) for additional informataion. If you would like to contribute to Firmata, please see the [Contributing](#contributing) section below.
##Usage
There are two main models of usage of Firmata. In one model, the author of the Arduino sketch uses the various methods provided by the Firmata library to selectively send and receive data between the Arduino device and the software running on the host computer. For example, a user can send analog data to the host using ``` Firmata.sendAnalog(analogPin, analogRead(analogPin)) ``` or send data packed in a string using ``` Firmata.sendString(stringToSend) ```. See File -> Examples -> Firmata -> AnalogFirmata & EchoString respectively for examples.
The second and more common model is to load a general purpose sketch called StandardFirmata on the Arduino board and then use the host computer exclusively to interact with the Arduino board. StandardFirmata is located in the Arduino IDE in File -> Examples -> Firmata.
##Firmata Client Libraries
Most of the time you will be interacting with arduino with a client library on the host computers. Several Firmata client libraries have been implemented in a variety of popular programming languages:
* procesing
* [https://github.com/firmata/processing]
* [http://funnel.cc]
* python
* [https://github.com/firmata/pyduino]
* [https://github.com/lupeke/python-firmata]
* [https://github.com/tino/pyFirmata]
* perl
* [https://github.com/ntruchsess/perl-firmata]
* [https://github.com/rcaputo/rx-firmata]
* ruby
* [https://github.com/hardbap/firmata]
* [https://github.com/PlasticLizard/rufinol]
* [http://funnel.cc]
* clojure
* [https://github.com/nakkaya/clodiuno]
* javascript
* [https://github.com/jgautier/firmata]
* [http://breakoutjs.com]
* [https://github.com/rwldrn/johnny-five]
* java
* [https://github.com/4ntoine/Firmata]
* [https://github.com/shigeodayo/Javarduino]
* .NET
* [http://www.imagitronics.org/projects/firmatanet/]
* Flash/AS3
* [http://funnel.cc]
* [http://code.google.com/p/as3glue/]
* PHP
* [https://bitbucket.org/ThomasWeinert/carica-firmata]
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 (< Arduino 1.5)
The version of firmata in the Arduino IDE contains an outdated version of Firmata. To update Firmata, clone the repo into the location of firmata in the arduino IDE or download the latest [tagged version](https://github.com/firmata/arduino/tags) (stable), rename the folder to "Firmata" and replace the existing Firmata folder in your Ardino application.
**Mac OSX**:
```bash
$ rm -r /Applications/Arduino.app/Contents/Resources/Java/libraries/Firmata
$ git clone git@github.com:firmata/arduino.git /Applications/Arduino.app/Contents/Resources/Java/libraries/Firmata
```
If you are downloading the latest tagged version of Firmata, rename it to "Firmata" and copy to /Applications/Arduino.app/Contents/Resources/Java/libraries/ overwriting the existing Firmata directory. Right-click (or conrol + click) on the Arduino application and choose "Show Package Contents" and navigate to the libraries directory.
**Windows**:
Using the Git Shell application installed with [GitHub for Windows](http://windows.github.com/) (set default shell in options to Git Bash) or other command line based git tool:
update the path and arduino version as necessary
```bash
$ rm -r c:/Program\ Files/arduino-1.x/libraries/Firmata
$ git clone git@github.com:firmata/arduino.git c:/Program\ Files/arduino-1.x/libraries/Firmata
```
Note: If you use GitHub for Windows, you must clone the firmata/arduino repository using the Git Shell application as described above. You can use the Github for Windows GUI only after you have cloned the repository. Drag the Firmata file into the Github for Windows GUI to track it.
**Linux**:
update the path and arduino version as necessary
```bash
$ rm -r ~/arduino-1.x/libraries/Firmata
$ git clone git@github.com:firmata/arduino.git ~/arduino-1.x/libraries/Firmata
```
##Updating Firmata in the Arduino IDE (>= Arduino 1.5.2)
As of Arduino 1.5.2 and there are separate library directories for the sam and
avr architectures. To update Firmata in Arduino 1.5.2 or higher, follow the
instructions above for pre Arduino 1.5 versions but update the path as follows:
**Mac OSX**:
```
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/libraries/Firmata
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/sam/libraries/Firmata
```
**Windows**:
```
/Program\ Files/arduino-1.5.x/hardware/arduino/avr/libraries/Firmata
/Program\ Files/arduino-1.5.x/hardware/arduino/sam/libraries/Firmata
```
**Linux**
```
~/arduino-1.5.x/hardware/arduino/avr/libraries/Firmata
~/arduino-1.5.x/hardware/arduino/sam/libraries/Firmata
```
<a name="contributing" />
##Contributing
If you discover a bug or would like to propose a new feature, please open a new [issue](https://github.com/firmata/arduino/issues?sort=created&state=open). Due to the limited memory of standard Arduino boards we cannot add every requested feature to StandardFirmata. Requests to add new features to StandardFirmata will be evaluated by the Firmata developers. However it is still possible to add new features to other Firmata implementations (Firmata is a protocol whereas StandardFirmata is just one of many possible implementations).
To contribute, fork this respository and create a new topic branch for the bug, feature or other existing issue you are addressing. Submit the pull request against the *dev* branch.
If you would like to contribute but don't have a specific bugfix or new feature to contribute, you can take on an existing issue, see issues labeled "pull-request-encouraged". Add a comment to the issue to express your intent to begin work and/or to get any additional information about the issue.
You must thorougly test your contributed code. In your pull request, describe tests performed to ensure that no existing code is broken and that any changes maintain backwards compatibility with the existing api. Test on multiple Arduino board variants if possible. We hope to enable some form of automated (or at least semi-automated) testing in the future, but for now any tests will need to be executed manually by the contributor and reviewsers.
Maintain the existing code style:
- Indentation is 2 spaces
- Use spaces instead of tabs
- Use camel case for both private and public properties and methods
- Document functions (specific doc style is TBD... for now just be sure to document)
- Insert first block bracket on line following the function definition:
<pre>void someFunction()
{
// do something
}
</pre>

View File

@@ -0,0 +1,56 @@
// COMMON SETTINGS
// ----------------------------------------------------------------------------------------------
// These settings are used in both SW UART, HW UART and SPI mode
// ----------------------------------------------------------------------------------------------
#define BUFSIZE 128 // Size of the read buffer for incoming data
#define VERBOSE_MODE true // If set to 'true' enables debug output
// SOFTWARE UART SETTINGS
// ----------------------------------------------------------------------------------------------
// The following macros declare the pins that will be used for 'SW' serial.
// You should use this option if you are connecting the UART Friend to an UNO
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_SWUART_RXD_PIN 9 // Required for software serial!
#define BLUEFRUIT_SWUART_TXD_PIN 10 // Required for software serial!
#define BLUEFRUIT_UART_CTS_PIN 11 // Required for software serial!
#define BLUEFRUIT_UART_RTS_PIN -1 // Optional, set to -1 if unused
// HARDWARE UART SETTINGS
// ----------------------------------------------------------------------------------------------
// The following macros declare the HW serial port you are using. Uncomment
// this line if you are connecting the BLE to Leonardo/Micro or Flora
// ----------------------------------------------------------------------------------------------
#ifdef Serial1 // this makes it not complain on compilation if there's no Serial1
#define BLUEFRUIT_HWSERIAL_NAME Serial1
#endif
// SHARED UART SETTINGS
// ----------------------------------------------------------------------------------------------
// The following sets the optional Mode pin, its recommended but not required
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_UART_MODE_PIN 12 // Set to -1 if unused
// SHARED SPI SETTINGS
// ----------------------------------------------------------------------------------------------
// The following macros declare the pins to use for HW and SW SPI communication.
// SCK, MISO and MOSI should be connected to the HW SPI pins on the Uno when
// using HW SPI. This should be used with nRF51822 based Bluefruit LE modules
// that use SPI (Bluefruit LE SPI Friend).
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_SPI_CS 8
#define BLUEFRUIT_SPI_IRQ 7
#define BLUEFRUIT_SPI_RST 4
// SOFTWARE SPI SETTINGS
// ----------------------------------------------------------------------------------------------
// The following macros declare the pins to use for SW SPI communication.
// This should be used with nRF51822 based Bluefruit LE modules that use SPI
// (Bluefruit LE SPI Friend).
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_SPI_SCK 13
#define BLUEFRUIT_SPI_MISO 12
#define BLUEFRUIT_SPI_MOSI 11

View File

@@ -0,0 +1,859 @@
#include <Servo.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BLE_Firmata.h>
#if not defined (_VARIANT_ARDUINO_DUE_X_) && not defined (_VARIANT_ARDUINO_ZERO_)
#include <SoftwareSerial.h>
#endif
// Change this to whatever is the Serial console you want, either Serial or SerialUSB
#define FIRMATADEBUG Serial
// Pause for Serial console before beginning?
#define WAITFORSERIAL true
// Print all BLE interactions?
#define VERBOSE_MODE false
/************************ CONFIGURATION SECTION ***********************************/
/*
Don't forget to also change the BluefruitConfig.h for the SPI or UART connection
and pinout you are using!
Then below, you can edit the list of pins that are available. Remove any pins
that are used for accessories or for talking to the BLE module!
*/
/************** For Bluefruit Micro or Feather 32u4 Bluefruit ************/
uint8_t boards_digitaliopins[] = {0,1,2,3,5,6,9,10,11,12,13,A0,A1,A2,A3,A4,A5};
/************** For UNO + nRF58122 SPI & shield ************/
//uint8_t boards_digitaliopins[] = {2, 3, 5, 6, 9, 10, A0, A1, A2, A3, A4, A5};
/************** For Bluefruit M0 Bluefruit ************/
//uint8_t boards_digitaliopins[] = {0,1,5,6,9,10,11,12,13,20,21,A0,A1,A2,A3,A4,A5};
#if defined(__AVR_ATmega328P__)
// Standard setup for UNO, no need to tweak
uint8_t boards_analogiopins[] = {A0, A1, A2, A3, A4, A5}; // A0 == digital 14, etc
uint8_t boards_pwmpins[] = {3, 5, 6, 9, 10, 11};
uint8_t boards_servopins[] = {9, 10};
uint8_t boards_i2cpins[] = {SDA, SCL};
#elif defined(__AVR_ATmega32U4__)
uint8_t boards_analogiopins[] = {A0, A1, A2, A3, A4, A5}; // A0 == digital 14, etc
uint8_t boards_pwmpins[] = {3, 5, 6, 9, 10, 11, 13};
uint8_t boards_servopins[] = {9, 10};
uint8_t boards_i2cpins[] = {SDA, SCL};
#elif defined(__SAMD21G18A__)
#define SDA PIN_WIRE_SDA
#define SCL PIN_WIRE_SCL
uint8_t boards_analogiopins[] = {PIN_A0, PIN_A1, PIN_A2, PIN_A3, PIN_A4, PIN_A5,PIN_A6, PIN_A7}; // A0 == digital 14, etc
uint8_t boards_pwmpins[] = {3,4,5,6,8,10,11,12,A0,A1,A2,A3,A4,A5};
uint8_t boards_servopins[] = {9, 10};
uint8_t boards_i2cpins[] = {SDA, SCL};
#define NUM_DIGITAL_PINS 26
#endif
#define TOTAL_PINS NUM_DIGITAL_PINS /* highest number in boards_digitaliopins MEMEFIXME:automate */
#define TOTAL_PORTS ((TOTAL_PINS + 7) / 8)
/***********************************************************/
#include "Adafruit_BLE_Firmata_Boards.h"
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"
#include "BluefruitConfig.h"
// Create the bluefruit object, either software serial...uncomment these lines
/*
SoftwareSerial bluefruitSS = SoftwareSerial(BLUEFRUIT_SWUART_TXD_PIN, BLUEFRUIT_SWUART_RXD_PIN);
Adafruit_BluefruitLE_UART bluefruit(bluefruitSS, BLUEFRUIT_UART_MODE_PIN,
BLUEFRUIT_UART_CTS_PIN, BLUEFRUIT_UART_RTS_PIN);
*/
/* ...or hardware serial, which does not need the RTS/CTS pins. Uncomment this line */
// Adafruit_BluefruitLE_UART bluefruit(BLUEFRUIT_HWSERIAL_NAME, BLUEFRUIT_UART_MODE_PIN);
/* ...hardware SPI, using SCK/MOSI/MISO hardware SPI pins and then user selected CS/IRQ/RST */
Adafruit_BluefruitLE_SPI bluefruit(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
/* ...software SPI, using SCK/MOSI/MISO user-defined SPI pins and then user selected CS/IRQ/RST */
//Adafruit_BluefruitLE_SPI bluefruit(BLUEFRUIT_SPI_SCK, BLUEFRUIT_SPI_MISO,
// BLUEFRUIT_SPI_MOSI, BLUEFRUIT_SPI_CS,
// BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
#define AUTO_INPUT_PULLUPS true
// our current connection status
boolean lastBTLEstatus, BTLEstatus;
// make one instance for the user to use
Adafruit_BLE_FirmataClass BLE_Firmata = Adafruit_BLE_FirmataClass(bluefruit);
// A small helper
void error(const __FlashStringHelper*err) {
FIRMATADEBUG.println(err);
while (1);
}
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
int lastAnalogReads[NUM_ANALOG_INPUTS];
/* 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
int samplingInterval = 200; // how often to run the main loop (in ms)
#define MINIMUM_SAMPLE_DELAY 150
#define ANALOG_SAMPLE_DELAY 50
/* i2c data */
struct i2c_device_info {
byte addr;
byte reg;
byte bytes;
};
/* for i2c read continuous more */
i2c_device_info query[MAX_QUERIES];
byte i2cRxData[32];
boolean isI2CEnabled = false;
signed char queryIndex = -1;
unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom()
Servo servos[MAX_SERVOS];
/*==============================================================================
* FUNCTIONS
*============================================================================*/
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 != REGISTER_NOT_SPECIFIED) {
Wire.beginTransmission(address);
#if ARDUINO >= 100
Wire.write((byte)theRegister);
#else
Wire.send((byte)theRegister);
#endif
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); // all bytes are returned in requestFrom
// 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++) {
#if ARDUINO >= 100
i2cRxData[2 + i] = Wire.read();
#else
i2cRxData[2 + i] = Wire.receive();
#endif
}
}
else {
if(numBytes > Wire.available()) {
BLE_Firmata.sendString("I2C Read Error: Too many bytes received");
} else {
BLE_Firmata.sendString("I2C Read Error: Too few bytes received");
}
}
// send slave address, register and received bytes
BLE_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) {
//FIRMATADEBUG.print(F("Sending update for port ")); FIRMATADEBUG.print(portNumber); FIRMATADEBUG.print(" = 0x"); FIRMATADEBUG.println(portValue, HEX);
BLE_Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using () */
void checkDigitalInputs(boolean forceSend = false)
{
/* 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. */
for (uint8_t i=0; i<TOTAL_PORTS; i++) {
if (reportPINs[i]) {
// FIRMATADEBUG.print("Reporting on port "); FIRMATADEBUG.print(i); FIRMATADEBUG.print(" mask 0x"); FIRMATADEBUG.println(portConfigInputs[i], HEX);
uint8_t x = BLE_Firmata.readPort(i, portConfigInputs[i]);
// FIRMATADEBUG.print("Read 0x"); FIRMATADEBUG.println(x, HEX);
outputPort(i, x, forceSend);
}
}
}
// -----------------------------------------------------------------------------
/* 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)
{
//FIRMATADEBUG.print("Setting pin #"); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(" to "); FIRMATADEBUG.println(mode);
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 (BLE_Firmata.IS_PIN_SERVO(pin) && mode != SERVO && servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached()) {
servos[BLE_Firmata.PIN_TO_SERVO(pin)].detach();
}
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
reportAnalogCallback(BLE_Firmata.PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting
}
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
if (mode == INPUT) {
portConfigInputs[pin/8] |= (1 << (pin & 7));
} else {
portConfigInputs[pin/8] &= ~(1 << (pin & 7));
}
// FIRMATADEBUG.print(F("Setting pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(F(" port config mask to = 0x"));
// FIRMATADEBUG.println(portConfigInputs[pin/8], HEX);
}
pinState[pin] = 0;
switch(mode) {
case ANALOG:
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to analog"));
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT); // disable output driver
}
pinConfig[pin] = ANALOG;
lastAnalogReads[BLE_Firmata.PIN_TO_ANALOG(pin)] = -1;
}
break;
case INPUT:
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to input"));
if (AUTO_INPUT_PULLUPS) {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT_PULLUP); // disable output driver
} else {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT); // disable output driver
}
pinConfig[pin] = INPUT;
// force sending state immediately
//delay(10);
//checkDigitalInputs(true);
}
break;
case OUTPUT:
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to output"));
digitalWrite(BLE_Firmata.PIN_TO_DIGITAL(pin), LOW); // disable PWM
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), OUTPUT);
pinConfig[pin] = OUTPUT;
}
break;
case PWM:
if (BLE_Firmata.IS_PIN_PWM(pin)) {
FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to PWM"));
pinMode(BLE_Firmata.PIN_TO_PWM(pin), OUTPUT);
analogWrite(BLE_Firmata.PIN_TO_PWM(pin), 0);
pinConfig[pin] = PWM;
}
break;
case SERVO:
if (BLE_Firmata.IS_PIN_SERVO(pin)) {
pinConfig[pin] = SERVO;
if (!servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached()) {
servos[BLE_Firmata.PIN_TO_SERVO(pin)].attach(BLE_Firmata.PIN_TO_DIGITAL(pin));
}
}
break;
case I2C:
if (BLE_Firmata.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:
FIRMATADEBUG.print(F("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 (BLE_Firmata.IS_PIN_SERVO(pin))
servos[BLE_Firmata.PIN_TO_SERVO(pin)].write(value);
pinState[pin] = value;
break;
case PWM:
if (BLE_Firmata.IS_PIN_PWM(pin))
analogWrite(BLE_Firmata.PIN_TO_PWM(pin), value);
FIRMATADEBUG.print("pwm("); FIRMATADEBUG.print(BLE_Firmata.PIN_TO_PWM(pin)); FIRMATADEBUG.print(","); FIRMATADEBUG.print(value); FIRMATADEBUG.println(")");
pinState[pin] = value;
break;
}
}
}
void digitalWriteCallback(byte port, int value)
{
//FIRMATADEBUG.print("DWCx"); FIRMATADEBUG.print(port, HEX); FIRMATADEBUG.print(" "); FIRMATADEBUG.println(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 (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
// only write to OUTPUT
// do not touch pins in PWM, ANALOG, SERVO or other modes
if (pinConfig[pin] == OUTPUT) {
pinWriteMask |= mask;
pinState[pin] = ((byte)value & mask) ? 1 : 0;
}
}
mask = mask << 1;
}
FIRMATADEBUG.print(F("Write digital port #")); FIRMATADEBUG.print(port);
FIRMATADEBUG.print(F(" = 0x")); FIRMATADEBUG.print(value, HEX);
FIRMATADEBUG.print(F(" mask = 0x")); FIRMATADEBUG.println(pinWriteMask, HEX);
BLE_Firmata.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 < BLE_Firmata._num_analogiopins) {
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << analogPin);
FIRMATADEBUG.print(F("Stop reporting analog pin #")); FIRMATADEBUG.println(analogPin);
} else {
analogInputsToReport |= (1 << analogPin);
FIRMATADEBUG.print(F("Will report analog pin #")); FIRMATADEBUG.println(analogPin);
}
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
if (port < TOTAL_PORTS) {
//FIRMATADEBUG.print(F("Will report 0x")); FIRMATADEBUG.print(value, HEX); FIRMATADEBUG.print(F(" digital mask on port ")); FIRMATADEBUG.println(port);
reportPINs[port] = (byte)value;
}
// 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 slaveRegister;
byte data;
unsigned int delayTime;
FIRMATADEBUG.println("********** Sysex callback");
switch(command) {
case I2C_REQUEST:
mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) {
//BLE_Firmata.sendString("10-bit addressing mode is not yet supported");
FIRMATADEBUG.println(F("10-bit addressing mode is not yet 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);
#if ARDUINO >= 100
Wire.write(data);
#else
Wire.send(data);
#endif
}
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
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
BLE_Firmata.sendString("too many queries");
break;
}
queryIndex++;
query[queryIndex].addr = slaveAddress;
query[queryIndex].reg = argv[2] + (argv[3] << 7);
query[queryIndex].bytes = argv[4] + (argv[5] << 7);
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 < MAX_QUERIES) {
query[i].addr = query[i+1].addr;
query[i].reg = query[i+1].addr;
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 (BLE_Firmata.IS_PIN_SERVO(pin)) {
if (servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached())
servos[BLE_Firmata.PIN_TO_SERVO(pin)].detach();
servos[BLE_Firmata.PIN_TO_SERVO(pin)].attach(BLE_Firmata.PIN_TO_DIGITAL(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 {
//BLE_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:
bluefruit.write(START_SYSEX);
bluefruit.write(CAPABILITY_RESPONSE);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(START_SYSEX, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(CAPABILITY_RESPONSE, HEX);
delay(10);
for (byte pin=0; pin < TOTAL_PINS; pin++) {
//FIRMATADEBUG.print("\t#"); FIRMATADEBUG.println(pin);
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
bluefruit.write((byte)INPUT);
bluefruit.write(1);
bluefruit.write((byte)OUTPUT);
bluefruit.write(1);
/*
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(INPUT, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(1, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(OUTPUT, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(1, HEX);
*/
delay(20);
} else {
bluefruit.write(127);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(127, HEX);
delay(20);
continue;
}
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
bluefruit.write(ANALOG);
bluefruit.write(10);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(ANALOG, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(10, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_PWM(pin)) {
bluefruit.write(PWM);
bluefruit.write(8);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(PWM, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(8, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_SERVO(pin)) {
bluefruit.write(SERVO);
bluefruit.write(14);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(SERVO, HEX);FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(14, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_I2C(pin)) {
bluefruit.write(I2C);
bluefruit.write(1); // to do: determine appropriate value
delay(20);
}
bluefruit.write(127);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(127, HEX);
}
bluefruit.write(END_SYSEX);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(END_SYSEX, HEX);
break;
case PIN_STATE_QUERY:
if (argc > 0) {
byte pin=argv[0];
bluefruit.write(START_SYSEX);
bluefruit.write(PIN_STATE_RESPONSE);
bluefruit.write(pin);
if (pin < TOTAL_PINS) {
bluefruit.write((byte)pinConfig[pin]);
bluefruit.write((byte)pinState[pin] & 0x7F);
if (pinState[pin] & 0xFF80) bluefruit.write((byte)(pinState[pin] >> 7) & 0x7F);
if (pinState[pin] & 0xC000) bluefruit.write((byte)(pinState[pin] >> 14) & 0x7F);
}
bluefruit.write(END_SYSEX);
}
break;
case ANALOG_MAPPING_QUERY:
FIRMATADEBUG.println("Analog mapping query");
bluefruit.write(START_SYSEX);
bluefruit.write(ANALOG_MAPPING_RESPONSE);
for (byte pin=0; pin < TOTAL_PINS; pin++) {
bluefruit.write(BLE_Firmata.IS_PIN_ANALOG(pin) ? BLE_Firmata.PIN_TO_ANALOG(pin) : 127);
}
bluefruit.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(BLE_Firmata.IS_PIN_I2C(i)) {
// mark pins as i2c so they are ignore in non i2c data requests
setPinModeCallback(i, I2C);
}
}
isI2CEnabled = true;
// is there enough time before the first I2C request to call this here?
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;
// uncomment the following if or when the end() method is added to Wire library
// Wire.end();
}
/*==============================================================================
* SETUP()
*============================================================================*/
void systemResetCallback()
{
// initialize a defalt state
FIRMATADEBUG.println(F("***RESET***"));
// 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;
}
// pins with analog capability default to analog input
// otherwise, pins default to digital output
for (byte i=0; i < TOTAL_PINS; i++) {
if (BLE_Firmata.IS_PIN_ANALOG(i)) {
// turns off pullup, configures everything
setPinModeCallback(i, ANALOG);
} else {
// sets the output to 0, configures portConfigInputs
setPinModeCallback(i, INPUT);
}
}
// by default, do not report any analog inputs
analogInputsToReport = 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);
}
*/
}
void setup()
{
if (WAITFORSERIAL) {
while (!FIRMATADEBUG) delay(1);
}
FIRMATADEBUG.begin(9600);
FIRMATADEBUG.println(F("Adafruit Bluefruit LE Firmata test"));
FIRMATADEBUG.print("Total pins: "); FIRMATADEBUG.println(NUM_DIGITAL_PINS);
FIRMATADEBUG.print("Analog pins: "); FIRMATADEBUG.println(sizeof(boards_analogiopins));
//for (uint8_t i=0; i<sizeof(boards_analogiopins); i++) {
// FIRMATADEBUG.println(boards_analogiopins[i]);
//}
BLE_Firmata.setUsablePins(boards_digitaliopins, sizeof(boards_digitaliopins),
boards_analogiopins, sizeof(boards_analogiopins),
boards_pwmpins, sizeof(boards_pwmpins),
boards_servopins, sizeof(boards_servopins), SDA, SCL);
/* Initialise the module */
FIRMATADEBUG.print(F("Initialising the Bluefruit LE module: "));
if ( !bluefruit.begin(VERBOSE_MODE) )
{
error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
}
FIRMATADEBUG.println( F("OK!") );
/* Perform a factory reset to make sure everything is in a known state */
FIRMATADEBUG.println(F("Performing a factory reset: "));
if (! bluefruit.factoryReset() ){
error(F("Couldn't factory reset"));
}
/* Disable command echo from Bluefruit */
bluefruit.echo(false);
FIRMATADEBUG.println("Requesting Bluefruit info:");
/* Print Bluefruit information */
bluefruit.info();
FIRMATADEBUG.println("Setting name to BLE Firmata");
bluefruit.println("AT+GAPDEVNAME=BLE_Firmata");
BTLEstatus = false;
}
void firmataInit() {
FIRMATADEBUG.println(F("Init firmata"));
//BLE_Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION);
//FIRMATADEBUG.println(F("firmata analog"));
BLE_Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
//FIRMATADEBUG.println(F("firmata digital"));
BLE_Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
//FIRMATADEBUG.println(F("firmata analog report"));
BLE_Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
//FIRMATADEBUG.println(F("firmata digital report"));
BLE_Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
//FIRMATADEBUG.println(F("firmata pinmode"));
BLE_Firmata.attach(SET_PIN_MODE, setPinModeCallback);
//FIRMATADEBUG.println(F("firmata sysex"));
BLE_Firmata.attach(START_SYSEX, sysexCallback);
//FIRMATADEBUG.println(F("firmata reset"));
BLE_Firmata.attach(SYSTEM_RESET, systemResetCallback);
FIRMATADEBUG.println(F("Begin firmata"));
BLE_Firmata.begin();
systemResetCallback(); // reset to default config
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
delay(100);
// Link status check
if (! BTLEstatus) {
bluefruit.setMode(BLUEFRUIT_MODE_COMMAND);
BTLEstatus = bluefruit.isConnected();
bluefruit.setMode(BLUEFRUIT_MODE_DATA);
}
// Check if something has changed
if (BTLEstatus != lastBTLEstatus) {
// print it out!
if (BTLEstatus == true) {
FIRMATADEBUG.println(F("* Connected!"));
// initialize Firmata cleanly
bluefruit.setMode(BLUEFRUIT_MODE_DATA);
firmataInit();
}
if (BTLEstatus == false) {
FIRMATADEBUG.println(F("* Disconnected or advertising timed out"));
}
// OK set the last status change to this one
lastBTLEstatus = BTLEstatus;
}
// if not connected... bail
if (! BTLEstatus) {
delay(100);
return;
}
// For debugging, see if there's data on the serial console, we would forwad it to BTLE
if (FIRMATADEBUG.available()) {
bluefruit.write(FIRMATADEBUG.read());
}
// Onto the Firmata main loop
byte pin, analogPin;
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* BTLE buffer using FIRMATADEBUG.print() */
checkDigitalInputs();
/* SERIALREAD - processing incoming messagse as soon as possible, while still
* checking digital inputs. */
while(BLE_Firmata.available()) {
// FIRMATADEBUG.println(F("*data available*"));
BLE_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. */
// make the sampling interval longer if we have more analog inputs!
uint8_t analogreportnums = 0;
for(uint8_t a=0; a<8; a++) {
if (analogInputsToReport & (1 << a)) {
analogreportnums++;
}
}
samplingInterval = (uint16_t)MINIMUM_SAMPLE_DELAY + (uint16_t)ANALOG_SAMPLE_DELAY * (1+analogreportnums);
currentMillis = millis();
if (currentMillis - previousMillis > samplingInterval) {
previousMillis += samplingInterval;
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
for(pin=0; pin<TOTAL_PINS; pin++) {
// FIRMATADEBUG.print("pin #"); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(" config = "); FIRMATADEBUG.println(pinConfig[pin]);
if (BLE_Firmata.IS_PIN_ANALOG(pin) && (pinConfig[pin] == ANALOG)) {
analogPin = BLE_Firmata.PIN_TO_ANALOG(pin);
if (analogInputsToReport & (1 << analogPin)) {
int currentRead = analogRead(analogPin);
if ((lastAnalogReads[analogPin] == -1) || (lastAnalogReads[analogPin] != currentRead)) {
//FIRMATADEBUG.print(F("Analog")); FIRMATADEBUG.print(analogPin); FIRMATADEBUG.print(F(" = ")); FIRMATADEBUG.println(currentRead);
BLE_Firmata.sendAnalog(analogPin, currentRead);
lastAnalogReads[analogPin] = currentRead;
}
}
}
}
// 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);
}
}
}
}

View File

@@ -0,0 +1,54 @@
// COMMON SETTINGS
// ----------------------------------------------------------------------------------------------
// These settings are used in both SW UART, HW UART and SPI mode
// ----------------------------------------------------------------------------------------------
#define BUFSIZE 128 // Size of the read buffer for incoming data
#define VERBOSE_MODE true // If set to 'true' enables debug output
// SOFTWARE UART SETTINGS
// ----------------------------------------------------------------------------------------------
// The following macros declare the pins that will be used for 'SW' serial.
// You should use this option if you are connecting the UART Friend to an UNO
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_SWUART_RXD_PIN 9 // Required for software serial!
#define BLUEFRUIT_SWUART_TXD_PIN 10 // Required for software serial!
#define BLUEFRUIT_UART_CTS_PIN 11 // Required for software serial!
#define BLUEFRUIT_UART_RTS_PIN -1 // Optional, set to -1 if unused
// HARDWARE UART SETTINGS
// ----------------------------------------------------------------------------------------------
// The following macros declare the HW serial port you are using. Uncomment
// this line if you are connecting the BLE to Leonardo/Micro or Flora
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_HWSERIAL_NAME Serial1
// SHARED UART SETTINGS
// ----------------------------------------------------------------------------------------------
// The following sets the optional Mode pin, its recommended but not required
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_UART_MODE_PIN 12 // Set to -1 if unused
// SHARED SPI SETTINGS
// ----------------------------------------------------------------------------------------------
// The following macros declare the pins to use for HW and SW SPI communication.
// SCK, MISO and MOSI should be connected to the HW SPI pins on the Uno when
// using HW SPI. This should be used with nRF51822 based Bluefruit LE modules
// that use SPI (Bluefruit LE SPI Friend).
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_SPI_CS 8
#define BLUEFRUIT_SPI_IRQ 7
#define BLUEFRUIT_SPI_RST 4
// SOFTWARE SPI SETTINGS
// ----------------------------------------------------------------------------------------------
// The following macros declare the pins to use for SW SPI communication.
// This should be used with nRF51822 based Bluefruit LE modules that use SPI
// (Bluefruit LE SPI Friend).
// ----------------------------------------------------------------------------------------------
#define BLUEFRUIT_SPI_SCK 13
#define BLUEFRUIT_SPI_MISO 12
#define BLUEFRUIT_SPI_MOSI 11

View File

@@ -0,0 +1,851 @@
// Adafruit Circuit Playground Bluefruit LE Friend Firmata Firmware
//
// This is a basic 'standard firmata' firmware that allows digital IO and other
// control of some (but not all) Circuit Playground components. You can read
// the push buttons (digital pins 4, 19) and slide switch (pin 21). You can light
// up the onboard pin 13 LED, but you can't light the NeoPixels on the board
// with this sketch (standard firmata doesn't support NeoPixels right now).
//
// Note this sketch is customized to ONLY work with Circuit Playground. Trying
// to use this sketch on other boards will NOT work. Use the BluefruitLE_nrf51822
// example instead and customize it for your board.
//
// By default this sketch assumes you are connecting to a Bluefruit LE friend
// using a hardware serial connection which is easiest on Circuit Playground.
// The Flora Bluefruit LE module (https://www.adafruit.com/products/2487) is the
// best option as it can easily be connected to Circuit Playground with alligator
// clips.
//
// Make the following connections between the Bluefruit LE module and Circuit Playground:
// - Bluefruit LE RX -> Circuit Playground TX #1
// - Bluefruit LE TX -> Circuit Playground RX #0
// - Bluefruit LE 3.3V -> Circuit Playground 3.3V
// - Bluefruit LE GND -> Circuit Playground GND
// - Bluefruit LE MODE -> Circuit Playground #12 (or any other digital input
// on Circuit Playground, but make sure to modify BluefruitConfig.h too!)
//
// Finally make sure the Bluefruit LE slide switch is in the DATA position. This
// is VERY important and the sketch won't work if it's in the CMD command mode!
#include <Servo.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BLE_Firmata.h>
// Change this to whatever is the Serial console you want, either Serial or SerialUSB
#define FIRMATADEBUG Serial
// Pause for Serial console before beginning?
#define WAITFORSERIAL false
// Print all BLE interactions?
#define VERBOSE_MODE false
/************************ CONFIGURATION SECTION ***********************************/
// You don't need to change this, it's setup for Circuit Playground already:
uint8_t boards_digitaliopins[] = {2,3,4,5,6,9,10,13,19,21};
uint8_t boards_analogiopins[] = {A0, A4, A5}; // A0 == digital 14, etc
uint8_t boards_pwmpins[] = {5, 6, 9, 10, 13};
uint8_t boards_servopins[] = {9, 10};
uint8_t boards_i2cpins[] = {SDA, SCL};
#define TOTAL_PINS NUM_DIGITAL_PINS /* highest number in boards_digitaliopins MEMEFIXME:automate */
#define TOTAL_PORTS ((TOTAL_PINS + 7) / 8)
/***********************************************************/
#include "Adafruit_BLE_Firmata_Boards.h"
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"
#include "BluefruitConfig.h"
// Create the bluefruit object, either software serial...uncomment these lines
/*
SoftwareSerial bluefruitSS = SoftwareSerial(BLUEFRUIT_SWUART_TXD_PIN, BLUEFRUIT_SWUART_RXD_PIN);
Adafruit_BluefruitLE_UART bluefruit(bluefruitSS, BLUEFRUIT_UART_MODE_PIN,
BLUEFRUIT_UART_CTS_PIN, BLUEFRUIT_UART_RTS_PIN);
*/
/* ...or hardware serial, which does not need the RTS/CTS pins. Uncomment this line */
Adafruit_BluefruitLE_UART bluefruit(BLUEFRUIT_HWSERIAL_NAME, BLUEFRUIT_UART_MODE_PIN);
/* ...hardware SPI, using SCK/MOSI/MISO hardware SPI pins and then user selected CS/IRQ/RST */
//Adafruit_BluefruitLE_SPI bluefruit(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
/* ...software SPI, using SCK/MOSI/MISO user-defined SPI pins and then user selected CS/IRQ/RST */
//Adafruit_BluefruitLE_SPI bluefruit(BLUEFRUIT_SPI_SCK, BLUEFRUIT_SPI_MISO,
// BLUEFRUIT_SPI_MOSI, BLUEFRUIT_SPI_CS,
// BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
#define AUTO_INPUT_PULLUPS true
// our current connection status
boolean lastBTLEstatus, BTLEstatus;
// make one instance for the user to use
Adafruit_BLE_FirmataClass BLE_Firmata = Adafruit_BLE_FirmataClass(bluefruit);
// A small helper
void error(const __FlashStringHelper*err) {
FIRMATADEBUG.println(err);
while (1);
}
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
int lastAnalogReads[NUM_ANALOG_INPUTS];
/* 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
int samplingInterval = 200; // how often to run the main loop (in ms)
#define MINIMUM_SAMPLE_DELAY 150
#define ANALOG_SAMPLE_DELAY 50
/* i2c data */
struct i2c_device_info {
byte addr;
byte reg;
byte bytes;
};
/* for i2c read continuous more */
i2c_device_info query[MAX_QUERIES];
byte i2cRxData[32];
boolean isI2CEnabled = false;
signed char queryIndex = -1;
unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom()
Servo servos[MAX_SERVOS];
/*==============================================================================
* FUNCTIONS
*============================================================================*/
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 != REGISTER_NOT_SPECIFIED) {
Wire.beginTransmission(address);
#if ARDUINO >= 100
Wire.write((byte)theRegister);
#else
Wire.send((byte)theRegister);
#endif
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); // all bytes are returned in requestFrom
// 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++) {
#if ARDUINO >= 100
i2cRxData[2 + i] = Wire.read();
#else
i2cRxData[2 + i] = Wire.receive();
#endif
}
}
else {
if(numBytes > Wire.available()) {
BLE_Firmata.sendString("I2C Read Error: Too many bytes received");
} else {
BLE_Firmata.sendString("I2C Read Error: Too few bytes received");
}
}
// send slave address, register and received bytes
BLE_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) {
//FIRMATADEBUG.print(F("Sending update for port ")); FIRMATADEBUG.print(portNumber); FIRMATADEBUG.print(" = 0x"); FIRMATADEBUG.println(portValue, HEX);
BLE_Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using () */
void checkDigitalInputs(boolean forceSend = false)
{
/* 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. */
for (uint8_t i=0; i<TOTAL_PORTS; i++) {
if (reportPINs[i]) {
// FIRMATADEBUG.print("Reporting on port "); FIRMATADEBUG.print(i); FIRMATADEBUG.print(" mask 0x"); FIRMATADEBUG.println(portConfigInputs[i], HEX);
uint8_t x = BLE_Firmata.readPort(i, portConfigInputs[i]);
// FIRMATADEBUG.print("Read 0x"); FIRMATADEBUG.println(x, HEX);
outputPort(i, x, forceSend);
}
}
}
// -----------------------------------------------------------------------------
/* 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)
{
//FIRMATADEBUG.print("Setting pin #"); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(" to "); FIRMATADEBUG.println(mode);
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 (BLE_Firmata.IS_PIN_SERVO(pin) && mode != SERVO && servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached()) {
servos[BLE_Firmata.PIN_TO_SERVO(pin)].detach();
}
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
reportAnalogCallback(BLE_Firmata.PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting
}
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
if (mode == INPUT) {
portConfigInputs[pin/8] |= (1 << (pin & 7));
} else {
portConfigInputs[pin/8] &= ~(1 << (pin & 7));
}
// FIRMATADEBUG.print(F("Setting pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(F(" port config mask to = 0x"));
// FIRMATADEBUG.println(portConfigInputs[pin/8], HEX);
}
pinState[pin] = 0;
switch(mode) {
case ANALOG:
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to analog"));
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT); // disable output driver
}
pinConfig[pin] = ANALOG;
lastAnalogReads[BLE_Firmata.PIN_TO_ANALOG(pin)] = -1;
}
break;
case INPUT:
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to input"));
if (AUTO_INPUT_PULLUPS) {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT_PULLUP); // disable output driver
} else {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT); // disable output driver
}
pinConfig[pin] = INPUT;
// force sending state immediately
//delay(10);
//checkDigitalInputs(true);
}
break;
case OUTPUT:
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to output"));
digitalWrite(BLE_Firmata.PIN_TO_DIGITAL(pin), LOW); // disable PWM
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), OUTPUT);
pinConfig[pin] = OUTPUT;
}
break;
case PWM:
if (BLE_Firmata.IS_PIN_PWM(pin)) {
FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to PWM"));
pinMode(BLE_Firmata.PIN_TO_PWM(pin), OUTPUT);
analogWrite(BLE_Firmata.PIN_TO_PWM(pin), 0);
pinConfig[pin] = PWM;
}
break;
case SERVO:
if (BLE_Firmata.IS_PIN_SERVO(pin)) {
pinConfig[pin] = SERVO;
if (!servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached()) {
servos[BLE_Firmata.PIN_TO_SERVO(pin)].attach(BLE_Firmata.PIN_TO_DIGITAL(pin));
}
}
break;
case I2C:
if (BLE_Firmata.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:
FIRMATADEBUG.print(F("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 (BLE_Firmata.IS_PIN_SERVO(pin))
servos[BLE_Firmata.PIN_TO_SERVO(pin)].write(value);
pinState[pin] = value;
break;
case PWM:
if (BLE_Firmata.IS_PIN_PWM(pin))
analogWrite(BLE_Firmata.PIN_TO_PWM(pin), value);
FIRMATADEBUG.print("pwm("); FIRMATADEBUG.print(BLE_Firmata.PIN_TO_PWM(pin)); FIRMATADEBUG.print(","); FIRMATADEBUG.print(value); FIRMATADEBUG.println(")");
pinState[pin] = value;
break;
}
}
}
void digitalWriteCallback(byte port, int value)
{
//FIRMATADEBUG.print("DWCx"); FIRMATADEBUG.print(port, HEX); FIRMATADEBUG.print(" "); FIRMATADEBUG.println(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 (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
// only write to OUTPUT
// do not touch pins in PWM, ANALOG, SERVO or other modes
if (pinConfig[pin] == OUTPUT) {
pinWriteMask |= mask;
pinState[pin] = ((byte)value & mask) ? 1 : 0;
}
}
mask = mask << 1;
}
FIRMATADEBUG.print(F("Write digital port #")); FIRMATADEBUG.print(port);
FIRMATADEBUG.print(F(" = 0x")); FIRMATADEBUG.print(value, HEX);
FIRMATADEBUG.print(F(" mask = 0x")); FIRMATADEBUG.println(pinWriteMask, HEX);
BLE_Firmata.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 < BLE_Firmata._num_analogiopins) {
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << analogPin);
FIRMATADEBUG.print(F("Stop reporting analog pin #")); FIRMATADEBUG.println(analogPin);
} else {
analogInputsToReport |= (1 << analogPin);
FIRMATADEBUG.print(F("Will report analog pin #")); FIRMATADEBUG.println(analogPin);
}
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
if (port < TOTAL_PORTS) {
//FIRMATADEBUG.print(F("Will report 0x")); FIRMATADEBUG.print(value, HEX); FIRMATADEBUG.print(F(" digital mask on port ")); FIRMATADEBUG.println(port);
reportPINs[port] = (byte)value;
}
// 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 slaveRegister;
byte data;
unsigned int delayTime;
FIRMATADEBUG.println("********** Sysex callback");
switch(command) {
case I2C_REQUEST:
mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) {
//BLE_Firmata.sendString("10-bit addressing mode is not yet supported");
FIRMATADEBUG.println(F("10-bit addressing mode is not yet 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);
#if ARDUINO >= 100
Wire.write(data);
#else
Wire.send(data);
#endif
}
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
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
BLE_Firmata.sendString("too many queries");
break;
}
queryIndex++;
query[queryIndex].addr = slaveAddress;
query[queryIndex].reg = argv[2] + (argv[3] << 7);
query[queryIndex].bytes = argv[4] + (argv[5] << 7);
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 < MAX_QUERIES) {
query[i].addr = query[i+1].addr;
query[i].reg = query[i+1].addr;
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 (BLE_Firmata.IS_PIN_SERVO(pin)) {
if (servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached())
servos[BLE_Firmata.PIN_TO_SERVO(pin)].detach();
servos[BLE_Firmata.PIN_TO_SERVO(pin)].attach(BLE_Firmata.PIN_TO_DIGITAL(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 {
//BLE_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:
bluefruit.write(START_SYSEX);
bluefruit.write(CAPABILITY_RESPONSE);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(START_SYSEX, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(CAPABILITY_RESPONSE, HEX);
delay(10);
for (byte pin=0; pin < TOTAL_PINS; pin++) {
//FIRMATADEBUG.print("\t#"); FIRMATADEBUG.println(pin);
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
bluefruit.write((byte)INPUT);
bluefruit.write(1);
bluefruit.write((byte)OUTPUT);
bluefruit.write(1);
/*
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(INPUT, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(1, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(OUTPUT, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(1, HEX);
*/
delay(20);
} else {
bluefruit.write(127);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(127, HEX);
delay(20);
continue;
}
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
bluefruit.write(ANALOG);
bluefruit.write(10);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(ANALOG, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(10, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_PWM(pin)) {
bluefruit.write(PWM);
bluefruit.write(8);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(PWM, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(8, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_SERVO(pin)) {
bluefruit.write(SERVO);
bluefruit.write(14);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(SERVO, HEX);FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(14, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_I2C(pin)) {
bluefruit.write(I2C);
bluefruit.write(1); // to do: determine appropriate value
delay(20);
}
bluefruit.write(127);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(127, HEX);
}
bluefruit.write(END_SYSEX);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(END_SYSEX, HEX);
break;
case PIN_STATE_QUERY:
if (argc > 0) {
byte pin=argv[0];
bluefruit.write(START_SYSEX);
bluefruit.write(PIN_STATE_RESPONSE);
bluefruit.write(pin);
if (pin < TOTAL_PINS) {
bluefruit.write((byte)pinConfig[pin]);
bluefruit.write((byte)pinState[pin] & 0x7F);
if (pinState[pin] & 0xFF80) bluefruit.write((byte)(pinState[pin] >> 7) & 0x7F);
if (pinState[pin] & 0xC000) bluefruit.write((byte)(pinState[pin] >> 14) & 0x7F);
}
bluefruit.write(END_SYSEX);
}
break;
case ANALOG_MAPPING_QUERY:
FIRMATADEBUG.println("Analog mapping query");
bluefruit.write(START_SYSEX);
bluefruit.write(ANALOG_MAPPING_RESPONSE);
for (byte pin=0; pin < TOTAL_PINS; pin++) {
bluefruit.write(BLE_Firmata.IS_PIN_ANALOG(pin) ? BLE_Firmata.PIN_TO_ANALOG(pin) : 127);
}
bluefruit.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(BLE_Firmata.IS_PIN_I2C(i)) {
// mark pins as i2c so they are ignore in non i2c data requests
setPinModeCallback(i, I2C);
}
}
isI2CEnabled = true;
// is there enough time before the first I2C request to call this here?
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;
// uncomment the following if or when the end() method is added to Wire library
// Wire.end();
}
/*==============================================================================
* SETUP()
*============================================================================*/
void systemResetCallback()
{
// initialize a defalt state
FIRMATADEBUG.println(F("***RESET***"));
// 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;
}
// pins with analog capability default to analog input
// otherwise, pins default to digital output
for (byte i=0; i < TOTAL_PINS; i++) {
if (BLE_Firmata.IS_PIN_ANALOG(i)) {
// turns off pullup, configures everything
setPinModeCallback(i, ANALOG);
} else {
// sets the output to 0, configures portConfigInputs
setPinModeCallback(i, INPUT);
}
}
// by default, do not report any analog inputs
analogInputsToReport = 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);
}
*/
}
void setup()
{
if (WAITFORSERIAL) {
while (!FIRMATADEBUG) delay(1);
}
FIRMATADEBUG.begin(9600);
FIRMATADEBUG.println(F("Adafruit Bluefruit LE Firmata test"));
FIRMATADEBUG.print("Total pins: "); FIRMATADEBUG.println(NUM_DIGITAL_PINS);
FIRMATADEBUG.print("Analog pins: "); FIRMATADEBUG.println(sizeof(boards_analogiopins));
//for (uint8_t i=0; i<sizeof(boards_analogiopins); i++) {
// FIRMATADEBUG.println(boards_analogiopins[i]);
//}
BLE_Firmata.setUsablePins(boards_digitaliopins, sizeof(boards_digitaliopins),
boards_analogiopins, sizeof(boards_analogiopins),
boards_pwmpins, sizeof(boards_pwmpins),
boards_servopins, sizeof(boards_servopins), SDA, SCL);
/* Initialise the module */
FIRMATADEBUG.print(F("Initialising the Bluefruit LE module: "));
if ( !bluefruit.begin(VERBOSE_MODE) )
{
error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
}
FIRMATADEBUG.println( F("OK!") );
/* Perform a factory reset to make sure everything is in a known state */
FIRMATADEBUG.println(F("Performing a factory reset: "));
if (! bluefruit.factoryReset() ){
error(F("Couldn't factory reset"));
}
/* Disable command echo from Bluefruit */
bluefruit.echo(false);
FIRMATADEBUG.println("Requesting Bluefruit info:");
/* Print Bluefruit information */
bluefruit.info();
FIRMATADEBUG.println("Setting name to BLE Firmata");
bluefruit.println("AT+GAPDEVNAME=BLE_Firmata");
BTLEstatus = false;
}
void firmataInit() {
FIRMATADEBUG.println(F("Init firmata"));
//BLE_Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION);
//FIRMATADEBUG.println(F("firmata analog"));
BLE_Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
//FIRMATADEBUG.println(F("firmata digital"));
BLE_Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
//FIRMATADEBUG.println(F("firmata analog report"));
BLE_Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
//FIRMATADEBUG.println(F("firmata digital report"));
BLE_Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
//FIRMATADEBUG.println(F("firmata pinmode"));
BLE_Firmata.attach(SET_PIN_MODE, setPinModeCallback);
//FIRMATADEBUG.println(F("firmata sysex"));
BLE_Firmata.attach(START_SYSEX, sysexCallback);
//FIRMATADEBUG.println(F("firmata reset"));
BLE_Firmata.attach(SYSTEM_RESET, systemResetCallback);
FIRMATADEBUG.println(F("Begin firmata"));
BLE_Firmata.begin();
systemResetCallback(); // reset to default config
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
delay(100);
// Link status check
if (! BTLEstatus) {
bluefruit.setMode(BLUEFRUIT_MODE_COMMAND);
BTLEstatus = bluefruit.isConnected();
bluefruit.setMode(BLUEFRUIT_MODE_DATA);
}
// Check if something has changed
if (BTLEstatus != lastBTLEstatus) {
// print it out!
if (BTLEstatus == true) {
FIRMATADEBUG.println(F("* Connected!"));
// initialize Firmata cleanly
bluefruit.setMode(BLUEFRUIT_MODE_DATA);
firmataInit();
}
if (BTLEstatus == false) {
FIRMATADEBUG.println(F("* Disconnected or advertising timed out"));
}
// OK set the last status change to this one
lastBTLEstatus = BTLEstatus;
}
// if not connected... bail
if (! BTLEstatus) {
delay(100);
return;
}
// For debugging, see if there's data on the serial console, we would forwad it to BTLE
if (FIRMATADEBUG.available()) {
bluefruit.write(FIRMATADEBUG.read());
}
// Onto the Firmata main loop
byte pin, analogPin;
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* BTLE buffer using FIRMATADEBUG.print() */
checkDigitalInputs();
/* SERIALREAD - processing incoming messagse as soon as possible, while still
* checking digital inputs. */
while(BLE_Firmata.available()) {
// FIRMATADEBUG.println(F("*data available*"));
BLE_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. */
// make the sampling interval longer if we have more analog inputs!
uint8_t analogreportnums = 0;
for(uint8_t a=0; a<8; a++) {
if (analogInputsToReport & (1 << a)) {
analogreportnums++;
}
}
samplingInterval = (uint16_t)MINIMUM_SAMPLE_DELAY + (uint16_t)ANALOG_SAMPLE_DELAY * (1+analogreportnums);
currentMillis = millis();
if (currentMillis - previousMillis > samplingInterval) {
previousMillis += samplingInterval;
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
for(pin=0; pin<TOTAL_PINS; pin++) {
// FIRMATADEBUG.print("pin #"); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(" config = "); FIRMATADEBUG.println(pinConfig[pin]);
if (BLE_Firmata.IS_PIN_ANALOG(pin) && (pinConfig[pin] == ANALOG)) {
analogPin = BLE_Firmata.PIN_TO_ANALOG(pin);
if (analogInputsToReport & (1 << analogPin)) {
int currentRead = analogRead(analogPin);
if ((lastAnalogReads[analogPin] == -1) || (lastAnalogReads[analogPin] != currentRead)) {
//FIRMATADEBUG.print(F("Analog")); FIRMATADEBUG.print(analogPin); FIRMATADEBUG.print(F(" = ")); FIRMATADEBUG.println(currentRead);
BLE_Firmata.sendAnalog(analogPin, currentRead);
lastAnalogReads[analogPin] = currentRead;
}
}
}
}
// 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);
}
}
}
}

View File

@@ -0,0 +1,9 @@
// Connect CLK/MISO/MOSI to hardware SPI
// e.g. On UNO & compatible: CLK = 13, MISO = 12, MOSI = 11
#define ADAFRUITBLE_REQ 10
#define ADAFRUITBLE_RST 9
#define ADAFRUITBLE_RDY 2 // This should be an interrupt pin, on Uno thats #2 or #3
// so we have digital 3-8 and analog 0-6 for use!

View File

@@ -0,0 +1,811 @@
#include <Servo.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BLE_Firmata.h>
#include "Adafruit_BLE_UART.h"
// Change this to whatever is the Serial console you want, either Serial or SerialUSB
#define FIRMATADEBUG Serial
// Pause for Serial console before beginning?
#define WAITFORSERIAL true
// Print all BLE interactions?
#define VERBOSE_MODE false
// Pullups on all input pins?
#define AUTO_INPUT_PULLUPS true
/************************ CONFIGURATION SECTION ***********************************/
/*
Don't forget to also change the BluefruitConfig.h for the SPI connection
and pinout you are using!
Then below, you can edit the list of pins that are available. Remove any pins
that are used for accessories or for talking to the BLE module!
*/
/************** For UNO + nRF8001 SPI breakout ************/
uint8_t boards_digitaliopins[] = {3, 4, 5, 6, 7, 8, A0, A1, A2, A3, A4, A5};
#if defined(__AVR_ATmega328P__)
// Standard setup for UNO, no need to tweak
uint8_t boards_analogiopins[] = {A0, A1, A2, A3, A4, A5}; // A0 == digital 14, etc
uint8_t boards_pwmpins[] = {3, 5, 6, 9, 10, 11};
uint8_t boards_servopins[] = {9, 10};
uint8_t boards_i2cpins[] = {SDA, SCL};
#elif defined(__AVR_ATmega32U4__)
uint8_t boards_analogiopins[] = {A0, A1, A2, A3, A4, A5}; // A0 == digital 14, etc
uint8_t boards_pwmpins[] = {3, 5, 6, 9, 10, 11, 13};
uint8_t boards_servopins[] = {9, 10};
uint8_t boards_i2cpins[] = {SDA, SCL};
#elif defined(__SAMD21G18A__)
#define SDA PIN_WIRE_SDA
#define SCL PIN_WIRE_SCL
uint8_t boards_analogiopins[] = {PIN_A0, PIN_A1, PIN_A2, PIN_A3, PIN_A4, PIN_A5,PIN_A6, PIN_A7}; // A0 == digital 14, etc
uint8_t boards_pwmpins[] = {3,4,5,6,8,10,11,12,A0,A1,A2,A3,A4,A5};
uint8_t boards_servopins[] = {9, 10};
uint8_t boards_i2cpins[] = {SDA, SCL};
#define NUM_DIGITAL_PINS 26
#endif
#define TOTAL_PINS NUM_DIGITAL_PINS /* highest number in boards_digitaliopins MEMEFIXME:automate */
#define TOTAL_PORTS ((TOTAL_PINS + 7) / 8)
/***********************************************************/
#include "Adafruit_BLE_Firmata_Boards.h"
#include "BluefruitConfig.h"
// Create the bluetooth breakout instance, set the pins in the BluefruitConfig.h file!
Adafruit_BLE_UART bluefruit = Adafruit_BLE_UART(ADAFRUITBLE_REQ, ADAFRUITBLE_RDY, ADAFRUITBLE_RST);
// our current connection status
aci_evt_opcode_t lastBTLEstatus, BTLEstatus;
// make one instance for the user to use
Adafruit_BLE_FirmataClass BLE_Firmata = Adafruit_BLE_FirmataClass(bluefruit);
// A small helper
void error(const __FlashStringHelper*err) {
FIRMATADEBUG.println(err);
while (1);
}
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
int lastAnalogReads[NUM_ANALOG_INPUTS];
/* 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
int samplingInterval = 200; // how often to run the main loop (in ms)
#define MINIMUM_SAMPLE_DELAY 150
#define ANALOG_SAMPLE_DELAY 50
/* i2c data */
struct i2c_device_info {
byte addr;
byte reg;
byte bytes;
};
/* for i2c read continuous more */
i2c_device_info query[MAX_QUERIES];
byte i2cRxData[32];
boolean isI2CEnabled = false;
signed char queryIndex = -1;
unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom()
Servo servos[MAX_SERVOS];
/*==============================================================================
* FUNCTIONS
*============================================================================*/
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 != REGISTER_NOT_SPECIFIED) {
Wire.beginTransmission(address);
#if ARDUINO >= 100
Wire.write((byte)theRegister);
#else
Wire.send((byte)theRegister);
#endif
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); // all bytes are returned in requestFrom
// 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++) {
#if ARDUINO >= 100
i2cRxData[2 + i] = Wire.read();
#else
i2cRxData[2 + i] = Wire.receive();
#endif
}
}
else {
if(numBytes > Wire.available()) {
BLE_Firmata.sendString("I2C Read Error: Too many bytes received");
} else {
BLE_Firmata.sendString("I2C Read Error: Too few bytes received");
}
}
// send slave address, register and received bytes
BLE_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) {
//FIRMATADEBUG.print(F("Sending update for port ")); FIRMATADEBUG.print(portNumber); FIRMATADEBUG.print(" = 0x"); FIRMATADEBUG.println(portValue, HEX);
BLE_Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using () */
void checkDigitalInputs(boolean forceSend = false)
{
/* 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. */
for (uint8_t i=0; i<TOTAL_PORTS; i++) {
if (reportPINs[i]) {
// FIRMATADEBUG.print("Reporting on port "); FIRMATADEBUG.print(i); FIRMATADEBUG.print(" mask 0x"); FIRMATADEBUG.println(portConfigInputs[i], HEX);
uint8_t x = BLE_Firmata.readPort(i, portConfigInputs[i]);
// FIRMATADEBUG.print("Read 0x"); FIRMATADEBUG.println(x, HEX);
outputPort(i, x, forceSend);
}
}
}
// -----------------------------------------------------------------------------
/* 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)
{
//FIRMATADEBUG.print("Setting pin #"); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(" to "); FIRMATADEBUG.println(mode);
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 (BLE_Firmata.IS_PIN_SERVO(pin) && mode != SERVO && servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached()) {
servos[BLE_Firmata.PIN_TO_SERVO(pin)].detach();
}
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
reportAnalogCallback(BLE_Firmata.PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting
}
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
if (mode == INPUT) {
portConfigInputs[pin/8] |= (1 << (pin & 7));
} else {
portConfigInputs[pin/8] &= ~(1 << (pin & 7));
}
// FIRMATADEBUG.print(F("Setting pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(F(" port config mask to = 0x"));
// FIRMATADEBUG.println(portConfigInputs[pin/8], HEX);
}
pinState[pin] = 0;
switch(mode) {
case ANALOG:
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
//FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to analog"));
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT); // disable output driver
}
pinConfig[pin] = ANALOG;
lastAnalogReads[BLE_Firmata.PIN_TO_ANALOG(pin)] = -1;
}
break;
case INPUT:
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
//FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to input"));
if (AUTO_INPUT_PULLUPS) {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT_PULLUP); // disable output driver
} else {
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), INPUT); // disable output driver
}
pinConfig[pin] = INPUT;
// force sending state immediately
//delay(10);
//checkDigitalInputs(true);
}
break;
case OUTPUT:
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
//FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to output"));
digitalWrite(BLE_Firmata.PIN_TO_DIGITAL(pin), LOW); // disable PWM
pinMode(BLE_Firmata.PIN_TO_DIGITAL(pin), OUTPUT);
pinConfig[pin] = OUTPUT;
}
break;
case PWM:
if (BLE_Firmata.IS_PIN_PWM(pin)) {
//FIRMATADEBUG.print(F("Set pin #")); FIRMATADEBUG.print(pin); FIRMATADEBUG.println(F(" to PWM"));
pinMode(BLE_Firmata.PIN_TO_PWM(pin), OUTPUT);
analogWrite(BLE_Firmata.PIN_TO_PWM(pin), 0);
pinConfig[pin] = PWM;
}
break;
case SERVO:
if (BLE_Firmata.IS_PIN_SERVO(pin)) {
pinConfig[pin] = SERVO;
if (!servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached()) {
servos[BLE_Firmata.PIN_TO_SERVO(pin)].attach(BLE_Firmata.PIN_TO_DIGITAL(pin));
}
}
break;
case I2C:
if (BLE_Firmata.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:
FIRMATADEBUG.print(F("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 (BLE_Firmata.IS_PIN_SERVO(pin))
servos[BLE_Firmata.PIN_TO_SERVO(pin)].write(value);
pinState[pin] = value;
break;
case PWM:
if (BLE_Firmata.IS_PIN_PWM(pin))
analogWrite(BLE_Firmata.PIN_TO_PWM(pin), value);
//FIRMATADEBUG.print("pwm("); FIRMATADEBUG.print(BLE_Firmata.PIN_TO_PWM(pin)); FIRMATADEBUG.print(","); FIRMATADEBUG.print(value); FIRMATADEBUG.println(")");
pinState[pin] = value;
break;
}
}
}
void digitalWriteCallback(byte port, int value)
{
//FIRMATADEBUG.print("DWCx"); FIRMATADEBUG.print(port, HEX); FIRMATADEBUG.print(" "); FIRMATADEBUG.println(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 (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
// only write to OUTPUT
// do not touch pins in PWM, ANALOG, SERVO or other modes
if (pinConfig[pin] == OUTPUT) {
pinWriteMask |= mask;
pinState[pin] = ((byte)value & mask) ? 1 : 0;
}
}
mask = mask << 1;
}
//FIRMATADEBUG.print(F("Write digital port #")); FIRMATADEBUG.print(port);
//FIRMATADEBUG.print(F(" = 0x")); FIRMATADEBUG.print(value, HEX);
//FIRMATADEBUG.print(F(" mask = 0x")); FIRMATADEBUG.println(pinWriteMask, HEX);
BLE_Firmata.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 < BLE_Firmata._num_analogiopins) {
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << analogPin);
//FIRMATADEBUG.print(F("Stop reporting analog pin #")); FIRMATADEBUG.println(analogPin);
} else {
analogInputsToReport |= (1 << analogPin);
//FIRMATADEBUG.print(F("Will report analog pin #")); FIRMATADEBUG.println(analogPin);
}
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
if (port < TOTAL_PORTS) {
//FIRMATADEBUG.print(F("Will report 0x")); FIRMATADEBUG.print(value, HEX); FIRMATADEBUG.print(F(" digital mask on port ")); FIRMATADEBUG.println(port);
reportPINs[port] = (byte)value;
}
// 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 slaveRegister;
byte data;
unsigned int delayTime;
FIRMATADEBUG.println("********** Sysex callback");
switch(command) {
case I2C_REQUEST:
mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) {
//BLE_Firmata.sendString("10-bit addressing mode is not yet supported");
//FIRMATADEBUG.println(F("10-bit addressing mode is not yet 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);
#if ARDUINO >= 100
Wire.write(data);
#else
Wire.send(data);
#endif
}
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
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
BLE_Firmata.sendString("too many queries");
break;
}
queryIndex++;
query[queryIndex].addr = slaveAddress;
query[queryIndex].reg = argv[2] + (argv[3] << 7);
query[queryIndex].bytes = argv[4] + (argv[5] << 7);
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 < MAX_QUERIES) {
query[i].addr = query[i+1].addr;
query[i].reg = query[i+1].addr;
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 (BLE_Firmata.IS_PIN_SERVO(pin)) {
if (servos[BLE_Firmata.PIN_TO_SERVO(pin)].attached())
servos[BLE_Firmata.PIN_TO_SERVO(pin)].detach();
servos[BLE_Firmata.PIN_TO_SERVO(pin)].attach(BLE_Firmata.PIN_TO_DIGITAL(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 {
//BLE_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:
bluefruit.write(START_SYSEX);
bluefruit.write(CAPABILITY_RESPONSE);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(START_SYSEX, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(CAPABILITY_RESPONSE, HEX);
delay(10);
for (byte pin=0; pin < TOTAL_PINS; pin++) {
//FIRMATADEBUG.print("\t#"); FIRMATADEBUG.println(pin);
if (BLE_Firmata.IS_PIN_DIGITAL(pin)) {
bluefruit.write((byte)INPUT);
bluefruit.write(1);
bluefruit.write((byte)OUTPUT);
bluefruit.write(1);
/*
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(INPUT, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(1, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(OUTPUT, HEX);
FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(1, HEX);
*/
delay(20);
} else {
bluefruit.write(127);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(127, HEX);
delay(20);
continue;
}
if (BLE_Firmata.IS_PIN_ANALOG(pin)) {
bluefruit.write(ANALOG);
bluefruit.write(10);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(ANALOG, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(10, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_PWM(pin)) {
bluefruit.write(PWM);
bluefruit.write(8);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(PWM, HEX); FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(8, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_SERVO(pin)) {
bluefruit.write(SERVO);
bluefruit.write(14);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.print(SERVO, HEX);FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(14, HEX);
delay(20);
}
if (BLE_Firmata.IS_PIN_I2C(pin)) {
bluefruit.write(I2C);
bluefruit.write(1); // to do: determine appropriate value
delay(20);
}
bluefruit.write(127);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(127, HEX);
}
bluefruit.write(END_SYSEX);
//FIRMATADEBUG.print(" 0x"); FIRMATADEBUG.println(END_SYSEX, HEX);
break;
case PIN_STATE_QUERY:
if (argc > 0) {
byte pin=argv[0];
bluefruit.write(START_SYSEX);
bluefruit.write(PIN_STATE_RESPONSE);
bluefruit.write(pin);
if (pin < TOTAL_PINS) {
bluefruit.write((byte)pinConfig[pin]);
bluefruit.write((byte)pinState[pin] & 0x7F);
if (pinState[pin] & 0xFF80) bluefruit.write((byte)(pinState[pin] >> 7) & 0x7F);
if (pinState[pin] & 0xC000) bluefruit.write((byte)(pinState[pin] >> 14) & 0x7F);
}
bluefruit.write(END_SYSEX);
}
break;
case ANALOG_MAPPING_QUERY:
//FIRMATADEBUG.println("Analog mapping query");
bluefruit.write(START_SYSEX);
bluefruit.write(ANALOG_MAPPING_RESPONSE);
for (byte pin=0; pin < TOTAL_PINS; pin++) {
bluefruit.write(BLE_Firmata.IS_PIN_ANALOG(pin) ? BLE_Firmata.PIN_TO_ANALOG(pin) : 127);
}
bluefruit.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(BLE_Firmata.IS_PIN_I2C(i)) {
// mark pins as i2c so they are ignore in non i2c data requests
setPinModeCallback(i, I2C);
}
}
isI2CEnabled = true;
// is there enough time before the first I2C request to call this here?
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;
// uncomment the following if or when the end() method is added to Wire library
// Wire.end();
}
/*==============================================================================
* SETUP()
*============================================================================*/
void systemResetCallback()
{
// initialize a defalt state
FIRMATADEBUG.println(F("***RESET***"));
// 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;
}
// pins with analog capability default to analog input
// otherwise, pins default to digital output
for (byte i=0; i < TOTAL_PINS; i++) {
if (BLE_Firmata.IS_PIN_ANALOG(i)) {
// turns off pullup, configures everything
setPinModeCallback(i, ANALOG);
} else {
// sets the output to 0, configures portConfigInputs
setPinModeCallback(i, INPUT);
}
}
// by default, do not report any analog inputs
analogInputsToReport = 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);
}
*/
}
void setup()
{
if (WAITFORSERIAL) {
while (!FIRMATADEBUG) delay(1);
}
FIRMATADEBUG.begin(9600);
FIRMATADEBUG.println(F("Adafruit Bluefruit nRF8001 Firmata test"));
FIRMATADEBUG.print("Total pins: "); FIRMATADEBUG.println(NUM_DIGITAL_PINS);
FIRMATADEBUG.print("Analog pins: "); FIRMATADEBUG.println(sizeof(boards_analogiopins));
//for (uint8_t i=0; i<sizeof(boards_analogiopins); i++) {
// FIRMATADEBUG.println(boards_analogiopins[i]);
//}
BLE_Firmata.setUsablePins(boards_digitaliopins, sizeof(boards_digitaliopins),
boards_analogiopins, sizeof(boards_analogiopins),
boards_pwmpins, sizeof(boards_pwmpins),
boards_servopins, sizeof(boards_servopins), SDA, SCL);
/* Initialise the module */
FIRMATADEBUG.print(F("Init nRF8001: "));
if (! bluefruit.begin()) {
error(F("Failed"));
}
bluefruit.setDeviceName("ADA_BLE");
FIRMATADEBUG.println(F("Done"));
BTLEstatus = lastBTLEstatus = ACI_EVT_DISCONNECTED;
}
void firmataInit() {
FIRMATADEBUG.println(F("Init firmata"));
//BLE_Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION);
//FIRMATADEBUG.println(F("firmata analog"));
BLE_Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
//FIRMATADEBUG.println(F("firmata digital"));
BLE_Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
//FIRMATADEBUG.println(F("firmata analog report"));
BLE_Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
//FIRMATADEBUG.println(F("firmata digital report"));
BLE_Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
//FIRMATADEBUG.println(F("firmata pinmode"));
BLE_Firmata.attach(SET_PIN_MODE, setPinModeCallback);
//FIRMATADEBUG.println(F("firmata sysex"));
BLE_Firmata.attach(START_SYSEX, sysexCallback);
//FIRMATADEBUG.println(F("firmata reset"));
BLE_Firmata.attach(SYSTEM_RESET, systemResetCallback);
FIRMATADEBUG.println(F("Begin firmata"));
BLE_Firmata.begin();
systemResetCallback(); // reset to default config
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
// Check the BTLE link, how're we doing?
bluefruit.pollACI();
// Link status check
BTLEstatus = bluefruit.getState();
// Check if something has changed
if (BTLEstatus != lastBTLEstatus) {
// print it out!
if (BTLEstatus == ACI_EVT_DEVICE_STARTED) {
FIRMATADEBUG.println(F("* Advertising"));
}
if (BTLEstatus == ACI_EVT_CONNECTED) {
FIRMATADEBUG.println(F("* Connected!"));
// initialize Firmata cleanly
firmataInit();
}
if (BTLEstatus == ACI_EVT_DISCONNECTED) {
FIRMATADEBUG.println(F("* Disconnected"));
}
// OK set the last status change to this one
lastBTLEstatus = BTLEstatus;
}
// if not connected... bail
if (BTLEstatus != ACI_EVT_CONNECTED) {
delay(100);
return;
}
// For debugging, see if there's data on the serial console, we would forwad it to BTLE
if (FIRMATADEBUG.available()) {
bluefruit.write(FIRMATADEBUG.read());
}
// Onto the Firmata main loop
byte pin, analogPin;
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* BTLE buffer using FIRMATADEBUG.print() */
checkDigitalInputs();
/* SERIALREAD - processing incoming messagse as soon as possible, while still
* checking digital inputs. */
while(BLE_Firmata.available()) {
//FIRMATADEBUG.println(F("*data available*"));
BLE_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. */
// make the sampling interval longer if we have more analog inputs!
uint8_t analogreportnums = 0;
for(uint8_t a=0; a<8; a++) {
if (analogInputsToReport & (1 << a)) {
analogreportnums++;
}
}
samplingInterval = (uint16_t)MINIMUM_SAMPLE_DELAY + (uint16_t)ANALOG_SAMPLE_DELAY * (1+analogreportnums);
currentMillis = millis();
if (currentMillis - previousMillis > samplingInterval) {
previousMillis += samplingInterval;
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
for(pin=0; pin<TOTAL_PINS; pin++) {
// FIRMATADEBUG.print("pin #"); FIRMATADEBUG.print(pin); FIRMATADEBUG.print(" config = "); FIRMATADEBUG.println(pinConfig[pin]);
if (BLE_Firmata.IS_PIN_ANALOG(pin) && (pinConfig[pin] == ANALOG)) {
analogPin = BLE_Firmata.PIN_TO_ANALOG(pin);
if (analogInputsToReport & (1 << analogPin)) {
int currentRead = analogRead(analogPin);
if ((lastAnalogReads[analogPin] == -1) || (lastAnalogReads[analogPin] != currentRead)) {
//FIRMATADEBUG.print(F("Analog")); FIRMATADEBUG.print(analogPin); FIRMATADEBUG.print(F(" = ")); FIRMATADEBUG.println(currentRead);
BLE_Firmata.sendAnalog(analogPin, currentRead);
lastAnalogReads[analogPin] = currentRead;
}
}
}
}
// 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);
}
}
}
}

View File

@@ -0,0 +1,458 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

View File

@@ -0,0 +1,273 @@
# Arduino makefile
#
# This makefile allows you to build sketches from the command line
# without the Arduino environment (or Java).
#
# The Arduino environment does preliminary processing on a sketch before
# compiling it. If you're using this makefile instead, you'll need to do
# a few things differently:
#
# - Give your program's file a .cpp extension (e.g. foo.cpp).
#
# - Put this line at top of your code: #include <WProgram.h>
#
# - Write prototypes for all your functions (or define them before you
# call them). A prototype declares the types of parameters a
# function will take and what type of value it will return. This
# means that you can have a call to a function before the definition
# of the function. A function prototype looks like the first line of
# the function, with a semi-colon at the end. For example:
# int digitalRead(int pin);
#
# Instructions for using the makefile:
#
# 1. Copy this file into the folder with your sketch.
#
# 2. Below, modify the line containing "TARGET" to refer to the name of
# of your program's file without an extension (e.g. TARGET = foo).
#
# 3. Modify the line containg "ARDUINO" to point the directory that
# contains the Arduino core (for normal Arduino installations, this
# is the hardware/cores/arduino sub-directory).
#
# 4. Modify the line containing "PORT" to refer to the filename
# representing the USB or serial connection to your Arduino board
# (e.g. PORT = /dev/tty.USB0). If the exact name of this file
# changes, you can use * as a wildcard (e.g. PORT = /dev/tty.USB*).
#
# 5. At the command line, change to the directory containing your
# program's file and the makefile.
#
# 6. Type "make" and press enter to compile/verify your program.
#
# 7. Type "make upload", reset your Arduino board, and press enter to
# upload your program to the Arduino board.
#
# $Id: Makefile,v 1.7 2007/04/13 05:28:23 eighthave Exp $
PORT = /dev/tty.usbserial-*
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)
MCU = atmega168
#MCU = atmega8
F_CPU = 16000000
FORMAT = ihex
UPLOAD_RATE = 19200
# Name of this Makefile (used for "make depend").
MAKEFILE = Makefile
# Debugging format.
# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
DEBUG = stabs
OPT = s
# Place -D or -U options here
CDEFS = -DF_CPU=$(F_CPU)
CXXDEFS = -DF_CPU=$(F_CPU)
# Compiler flag to set the C Standard level.
# c89 - "ANSI" C
# gnu89 - c89 plus GCC extensions
# c99 - ISO C99 standard (not yet fully implemented)
# gnu99 - c99 plus GCC extensions
CSTANDARD = -std=gnu99
CDEBUG = -g$(DEBUG)
CWARN = -Wall -Wstrict-prototypes
CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
#CEXTRA = -Wa,-adhlns=$(<:.c=.lst)
CFLAGS = $(CDEBUG) $(CDEFS) $(INCLUDE) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA)
CXXFLAGS = $(CDEFS) $(INCLUDE) -O$(OPT)
#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
LDFLAGS =
# Programming support using avrdude. Settings and variables.
AVRDUDE_PROGRAMMER = stk500
AVRDUDE_PORT = $(PORT)
AVRDUDE_WRITE_FLASH = -U flash:w:applet/$(TARGET).hex
AVRDUDE_FLAGS = -F -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \
-b $(UPLOAD_RATE) -q -V
# Program settings
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
# Define all object files.
OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o)
# Define all listing files.
LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst)
# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS)
ALL_CXXFLAGS = -mmcu=$(MCU) -I. $(CXXFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
# Default target.
all: build
build: applet/$(TARGET).hex
eep: applet/$(TARGET).eep
lss: applet/$(TARGET).lss
sym: applet/$(TARGET).sym
# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000
coff: applet/$(TARGET).elf
$(COFFCONVERT) -O coff-avr applet/$(TARGET).elf applet/$(TARGET).cof
extcoff: applet/$(TARGET).elf
$(COFFCONVERT) -O coff-ext-avr applet/$(TARGET).elf applet/$(TARGET).cof
.SUFFIXES: .elf .hex .eep .lss .sym .pde
.elf.hex:
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
.elf.eep:
-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 -O $(FORMAT) $< $@
# Create extended listing file from ELF output file.
.elf.lss:
$(OBJDUMP) -h -S $< > $@
# Create a symbol table from ELF output file.
.elf.sym:
$(NM) -n $< > $@
# Compile: create object files from C++ source files.
.cpp.o: $(HEADERS)
$(CXX) -c $(ALL_CXXFLAGS) $< -o $@
# Compile: create object files from C source files.
.c.o: $(HEADERS)
$(CC) -c $(ALL_CFLAGS) $< -o $@
# Compile: create assembler files from C source files.
.c.s:
$(CC) -S $(ALL_CFLAGS) $< -o $@
# Assemble: create object files from assembler source files.
.S.o:
$(CC) -c $(ALL_ASFLAGS) $< -o $@
applet/$(TARGET).cpp: $(TARGET).pde
test -d applet || mkdir applet
echo '#include "WProgram.h"' > applet/$(TARGET).cpp
echo '#include "avr/interrupt.h"' >> applet/$(TARGET).cpp
sed -n 's|^\(void .*)\).*|\1;|p' $(TARGET).pde | grep -v 'setup()' | \
grep -v 'loop()' >> applet/$(TARGET).cpp
cat $(TARGET).pde >> applet/$(TARGET).cpp
cat $(ARDUINO_SRC)/main.cxx >> applet/$(TARGET).cpp
# Link: create ELF output file from object files.
applet/$(TARGET).elf: applet/$(TARGET).cpp $(OBJ)
$(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
# Program the device.
upload: applet/$(TARGET).hex
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
pd_test: build pd_close_serial upload
# Target: clean project.
clean:
$(REMOVE) -- applet/$(TARGET).hex applet/$(TARGET).eep \
applet/$(TARGET).cof applet/$(TARGET).elf $(TARGET).map \
applet/$(TARGET).sym applet/$(TARGET).lss applet/$(TARGET).cpp \
$(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d)
rmdir -- applet
depend:
if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \
then \
sed -e '/^# DO NOT DELETE/,$$d' $(MAKEFILE) > \
$(MAKEFILE).$$$$ && \
$(MV) $(MAKEFILE).$$$$ $(MAKEFILE); \
fi
echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \
>> $(MAKEFILE); \
$(CC) -M -mmcu=$(MCU) $(CDEFS) $(INCLUDE) $(SRC) $(ASRC) >> $(MAKEFILE)
.PHONY: all build eep lss sym coff extcoff clean depend pd_close_serial pd_test
# for emacs
etags:
make etags_`uname -s`
etags *.pde \
$(ARDUINO_SRC)/*.[ch] \
$(ARDUINO_SRC)/*.cpp \
$(ARDUINO_LIB_SRC)/*/*.[ch] \
$(ARDUINO_LIB_SRC)/*/*.cpp \
$(ARDUINO)/hardware/tools/avr/avr/include/avr/*.[ch] \
$(ARDUINO)/hardware/tools/avr/avr/include/*.[ch]
etags_Darwin:
# etags -a
etags_Linux:
# etags -a /usr/include/*.h linux/input.h /usr/include/sys/*.h
etags_MINGW:
# etags -a /usr/include/*.h /usr/include/sys/*.h
path:
echo $(PATH)
echo $$PATH

View File

@@ -0,0 +1,738 @@
/*
* 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.
*
* This version is modified to specifically work with Adafruit's BLE
* library. It no longer works over the standard "USB" connection!
*/
/*
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-2011 Jeff Hoefs. All rights reserved.
Copyright (C) 2014 Limor Fried/Kevin Townsend 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: use Program Control to load stored profiles from EEPROM
*/
#include <Servo.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BLE_Firmata.h>
#include "Adafruit_BLE_UART.h"
#define AUTO_INPUT_PULLUPS true
// Connect CLK/MISO/MOSI to hardware SPI
// e.g. On UNO & compatible: CLK = 13, MISO = 12, MOSI = 11
#define ADAFRUITBLE_REQ 10
#define ADAFRUITBLE_RDY 2 // This should be an interrupt pin, on Uno thats #2 or #3
#define ADAFRUITBLE_RST 9
// so we have digital 3-8 and analog 0-6
Adafruit_BLE_UART BLEserial = Adafruit_BLE_UART(ADAFRUITBLE_REQ, ADAFRUITBLE_RDY, ADAFRUITBLE_RST);
// make one instance for the user to use
Adafruit_BLE_FirmataClass BLE_Firmata(BLEserial);
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
int lastAnalogReads[NUM_ANALOG_INPUTS];
/* 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
int samplingInterval = 200; // how often to run the main loop (in ms)
#define MINIMUM_SAMPLE_DELAY 150
#define ANALOG_SAMPLE_DELAY 50
/* i2c data */
struct i2c_device_info {
byte addr;
byte reg;
byte bytes;
};
/* for i2c read continuous more */
i2c_device_info query[MAX_QUERIES];
byte i2cRxData[32];
boolean isI2CEnabled = false;
signed char queryIndex = -1;
unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom()
Servo servos[MAX_SERVOS];
/*==============================================================================
* FUNCTIONS
*============================================================================*/
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 != REGISTER_NOT_SPECIFIED) {
Wire.beginTransmission(address);
#if ARDUINO >= 100
Wire.write((byte)theRegister);
#else
Wire.send((byte)theRegister);
#endif
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); // all bytes are returned in requestFrom
// 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++) {
#if ARDUINO >= 100
i2cRxData[2 + i] = Wire.read();
#else
i2cRxData[2 + i] = Wire.receive();
#endif
}
}
else {
if(numBytes > Wire.available()) {
BLE_Firmata.sendString("I2C Read Error: Too many bytes received");
} else {
BLE_Firmata.sendString("I2C Read Error: Too few bytes received");
}
}
// send slave address, register and received bytes
BLE_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) {
Serial.print(F("Sending update for port ")); Serial.print(portNumber); Serial.print(" = 0x"); Serial.println(portValue, HEX);
BLE_Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[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(boolean forceSend = false)
{
/* 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. */
for (uint8_t i=0; i<TOTAL_PORTS; i++) {
if (reportPINs[i]) {
// Serial.print("Reporting on port "); Serial.print(i); Serial.print(" mask 0x"); Serial.println(portConfigInputs[i], HEX);
uint8_t x = readPort(i, portConfigInputs[i]);
// Serial.print("Read 0x"); Serial.println(x, HEX);
outputPort(i, x, forceSend);
}
}
}
// -----------------------------------------------------------------------------
/* 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] == 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_SERVO(pin) && mode != SERVO && servos[PIN_TO_SERVO(pin)].attached()) {
servos[PIN_TO_SERVO(pin)].detach();
}
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));
}
// Serial.print(F("Setting pin #")); Serial.print(pin); Serial.print(F(" port config mask to = 0x"));
// Serial.println(portConfigInputs[pin/8], HEX);
}
pinState[pin] = 0;
switch(mode) {
case ANALOG:
if (IS_PIN_ANALOG(pin)) {
Serial.print(F("Set pin #")); Serial.print(pin); Serial.println(F(" to analog"));
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;
lastAnalogReads[PIN_TO_ANALOG(pin)] = -1;
}
break;
case INPUT:
if (IS_PIN_DIGITAL(pin)) {
Serial.print(F("Set pin #")); Serial.print(pin); Serial.println(F(" to input"));
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
if (AUTO_INPUT_PULLUPS) {
digitalWrite(PIN_TO_DIGITAL(pin), HIGH); // enable internal pull-ups
} else {
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
}
pinConfig[pin] = INPUT;
// force sending state immediately
//delay(10);
//checkDigitalInputs(true);
}
break;
case OUTPUT:
if (IS_PIN_DIGITAL(pin)) {
Serial.print(F("Set pin #")); Serial.print(pin); Serial.println(F(" to output"));
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_SERVO(pin)) {
pinConfig[pin] = SERVO;
if (!servos[PIN_TO_SERVO(pin)].attached()) {
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));
}
}
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:
Serial.print(F("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_SERVO(pin))
servos[PIN_TO_SERVO(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;
if (AUTO_INPUT_PULLUPS && ( pinConfig[pin] == INPUT)) {
value |= mask;
}
}
}
mask = mask << 1;
}
Serial.print(F("Write digital port #")); Serial.print(port);
Serial.print(F(" = 0x")); Serial.print(value, HEX);
Serial.print(F(" mask = 0x")); Serial.println(pinWriteMask, HEX);
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);
Serial.print(F("Stop reporting analog pin #")); Serial.println(analogPin);
} else {
analogInputsToReport |= (1 << analogPin);
Serial.print(F("Will report analog pin #")); Serial.println(analogPin);
}
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
if (port < TOTAL_PORTS) {
Serial.print(F("Will report 0x")); Serial.print(value, HEX); Serial.print(F(" digital mask on port ")); Serial.println(port);
reportPINs[port] = (byte)value;
}
// 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 slaveRegister;
byte data;
unsigned int delayTime;
switch(command) {
case I2C_REQUEST:
mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) {
//BLE_Firmata.sendString("10-bit addressing mode is not yet supported");
Serial.println(F("10-bit addressing mode is not yet 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);
#if ARDUINO >= 100
Wire.write(data);
#else
Wire.send(data);
#endif
}
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
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
BLE_Firmata.sendString("too many queries");
break;
}
queryIndex++;
query[queryIndex].addr = slaveAddress;
query[queryIndex].reg = argv[2] + (argv[3] << 7);
query[queryIndex].bytes = argv[4] + (argv[5] << 7);
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 < MAX_QUERIES) {
query[i].addr = query[i+1].addr;
query[i].reg = query[i+1].addr;
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_SERVO(pin)) {
if (servos[PIN_TO_SERVO(pin)].attached())
servos[PIN_TO_SERVO(pin)].detach();
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(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 {
//BLE_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:
Serial.write(START_SYSEX);
Serial.write(CAPABILITY_RESPONSE);
for (byte pin=0; pin < TOTAL_PINS; pin++) {
if (IS_PIN_DIGITAL(pin)) {
Serial.write((byte)INPUT);
Serial.write(1);
Serial.write((byte)OUTPUT);
Serial.write(1);
}
if (IS_PIN_ANALOG(pin)) {
Serial.write(ANALOG);
Serial.write(10);
}
if (IS_PIN_PWM(pin)) {
Serial.write(PWM);
Serial.write(8);
}
if (IS_PIN_SERVO(pin)) {
Serial.write(SERVO);
Serial.write(14);
}
if (IS_PIN_I2C(pin)) {
Serial.write(I2C);
Serial.write(1); // to do: determine appropriate value
}
Serial.write(127);
}
Serial.write(END_SYSEX);
break;
case PIN_STATE_QUERY:
if (argc > 0) {
byte pin=argv[0];
Serial.write(START_SYSEX);
Serial.write(PIN_STATE_RESPONSE);
Serial.write(pin);
if (pin < TOTAL_PINS) {
Serial.write((byte)pinConfig[pin]);
Serial.write((byte)pinState[pin] & 0x7F);
if (pinState[pin] & 0xFF80) Serial.write((byte)(pinState[pin] >> 7) & 0x7F);
if (pinState[pin] & 0xC000) Serial.write((byte)(pinState[pin] >> 14) & 0x7F);
}
Serial.write(END_SYSEX);
}
break;
case ANALOG_MAPPING_QUERY:
Serial.write(START_SYSEX);
Serial.write(ANALOG_MAPPING_RESPONSE);
for (byte pin=0; pin < TOTAL_PINS; pin++) {
Serial.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127);
}
Serial.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;
// is there enough time before the first I2C request to call this here?
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;
// uncomment the following if or when the end() method is added to Wire library
// Wire.end();
}
/*==============================================================================
* SETUP()
*============================================================================*/
void systemResetCallback()
{
// initialize a defalt state
Serial.println(F("***RESET***"));
// 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;
}
// pins with analog capability default to analog input
// otherwise, pins default to digital output
for (byte i=0; i < TOTAL_PINS; i++) {
if (IS_PIN_ANALOG(i)) {
// turns off pullup, configures everything
setPinModeCallback(i, ANALOG);
} else {
// sets the output to 0, configures portConfigInputs
setPinModeCallback(i, INPUT);
}
}
// by default, do not report any analog inputs
analogInputsToReport = 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);
}
*/
}
aci_evt_opcode_t lastBTLEstatus, BTLEstatus;
void setup()
{
Serial.begin(9600);
Serial.println(F("Adafruit BTLE Firmata test"));
BLEserial.begin();
BTLEstatus = lastBTLEstatus = ACI_EVT_DISCONNECTED;
}
void firmataInit() {
Serial.println(F("Init firmata"));
//BLE_Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION);
//Serial.println(F("firmata analog"));
BLE_Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
//Serial.println(F("firmata digital"));
BLE_Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
//Serial.println(F("firmata analog report"));
BLE_Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
//Serial.println(F("firmata digital report"));
BLE_Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
//Serial.println(F("firmata pinmode"));
BLE_Firmata.attach(SET_PIN_MODE, setPinModeCallback);
//Serial.println(F("firmata sysex"));
BLE_Firmata.attach(START_SYSEX, sysexCallback);
//Serial.println(F("firmata reset"));
BLE_Firmata.attach(SYSTEM_RESET, systemResetCallback);
Serial.println(F("Begin firmata"));
BLE_Firmata.begin();
systemResetCallback(); // reset to default config
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
// Check the BTLE link, how're we doing?
BLEserial.pollACI();
// Link status check
BTLEstatus = BLEserial.getState();
// Check if something has changed
if (BTLEstatus != lastBTLEstatus) {
// print it out!
if (BTLEstatus == ACI_EVT_DEVICE_STARTED) {
Serial.println(F("* Advertising started"));
}
if (BTLEstatus == ACI_EVT_CONNECTED) {
Serial.println(F("* Connected!"));
// initialize Firmata cleanly
firmataInit();
}
if (BTLEstatus == ACI_EVT_DISCONNECTED) {
Serial.println(F("* Disconnected or advertising timed out"));
}
// OK set the last status change to this one
lastBTLEstatus = BTLEstatus;
}
// if not connected... bail
if (BTLEstatus != ACI_EVT_CONNECTED) {
delay(100);
return;
}
// For debugging, see if there's data on the serial console, we would forwad it to BTLE
if (Serial.available()) {
BLEserial.write(Serial.read());
}
// Onto the Firmata main loop
byte pin, analogPin;
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* BTLE buffer using Serial.print() */
checkDigitalInputs();
/* SERIALREAD - processing incoming messagse as soon as possible, while still
* checking digital inputs. */
while(BLE_Firmata.available()) {
//Serial.println(F("*data available*"));
BLE_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. */
// make the sampling interval longer if we have more analog inputs!
uint8_t analogreportnums = 0;
for(uint8_t a=0; a<8; a++) {
if (analogInputsToReport & (1 << a)) {
analogreportnums++;
}
}
samplingInterval = (uint16_t)MINIMUM_SAMPLE_DELAY + (uint16_t)ANALOG_SAMPLE_DELAY * (1+analogreportnums);
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)) {
int currentRead = analogRead(analogPin);
if ((lastAnalogReads[analogPin] == -1) || (lastAnalogReads[analogPin] != currentRead)) {
Serial.print(F("Analog")); Serial.print(analogPin); Serial.print(F(" = ")); Serial.println(currentRead);
BLE_Firmata.sendAnalog(analogPin, currentRead);
lastAnalogReads[analogPin] = currentRead;
}
}
}
}
// 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);
}
}
}
}

View File

@@ -0,0 +1,62 @@
#######################################
# Syntax Coloring Map For Firmata
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Firmata KEYWORD1
callbackFunction KEYWORD1
systemResetCallbackFunction KEYWORD1
stringCallbackFunction KEYWORD1
sysexCallbackFunction KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
begin KEYWORD2
printVersion KEYWORD2
blinkVersion KEYWORD2
printFirmwareVersion KEYWORD2
setFirmwareVersion KEYWORD2
setFirmwareNameAndVersion KEYWORD2
available KEYWORD2
processInput KEYWORD2
sendAnalog KEYWORD2
sendDigital KEYWORD2
sendDigitalPortPair KEYWORD2
sendDigitalPort KEYWORD2
sendString KEYWORD2
sendString KEYWORD2
sendSysex KEYWORD2
attach KEYWORD2
detach KEYWORD2
flush KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
MAX_DATA_BYTES LITERAL1
DIGITAL_MESSAGE LITERAL1
ANALOG_MESSAGE LITERAL1
REPORT_ANALOG LITERAL1
REPORT_DIGITAL LITERAL1
REPORT_VERSION LITERAL1
SET_PIN_MODE LITERAL1
SYSTEM_RESET LITERAL1
START_SYSEX LITERAL1
END_SYSEX LITERAL1
PWM LITERAL1
TOTAL_ANALOG_PINS LITERAL1
TOTAL_DIGITAL_PINS LITERAL1
TOTAL_PORTS LITERAL1
ANALOG_PORT LITERAL1

View File

@@ -0,0 +1,9 @@
name=Adafruit BLEFirmata
version=1.1.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Modified Firmata code to work with Adafruit's nRF8001 Breakout
paragraph=Modified Firmata code to work with Adafruit's nRF8001 Breakout
category=Communication
url=https://github.com/adafruit/Adafruit_BLEFirmata
architectures=*

View File

@@ -0,0 +1,270 @@
/*------------------------------------------------------------------------
An Arduino library for the ESP8266 WiFi-serial bridge
https://www.adafruit.com/product/2282
The ESP8266 is a 3.3V device. Safe operation with 5V devices (most
Arduino boards) requires a logic-level shifter for TX and RX signals.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried and Phil Burgess for Adafruit Industries.
MIT license, all text above must be included in any redistribution.
------------------------------------------------------------------------*/
#include "Adafruit_ESP8266.h"
// Constructor
Adafruit_ESP8266::Adafruit_ESP8266(Stream *s, Stream *d, int8_t r) :
stream(s), debug(d), reset_pin(r), host(NULL), writing(false) {
setTimeouts();
};
// Override various timings. Passing 0 for an item keeps current setting.
void Adafruit_ESP8266::setTimeouts(
uint32_t rcv, uint32_t rst, uint32_t con, uint32_t ipd) {
if(rcv) {
stream->setTimeout(rcv);
receiveTimeout = rcv;
}
if(rst) resetTimeout = rst;
if(con) connectTimeout = con;
if(ipd) ipdTimeout = ipd;
}
// Override boot marker string, or pass NULL to restore default.
void Adafruit_ESP8266::setBootMarker(Fstr *s) {
bootMarker = s ? s : defaultBootMarker;
}
// Anything printed to the EPS8266 object will be split to both the WiFi
// and debug streams. Saves having to print everything twice in debug code.
size_t Adafruit_ESP8266::write(uint8_t c) {
if(debug) {
if(!writing) {
debug->print(F("---> "));
writing = true;
}
debug->write(c);
}
return stream->write(c);
}
// Equivalent to Arduino Stream find() function, but with search string in
// flash/PROGMEM rather than RAM-resident. Returns true if string found
// (any further pending input remains in stream), false if timeout occurs.
// Can optionally pass NULL (or no argument) to read/purge the OK+CR/LF
// returned by most AT commands. The ipd flag indicates this call follows
// a CIPSEND request and might be broken into multiple sections with +IPD
// delimiters, which must be parsed and handled (as the search string may
// cross these delimiters and/or contain \r or \n itself).
boolean Adafruit_ESP8266::find(Fstr *str, boolean ipd) {
uint8_t stringLength, matchedLength = 0;
int c;
boolean found = false;
uint32_t t, save;
uint16_t bytesToGo = 0;
if(ipd) { // IPD stream stalls really long occasionally, what gives?
save = receiveTimeout;
setTimeouts(ipdTimeout);
}
if(str == NULL) str = F("OK\r\n");
stringLength = strlen_P((Pchr *)str);
if(debug && writing) {
debug->print(F("<--- '"));
writing = false;
}
for(t = millis();;) {
if(ipd && !bytesToGo) { // Expecting next IPD marker?
if(find(F("+IPD,"))) { // Find marker in stream
for(;;) {
if((c = stream->read()) > 0) { // Read subsequent chars...
if(debug) debug->write(c);
if(c == ':') break; // ...until delimiter.
bytesToGo = (bytesToGo * 10) + (c - '0'); // Keep count
t = millis(); // Timeout resets w/each byte received
} else if(c < 0) { // No data on stream, check for timeout
if((millis() - t) > receiveTimeout) goto bail;
} else goto bail; // EOD on stream
}
} else break; // OK (EOD) or ERROR
}
if((c = stream->read()) > 0) { // Character received?
if(debug) debug->write(c); // Copy to debug stream
bytesToGo--;
if(c == pgm_read_byte((Pchr *)str +
matchedLength)) { // Match next byte?
if(++matchedLength == stringLength) { // Matched whole string?
found = true; // Winner!
break;
}
} else { // Character mismatch; reset match pointer+counter
matchedLength = 0;
}
t = millis(); // Timeout resets w/each byte received
} else if(c < 0) { // No data on stream, check for timeout
if((millis() - t) > receiveTimeout) break; // You lose, good day sir
} else break; // End-of-data on stream
}
bail: // Sorry, dreaded goto. Because nested loops.
if(debug) debug->println('\'');
if(ipd) setTimeouts(save);
return found;
}
// Read from ESP8266 stream into RAM, up to a given size. Max number of
// chars read is 1 less than this, so NUL can be appended on string.
int Adafruit_ESP8266::readLine(char *buf, int bufSiz) {
if(debug && writing) {
debug->print(F("<--- '"));
writing = false;
}
int bytesRead = stream->readBytesUntil('\r', buf, bufSiz-1);
buf[bytesRead] = 0;
if(debug) {
debug->print(buf);
debug->println('\'');
}
while(stream->read() != '\n'); // Discard thru newline
return bytesRead;
}
// ESP8266 is reset by momentarily connecting RST to GND. Level shifting is
// not necessary provided you don't accidentally set the pin to HIGH output.
// It's generally safe-ish as the default Arduino pin state is INPUT (w/no
// pullup) -- setting to LOW provides an open-drain for reset.
// Returns true if expected boot message is received (or if RST is unused),
// false otherwise.
boolean Adafruit_ESP8266::hardReset(void) {
if(reset_pin < 0) return true;
digitalWrite(reset_pin, LOW);
pinMode(reset_pin, OUTPUT); // Open drain; reset -> GND
delay(10); // Hold a moment
pinMode(reset_pin, INPUT); // Back to high-impedance pin state
return find(bootMarker); // Purge boot message from stream
}
// Soft reset. Returns true if expected boot message received, else false.
boolean Adafruit_ESP8266::softReset(void) {
boolean found = false;
uint32_t save = receiveTimeout; // Temporarily override recveive timeout,
receiveTimeout = resetTimeout; // reset time is longer than normal I/O.
println(F("AT+RST")); // Issue soft-reset command
if(find(bootMarker)) { // Wait for boot message
println(F("ATE0")); // Turn off echo
found = find(); // OK?
}
receiveTimeout = save; // Restore normal receive timeout
return found;
}
// For interactive debugging...shuttle data between Serial Console <-> WiFi
void Adafruit_ESP8266::debugLoop(void) {
if(!debug) for(;;); // If no debug connection, nothing to do.
debug->println(F("\n========================"));
for(;;) {
if(debug->available()) stream->write(debug->read());
if(stream->available()) debug->write(stream->read());
}
}
// Connect to WiFi access point. SSID and password are flash-resident
// strings. May take several seconds to execute, this is normal.
// Returns true on successful connection, false otherwise.
boolean Adafruit_ESP8266::connectToAP(Fstr *ssid, Fstr *pass) {
char buf[256];
println(F("AT+CWMODE=1")); // WiFi mode = Sta
readLine(buf, sizeof(buf));
if(!(strstr_P(buf, (Pchr *)F("OK")) ||
strstr_P(buf, (Pchr *)F("no change")))) return false;
print(F("AT+CWJAP=\"")); // Join access point
print(ssid);
print(F("\",\""));
print(pass);
println('\"');
uint32_t save = receiveTimeout; // Temporarily override recv timeout,
receiveTimeout = connectTimeout; // connection time is much longer!
boolean found = find(); // Await 'OK' message
receiveTimeout = save; // Restore normal receive timeout
if(found) {
println(F("AT+CIPMUX=0")); // Set single-client mode
found = find(); // Await 'OK'
}
return found;
}
void Adafruit_ESP8266::closeAP(void) {
println(F("AT+CWQAP")); // Quit access point
find(); // Purge 'OK'
}
// Open TCP connection. Hostname is flash-resident string.
// Returns true on successful connection, else false.
boolean Adafruit_ESP8266::connectTCP(Fstr *h, int port) {
print(F("AT+CIPSTART=\"TCP\",\""));
print(h);
print(F("\","));
println(port);
if(find(F("Linked"))) {
host = h;
return true;
}
return false;
}
void Adafruit_ESP8266::closeTCP(void) {
println(F("AT+CIPCLOSE"));
find(F("Unlink\r\n"));
}
// Requests page from currently-open TCP connection. URL is
// flash-resident string. Returns true if request issued successfully,
// else false. Calling function should then handle data returned, may
// need to parse IPD delimiters (see notes in find() function.
// (Can call find(F("Unlink"), true) to dump to debug.)
boolean Adafruit_ESP8266::requestURL(Fstr *url) {
print(F("AT+CIPSEND="));
println(25 + strlen_P((Pchr *)url) + strlen_P((Pchr *)host));
if(find(F("> "))) { // Wait for prompt
print(F("GET ")); // 4
print(url);
print(F(" HTTP/1.1\r\nHost: ")); // 17
print(host);
print(F("\r\n\r\n")); // 4
return(find()); // Gets 'SEND OK' line
}
return false;
}
// Requests page from currently-open TCP connection. URL is
// character string in SRAM. Returns true if request issued successfully,
// else false. Calling function should then handle data returned, may
// need to parse IPD delimiters (see notes in find() function.
// (Can call find(F("Unlink"), true) to dump to debug.)
boolean Adafruit_ESP8266::requestURL(char* url) {
print(F("AT+CIPSEND="));
println(25 + strlen(url) + strlen_P((Pchr *)host));
if(find(F("> "))) { // Wait for prompt
print(F("GET ")); // 4
print(url);
print(F(" HTTP/1.1\r\nHost: ")); // 17
print(host);
print(F("\r\n\r\n")); // 4
return(find()); // Gets 'SEND OK' line
}
return false;
}

View File

@@ -0,0 +1,65 @@
/*------------------------------------------------------------------------
An Arduino library for the ESP8266 WiFi-serial bridge
https://www.adafruit.com/product/2282
The ESP8266 is a 3.3V device. Safe operation with 5V devices (most
Arduino boards) requires a logic-level shifter for TX and RX signals.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried and Phil Burgess for Adafruit Industries.
MIT license, all text above must be included in any redistribution.
------------------------------------------------------------------------*/
#ifndef _ADAFRUIT_ESP8266_H_
#define _ADAFRUIT_ESP8266_H_
#include <Arduino.h>
#define ESP_RECEIVE_TIMEOUT 1000L
#define ESP_RESET_TIMEOUT 5000L
#define ESP_CONNECT_TIMEOUT 15000L
#define ESP_IPD_TIMEOUT 120000L
typedef const __FlashStringHelper Fstr; // PROGMEM/flash-resident string
typedef const PROGMEM char Pchr; // Ditto, kindasorta
#define defaultBootMarker F("ready\r\n")
// Subclassing Print makes debugging easier -- output en route to
// WiFi module can be duplicated on a second stream (e.g. Serial).
class Adafruit_ESP8266 : public Print {
public:
Adafruit_ESP8266(Stream *s = &Serial, Stream *d = NULL, int8_t r = -1);
boolean hardReset(void),
softReset(void),
find(Fstr *str = NULL, boolean ipd = false),
connectToAP(Fstr *ssid, Fstr *pass),
connectTCP(Fstr *host, int port),
requestURL(Fstr *url),
requestURL(char* url);
int readLine(char *buf, int bufSiz);
void closeAP(void),
closeTCP(void),
debugLoop(void),
setDebug(Stream *d = NULL),
setTimeouts(uint32_t rcv = ESP_RECEIVE_TIMEOUT,
uint32_t rst = ESP_RESET_TIMEOUT,
uint32_t con = ESP_CONNECT_TIMEOUT,
uint32_t ipd = ESP_IPD_TIMEOUT),
setBootMarker(Fstr *s = NULL);
private:
Stream *stream, // -> ESP8266, e.g. SoftwareSerial or Serial1
*debug; // -> host, e.g. Serial
uint32_t receiveTimeout, resetTimeout, connectTimeout, ipdTimeout;
int8_t reset_pin; // -1 if RST not connected
Fstr *host, // Non-NULL when TCP connection open
*bootMarker; // String indicating successful boot
boolean writing;
virtual size_t write(uint8_t); // Because Print subclass
};
#endif // _ADAFRUIT_ESP8266_H_

View File

@@ -0,0 +1,4 @@
Adafruit_ESP8266
================
Example code for ESP8266 chipset

View File

@@ -0,0 +1,117 @@
/*------------------------------------------------------------------------
Simple ESP8266 test. Requires SoftwareSerial and an ESP8266 that's been
flashed with recent 'AT' firmware operating at 9600 baud. Only tested
w/Adafruit-programmed modules: https://www.adafruit.com/product/2282
The ESP8266 is a 3.3V device. Safe operation with 5V devices (most
Arduino boards) requires a logic-level shifter for TX and RX signals.
------------------------------------------------------------------------*/
#include <Adafruit_ESP8266.h>
#include <SoftwareSerial.h>
#define ESP_RX 2
#define ESP_TX 3
#define ESP_RST 4
SoftwareSerial softser(ESP_RX, ESP_TX);
// Must declare output stream before Adafruit_ESP8266 constructor; can be
// a SoftwareSerial stream, or Serial/Serial1/etc. for UART.
Adafruit_ESP8266 wifi(&softser, &Serial, ESP_RST);
// Must call begin() on the stream(s) before using Adafruit_ESP8266 object.
#define ESP_SSID "SSIDNAME" // Your network name here
#define ESP_PASS "PASSWORD" // Your network password here
#define HOST "www.adafruit.com" // Host to contact
#define PAGE "/testwifi/index.html" // Web page to request
#define PORT 80 // 80 = HTTP default port
#define LED_PIN 13
void setup() {
char buffer[50];
// Flash LED on power-up
pinMode(LED_PIN, OUTPUT);
for(uint8_t i=0; i<3; i++) {
digitalWrite(13, HIGH); delay(50);
digitalWrite(13, LOW); delay(100);
}
// This might work with other firmware versions (no guarantees)
// by providing a string to ID the tail end of the boot message:
// comment/replace this if you are using something other than v 0.9.2.4!
wifi.setBootMarker(F("Version:0.9.2.4]\r\n\r\nready"));
softser.begin(9600); // Soft serial connection to ESP8266
Serial.begin(57600); while(!Serial); // UART serial debug
Serial.println(F("Adafruit ESP8266 Demo"));
// Test if module is ready
Serial.print(F("Hard reset..."));
if(!wifi.hardReset()) {
Serial.println(F("no response from module."));
for(;;);
}
Serial.println(F("OK."));
Serial.print(F("Soft reset..."));
if(!wifi.softReset()) {
Serial.println(F("no response from module."));
for(;;);
}
Serial.println(F("OK."));
Serial.print(F("Checking firmware version..."));
wifi.println(F("AT+GMR"));
if(wifi.readLine(buffer, sizeof(buffer))) {
Serial.println(buffer);
wifi.find(); // Discard the 'OK' that follows
} else {
Serial.println(F("error"));
}
Serial.print(F("Connecting to WiFi..."));
if(wifi.connectToAP(F(ESP_SSID), F(ESP_PASS))) {
// IP addr check isn't part of library yet, but
// we can manually request and place in a string.
Serial.print(F("OK\nChecking IP addr..."));
wifi.println(F("AT+CIFSR"));
if(wifi.readLine(buffer, sizeof(buffer))) {
Serial.println(buffer);
wifi.find(); // Discard the 'OK' that follows
Serial.print(F("Connecting to host..."));
if(wifi.connectTCP(F(HOST), PORT)) {
Serial.print(F("OK\nRequesting page..."));
if(wifi.requestURL(F(PAGE))) {
Serial.println("OK\nSearching for string...");
// Search for a phrase in the open stream.
// Must be a flash-resident string (F()).
if(wifi.find(F("working"), true)) {
Serial.println(F("found!"));
} else {
Serial.println(F("not found."));
}
} else { // URL request failed
Serial.println(F("error"));
}
wifi.closeTCP();
} else { // TCP connect failed
Serial.println(F("D'oh!"));
}
} else { // IP addr check failed
Serial.println(F("error"));
}
wifi.closeAP();
} else { // WiFi connection failed
Serial.println(F("FAIL"));
}
}
void loop() {
}

View File

@@ -0,0 +1,9 @@
name=Adafruit ESP8266
version=1.0.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Example code for ESP8266 chipset
paragraph=Example code for ESP8266 chipset
category=Communication
url=https://github.com/adafruit/Adafruit_ESP8266
architectures=*

View File

@@ -0,0 +1,367 @@
// This is a super simple demo program for ESP8266's that can use software serial
// @ 9600 baud. Requires firmware that runs at 9600 baud, only tested with Adafruit
// programmed modules!
#include <SoftwareSerial.h>
#define SSID "SSIDNAME" //your wifi ssid here
#define PASS "PASSWORD" //your wifi key here
// www.adafruit.com/testwifi/index.html
#define HOST "www.adafruit.com"
#define WEBPAGE "/testwifi/index.html"
#define PORT "80"
#define ESP_RST 4
// Use software serial (check to make sure these are valid softserial pins!)
#define ESP_RX 2
#define ESP_TX 3
SoftwareSerial softser(ESP_RX, ESP_TX); // RX, TX
Stream *esp = &softser;
// can also do
// Stream *esp = &Serial1;
#define REPLYBUFFSIZ 255
char replybuffer[REPLYBUFFSIZ];
uint8_t getReply(char *send, uint16_t timeout = 500, boolean echo = true);
uint8_t espreadline(uint16_t timeout = 500, boolean multiline = false);
boolean sendCheckReply(char *send, char *reply, uint16_t timeout = 500);
enum {WIFI_ERROR_NONE=0, WIFI_ERROR_AT, WIFI_ERROR_RST, WIFI_ERROR_SSIDPWD, WIFI_ERROR_SERVER, WIFI_ERROR_UNKNOWN};
void setup()
{
pinMode(9, OUTPUT);
pinMode(13, OUTPUT);
//blink led13 to indicate power up
for(int i = 0; i<3; i++)
{
digitalWrite(13,HIGH);
delay(50);
digitalWrite(13,LOW);
delay(100);
}
// Serial debug console
Serial.begin(115200);
// Set time to wait for response strings to be found
//Open software serial for chatting to ESP
softser.begin(9600); // requires new firmware!
// OR use hardware serial
//Serial1.begin(9600);
Serial.println(F("Adafruit's ESP8266 Demo"));
Serial.println(F("Hard reset..."));
// hard reset if you can
pinMode(ESP_RST, INPUT);
digitalWrite(ESP_RST, LOW);
pinMode(ESP_RST, OUTPUT);
delay(100);
pinMode(ESP_RST, INPUT);
delay(2000);
//test if the module is ready
if(! espReset()) {
Serial.println("Module didn't respond :(");
debugLoop();
}
Serial.println(F("ESP Module is ready! :)"));
//connect to the wifi
byte err = setupWiFi();
if (err) {
// error, print error code
Serial.print("setup error:"); Serial.println((int)err);
debugLoop();
}
// success, print IP
uint32_t ip = getIP();
Serial.print("ESP setup success, my IP addr:");
if (ip) {
Serial.println(ip, HEX);
} else {
Serial.println("none");
}
sendCheckReply("AT+CIPSTO=0", "OK");
//set the single connection mode
}
boolean ESP_GETpage(char *host, uint16_t port, char *page) {
String cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += host;
cmd += "\",";
cmd += port;
cmd.toCharArray(replybuffer, REPLYBUFFSIZ);
getReply(replybuffer);
if (strcmp(replybuffer, "OK") != 0) {
// this is OK! could be a version that says "Linked"
if (strcmp(replybuffer, "Linked") != 0) {
sendCheckReply("AT+CIPCLOSE", "OK");
return false;
}
}
String request = "GET ";
request += page;
request += " HTTP/1.1\r\nHost: ";
request += host;
request += "\r\n\r\n";
cmd = "AT+CIPSEND=";
cmd += request.length();
cmd.toCharArray(replybuffer, REPLYBUFFSIZ);
sendCheckReply(replybuffer, ">");
Serial.print("Sending: "); Serial.println(request.length());
Serial.println(F("*********SENDING*********"));
Serial.print(request);
Serial.println(F("*************************"));
request.toCharArray(replybuffer, REPLYBUFFSIZ);
esp->println(request);
while (true) {
espreadline(3000); // this is the 'echo' from the data
Serial.print(">"); Serial.println(replybuffer); // probably the 'busy s...'
// LOOK AT ALL THESE POSSIBLE ARBITRARY RESPONSES!!!
if (strstr(replybuffer, "wrong syntax"))
continue;
else if (strstr(replybuffer, "ERROR"))
continue;
else if (strstr(replybuffer, "busy s..."))
continue;
else break;
}
if (! strstr(replybuffer, "SEND OK") ) return false;
espreadline(1000); Serial.print("3>"); Serial.println(replybuffer);
char *s = strstr(replybuffer, "+IPD,");
if (!s) return false;
uint16_t len = atoi(s+5);
//Serial.print(len); Serial.println(" bytes total");
int16_t contentlen = 0;
while (1) {
espreadline(50);
s = strstr(replybuffer, "Content-Length: ");
if (s) {
//Serial.println(replybuffer);
contentlen = atoi(s+16);
Serial.print(F("C-Len = ")); Serial.println(contentlen);
}
s = strstr(replybuffer, "Content-Type: ");
if (s && contentlen) {
int16_t i;
char c;
for (i=-2; i<contentlen; i++) { // eat the first 2 chars (\n\r)
while (!esp->available());
c = esp->read(); //UDR0 = c;
if (i >= 0) {
replybuffer[i] = c;
}
}
replybuffer[i] = 0;
return true;
}
}
//while (1) {
// if (esp.available()) UDR0 = esp.read();
//}
}
void loop()
{
ESP_GETpage(HOST, 80, WEBPAGE);
Serial.println(F("**********REPLY***********"));
Serial.print(replybuffer);
Serial.println(F("**************************"));
sendCheckReply("AT+CIPCLOSE", "OK");
debugLoop();
delay(1000);
while (1);
}
boolean getVersion() {
// Get version?
getReply("AT+GMR", 250, true);
}
boolean espReset() {
getReply("AT+RST", 1000, true);
if (! strstr(replybuffer, "OK")) return false;
delay(2000);
// turn off echo
getReply("ATE0", 250, true);
return true;
}
boolean ESPconnectAP(char *s, char *p) {
getReply("AT+CWMODE=1", 500, true);
if (! (strstr(replybuffer, "OK") || strstr(replybuffer, "no change")) )
return false;
String connectStr = "AT+CWJAP=\"";
connectStr += SSID;
connectStr += "\",\"";
connectStr += PASS;
connectStr += "\"";
connectStr.toCharArray(replybuffer, REPLYBUFFSIZ);
getReply(replybuffer, 500, true);
espreadline(5000);
Serial.print("<-- "); Serial.println(replybuffer);
return (strstr(replybuffer, "OK") != 0);
}
byte setupWiFi() {
// reset WiFi module
Serial.println(F("Soft resetting..."));
if (!espReset())
return WIFI_ERROR_RST;
delay(1000);
Serial.println(F("Checking for ESP AT response"));
if (!sendCheckReply("AT", "OK"))
return WIFI_ERROR_AT;
getVersion();
Serial.print(F("Firmware Version #")); Serial.println(replybuffer);
Serial.print(F("Connecting to ")); Serial.println(SSID);
if (!ESPconnectAP(SSID, PASS))
return WIFI_ERROR_SSIDPWD;
Serial.println(F("Single Client Mode"));
if (!sendCheckReply("AT+CIPMUX=0", "OK"))
return WIFI_ERROR_SERVER;
return WIFI_ERROR_NONE;
}
// NOT IMPLEMENTED YET!
uint32_t getIP() {
getReply("AT+CIFSR", 500, true);
return 0;
}
/************************/
uint8_t espreadline(uint16_t timeout, boolean multiline) {
uint16_t replyidx = 0;
while (timeout--) {
if (replyidx > REPLYBUFFSIZ-1) break;
while(esp->available()) {
char c = esp->read();
if (c == '\r') continue;
if (c == 0xA) {
if (replyidx == 0) // the first 0x0A is ignored
continue;
if (!multiline) {
timeout = 0; // the second 0x0A is the end of the line
break;
}
}
replybuffer[replyidx] = c;
// Serial.print(c, HEX); Serial.print("#"); Serial.println(c);
replyidx++;
}
if (timeout == 0) break;
delay(1);
}
replybuffer[replyidx] = 0; // null term
return replyidx;
}
uint8_t getReply(char *send, uint16_t timeout, boolean echo) {
// flush input
while(esp->available()) {
esp->read();
}
if (echo) {
Serial.print("---> "); Serial.println(send);
}
esp->println(send);
// eat first reply sentence (echo)
uint8_t readlen = espreadline(timeout);
//Serial.print("echo? "); Serial.print(readlen); Serial.print(" vs "); Serial.println(strlen(send));
if (strncmp(send, replybuffer, readlen) == 0) {
// its an echo, read another line!
readlen = espreadline();
}
if (echo) {
Serial.print ("<--- "); Serial.println(replybuffer);
}
return readlen;
}
boolean sendCheckReply(char *send, char *reply, uint16_t timeout) {
getReply(send, timeout, true);
/*
for (uint8_t i=0; i<strlen(replybuffer); i++) {
Serial.print(replybuffer[i], HEX); Serial.print(" ");
}
Serial.println();
for (uint8_t i=0; i<strlen(reply); i++) {
Serial.print(reply[i], HEX); Serial.print(" ");
}
Serial.println();
*/
return (strcmp(replybuffer, reply) == 0);
}
void debugLoop() {
Serial.println("========================");
//serial loop mode for diag
while(1) {
if (Serial.available()) {
esp->write(Serial.read());
delay(1);
}
if (esp->available()) {
Serial.write(esp->read());
delay(1);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
/*--------------------------------------------------------------------
This file is part of the Adafruit NeoPixel library.
NeoPixel 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 3 of
the License, or (at your option) any later version.
NeoPixel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------*/
#ifndef ADAFRUIT_NEOPIXEL_H
#define ADAFRUIT_NEOPIXEL_H
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#include <pins_arduino.h>
#endif
// The order of primary colors in the NeoPixel data stream can vary
// among device types, manufacturers and even different revisions of
// the same item. The third parameter to the Adafruit_NeoPixel
// constructor encodes the per-pixel byte offsets of the red, green
// and blue primaries (plus white, if present) in the data stream --
// the following #defines provide an easier-to-use named version for
// each permutation. e.g. NEO_GRB indicates a NeoPixel-compatible
// device expecting three bytes per pixel, with the first byte
// containing the green value, second containing red and third
// containing blue. The in-memory representation of a chain of
// NeoPixels is the same as the data-stream order; no re-ordering of
// bytes is required when issuing data to the chain.
// Bits 5,4 of this value are the offset (0-3) from the first byte of
// a pixel to the location of the red color byte. Bits 3,2 are the
// green offset and 1,0 are the blue offset. If it is an RGBW-type
// device (supporting a white primary in addition to R,G,B), bits 7,6
// are the offset to the white byte...otherwise, bits 7,6 are set to
// the same value as 5,4 (red) to indicate an RGB (not RGBW) device.
// i.e. binary representation:
// 0bWWRRGGBB for RGBW devices
// 0bRRRRGGBB for RGB
// RGB NeoPixel permutations; white and red offsets are always same
// Offset: W R G B
#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2))
#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1))
#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2))
#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1))
#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0))
#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0))
// RGBW NeoPixel permutations; all 4 offsets are distinct
// Offset: W R G B
#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3))
#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2))
#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3))
#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2))
#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1))
#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1))
#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3))
#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2))
#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3))
#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2))
#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1))
#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1))
#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3))
#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2))
#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3))
#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2))
#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1))
#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1))
#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0))
#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0))
#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0))
#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0))
#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0))
#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0))
// Add NEO_KHZ400 to the color order value to indicate a 400 KHz
// device. All but the earliest v1 NeoPixels expect an 800 KHz data
// stream, this is the default if unspecified. Because flash space
// is very limited on ATtiny devices (e.g. Trinket, Gemma), v1
// NeoPixels aren't handled by default on those chips, though it can
// be enabled by removing the ifndef/endif below -- but code will be
// bigger. Conversely, can disable the NEO_KHZ400 line on other MCUs
// to remove v1 support and save a little space.
#define NEO_KHZ800 0x0000 // 800 KHz datastream
#ifndef __AVR_ATtiny85__
#define NEO_KHZ400 0x0100 // 400 KHz datastream
#endif
// If 400 KHz support is enabled, the third parameter to the constructor
// requires a 16-bit value (in order to select 400 vs 800 KHz speed).
// If only 800 KHz is enabled (as is default on ATtiny), an 8-bit value
// is sufficient to encode pixel color order, saving some space.
#ifdef NEO_KHZ400
typedef uint16_t neoPixelType;
#else
typedef uint8_t neoPixelType;
#endif
class Adafruit_NeoPixel {
public:
// Constructor: number of LEDs, pin number, LED type
Adafruit_NeoPixel(uint16_t n, uint8_t p=6, neoPixelType t=NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel(void);
~Adafruit_NeoPixel();
void
begin(void),
show(void),
setPin(uint8_t p),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w),
setPixelColor(uint16_t n, uint32_t c),
setBrightness(uint8_t),
clear(),
updateLength(uint16_t n),
updateType(neoPixelType t);
uint8_t
*getPixels(void) const,
getBrightness(void) const,
sine8(uint8_t) const,
gamma8(uint8_t) const;
int8_t
getPin(void) { return pin; };
uint16_t
numPixels(void) const;
static uint32_t
Color(uint8_t r, uint8_t g, uint8_t b),
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w);
uint32_t
getPixelColor(uint16_t n) const;
inline bool
canShow(void) { return (micros() - endTime) >= 300L; }
protected:
boolean
#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled...
is800KHz, // ...true if 800 KHz pixels
#endif
begun; // true if begin() previously called
uint16_t
numLEDs, // Number of RGB LEDs in strip
numBytes; // Size of 'pixels' buffer below (3 or 4 bytes/pixel)
int8_t
pin; // Output pin number (-1 if not yet set)
uint8_t
brightness,
*pixels, // Holds LED color values (3 or 4 bytes each)
rOffset, // Index of red byte within each 3- or 4-byte pixel
gOffset, // Index of green byte
bOffset, // Index of blue byte
wOffset; // Index of white byte (same as rOffset if no white)
uint32_t
endTime; // Latch timing reference
#ifdef __AVR__
volatile uint8_t
*port; // Output PORT register
uint8_t
pinMask; // Output PORT bitmask
#endif
};
#endif // ADAFRUIT_NEOPIXEL_H

View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -0,0 +1,51 @@
# Adafruit NeoPixel Library [![Build Status](https://travis-ci.org/adafruit/Adafruit_NeoPixel.svg?branch=master)](https://travis-ci.org/adafruit/Adafruit_NeoPixel)
Arduino library for controlling single-wire-based LED pixels and strip such as the [Adafruit 60 LED/meter Digital LED strip][strip], the [Adafruit FLORA RGB Smart Pixel][flora], the [Adafruit Breadboard-friendly RGB Smart Pixel][pixel], the [Adafruit NeoPixel Stick][stick], and the [Adafruit NeoPixel Shield][shield].
After downloading, rename folder to 'Adafruit_NeoPixel' and install in Arduino Libraries folder. Restart Arduino IDE, then open File->Sketchbook->Library->Adafruit_NeoPixel->strandtest sketch.
Compatibility notes: Port A is not supported on any AVR processors at this time
[flora]: http://adafruit.com/products/1060
[strip]: http://adafruit.com/products/1138
[pixel]: http://adafruit.com/products/1312
[stick]: http://adafruit.com/products/1426
[shield]: http://adafruit.com/products/1430
---
## Supported chipsets
We have included code for the following chips - *sometimes these break for exciting reasons that we can't control* in which case please open an issue!
* AVR ATmega and ATtiny (any 8-bit) - 8 MHz, 12 MHz and 16 MHz
* Teensy 3.x and LC
* Arduino Due
* Arduino 101
* ATSAMD21 (Arduino Zero/M0 and other SAMD21 boards) @ 48 MHz
* ATSAMD51 @ 120 MHz
* Adafruit STM32 Feather @ 120 MHz
* ESP8266 any speed
* ESP32 any speed
* Nordic nRF52 (Adafruit Feather nRF52), nRF51 (micro:bit)
Check forks for other architectures not listed here!
---
### Roadmap
The PRIME DIRECTIVE is to maintain backward compatibility with existing Arduino sketches -- many are hosted elsewhere and don't track changes here, some are in print and can never be changed!
Please don't reformat code for the sake of reformatting code. The resulting large "visual diff" makes it impossible to untangle actual bug fixes from merely rearranged lines. (Exception for first item in wishlist below.)
Things I'd Like To Do But There's No Official Timeline So Please Don't Count On Any Of This Ever Being Canonical:
* For the show() function (with all the delicate pixel timing stuff), break out each architecture into separate source files rather than the current unmaintainable tangle of #ifdef statements!
* Really the only reason I've never incorporated an HSV color function is that I haven't settled on a type and range for the hue element (mathematically an integer from 0 to 1529 yields a "most correct" approach but it's weird to use and would probably annoy people).
* Add a fill function with the arguments: (color, first, count). Count, if unspecified, fills to end of strip. First, if unspecified, is zero. Color, if unspecified, is zero (effectively a strip clear operation). Do NOT then implement fifty billion minor variations (such as first, last). No. This argument sequence was very specifically chosen because reasons, and equivalents to such variations are trivially made in one's call. Just one fill function, please.
* At such time that the prior two items are settled, revisit the DotStar library (and maybe even LPD8806 or anything else we've got) and add the same functions and behaviors so there's a good degree of sketch compatibility across different pixel types.
* I wouldn't mind paring down strandtest a bit. More diagnostic, less Amiga demo.
* Please don't use updateLength() or updateType() in new code. They should not have been implemented this way (use the C++ 'new' operator with the regular constructor instead) and are only sticking around because of the Prime Directive. setPin() is OK for now though, it's a trick we can use to 'recycle' pixel memory across multiple strips.
* In the M0 and M4 code, use the hardware systick counter for bit timing rather than hand-tweaked NOPs (a temporary kludge at the time because I wasn't reading systick correctly).
* As currently written, brightness scaling is still a "destructive" operation -- pixel values are altered in RAM and the original value as set can't be accurately read back, only approximated, which has been confusing and frustrating to users. It was done this way at the time because NeoPixel timing is strict, AVR microcontrollers (all we had at the time) are limited, and assembly language is hard. All the 32-bit architectures should have no problem handling nondestructive brightness scaling -- calculating each byte immediately before it's sent out the wire, maintaining the original set value in RAM -- the work just hasn't been done. There's a fair chance even the AVR code could manage it with some intense focus. (The DotStar library achieves nondestructive brightness scaling because it doesn't have to manage data timing so carefully...every architecture, even ATtiny, just takes whatever cycles it needs for the multiply/shift operations.)

View File

@@ -0,0 +1,82 @@
// This is a mash-up of the Due show() code + insights from Michael Miller's
// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus
// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution.
#if defined(ESP8266) || defined(ESP32)
#include <Arduino.h>
#ifdef ESP8266
#include <eagle_soc.h>
#endif
static uint32_t _getCycleCount(void) __attribute__((always_inline));
static inline uint32_t _getCycleCount(void) {
uint32_t ccount;
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
return ccount;
}
#ifdef ESP8266
void ICACHE_RAM_ATTR espShow(
uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) {
#else
void espShow(
uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) {
#endif
#define CYCLES_800_T0H (F_CPU / 2500000) // 0.4us
#define CYCLES_800_T1H (F_CPU / 1250000) // 0.8us
#define CYCLES_800 (F_CPU / 800000) // 1.25us per bit
#define CYCLES_400_T0H (F_CPU / 2000000) // 0.5uS
#define CYCLES_400_T1H (F_CPU / 833333) // 1.2us
#define CYCLES_400 (F_CPU / 400000) // 2.5us per bit
uint8_t *p, *end, pix, mask;
uint32_t t, time0, time1, period, c, startTime, pinMask;
pinMask = _BV(pin);
p = pixels;
end = p + numBytes;
pix = *p++;
mask = 0x80;
startTime = 0;
#ifdef NEO_KHZ400
if(is800KHz) {
#endif
time0 = CYCLES_800_T0H;
time1 = CYCLES_800_T1H;
period = CYCLES_800;
#ifdef NEO_KHZ400
} else { // 400 KHz bitstream
time0 = CYCLES_400_T0H;
time1 = CYCLES_400_T1H;
period = CYCLES_400;
}
#endif
for(t = time0;; t = time0) {
if(pix & mask) t = time1; // Bit high duration
while(((c = _getCycleCount()) - startTime) < period); // Wait for bit start
#ifdef ESP8266
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask); // Set high
#else
gpio_set_level(pin, HIGH);
#endif
startTime = c; // Save start time
while(((c = _getCycleCount()) - startTime) < t); // Wait high duration
#ifdef ESP8266
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinMask); // Set low
#else
gpio_set_level(pin, LOW);
#endif
if(!(mask >>= 1)) { // Next bit/byte
if(p >= end) break;
pix = *p++;
mask = 0x80;
}
}
while((_getCycleCount() - startTime) < period); // Wait for last bit
}
#endif // ESP8266

View File

@@ -0,0 +1,261 @@
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 6
#define NUM_LEDS 60
#define BRIGHTNESS 50
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRBW + NEO_KHZ800);
byte neopix_gamma[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
void setup() {
// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code
strip.setBrightness(BRIGHTNESS);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
// Some example procedures showing how to display to the pixels:
colorWipe(strip.Color(255, 0, 0), 50); // Red
colorWipe(strip.Color(0, 255, 0), 50); // Green
colorWipe(strip.Color(0, 0, 255), 50); // Blue
colorWipe(strip.Color(0, 0, 0, 255), 50); // White
whiteOverRainbow(20,75,5);
pulseWhite(5);
// fullWhite();
// delay(2000);
rainbowFade2White(3,3,1);
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void pulseWhite(uint8_t wait) {
for(int j = 0; j < 256 ; j++){
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, neopix_gamma[j] ) );
}
delay(wait);
strip.show();
}
for(int j = 255; j >= 0 ; j--){
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, neopix_gamma[j] ) );
}
delay(wait);
strip.show();
}
}
void rainbowFade2White(uint8_t wait, int rainbowLoops, int whiteLoops) {
float fadeMax = 100.0;
int fadeVal = 0;
uint32_t wheelVal;
int redVal, greenVal, blueVal;
for(int k = 0 ; k < rainbowLoops ; k ++){
for(int j=0; j<256; j++) { // 5 cycles of all colors on wheel
for(int i=0; i< strip.numPixels(); i++) {
wheelVal = Wheel(((i * 256 / strip.numPixels()) + j) & 255);
redVal = red(wheelVal) * float(fadeVal/fadeMax);
greenVal = green(wheelVal) * float(fadeVal/fadeMax);
blueVal = blue(wheelVal) * float(fadeVal/fadeMax);
strip.setPixelColor( i, strip.Color( redVal, greenVal, blueVal ) );
}
//First loop, fade in!
if(k == 0 && fadeVal < fadeMax-1) {
fadeVal++;
}
//Last loop, fade out!
else if(k == rainbowLoops - 1 && j > 255 - fadeMax ){
fadeVal--;
}
strip.show();
delay(wait);
}
}
delay(500);
for(int k = 0 ; k < whiteLoops ; k ++){
for(int j = 0; j < 256 ; j++){
for(uint16_t i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, neopix_gamma[j] ) );
}
strip.show();
}
delay(2000);
for(int j = 255; j >= 0 ; j--){
for(uint16_t i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, neopix_gamma[j] ) );
}
strip.show();
}
}
delay(500);
}
void whiteOverRainbow(uint8_t wait, uint8_t whiteSpeed, uint8_t whiteLength ) {
if(whiteLength >= strip.numPixels()) whiteLength = strip.numPixels() - 1;
int head = whiteLength - 1;
int tail = 0;
int loops = 3;
int loopNum = 0;
static unsigned long lastTime = 0;
while(true){
for(int j=0; j<256; j++) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
if((i >= tail && i <= head) || (tail > head && i >= tail) || (tail > head && i <= head) ){
strip.setPixelColor(i, strip.Color(0,0,0, 255 ) );
}
else{
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
}
if(millis() - lastTime > whiteSpeed) {
head++;
tail++;
if(head == strip.numPixels()){
loopNum++;
}
lastTime = millis();
}
if(loopNum == loops) return;
head%=strip.numPixels();
tail%=strip.numPixels();
strip.show();
delay(wait);
}
}
}
void fullWhite() {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0,0,0, 255 ) );
}
strip.show();
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256 * 5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3,0);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3,0);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0,0);
}
uint8_t red(uint32_t c) {
return (c >> 16);
}
uint8_t green(uint32_t c) {
return (c >> 8);
}
uint8_t blue(uint32_t c) {
return (c);
}

View File

@@ -0,0 +1,133 @@
#include "BLESerial.h"
// #define BLE_SERIAL_DEBUG
BLESerial* BLESerial::_instance = NULL;
BLESerial::BLESerial(unsigned char req, unsigned char rdy, unsigned char rst) :
BLEPeripheral(req, rdy, rst)
{
this->_txCount = 0;
this->_rxHead = this->_rxTail = 0;
this->_flushed = 0;
BLESerial::_instance = this;
addAttribute(this->_uartService);
addAttribute(this->_uartNameDescriptor);
setAdvertisedServiceUuid(this->_uartService.uuid());
addAttribute(this->_rxCharacteristic);
addAttribute(this->_rxNameDescriptor);
this->_rxCharacteristic.setEventHandler(BLEWritten, BLESerial::_received);
addAttribute(this->_txCharacteristic);
addAttribute(this->_txNameDescriptor);
}
void BLESerial::begin(...) {
BLEPeripheral::begin();
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLESerial::begin()"));
#endif
}
void BLESerial::poll() {
if (millis() < this->_flushed + 100) {
BLEPeripheral::poll();
} else {
flush();
}
}
void BLESerial::end() {
this->_rxCharacteristic.setEventHandler(BLEWritten, NULL);
this->_rxHead = this->_rxTail = 0;
flush();
BLEPeripheral::disconnect();
}
int BLESerial::available(void) {
BLEPeripheral::poll();
int retval = (this->_rxHead - this->_rxTail + sizeof(this->_rxBuffer)) % sizeof(this->_rxBuffer);
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::available() = "));
Serial.println(retval);
#endif
return retval;
}
int BLESerial::peek(void) {
BLEPeripheral::poll();
if (this->_rxTail == this->_rxHead) return -1;
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::peek() = "));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
int BLESerial::read(void) {
BLEPeripheral::poll();
if (this->_rxTail == this->_rxHead) return -1;
this->_rxTail = (this->_rxTail + 1) % sizeof(this->_rxBuffer);
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::read() = "));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
void BLESerial::flush(void) {
if (this->_txCount == 0) return;
this->_txCharacteristic.setValue(this->_txBuffer, this->_txCount);
this->_flushed = millis();
this->_txCount = 0;
BLEPeripheral::poll();
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLESerial::flush()"));
#endif
}
size_t BLESerial::write(uint8_t byte) {
BLEPeripheral::poll();
if (this->_txCharacteristic.subscribed() == false) return 0;
this->_txBuffer[this->_txCount++] = byte;
if (this->_txCount == sizeof(this->_txBuffer)) flush();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::write("));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.print(byte, HEX);
Serial.println(F(") = 1"));
#endif
return 1;
}
BLESerial::operator bool() {
bool retval = BLEPeripheral::connected();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::operator bool() = "));
Serial.println(retval);
#endif
return retval;
}
void BLESerial::_received(const uint8_t* data, size_t size) {
for (int i = 0; i < size; i++) {
this->_rxHead = (this->_rxHead + 1) % sizeof(this->_rxBuffer);
this->_rxBuffer[this->_rxHead] = data[i];
}
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::received("));
for (int i = 0; i < size; i++) Serial.print((char) data[i]);
Serial.println(F(")"));
#endif
}
void BLESerial::_received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic) {
BLESerial::_instance->_received(rxCharacteristic.value(), rxCharacteristic.valueLength());
}

View File

@@ -0,0 +1,46 @@
#ifndef _BLE_SERIAL_H_
#define _BLE_SERIAL_H_
#include <Arduino.h>
#include <BLEPeripheral.h>
class BLESerial : public BLEPeripheral, public Stream
{
public:
BLESerial(unsigned char req, unsigned char rdy, unsigned char rst);
void begin(...);
void poll();
void end();
virtual int available(void);
virtual int peek(void);
virtual int read(void);
virtual void flush(void);
virtual size_t write(uint8_t byte);
using Print::write;
virtual operator bool();
private:
unsigned long _flushed;
static BLESerial* _instance;
size_t _rxHead;
size_t _rxTail;
size_t _rxCount() const;
uint8_t _rxBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH];
size_t _txCount;
uint8_t _txBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH];
BLEService _uartService = BLEService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
BLEDescriptor _uartNameDescriptor = BLEDescriptor("2901", "UART");
BLECharacteristic _rxCharacteristic = BLECharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse, BLE_ATTRIBUTE_MAX_VALUE_LENGTH);
BLEDescriptor _rxNameDescriptor = BLEDescriptor("2901", "RX - Receive Data (Write)");
BLECharacteristic _txCharacteristic = BLECharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, BLE_ATTRIBUTE_MAX_VALUE_LENGTH);
BLEDescriptor _txNameDescriptor = BLEDescriptor("2901", "TX - Transfer Data (Notify)");
void _received(const uint8_t* data, size_t size);
static void _received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic);
};
#endif

View File

@@ -0,0 +1,208 @@
/****************************************************************************
* This example was developed by the Hackerspace San Salvador to demonstrate
* the simultaneous use of the NeoPixel library and the Bluetooth SoftDevice.
* To compile this example you'll need to add support for the NRF52 based
* following the instructions at:
* https://github.com/sandeepmistry/arduino-nRF5
* Or adding the following URL to the board manager URLs on Arduino preferences:
* https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json
* Then you can install the BLEPeripheral library avaiable at:
* https://github.com/sandeepmistry/arduino-BLEPeripheral
* To test it, compile this example and use the UART module from the nRF
* Toolbox App for Android. Edit the interface and send the characters
* 'a' to 'i' to switch the animation.
* There is a delay because this example blocks the thread of execution but
* the change will be shown after the current animation ends. (This might
* take a couple of seconds)
* For more info write us at: info _at- teubi.co
*/
#include <SPI.h>
#include <BLEPeripheral.h>
#include "BLESerial.h"
#include <Adafruit_NeoPixel.h>
#define PIN 15
// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(64, PIN, NEO_GRB + NEO_KHZ800);
// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel. Avoid connecting
// on a live circuit...if you must, connect GND first.
// define pins (varies per shield/board)
#define BLE_REQ 10
#define BLE_RDY 2
#define BLE_RST 9
// create ble serial instance, see pinouts above
BLESerial BLESerial(BLE_REQ, BLE_RDY, BLE_RST);
uint8_t current_state = 0;
uint8_t rgb_values[3];
void setup() {
Serial.begin(115200);
Serial.println("Hello World!");
// custom services and characteristics can be added as well
BLESerial.setLocalName("UART_HS");
BLESerial.begin();
strip.begin();
changeColor(strip.Color(0, 0, 0));
//pinMode(PIN, OUTPUT);
//digitalWrite(PIN, LOW);
current_state = 'a';
}
void loop() {
while(BLESerial.available()) {
uint8_t character = BLESerial.read();
switch(character) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
current_state = character;
break;
};
}
switch(current_state) {
case 'a':
colorWipe(strip.Color(255, 0, 0), 20); // Red
break;
case 'b':
colorWipe(strip.Color(0, 255, 0), 20); // Green
break;
case 'c':
colorWipe(strip.Color(0, 0, 255), 20); // Blue
break;
case 'd':
theaterChase(strip.Color(255, 0, 0), 20); // Red
break;
case 'e':
theaterChase(strip.Color(0, 255, 0), 20); // Green
break;
case 'f':
theaterChase(strip.Color(255, 0, 255), 20); // Green
break;
case 'g':
rainbowCycle(20);
break;
case 'h':
rainbow(20);
break;
case 'i':
theaterChaseRainbow(20);
break;
}
}
void changeColor(uint32_t c) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
}
strip.show();
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
delay(wait);
strip.show();
delay(wait);
}
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

View File

@@ -0,0 +1,165 @@
// This is a demonstration on how to use an input device to trigger changes on your neo pixels.
// You should wire a momentary push button to connect from ground to a digital IO pin. When you
// press the button it will change to a new pixel animation. Note that you need to press the
// button once to start the first animation!
#include <Adafruit_NeoPixel.h>
#define BUTTON_PIN 2 // Digital IO pin connected to the button. This will be
// driven with a pull-up resistor so the switch should
// pull the pin to ground momentarily. On a high -> low
// transition the button press logic will execute.
#define PIXEL_PIN 6 // Digital IO pin connected to the NeoPixels.
#define PIXEL_COUNT 16
// Parameter 1 = number of pixels in strip, neopixel stick has 8
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_RGB Pixels are wired for RGB bitstream
// NEO_GRB Pixels are wired for GRB bitstream, correct for neopixel stick
// NEO_KHZ400 400 KHz bitstream (e.g. FLORA pixels)
// NEO_KHZ800 800 KHz bitstream (e.g. High Density LED strip), correct for neopixel stick
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
bool oldState = HIGH;
int showType = 0;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
// Get current button state.
bool newState = digitalRead(BUTTON_PIN);
// Check if state changed from high to low (button press).
if (newState == LOW && oldState == HIGH) {
// Short delay to debounce button.
delay(20);
// Check if button is still low after debounce.
newState = digitalRead(BUTTON_PIN);
if (newState == LOW) {
showType++;
if (showType > 9)
showType=0;
startShow(showType);
}
}
// Set the last button state to the old state.
oldState = newState;
}
void startShow(int i) {
switch(i){
case 0: colorWipe(strip.Color(0, 0, 0), 50); // Black/off
break;
case 1: colorWipe(strip.Color(255, 0, 0), 50); // Red
break;
case 2: colorWipe(strip.Color(0, 255, 0), 50); // Green
break;
case 3: colorWipe(strip.Color(0, 0, 255), 50); // Blue
break;
case 4: theaterChase(strip.Color(127, 127, 127), 50); // White
break;
case 5: theaterChase(strip.Color(127, 0, 0), 50); // Red
break;
case 6: theaterChase(strip.Color( 0, 0, 127), 50); // Blue
break;
case 7: rainbow(20);
break;
case 8: rainbowCycle(20);
break;
case 9: theaterChaseRainbow(50);
break;
}
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

View File

@@ -0,0 +1,47 @@
// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// released under the GPLv3 license to match the rest of the AdaFruit NeoPixel library
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN 6
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 16
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int delayval = 500; // delay for half a second
void setup() {
// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code
pixels.begin(); // This initializes the NeoPixel library.
}
void loop() {
// For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one.
for(int i=0;i<NUMPIXELS;i++){
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixels.setPixelColor(i, pixels.Color(0,150,0)); // Moderately bright green color.
pixels.show(); // This sends the updated pixel color to the hardware.
delay(delayval); // Delay for a period of time (in milliseconds).
}
}

View File

@@ -0,0 +1,134 @@
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 6
// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel. Avoid connecting
// on a live circuit...if you must, connect GND first.
void setup() {
// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
// Some example procedures showing how to display to the pixels:
colorWipe(strip.Color(255, 0, 0), 50); // Red
colorWipe(strip.Color(0, 255, 0), 50); // Green
colorWipe(strip.Color(0, 0, 255), 50); // Blue
//colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW
// Send a theater pixel chase in...
theaterChase(strip.Color(127, 127, 127), 50); // White
theaterChase(strip.Color(127, 0, 0), 50); // Red
theaterChase(strip.Color(0, 0, 127), 50); // Blue
rainbow(20);
rainbowCycle(20);
theaterChaseRainbow(50);
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

View File

@@ -0,0 +1,40 @@
#######################################
# Syntax Coloring Map For Adafruit_NeoPixel
#######################################
# Class
#######################################
Adafruit_NeoPixel KEYWORD1
#######################################
# Methods and Functions
#######################################
setPixelColor KEYWORD2
setPin KEYWORD2
setBrightness KEYWORD2
numPixels KEYWORD2
getPixelColor KEYWORD2
Color KEYWORD2
show KEYWORD2
clear KEYWORD2
updateLength KEYWORD2
updateType KEYWORD2
getPixels KEYWORD2
getBrightness KEYWORD2
getPin KEYWORD2
canShow KEYWORD2
#######################################
# Constants
#######################################
NEO_COLMASK LITERAL1
NEO_SPDMASK LITERAL1
NEO_KHZ800 LITERAL1
NEO_KHZ400 LITERAL1
NEO_GRB LITERAL1
NEO_RGB LITERAL1
NEO_RGBW LITERAL1

View File

@@ -0,0 +1,9 @@
name=Adafruit NeoPixel
version=1.1.6
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Arduino library for controlling single-wire-based LED pixels and strip.
paragraph=Arduino library for controlling single-wire-based LED pixels and strip.
category=Display
url=https://github.com/adafruit/Adafruit_NeoPixel
architectures=*

View File

@@ -0,0 +1,194 @@
/***************************************************
This is a library for our Adafruit 16-channel PWM & Servo driver
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/815
These displays use I2C to communicate, 2 pins are required to
interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include <Adafruit_PWMServoDriver.h>
#include <Wire.h>
// Set to true to print some debug messages, or false to disable them.
//#define ENABLE_DEBUG_OUTPUT
/**************************************************************************/
/*!
@brief Instantiates a new PCA9685 PWM driver chip with the I2C address on the Wire interface. On Due we use Wire1 since its the interface on the 'default' I2C pins.
@param addr The 7-bit I2C address to locate this chip, default is 0x40
*/
/**************************************************************************/
Adafruit_PWMServoDriver::Adafruit_PWMServoDriver(uint8_t addr) {
_i2caddr = addr;
#if defined(ARDUINO_SAM_DUE)
_i2c = &Wire1;
#else
_i2c = &Wire;
#endif
}
/**************************************************************************/
/*!
@brief Instantiates a new PCA9685 PWM driver chip with the I2C address on a TwoWire interface
@param i2c A pointer to a 'Wire' compatible object that we'll use to communicate with
@param addr The 7-bit I2C address to locate this chip, default is 0x40
*/
/**************************************************************************/
Adafruit_PWMServoDriver::Adafruit_PWMServoDriver(TwoWire *i2c, uint8_t addr) {
_i2c = i2c;
_i2caddr = addr;
}
/**************************************************************************/
/*!
@brief Setups the I2C interface and hardware
*/
/**************************************************************************/
void Adafruit_PWMServoDriver::begin(void) {
_i2c->begin();
reset();
// set a default frequency
setPWMFreq(1000);
}
/**************************************************************************/
/*!
@brief Sends a reset command to the PCA9685 chip over I2C
*/
/**************************************************************************/
void Adafruit_PWMServoDriver::reset(void) {
write8(PCA9685_MODE1, 0x80);
delay(10);
}
/**************************************************************************/
/*!
@brief Sets the PWM frequency for the entire chip, up to ~1.6 KHz
@param freq Floating point frequency that we will attempt to match
*/
/**************************************************************************/
void Adafruit_PWMServoDriver::setPWMFreq(float freq) {
#ifdef ENABLE_DEBUG_OUTPUT
Serial.print("Attempting to set freq ");
Serial.println(freq);
#endif
freq *= 0.9; // Correct for overshoot in the frequency setting (see issue #11).
float prescaleval = 25000000;
prescaleval /= 4096;
prescaleval /= freq;
prescaleval -= 1;
#ifdef ENABLE_DEBUG_OUTPUT
Serial.print("Estimated pre-scale: "); Serial.println(prescaleval);
#endif
uint8_t prescale = floor(prescaleval + 0.5);
#ifdef ENABLE_DEBUG_OUTPUT
Serial.print("Final pre-scale: "); Serial.println(prescale);
#endif
uint8_t oldmode = read8(PCA9685_MODE1);
uint8_t newmode = (oldmode&0x7F) | 0x10; // sleep
write8(PCA9685_MODE1, newmode); // go to sleep
write8(PCA9685_PRESCALE, prescale); // set the prescaler
write8(PCA9685_MODE1, oldmode);
delay(5);
write8(PCA9685_MODE1, oldmode | 0xa0); // This sets the MODE1 register to turn on auto increment.
#ifdef ENABLE_DEBUG_OUTPUT
Serial.print("Mode now 0x"); Serial.println(read8(PCA9685_MODE1), HEX);
#endif
}
/**************************************************************************/
/*!
@brief Sets the PWM output of one of the PCA9685 pins
@param num One of the PWM output pins, from 0 to 15
@param on At what point in the 4096-part cycle to turn the PWM output ON
@param off At what point in the 4096-part cycle to turn the PWM output OFF
*/
/**************************************************************************/
void Adafruit_PWMServoDriver::setPWM(uint8_t num, uint16_t on, uint16_t off) {
#ifdef ENABLE_DEBUG_OUTPUT
Serial.print("Setting PWM "); Serial.print(num); Serial.print(": "); Serial.print(on); Serial.print("->"); Serial.println(off);
#endif
_i2c->beginTransmission(_i2caddr);
_i2c->write(LED0_ON_L+4*num);
_i2c->write(on);
_i2c->write(on>>8);
_i2c->write(off);
_i2c->write(off>>8);
_i2c->endTransmission();
}
/**************************************************************************/
/*!
@brief Helper to set pin PWM output. Sets pin without having to deal with on/off tick placement and properly handles a zero value as completely off and 4095 as completely on. Optional invert parameter supports inverting the pulse for sinking to ground.
@param num One of the PWM output pins, from 0 to 15
@param val The number of ticks out of 4096 to be active, should be a value from 0 to 4095 inclusive.
@param invert If true, inverts the output, defaults to 'false'
*/
/**************************************************************************/
void Adafruit_PWMServoDriver::setPin(uint8_t num, uint16_t val, bool invert)
{
// Clamp value between 0 and 4095 inclusive.
val = min(val, (uint16_t)4095);
if (invert) {
if (val == 0) {
// Special value for signal fully on.
setPWM(num, 4096, 0);
}
else if (val == 4095) {
// Special value for signal fully off.
setPWM(num, 0, 4096);
}
else {
setPWM(num, 0, 4095-val);
}
}
else {
if (val == 4095) {
// Special value for signal fully on.
setPWM(num, 4096, 0);
}
else if (val == 0) {
// Special value for signal fully off.
setPWM(num, 0, 4096);
}
else {
setPWM(num, 0, val);
}
}
}
/*******************************************************************************************/
uint8_t Adafruit_PWMServoDriver::read8(uint8_t addr) {
_i2c->beginTransmission(_i2caddr);
_i2c->write(addr);
_i2c->endTransmission();
_i2c->requestFrom((uint8_t)_i2caddr, (uint8_t)1);
return _i2c->read();
}
void Adafruit_PWMServoDriver::write8(uint8_t addr, uint8_t d) {
_i2c->beginTransmission(_i2caddr);
_i2c->write(addr);
_i2c->write(d);
_i2c->endTransmission();
}

View File

@@ -0,0 +1,69 @@
/***************************************************
This is a library for our Adafruit 16-channel PWM & Servo driver
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/815
These displays use I2C to communicate, 2 pins are required to
interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#ifndef _ADAFRUIT_PWMServoDriver_H
#define _ADAFRUIT_PWMServoDriver_H
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Wire.h"
#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4
#define PCA9685_MODE1 0x0
#define PCA9685_PRESCALE 0xFE
#define LED0_ON_L 0x6
#define LED0_ON_H 0x7
#define LED0_OFF_L 0x8
#define LED0_OFF_H 0x9
#define ALLLED_ON_L 0xFA
#define ALLLED_ON_H 0xFB
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD
/**************************************************************************/
/*!
@brief Class that stores state and functions for interacting with PCA9685 PWM chip
*/
/**************************************************************************/
class Adafruit_PWMServoDriver {
public:
Adafruit_PWMServoDriver(uint8_t addr = 0x40);
Adafruit_PWMServoDriver(TwoWire *I2C, uint8_t addr = 0x40);
void begin(void);
void reset(void);
void setPWMFreq(float freq);
void setPWM(uint8_t num, uint16_t on, uint16_t off);
void setPin(uint8_t num, uint16_t val, bool invert=false);
private:
uint8_t _i2caddr;
TwoWire *_i2c;
uint8_t read8(uint8_t addr);
void write8(uint8_t addr, uint8_t d);
};
#endif

View File

@@ -0,0 +1,50 @@
# Adafruit PCA9685 PWM Servo Driver Library [![Build Status](https://travis-ci.org/adafruit/Adafruit_PWMServoDriver.svg?branch=master)](https://travis-ci.org/adafruit/Adafruit_PWMServoDriver)
This is a library for our Adafruit 16-channel PWM & Servo driver, shield or FeatherWing
<img src="https://cdn-shop.adafruit.com/970x728/815-04.jpg" height="300"/>
Pick one up today in the adafruit shop!
* https://www.adafruit.com/products/815
* https://www.adafruit.com/product/1411
* https://www.adafruit.com/product/2928
These drivers use I2C to communicate, 2 pins are required to interface.
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, check license.txt for more information.
All text above must be included in any redistribution
<!-- START COMPATIBILITY TABLE -->
## Compatibility
MCU | Tested Works | Doesn't Work | Not Tested | Notes
------------------ | :----------: | :----------: | :---------: | -----
Atmega328 @ 16MHz | X | | |
Atmega328 @ 12MHz | X | | |
Atmega32u4 @ 16MHz | X | | |
Atmega32u4 @ 8MHz | X | | |
ESP8266 | X | | |
Atmega2560 @ 16MHz | X | | |
ATSAM3X8E | X | | | Use SDA1/SCL1
ATSAM21D | X | | |
ATtiny85 @ 16MHz | X | | |
ATtiny85 @ 8MHz | X | | |
Intel Curie @ 32MHz | | | X |
STM32F2 | | | X |
* ATmega328 @ 16MHz : Arduino UNO, Adafruit Pro Trinket 5V, Adafruit Metro 328, Adafruit Metro Mini
* ATmega328 @ 12MHz : Adafruit Pro Trinket 3V
* ATmega32u4 @ 16MHz : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0
* ATmega32u4 @ 8MHz : Adafruit Flora, Bluefruit Micro
* ESP8266 : Adafruit Huzzah
* ATmega2560 @ 16MHz : Arduino Mega
* ATSAM3X8E : Arduino Due
* ATSAM21D : Arduino Zero, M0 Pro
* ATtiny85 @ 16MHz : Adafruit Trinket 5V
* ATtiny85 @ 8MHz : Adafruit Gemma, Arduino Gemma, Adafruit Trinket 3V
<!-- END COMPATIBILITY TABLE -->

View File

@@ -0,0 +1,49 @@
/***************************************************
This is an example for our Adafruit 16-channel PWM & Servo driver
GPIO test - this will set a pin high/low
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/815
These drivers use I2C to communicate, 2 pins are required to
interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(&Wire, 0x40);
void setup() {
Serial.begin(9600);
Serial.println("GPIO test!");
pwm.begin();
pwm.setPWMFreq(1000); // Set to whatever you like, we don't use it in this demo!
// if you want to really speed stuff up, you can go into 'fast 400khz I2C' mode
// some i2c devices dont like this so much so if you're sharing the bus, watch
// out for this!
Wire.setClock(400000);
}
void loop() {
// Drive each pin in a 'wave'
for (uint8_t pin=0; pin<16; pin++) {
pwm.setPWM(pin, 4096, 0); // turns pin fully on
delay(100);
pwm.setPWM(pin, 0, 4096); // turns pin fully off
}
}

View File

@@ -0,0 +1,52 @@
/***************************************************
This is an example for our Adafruit 16-channel PWM & Servo driver
PWM test - this will drive 16 PWMs in a 'wave'
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/815
These drivers use I2C to communicate, 2 pins are required to
interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(&Wire, 0x40);
void setup() {
Serial.begin(9600);
Serial.println("16 channel PWM test!");
pwm.begin();
pwm.setPWMFreq(1600); // This is the maximum PWM frequency
// if you want to really speed stuff up, you can go into 'fast 400khz I2C' mode
// some i2c devices dont like this so much so if you're sharing the bus, watch
// out for this!
Wire.setClock(400000);
}
void loop() {
// Drive each PWM in a 'wave'
for (uint16_t i=0; i<4096; i += 8) {
for (uint8_t pwmnum=0; pwmnum < 16; pwmnum++) {
pwm.setPWM(pwmnum, 0, (i + (4096/16)*pwmnum) % 4096 );
}
#ifdef ESP8266
yield(); // take a breather, required for ESP8266
#endif
}
}

View File

@@ -0,0 +1,83 @@
/***************************************************
This is an example for our Adafruit 16-channel PWM & Servo driver
Servo test - this will drive 8 servos, one after the other on the
first 8 pins of the PCA9685
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/815
These drivers use I2C to communicate, 2 pins are required to
interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(&Wire, 0x40);
// Depending on your servo make, the pulse width min and max may vary, you
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
#define SERVOMIN 150 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 600 // this is the 'maximum' pulse length count (out of 4096)
// our servo # counter
uint8_t servonum = 0;
void setup() {
Serial.begin(9600);
Serial.println("8 channel Servo test!");
pwm.begin();
pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates
delay(10);
}
// you can use this function if you'd like to set the pulse length in seconds
// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. its not precise!
void setServoPulse(uint8_t n, double pulse) {
double pulselength;
pulselength = 1000000; // 1,000,000 us per second
pulselength /= 60; // 60 Hz
Serial.print(pulselength); Serial.println(" us per period");
pulselength /= 4096; // 12 bits of resolution
Serial.print(pulselength); Serial.println(" us per bit");
pulse *= 1000000; // convert to us
pulse /= pulselength;
Serial.println(pulse);
pwm.setPWM(n, 0, pulse);
}
void loop() {
// Drive each servo one at a time
Serial.println(servonum);
for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
pwm.setPWM(servonum, 0, pulselen);
}
delay(500);
for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
pwm.setPWM(servonum, 0, pulselen);
}
delay(500);
servonum ++;
if (servonum > 7) servonum = 0;
}

View File

@@ -0,0 +1,9 @@
name=Adafruit PWM Servo Driver Library
version=1.0.2
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Adafruit PWM Servo Driver Library
paragraph=Adafruit PWM Servo Driver Library
category=Device Control
url=https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library
architectures=*

View File

@@ -0,0 +1,26 @@
Software License Agreement (BSD License)
Copyright (c) 2012, Adafruit Industries
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,441 @@
/*
Firmata is a generic protocol for communicating with microcontrollers
from software on a host computer. It is intended to work with
any host computer software package.
To download a host software package, please clink on the following link
to open the download page in your default browser.
https://github.com/firmata/ConfigurableFirmata#firmata-client-libraries
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2014 Nicolas Panel. All rights reserved.
Copyright (C) 2009-2017 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: September 16th, 2017
*/
/*
README
This is an example use of ConfigurableFirmata. The easiest way to create a configuration is to
use http://firmatabuilder.com and select the communication transport and the firmata features
to include and an Arduino sketch (.ino) file will be generated and downloaded automatically.
To manually configure a sketch, copy this file and follow the instructions in the
ETHERNET CONFIGURATION OPTION (if you want to use Ethernet instead of Serial/USB) and
FIRMATA FEATURE CONFIGURATION sections in this file.
*/
#include "ConfigurableFirmata.h"
/*==============================================================================
* ETHERNET CONFIGURATION OPTION
*
* By default Firmata uses the Serial-port (over USB) of the Arduino. ConfigurableFirmata may also
* comunicate over ethernet using tcp/ip. To configure this sketch to use Ethernet instead of
* Serial, uncomment the approprate includes for your particular hardware. See STEPS 1 - 5 below.
* If you want to use Serial (over USB) then skip ahead to the FIRMATA FEATURE CONFIGURATION
* section further down in this file.
*
* If you enable Ethernet, you will need a Firmata client library with a network transport that can
* act as a server in order to establish a connection between ConfigurableFirmataEthernet and the
* Firmata host application (your application).
*
* To use ConfigurableFirmata with Ethernet you will need to have one of the following
* boards or shields:
*
* - Arduino Ethernet shield (or clone)
* - Arduino Ethernet board (or clone)
* - Arduino Yun
*
* If you are using an Arduino Ethernet shield you cannot use the following pins on
* the following boards. Firmata will ignore any requests to use these pins:
*
* - Arduino Uno or other ATMega328 boards: (D4, D10, D11, D12, D13)
* - Arduino Mega: (D4, D10, D50, D51, D52, D53)
* - Arduino Leonardo: (D4, D10)
* - Arduino Due: (D4, D10)
* - Arduino Zero: (D4, D10)
*
* If you are using an ArduinoEthernet board, the following pins cannot be used (same as Uno):
* - D4, D10, D11, D12, D13
*============================================================================*/
// STEP 1 [REQUIRED]
// Uncomment / comment the appropriate set of includes for your hardware (OPTION A, B or C)
/*
* OPTION A: Configure for Arduino Ethernet board or Arduino Ethernet shield (or clone)
*
* To configure ConfigurableFirmata to use the an Arduino Ethernet Shield or Arduino Ethernet
* Board (both use the same WIZ5100-based Ethernet controller), uncomment the SPI and Ethernet
* includes below.
*/
//#include <SPI.h>
//#include <Ethernet.h>
/*
* OPTION B: Configure for a board or shield using an ENC28J60-based Ethernet controller,
* uncomment out the UIPEthernet include below.
*
* The UIPEthernet-library can be downloaded
* from: https://github.com/ntruchsess/arduino_uip
*/
//#include <UIPEthernet.h>
/*
* OPTION C: Configure for Arduino Yun
*
* The Ethernet port on the Arduino Yun board can be used with Firmata in this configuration.
* To execute StandardFirmataEthernet on Yun uncomment the Bridge and YunClient includes below.
*
* NOTE: in order to compile for the Yun you will also need to comment out some of the includes
* and declarations in the FIRMATA FEATURE CONFIGURATION section later in this file. Including all
* features exceeds the RAM and Flash memory of the Yun. Comment out anything you don't need.
*
* On Yun there's no need to configure local_ip and mac address as this is automatically
* configured on the linux-side of Yun.
*
* Establishing a connection with the Yun may take several seconds.
*/
//#include <Bridge.h>
//#include <YunClient.h>
#if defined ethernet_h || defined UIPETHERNET_H || defined _YUN_CLIENT_H_
#define NETWORK_FIRMATA
// STEP 2 [REQUIRED for all boards and shields]
// replace with IP of the server you want to connect to, comment out if using 'remote_host'
#define remote_ip IPAddress(192, 168, 0, 1)
// OR replace with hostname of server you want to connect to, comment out if using 'remote_ip'
// #define remote_host "server.local"
// STEP 3 [REQUIRED unless using Arduino Yun]
// Replace with the port that your server is listening on
#define remote_port 3030
// STEP 4 [REQUIRED unless using Arduino Yun OR if not using DHCP]
// Replace with your board or Ethernet shield's IP address
// Comment out if you want to use DHCP
#define local_ip IPAddress(192, 168, 0, 6)
// STEP 5 [REQUIRED unless using Arduino Yun]
// replace with Ethernet shield mac. Must be unique for your network
const byte mac[] = {0x90, 0xA2, 0xDA, 0x0D, 0x07, 0x02};
#endif
/*==============================================================================
* FIRMATA FEATURE CONFIGURATION
*
* Comment out the include and declaration for any features that you do not need
* below.
*
* WARNING: Including all of the following features (especially if also using
* Ethernet) may exceed the Flash and/or RAM of lower memory boards such as the
* Arduino Uno or Leonardo.
*============================================================================*/
#include <DigitalInputFirmata.h>
DigitalInputFirmata digitalInput;
#include <DigitalOutputFirmata.h>
DigitalOutputFirmata digitalOutput;
#include <AnalogInputFirmata.h>
AnalogInputFirmata analogInput;
#include <AnalogOutputFirmata.h>
AnalogOutputFirmata analogOutput;
#include <Servo.h>
#include <ServoFirmata.h>
ServoFirmata servo;
// ServoFirmata depends on AnalogOutputFirmata
#if defined ServoFirmata_h && ! defined AnalogOutputFirmata_h
#error AnalogOutputFirmata must be included to use ServoFirmata
#endif
#include <Wire.h>
#include <I2CFirmata.h>
I2CFirmata i2c;
#include <OneWireFirmata.h>
OneWireFirmata oneWire;
// StepperFirmata is deprecated as of ConfigurableFirmata v2.10.0. Please update your
// client implementation to use the new, more full featured and scalable AccelStepperFirmata.
#include <StepperFirmata.h>
StepperFirmata stepper;
#include <AccelStepperFirmata.h>
AccelStepperFirmata accelStepper;
#include <SerialFirmata.h>
SerialFirmata serial;
#include <FirmataExt.h>
FirmataExt firmataExt;
#include <FirmataScheduler.h>
FirmataScheduler scheduler;
// To add Encoder support you must first install the FirmataEncoder and Encoder libraries:
// https://github.com/firmata/FirmataEncoder
// https://www.pjrc.com/teensy/td_libs_Encoder.html
// #include <Encoder.h>
// #include <FirmataEncoder.h>
// FirmataEncoder encoder;
/*===================================================================================
* END FEATURE CONFIGURATION - you should not need to change anything below this line
*==================================================================================*/
// dependencies. Do not comment out the following lines
#if defined AnalogOutputFirmata_h || defined ServoFirmata_h
#include <AnalogWrite.h>
#endif
#if defined AnalogInputFirmata_h || defined I2CFirmata_h || defined FirmataEncoder_h
#include <FirmataReporting.h>
FirmataReporting reporting;
#endif
// dependencies for Network Firmata. Do not comment out.
#ifdef NETWORK_FIRMATA
#if defined remote_ip && defined remote_host
#error "cannot define both remote_ip and remote_host at the same time!"
#endif
#include <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
#endif
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void systemResetCallback()
{
// initialize a default state
// pins with analog capability default to analog input
// otherwise, pins default to digital output
for (byte i = 0; i < TOTAL_PINS; i++) {
if (IS_PIN_ANALOG(i)) {
#ifdef AnalogInputFirmata_h
// turns off pull-up, configures everything
Firmata.setPinMode(i, PIN_MODE_ANALOG);
#endif
} else if (IS_PIN_DIGITAL(i)) {
#ifdef DigitalOutputFirmata_h
// sets the output to 0, configures portConfigInputs
Firmata.setPinMode(i, OUTPUT);
#endif
}
}
#ifdef FirmataExt_h
firmataExt.reset();
#endif
}
/*==============================================================================
* SETUP()
*============================================================================*/
void setup()
{
/*
* ETHERNET SETUP
*/
#ifdef NETWORK_FIRMATA
#ifdef _YUN_CLIENT_H_
Bridge.begin();
#else
#ifdef local_ip
Ethernet.begin((uint8_t *)mac, local_ip); //start Ethernet
#else
Ethernet.begin((uint8_t *)mac); //start Ethernet using dhcp
#endif
#endif
delay(1000);
#endif
/*
* FIRMATA SETUP
*/
Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION);
#ifdef FirmataExt_h
#ifdef DigitalInputFirmata_h
firmataExt.addFeature(digitalInput);
#endif
#ifdef DigitalOutputFirmata_h
firmataExt.addFeature(digitalOutput);
#endif
#ifdef AnalogInputFirmata_h
firmataExt.addFeature(analogInput);
#endif
#ifdef AnalogOutputFirmata_h
firmataExt.addFeature(analogOutput);
#endif
#ifdef ServoFirmata_h
firmataExt.addFeature(servo);
#endif
#ifdef I2CFirmata_h
firmataExt.addFeature(i2c);
#endif
#ifdef OneWireFirmata_h
firmataExt.addFeature(oneWire);
#endif
#ifdef StepperFirmata_h
firmataExt.addFeature(stepper);
#endif
#ifdef AccelStepperFirmata_h
firmataExt.addFeature(accelStepper);
#endif
#ifdef SerialFirmata_h
firmataExt.addFeature(serial);
#endif
#ifdef FirmataReporting_h
firmataExt.addFeature(reporting);
#endif
#ifdef FirmataScheduler_h
firmataExt.addFeature(scheduler);
#endif
#ifdef FirmataEncoder_h
firmataExt.addFeature(encoder);
#endif
#endif
/* systemResetCallback is declared here (in ConfigurableFirmata.ino) */
Firmata.attach(SYSTEM_RESET, systemResetCallback);
// Network Firmata communicates with Ethernet-shields over SPI. Therefor all
// SPI-pins must be set to PIN_MODE_IGNORE. Otherwise Firmata would break SPI-communication.
// add Pin 10 and configure pin 53 as output if using a MEGA with Ethernetshield.
// No need to ignore pin 10 on MEGA with ENC28J60, as here pin 53 should be connected to SS:
#ifdef NETWORK_FIRMATA
#ifndef _YUN_CLIENT_H_
// ignore SPI and pin 4 that is SS for SD-Card on Ethernet-shield
for (byte i = 0; i < TOTAL_PINS; i++) {
if (IS_PIN_SPI(i)
|| 4 == i // SD Card on Ethernet shield uses pin 4 for SS
|| 10 == i // Ethernet-shield uses pin 10 for SS
) {
Firmata.setPinMode(i, PIN_MODE_IGNORE);
}
}
// pinMode(PIN_TO_DIGITAL(53), OUTPUT); configure hardware-SS as output on MEGA
pinMode(PIN_TO_DIGITAL(4), OUTPUT); // switch off SD-card bypassing Firmata
digitalWrite(PIN_TO_DIGITAL(4), HIGH); // SS is active low;
#endif
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
pinMode(PIN_TO_DIGITAL(53), OUTPUT); // configure hardware SS as output on MEGA
#endif
// start up Network Firmata:
Firmata.begin(stream);
#else
// Uncomment to save a couple of seconds by disabling the startup blink sequence.
// Firmata.disableBlinkVersion();
// start up the default Firmata using Serial interface:
Firmata.begin(57600);
#endif
systemResetCallback(); // reset to default config
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
#ifdef DigitalInputFirmata_h
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* stream buffer using Firmata.write() */
digitalInput.report();
#endif
/* STREAMREAD - processing incoming message as soon as possible, while still
* checking digital inputs. */
while (Firmata.available()) {
Firmata.processInput();
#ifdef FirmataScheduler_h
if (!Firmata.isParsingMessage()) {
goto runtasks;
}
}
if (!Firmata.isParsingMessage()) {
runtasks: scheduler.runTasks();
#endif
}
/* SEND STREAM WRITE BUFFER - TO DO: make sure that the stream buffer doesn't go over
* 60 bytes. use a timer to sending an event character every 4 ms to
* trigger the buffer to dump. */
#ifdef FirmataReporting_h
if (reporting.elapsed()) {
#ifdef AnalogInputFirmata_h
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
analogInput.report();
#endif
#ifdef I2CFirmata_h
// report i2c data for all device with read continuous mode enabled
i2c.report();
#endif
#ifdef FirmataEncoder_h
// report encoders positions if reporting enabled.
encoder.report();
#endif
}
#endif
#ifdef StepperFirmata_h
stepper.update();
#endif
#ifdef AccelStepperFirmata_h
accelStepper.update();
#endif
#ifdef SerialFirmata_h
serial.update();
#endif
#if defined NETWORK_FIRMATA && !defined local_ip &&!defined _YUN_CLIENT_H_
// only necessary when using DHCP, ensures local IP is updated appropriately if it changes
if (Ethernet.maintain()) {
stream.maintain(Ethernet.localIP());
}
#endif
}

View File

@@ -0,0 +1,382 @@
/*
Firmata is a generic protocol for communicating with microcontrollers
from software on a host computer. It is intended to work with
any host computer software package.
To download a host software package, please clink on the following link
to open the download page in your default browser.
https://github.com/firmata/ConfigurableFirmata#firmata-client-libraries
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2014 Nicolas Panel. All rights reserved.
Copyright (C) 2009-2017 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 September 16th, 2017
*/
/*
README
This is an example use of ConfigurableFirmata with BLE.
ConfigurableFirmataBLE enables the use of Firmata over a BLE connection. To configure your
connection, follow the instructions in the BLE CONFIGURATION section of this file.
To use ConfigurableFirmataBLE you will need to have one of the following boards or shields:
- Arduino 101 (recommended)
- RedBear BLE Nano ** works with modifications **
- RedBear BLE Shield v2 ** to be verfied **
If you are using an Arduino 101, make sure you have the Intel Curie Boards package v1.0.6
or higer installed via the Arduino Boards Manager.
*/
#include "ConfigurableFirmata.h"
// min cannot be < 0x0006. Adjust max if necessary
#define FIRMATA_BLE_MIN_INTERVAL 0x0006 // 7.5ms (7.5 / 1.25)
#define FIRMATA_BLE_MAX_INTERVAL 0x0018 // 30ms (30 / 1.25)
/*==============================================================================
* BLE CONFIGURATION
*
* If you are using an Arduino 101, you do not need to make any changes to this
* section of the file unless you need a unique ble local name. If you are using
* another supported BLE board or shield, follow the instructions for the specific
* board or shield below.
*============================================================================*/
// change this to a unique name per board if running StandardFirmataBLE on multiple boards
// within the same physical space
#define FIRMATA_BLE_LOCAL_NAME "FIRMATA"
/*
* Arduino 101
*
* Make sure you have the Intel Curie Boards package v1.0.6 or higher installed via the Arduino
* Boards Manager.
*
* Test script: https://gist.github.com/soundanalogous/927360b797574ed50e27
*/
#ifdef _VARIANT_ARDUINO_101_X_
#include <CurieBLE.h>
#include "utility/BLEStream.h"
BLEStream stream;
#endif
/*
* RedBearLab BLE Nano (with default switch settings)
*
* Blocked on this issue: https://github.com/RedBearLab/nRF51822-Arduino/pull/97
* Works with modifications. See comments at top of the test script referenced below.
* When the RBL nRF51822-Arduino library issue is resolved, this should work witout
* any modifications.
*
* Test script: https://gist.github.com/soundanalogous/d39bb3eb36333a0906df
*
* Note: If you have changed the solder jumpers on the Nano you may encounter issues since
* the pins are currently mapped in Firmata only for the default (factory) jumper settings.
*/
#ifdef BLE_NANO
#include <BLEPeripheral.h>
#include "utility/BLEStream.h"
BLEStream stream;
#endif
/*
* RedBearLab BLE Shield
*
* If you are using a RedBearLab BLE shield, uncomment the define below.
* Also, change the define for BLE_RST if you have the jumper set to pin 7 rather than pin 4.
*
* You will need to use the shield with an Arduino Zero, Due, Mega, or other board with sufficient
* Flash and RAM. Arduino Uno, Leonardo and other ATmega328p and Atmega32u4 boards do not have
* enough memory to run StandardFirmataBLE.
*
* TODO: verify if this works and with which boards it works.
*
* Test script: https://gist.github.com/soundanalogous/927360b797574ed50e27
*/
//#define REDBEAR_BLE_SHIELD
#ifdef REDBEAR_BLE_SHIELD
#include <SPI.h>
#include <BLEPeripheral.h>
#include "utility/BLEStream.h"
#define BLE_REQ 9
#define BLE_RDY 8
#define BLE_RST 4 // 4 or 7 via jumper on shield
BLEStream stream(BLE_REQ, BLE_RDY, BLE_RST);
#endif
#if defined(BLE_REQ) && defined(BLE_RDY) && defined(BLE_RST)
#define IS_IGNORE_BLE_PINS(p) ((p) == BLE_REQ || (p) == BLE_RDY || (p) == BLE_RST)
#endif
/*==============================================================================
* FIRMATA FEATURE CONFIGURATION
*
* Comment out the include and declaration for any features that you do not need
* below.
*
* WARNING: Including all of the following features (especially if also using
* Ethernet) may exceed the Flash and/or RAM of lower memory boards such as the
* Arduino Uno or Leonardo.
*============================================================================*/
#include <DigitalInputFirmata.h>
DigitalInputFirmata digitalInput;
#include <DigitalOutputFirmata.h>
DigitalOutputFirmata digitalOutput;
#include <AnalogInputFirmata.h>
AnalogInputFirmata analogInput;
#include <AnalogOutputFirmata.h>
AnalogOutputFirmata analogOutput;
#include <Servo.h>
#include <ServoFirmata.h>
ServoFirmata servo;
// ServoFirmata depends on AnalogOutputFirmata
#if defined ServoFirmata_h && ! defined AnalogOutputFirmata_h
#error AnalogOutputFirmata must be included to use ServoFirmata
#endif
#include <Wire.h>
#include <I2CFirmata.h>
I2CFirmata i2c;
#include <OneWireFirmata.h>
OneWireFirmata oneWire;
// StepperFirmata is deprecated as of ConfigurableFirmata v2.10.0. Please update your
// client implementation to use the new, more full featured and scalable AccelStepperFirmata.
#include <StepperFirmata.h>
StepperFirmata stepper;
#include <AccelStepperFirmata.h>
AccelStepperFirmata accelStepper;
#include <SerialFirmata.h>
SerialFirmata serial;
#include <FirmataExt.h>
FirmataExt firmataExt;
#include <FirmataScheduler.h>
FirmataScheduler scheduler;
// To add Encoder support you must first install the FirmataEncoder and Encoder libraries:
// https://github.com/firmata/FirmataEncoder
// https://www.pjrc.com/teensy/td_libs_Encoder.html
// #include <Encoder.h>
// #include <FirmataEncoder.h>
// FirmataEncoder encoder;
/*===================================================================================
* END FEATURE CONFIGURATION - you should not need to change anything below this line
*==================================================================================*/
// dependencies. Do not comment out the following lines
#if defined AnalogOutputFirmata_h || defined ServoFirmata_h
#include <AnalogWrite.h>
#endif
#if defined AnalogInputFirmata_h || defined I2CFirmata_h || defined FirmataEncoder_h
#include <FirmataReporting.h>
FirmataReporting reporting;
#endif
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void systemResetCallback()
{
// initialize a default state
// pins with analog capability default to analog input
// otherwise, pins default to digital output
for (byte i = 0; i < TOTAL_PINS; i++) {
if (IS_PIN_ANALOG(i)) {
#ifdef AnalogInputFirmata_h
// turns off pull-up, configures everything
Firmata.setPinMode(i, PIN_MODE_ANALOG);
#endif
} else if (IS_PIN_DIGITAL(i)) {
#ifdef DigitalOutputFirmata_h
// sets the output to 0, configures portConfigInputs
Firmata.setPinMode(i, OUTPUT);
#endif
}
}
#ifdef FirmataExt_h
firmataExt.reset();
#endif
}
/*==============================================================================
* SETUP()
*============================================================================*/
void ignorePins()
{
#ifdef BLE_REQ
for (byte i = 0; i < TOTAL_PINS; i++) {
if (IS_IGNORE_BLE_PINS(i)) {
Firmata.setPinMode(i, PIN_MODE_IGNORE);
}
}
#endif
}
void initTransport()
{
stream.setLocalName(FIRMATA_BLE_LOCAL_NAME);
// set the BLE connection interval - this is the fastest interval you can read inputs
stream.setConnectionInterval(FIRMATA_BLE_MIN_INTERVAL, FIRMATA_BLE_MAX_INTERVAL);
// set how often the BLE TX buffer is flushed (if not full)
stream.setFlushInterval(FIRMATA_BLE_MAX_INTERVAL);
stream.begin();
}
void initFirmata()
{
Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION);
#ifdef FirmataExt_h
#ifdef DigitalInputFirmata_h
firmataExt.addFeature(digitalInput);
#endif
#ifdef DigitalOutputFirmata_h
firmataExt.addFeature(digitalOutput);
#endif
#ifdef AnalogInputFirmata_h
firmataExt.addFeature(analogInput);
#endif
#ifdef AnalogOutputFirmata_h
firmataExt.addFeature(analogOutput);
#endif
#ifdef ServoFirmata_h
firmataExt.addFeature(servo);
#endif
#ifdef I2CFirmata_h
firmataExt.addFeature(i2c);
#endif
#ifdef OneWireFirmata_h
firmataExt.addFeature(oneWire);
#endif
#ifdef StepperFirmata_h
firmataExt.addFeature(stepper);
#endif
#ifdef AccelStepperFirmata_h
firmataExt.addFeature(accelStepper);
#endif
#ifdef SerialFirmata_h
firmataExt.addFeature(serial);
#endif
#ifdef FirmataReporting_h
firmataExt.addFeature(reporting);
#endif
#ifdef FirmataScheduler_h
firmataExt.addFeature(scheduler);
#endif
#ifdef FirmataEncoder_h
firmataExt.addFeature(encoder);
#endif
#endif
/* systemResetCallback is declared here (in ConfigurableFirmata.ino) */
Firmata.attach(SYSTEM_RESET, systemResetCallback);
ignorePins();
// Initialize Firmata to use the BLE stream object as the transport.
Firmata.begin(stream);
systemResetCallback(); // reset to default config
}
void setup()
{
initTransport();
initFirmata();
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
// do not process data if no BLE connection is established
// poll will send the TX buffer at the specified flush interval or when the buffer is full
if (!stream.poll()) return;
#ifdef DigitalInputFirmata_h
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* stream buffer using Firmata.write() */
digitalInput.report();
#endif
/* STREAMREAD - processing incoming message as soon as possible, while still
* checking digital inputs. */
while (Firmata.available()) {
Firmata.processInput();
#ifdef FirmataScheduler_h
if (!Firmata.isParsingMessage()) {
goto runtasks;
}
}
if (!Firmata.isParsingMessage()) {
runtasks: scheduler.runTasks();
#endif
}
#ifdef FirmataReporting_h
if (reporting.elapsed()) {
#ifdef AnalogInputFirmata_h
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
analogInput.report();
#endif
#ifdef I2CFirmata_h
// report i2c data for all device with read continuous mode enabled
i2c.report();
#endif
#ifdef FirmataEncoder_h
// report encoders positions if reporting enabled.
encoder.report();
#endif
}
#endif
#ifdef StepperFirmata_h
stepper.update();
#endif
#ifdef AccelStepperFirmata_h
accelStepper.update();
#endif
#ifdef SerialFirmata_h
serial.update();
#endif
}

View File

@@ -0,0 +1,690 @@
/*
Firmata is a generic protocol for communicating with microcontrollers
from software on a host computer. It is intended to work with
any host computer software package.
To download a host software package, please clink on the following link
to open the download page in your default browser.
https://github.com/firmata/ConfigurableFirmata#firmata-client-libraries
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2014 Nicolas Panel. All rights reserved.
Copyright (C) 2015-2016 Jesse Frush. All rights reserved.
Copyright (C) 2009-2017 Jeff Hoefs. All rights reserved.
Copyright (C) 2016 Jens B. 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: September 16th, 2017
*/
/*
README
This is an example use of ConfigurableFirmata with WiFi.
ConfigurableFirmataWiFi enables the use of Firmata over a TCP connection. It can be configured as
either a TCP server or TCP client. To configure your Wi-Fi connection, follow the instructions in
the WIFI CONFIGURATION section of this file.
To use ConfigurableFirmataWiFi you will need to have one of the following
boards or shields:
- Arduino MKR1000 board (recommended)
- ESP8266 WiFi board compatible with ESP8266 Arduino core
- Arduino WiFi Shield 101
- Arduino WiFi Shield (or clone)
Follow the instructions in the wifiConfig.h file (wifiConfig.h tab in Arduino IDE) to
configure your particular hardware.
Dependencies:
- WiFi Shield 101 requires version 0.7.0 or higher of the WiFi101 library (available in Arduino
1.6.8 or higher, or update the library via the Arduino Library Manager or clone from source:
https://github.com/arduino-libraries/WiFi101)
- ESP8266 requires the Arduino ESP8266 core v2.1.0 or higher which can be obtained here:
https://github.com/esp8266/Arduino
In order to use the WiFi Shield 101 with Firmata you will need a board with at least 35k of Flash
memory. This means you cannot use the WiFi Shield 101 with an Arduino Uno or any other
ATmega328p-based microcontroller or with an Arduino Leonardo or other ATmega32u4-based
microcontroller. Some boards that will work are:
- Arduino Zero
- Arduino Due
- Arduino 101
- Arduino Mega
NOTE: If you are using an Arduino WiFi (legacy) 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, D7, D10, D11, D12, D13)
- Arduino Mega: (D4, D7, D10, D50, D51, D52, D53)
- Arduino Due, Zero or Leonardo: (D4, D7, D10)
If you are using an Arduino WiFi 101 shield you cannot use the following pins on the following
boards:
- Arduino Due or Zero: (D5, D7, D10)
- Arduino Mega: (D5, D7, D10, D50, D52, D53)
*/
#include "ConfigurableFirmata.h"
/*
* Uncomment the #define SERIAL_DEBUG line below to receive serial output messages relating to your
* connection that may help in the event of connection issues. If defined, some boards may not begin
* executing this sketch until the Serial console is opened.
*/
//#define SERIAL_DEBUG
#include "utility/firmataDebug.h"
#define MAX_CONN_ATTEMPTS 20 // [500 ms] -> 10 s
/*==============================================================================
* WIFI CONFIGURATION
*
* You must configure your particular hardware. Follow the steps below.
*
* By default, ConfigurableFirmataWiFi is configured as a TCP server, to configure
* as a TCP client, see STEP 2.
*============================================================================*/
// STEP 1 [REQUIRED]
// Uncomment / comment the appropriate set of includes for your hardware (OPTION A, B or C)
// Arduino MKR1000 or ESP8266 are enabled by default if compiling for either of those boards.
/*
* OPTION A: Configure for Arduino MKR1000 or Arduino WiFi Shield 101
*
* This will configure ConfigurableFirmataWiFi to use the WiFi101 library, which works with the
* Arduino WiFi101 shield and devices that have the WiFi101 chip built in (such as the MKR1000).
* It is compatible with 802.11 B/G/N networks.
*
* If you are using the MKR1000 board, continue on to STEP 2. If you are using the WiFi 101 shield,
* follow the instructions below.
*
* To enable for the WiFi 101 shield, uncomment the #define WIFI_101 below and verify the
* #define ARDUINO_WIFI_SHIELD is commented out for OPTION B.
*
* IMPORTANT: You must have the WiFI 101 library installed. To easily install this library, open
* the library manager via: Arduino IDE Menus: Sketch > Include Library > Manage Libraries > filter
* search for "WiFi101" > Select the result and click 'install'
*/
//#define WIFI_101
//do not modify the following 11 lines
#if defined(ARDUINO_SAMD_MKR1000) && !defined(WIFI_101)
// automatically include if compiling for MRK1000
#define WIFI_101
#endif
#ifdef WIFI_101
#include <WiFi101.h>
#include "utility/WiFiClientStream.h"
#include "utility/WiFiServerStream.h"
#define WIFI_LIB_INCLUDED
#endif
/*
* OPTION B: Configure for legacy Arduino WiFi shield
*
* This will configure ConfigurableFirmataWiFi to use the original WiFi library (deprecated) provided
* with the Arduino IDE. It is supported by the Arduino WiFi shield (a discontinued product) and
* is compatible with 802.11 B/G networks.
*
* To configure ConfigurableFirmataWiFi to use the legacy Arduino WiFi shield
* leave the #define below uncommented and ensure #define WIFI_101 is commented out for OPTION A.
*/
//#define ARDUINO_WIFI_SHIELD
//do not modify the following 10 lines
#ifdef ARDUINO_WIFI_SHIELD
#include <WiFi.h>
#include "utility/WiFiClientStream.h"
#include "utility/WiFiServerStream.h"
#ifdef WIFI_LIB_INCLUDED
#define MULTIPLE_WIFI_LIB_INCLUDES
#else
#define WIFI_LIB_INCLUDED
#endif
#endif
/*
* OPTION C: Configure for ESP8266
*
* This will configure ConfigurableFirmataWiFi to use the ESP8266WiFi library for boards
* with an ESP8266 chip. It is compatible with 802.11 B/G/N networks.
*
* The appropriate libraries are included automatically when compiling for the ESP8266 so
* continue on to STEP 2.
*
* IMPORTANT: You must have the esp8266 board support installed. To easily install this board see
* the instructions here: https://github.com/esp8266/Arduino#installing-with-boards-manager.
*/
//do not modify the following 14 lines
#ifdef ESP8266
// automatically include if compiling for ESP8266
#define ESP8266_WIFI
#endif
#ifdef ESP8266_WIFI
#include <ESP8266WiFi.h>
#include "utility/WiFiClientStream.h"
#include "utility/WiFiServerStream.h"
#ifdef WIFI_LIB_INCLUDED
#define MULTIPLE_WIFI_LIB_INCLUDES
#else
#define WIFI_LIB_INCLUDED
#endif
#endif
// STEP 2 [OPTIONAL for all boards and shields]
// By default the board/shield is configured as a TCP server.
// If you want to setup you board/shield as a TCP client, uncomment the following define and
// replace the REMOTE_SERVER_IP address below with the IP address of your remote server.
//#define REMOTE_SERVER_IP 10, 0, 0, 15
// STEP 3 [REQUIRED for all boards and shields]
// replace this with your wireless network SSID
char ssid[] = "your_network_name";
// STEP 4 [OPTIONAL for all boards and shields]
// If you want to use a static IP (v4) address, uncomment the line below. You can also change the IP.
// If the first line is commented out, the WiFi shield will attempt to get an IP from the DHCP server.
// If you are using a static IP with the ESP8266 then you must also uncomment the SUBNET and GATEWAY.
//#define STATIC_IP_ADDRESS 192,168,1,113
//#define SUBNET_MASK 255,255,255,0 // REQUIRED for ESP8266_WIFI, optional for others
//#define GATEWAY_IP_ADDRESS 0,0,0,0 // REQUIRED for ESP8266_WIFI, optional for others
// STEP 5 [REQUIRED for all boards and shields]
// define your port number here, you will need this to open a TCP connection to your Arduino
#define NETWORK_PORT 3030
// STEP 6 [REQUIRED for all boards and shields]
// determine your network security type (OPTION A, B, or C). Option A is the most common, and the
// default.
/*
* OPTION A: WPA / WPA2
*
* WPA is the most common network security type. A passphrase is required to connect to this type.
*
* To enable, leave #define WIFI_WPA_SECURITY uncommented below, set your wpa_passphrase value
* appropriately, and do not uncomment the #define values under options B and C
*/
#define WIFI_WPA_SECURITY
#ifdef WIFI_WPA_SECURITY
char wpa_passphrase[] = "your_wpa_passphrase";
#endif //WIFI_WPA_SECURITY
/*
* OPTION B: WEP (not supported for ESP8266)
*
* WEP is a less common (and regarded as less safe) security type. A WEP key and its associated
* index are required to connect to this type.
*
* To enable, Uncomment the #define below, set your wep_index and wep_key values appropriately,
* and verify the #define values under options A and C are commented out.
*/
//#define WIFI_WEP_SECURITY
#ifdef WIFI_WEP_SECURITY
//The wep_index below is a zero-indexed value.
//Valid indices are [0-3], even if your router/gateway numbers your keys [1-4].
byte wep_index = 0;
char wep_key[] = "your_wep_key";
#endif //WIFI_WEP_SECURITY
/*
* OPTION C: Open network (no security)
*
* Open networks have no security, can be connected to by any device that knows the ssid, and are
* unsafe.
*
* To enable, uncomment #define WIFI_NO_SECURITY below and verify the #define values
* under options A and B are commented out.
*/
//#define WIFI_NO_SECURITY
/*==============================================================================
* CONFIGURATION ERROR CHECK (don't change anything here)
*============================================================================*/
#ifdef MULTIPLE_WIFI_LIB_INCLUDES
#error "you may not define more than one wifi device type in wifiConfig.h."
#endif
#ifndef WIFI_LIB_INCLUDED
#error "you must define a wifi device type in wifiConfig.h."
#endif
#if ((defined(WIFI_NO_SECURITY) && (defined(WIFI_WEP_SECURITY) || defined(WIFI_WPA_SECURITY))) || (defined(WIFI_WEP_SECURITY) && defined(WIFI_WPA_SECURITY)))
#error "you may not define more than one security type at the same time in wifiConfig.h."
#endif //WIFI_* security define check
#if !(defined(WIFI_NO_SECURITY) || defined(WIFI_WEP_SECURITY) || defined(WIFI_WPA_SECURITY))
#error "you must define a wifi security type in wifiConfig.h."
#endif //WIFI_* security define check
#if (defined(ESP8266_WIFI) && !(defined(WIFI_NO_SECURITY) || (defined(WIFI_WPA_SECURITY))))
#error "you must choose between WIFI_NO_SECURITY and WIFI_WPA_SECURITY"
#endif
/*==============================================================================
* WIFI STREAM (don't change anything here)
*============================================================================*/
#ifdef REMOTE_SERVER_IP
WiFiClientStream stream(IPAddress(REMOTE_SERVER_IP), NETWORK_PORT);
#else
WiFiServerStream stream(NETWORK_PORT);
#endif
/*==============================================================================
* PIN IGNORE MACROS (don't change anything here)
*============================================================================*/
#if defined(WIFI_101) && !defined(ARDUINO_SAMD_MKR1000)
// ignore SPI pins, pin 5 (reset WiFi101 shield), pin 7 (WiFi handshake) and pin 10 (WiFi SS)
// also don't ignore SS pin if it's not pin 10. Not needed for Arduino MKR1000.
#define IS_IGNORE_PIN(p) ((p) == 10 || (IS_PIN_SPI(p) && (p) != SS) || (p) == 5 || (p) == 7)
#elif defined(ARDUINO_WIFI_SHIELD) && defined(__AVR_ATmega32U4__)
// ignore SPI pins, pin 4 (SS for SD-Card on WiFi-shield), pin 7 (WiFi handshake) and pin 10 (WiFi SS)
// On Leonardo, pin 24 maps to D4 and pin 28 maps to D10
#define IS_IGNORE_PIN(p) ((IS_PIN_SPI(p) || (p) == 4) || (p) == 7 || (p) == 10 || (p) == 24 || (p) == 28)
#elif defined(ARDUINO_WIFI_SHIELD)
// ignore SPI pins, pin 4 (SS for SD-Card on WiFi-shield), pin 7 (WiFi handshake) and pin 10 (WiFi SS)
#define IS_IGNORE_PIN(p) ((IS_PIN_SPI(p) || (p) == 4) || (p) == 7 || (p) == 10)
#elif defined(ESP8266_WIFI) && defined(SERIAL_DEBUG)
#define IS_IGNORE_PIN(p) ((p) == 1)
#endif
/*==============================================================================
* FIRMATA FEATURE CONFIGURATION
*
* Comment out the include and declaration for any features that you do not need
* below.
*
* WARNING: Including all of the following features (especially if also using
* Ethernet) may exceed the Flash and/or RAM of lower memory boards such as the
* Arduino Uno or Leonardo.
*============================================================================*/
#include <DigitalInputFirmata.h>
DigitalInputFirmata digitalInput;
#include <DigitalOutputFirmata.h>
DigitalOutputFirmata digitalOutput;
#include <AnalogInputFirmata.h>
AnalogInputFirmata analogInput;
#include <AnalogOutputFirmata.h>
AnalogOutputFirmata analogOutput;
#include <Servo.h>
#include <ServoFirmata.h>
ServoFirmata servo;
// ServoFirmata depends on AnalogOutputFirmata
#if defined ServoFirmata_h && ! defined AnalogOutputFirmata_h
#error AnalogOutputFirmata must be included to use ServoFirmata
#endif
#include <Wire.h>
#include <I2CFirmata.h>
I2CFirmata i2c;
#include <OneWireFirmata.h>
OneWireFirmata oneWire;
// StepperFirmata is deprecated as of ConfigurableFirmata v2.10.0. Please update your
// client implementation to use the new, more full featured and scalable AccelStepperFirmata.
#include <StepperFirmata.h>
StepperFirmata stepper;
#include <AccelStepperFirmata.h>
AccelStepperFirmata accelStepper;
#include <SerialFirmata.h>
SerialFirmata serial;
#include <FirmataExt.h>
FirmataExt firmataExt;
#include <FirmataScheduler.h>
FirmataScheduler scheduler;
// To add Encoder support you must first install the FirmataEncoder and Encoder libraries:
// https://github.com/firmata/FirmataEncoder
// https://www.pjrc.com/teensy/td_libs_Encoder.html
// #include <Encoder.h>
// #include <FirmataEncoder.h>
// FirmataEncoder encoder;
/*===================================================================================
* END FEATURE CONFIGURATION - you should not need to change anything below this line
*==================================================================================*/
// dependencies. Do not comment out the following lines
#if defined AnalogOutputFirmata_h || defined ServoFirmata_h
#include <AnalogWrite.h>
#endif
#if defined AnalogInputFirmata_h || defined I2CFirmata_h || defined FirmataEncoder_h
#include <FirmataReporting.h>
FirmataReporting reporting;
#endif
#ifdef STATIC_IP_ADDRESS
IPAddress local_ip(STATIC_IP_ADDRESS);
#endif
#ifdef SUBNET_MASK
IPAddress subnet(SUBNET_MASK);
#endif
#ifdef GATEWAY_IP_ADDRESS
IPAddress gateway(GATEWAY_IP_ADDRESS);
#endif
int connectionAttempts = 0;
bool streamConnected = false;
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void systemResetCallback()
{
// initialize a default state
// pins with analog capability default to analog input
// otherwise, pins default to digital output
for (byte i = 0; i < TOTAL_PINS; i++) {
if (IS_PIN_ANALOG(i)) {
#ifdef AnalogInputFirmata_h
// turns off pull-up, configures everything
Firmata.setPinMode(i, PIN_MODE_ANALOG);
#endif
} else if (IS_PIN_DIGITAL(i)) {
#ifdef DigitalOutputFirmata_h
// sets the output to 0, configures portConfigInputs
Firmata.setPinMode(i, OUTPUT);
#endif
}
}
#ifdef FirmataExt_h
firmataExt.reset();
#endif
}
/*
* Called when a TCP connection is either connected or disconnected.
* TODO:
* - report connected or reconnected state to host (to be added to protocol)
*/
void hostConnectionCallback(byte state)
{
switch (state) {
case HOST_CONNECTION_CONNECTED:
DEBUG_PRINTLN( "TCP connection established" );
break;
case HOST_CONNECTION_DISCONNECTED:
DEBUG_PRINTLN( "TCP connection disconnected" );
break;
}
}
void printWifiStatus() {
if ( WiFi.status() != WL_CONNECTED )
{
DEBUG_PRINT( "WiFi connection failed. Status value: " );
DEBUG_PRINTLN( WiFi.status() );
}
else
{
// print the SSID of the network you're attached to:
DEBUG_PRINT( "SSID: " );
DEBUG_PRINTLN( WiFi.SSID() );
// print your WiFi shield's IP address:
DEBUG_PRINT( "IP Address: " );
IPAddress ip = WiFi.localIP();
DEBUG_PRINTLN( ip );
// print the received signal strength:
DEBUG_PRINT( "signal strength (RSSI): " );
long rssi = WiFi.RSSI();
DEBUG_PRINT( rssi );
DEBUG_PRINTLN( " dBm" );
}
}
/*
* ConfigurableFirmataWiFi communicates with WiFi shields over SPI. Therefore all
* SPI pins must be set to IGNORE. Otherwise Firmata would break SPI communication.
* Additional pins may also need to be ignored depending on the particular board or
* shield in use.
*/
void ignorePins()
{
#ifdef IS_IGNORE_PIN
for (byte i = 0; i < TOTAL_PINS; i++) {
if (IS_IGNORE_PIN(i)) {
Firmata.setPinMode(i, PIN_MODE_IGNORE);
}
}
#endif
//Set up controls for the Arduino WiFi Shield SS for the SD Card
#ifdef ARDUINO_WIFI_SHIELD
// Arduino WiFi Shield has 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
#endif //ARDUINO_WIFI_SHIELD
}
void initTransport()
{
// This statement will clarify how a connection is being made
DEBUG_PRINT( "ConfigurableFirmataWiFi will attempt a WiFi connection " );
#if defined(WIFI_101)
DEBUG_PRINTLN( "using the WiFi 101 library." );
#elif defined(ARDUINO_WIFI_SHIELD)
DEBUG_PRINTLN( "using the legacy WiFi library." );
#elif defined(ESP8266_WIFI)
DEBUG_PRINTLN( "using the ESP8266 WiFi library." );
//else should never happen here as error-checking in wifiConfig.h will catch this
#endif //defined(WIFI_101)
// Configure WiFi IP Address
#ifdef STATIC_IP_ADDRESS
DEBUG_PRINT( "Using static IP: " );
DEBUG_PRINTLN( local_ip );
#if defined(ESP8266_WIFI) || (defined(SUBNET_MASK) && defined(GATEWAY_IP_ADDRESS))
stream.config( local_ip , gateway, subnet );
#else
// you can also provide a static IP in the begin() functions, but this simplifies
// ifdef logic in this sketch due to support for all different encryption types.
stream.config( local_ip );
#endif
#else
DEBUG_PRINTLN( "IP will be requested from DHCP ..." );
#endif
stream.attach(hostConnectionCallback);
// Configure WiFi security and initiate WiFi connection
#if defined(WIFI_WEP_SECURITY)
DEBUG_PRINT( "Attempting to connect to WEP SSID: " );
DEBUG_PRINTLN(ssid);
stream.begin(ssid, wep_index, wep_key);
#elif defined(WIFI_WPA_SECURITY)
DEBUG_PRINT( "Attempting to connect to WPA SSID: " );
DEBUG_PRINTLN(ssid);
stream.begin(ssid, wpa_passphrase);
#else //OPEN network
DEBUG_PRINTLN( "Attempting to connect to open SSID: " );
DEBUG_PRINTLN(ssid);
stream.begin(ssid);
#endif //defined(WIFI_WEP_SECURITY)
DEBUG_PRINTLN( "WiFi setup done" );
// Wait for connection to access point to be established.
while (WiFi.status() != WL_CONNECTED && ++connectionAttempts <= MAX_CONN_ATTEMPTS) {
delay(500);
DEBUG_PRINT(".");
}
printWifiStatus();
}
void initFirmata()
{
Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION);
#ifdef FirmataExt_h
#ifdef DigitalInputFirmata_h
firmataExt.addFeature(digitalInput);
#endif
#ifdef DigitalOutputFirmata_h
firmataExt.addFeature(digitalOutput);
#endif
#ifdef AnalogInputFirmata_h
firmataExt.addFeature(analogInput);
#endif
#ifdef AnalogOutputFirmata_h
firmataExt.addFeature(analogOutput);
#endif
#ifdef ServoFirmata_h
firmataExt.addFeature(servo);
#endif
#ifdef I2CFirmata_h
firmataExt.addFeature(i2c);
#endif
#ifdef OneWireFirmata_h
firmataExt.addFeature(oneWire);
#endif
#ifdef StepperFirmata_h
firmataExt.addFeature(stepper);
#endif
#ifdef AccelStepperFirmata_h
firmataExt.addFeature(accelStepper);
#endif
#ifdef SerialFirmata_h
firmataExt.addFeature(serial);
#endif
#ifdef FirmataReporting_h
firmataExt.addFeature(reporting);
#endif
#ifdef FirmataScheduler_h
firmataExt.addFeature(scheduler);
#endif
#ifdef FirmataEncoder_h
firmataExt.addFeature(encoder);
#endif
#endif
/* systemResetCallback is declared here (in ConfigurableFirmata.ino) */
Firmata.attach(SYSTEM_RESET, systemResetCallback);
ignorePins();
// Initialize Firmata to use the WiFi stream object as the transport.
Firmata.begin(stream);
systemResetCallback(); // reset to default config
}
/*==============================================================================
* SETUP()
*============================================================================*/
void setup()
{
DEBUG_BEGIN(9600);
initTransport();
initFirmata();
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
#ifdef DigitalInputFirmata_h
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* stream buffer using Firmata.write() */
digitalInput.report();
#endif
/* STREAMREAD - processing incoming message as soon as possible, while still
* checking digital inputs. */
while (Firmata.available()) {
Firmata.processInput();
#ifdef FirmataScheduler_h
if (!Firmata.isParsingMessage()) {
goto runtasks;
}
}
if (!Firmata.isParsingMessage()) {
runtasks: scheduler.runTasks();
#endif
}
// TODO - ensure that Stream buffer doesn't go over 60 bytes
#ifdef FirmataReporting_h
if (reporting.elapsed()) {
#ifdef AnalogInputFirmata_h
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
analogInput.report();
#endif
#ifdef I2CFirmata_h
// report i2c data for all device with read continuous mode enabled
i2c.report();
#endif
#ifdef FirmataEncoder_h
// report encoders positions if reporting enabled.
encoder.report();
#endif
}
#endif
#ifdef StepperFirmata_h
stepper.update();
#endif
#ifdef AccelStepperFirmata_h
accelStepper.update();
#endif
#ifdef SerialFirmata_h
serial.update();
#endif
// keep the WiFi connection live. Attempts to reconnect automatically if disconnected.
stream.maintain();
}

View File

@@ -0,0 +1,458 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

View File

@@ -0,0 +1,96 @@
#######################################
# Syntax Coloring Map For Firmata
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Firmata KEYWORD1 Firmata
callbackFunction KEYWORD1 callbackFunction
systemResetCallbackFunction KEYWORD1 systemResetCallbackFunction
stringCallbackFunction KEYWORD1 stringCallbackFunction
sysexCallbackFunction KEYWORD1 sysexCallbackFunction
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
printVersion KEYWORD2
blinkVersion KEYWORD2
printFirmwareVersion KEYWORD2
setFirmwareVersion KEYWORD2
setFirmwareNameAndVersion KEYWORD2
available KEYWORD2
processInput KEYWORD2
isParsingMessage KEYWORD2
parse KEYWORD2
sendAnalog KEYWORD2
sendDigital KEYWORD2
sendDigitalPort KEYWORD2
sendString KEYWORD2
sendSysex KEYWORD2
attach KEYWORD2
detach KEYWORD2
write KEYWORD2
sendValueAsTwo7bitBytes KEYWORD2
startSysex KEYWORD2
endSysex KEYWORD2
attachDelayTask KEYWORD2
delayTask KEYWORD2
getPinMode KEYWORD2
setPinMode KEYWORD2
getPinState KEYWORD2
setPinState KEYWORD2
writePort KEYWORD2
readPort KEYWORD2
disableBlinkVersion KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
FIRMATA_PROTOCOL_MAJOR_VERSION LITERAL1
FIRMATA_PROTOCOL_MINOR_VERSION LITERAL1
FIRMATA_PROTOCOL_BUGFIX_VERSION LITERAL1
FIRMATA_FIRMWARE_MAJOR_VERSION LITERAL1
FIRMATA_FIRMWARE_MINOR_VERSION LITERAL1
FIRMATA_FIRMWARE_BUGFIX_VERSION LITERAL1
MAX_DATA_BYTES LITERAL1
DIGITAL_MESSAGE LITERAL1
ANALOG_MESSAGE LITERAL1
REPORT_ANALOG LITERAL1
REPORT_DIGITAL LITERAL1
REPORT_VERSION LITERAL1
SET_PIN_MODE LITERAL1
SET_DIGITAL_PIN_VALUE LITERAL1
SYSTEM_RESET LITERAL1
START_SYSEX LITERAL1
END_SYSEX LITERAL1
REPORT_FIRMWARE LITERAL1
STRING_DATA LITERAL1
PIN_MODE_ANALOG LITERAL1
PIN_MODE_PWM LITERAL1
PIN_MODE_SERVO LITERAL1
PIN_MODE_SHIFT LITERAL1
PIN_MODE_I2C LITERAL1
PIN_MODE_ONEWIRE LITERAL1
PIN_MODE_STEPPER LITERAL1
PIN_MODE_ENCODER LITERAL1
PIN_MODE_SERIAL LITERAL1
PIN_MODE_PULLUP LITERAL1
PIN_MODE_IGNORE LITERAL1
TOTAL_PINS LITERAL1
TOTAL_ANALOG_PINS LITERAL1
TOTAL_DIGITAL_PINS LITERAL1
TOTAL_PIN_MODES LITERAL1
TOTAL_PORTS LITERAL1
ANALOG_PORT LITERAL1
MAX_SERVOS LITERAL1

View File

@@ -0,0 +1,16 @@
{
"name": "ConfigurableFirmata",
"keywords": "interface, protocol",
"description": "This library implements the Firmata protocol as a set of plugins that can be used to create applications to remotely interface with an Arduino board.",
"repository": {
"type": "git",
"url": "https://github.com/firmata/ConfigurableFirmata.git"
},
"version": "2.10.0",
"exclude": [
"extras",
"test"
],
"frameworks": "arduino",
"platforms": "*"
}

View File

@@ -0,0 +1,9 @@
name=ConfigurableFirmata
version=2.10.0
author=Firmata Developers
maintainer=https://github.com/firmata/ConfigurableFirmata
sentence=This library implements the Firmata protocol as a set of plugins that can be used to create applications to remotely interface with an Arduino board.
paragraph=ConfigurableFirmata is an implementation of the Firmata protocol that breaks features such as Digital Input, Digital Output, Analog Input, Analog Output, I2C, etc into individual classes making it easier to mix and match standard features with custom features.
category=Device Control
url=https://github.com/firmata/ConfigurableFirmata
architectures=*

View File

@@ -0,0 +1,73 @@
# ConfigurableFirmata
[![Join the chat at https://gitter.im/firmata/ConfigurableFirmata](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/firmata/ConfigurableFirmata?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Firmata is a protocol for communicating with microcontrollers from software on a host computer. The [protocol](https://github.com/firmata/protocol) can be implemented in firmware on any microcontroller architecture as well as software on any host computer software package. The arduino repository described here is a Firmata library for Arduino and Arduino-compatible devices. If you would like to contribute to Firmata, please see the [Contributing](#contributing) section below.
## Installation
- **If you are using Arduino IDE version 1.6.4 or higher** go to `Sketch > Include Library > Manage Libraries` and then search for "ConfigurableFirmata" and click on `Install` after tapping on the ConfigurableFirmata item in the filtered results. You can also use this same method to update ConfigurableFirmata in the future.
- **If you are using an older version of the Arduino IDE**, download or clone ConfigurableFirmata to your Arduino sketchbook library folder. This is typically `/Documents/Arduino/libraries/` on Mac or Linux or `\My Documents\Arduino\libraries\` on Windows.
## Usage
ConfigurableFirmata is a version of Firmata that breaks features such as Digital Input, Digital Output, Analog Input, Analog Output, I2C, etc into [individual classes](https://github.com/firmata/ConfigurableFirmata/tree/master/src) making it easier to mix and match standard features with custom features.
The easiest way to use ConfigurableFirmata is with [firmatabuilder](http://firmatabuilder.com) which is a simple web application that generates an Arduino sketch based on a selection of Firmata features. Download the generated sketch, compile and upload it to your board.
Another way to use ConfigurableFirmata is by adding or removing various include statements in the [ConfigurableFirmata.ino](https://github.com/firmata/ConfigurableFirmata/blob/master/examples/ConfigurableFirmata/ConfigurableFirmata.ino) example file.
## Firmata Wrapper Libraries
You can use the ConfigurableFirmata architecture to wrap 3rd party libraries to include
functionality not included in the base ConfigurableFirmata.ino example. See [FirmataEncoder](https://github.com/firmata/FirmataEncoder) for an example of a Firmata wrapper. To include a Firmata wrapper your
ino file, you must install both the sketch and the 3rd party library into your `/Arduino/libraries/`
directory (where all 3rd party libraries are installed).
When creating a new Firmata wrapper library, you generally should not include the 3rd party
library it wraps. For example, the Encoder library that FirmataEncoder wraps is not included with
the FirmataEncoder library.
If you create a wrapper library, prepend the name with 'Firmata'. Hence 'FirmataEncoder' in the
referenced example. This will keep the wrapper libraries together in the user's Arduino libraries
directory.
A Firmata wrapper template library will be published soon along with instructions for creating
a wrapper library.
## Firmata Client Libraries
Only a few Firmata client libraries currently support ConfigurableFirmata:
* javascript
* [https://github.com/jgautier/firmata]
* [https://github.com/rwldrn/johnny-five]
* [http://breakoutjs.com]
* perl
* [https://github.com/ntruchsess/perl-firmata]
*Additional Firmata client libraries may work as well. If you're a client library developer and have verified that you library works with ConfigurableFirmata, please [open an issue](https://github.com/firmata/ConfigurableFirmata/issues) with a request to add the link.*
## Contributing
If you discover a bug or would like to propose a new feature, please open a new [issue](https://github.com/firmata/ConfigurableFirmata/issues?sort=created&state=open).
To contribute, fork this repository and create a new topic branch for the bug, feature or other existing issue you are addressing. Submit the pull request against the *master* branch.
You must thoroughly test your contributed code. In your pull request, describe tests performed to ensure that no existing code is broken and that any changes maintain backwards compatibility with the existing api. Test on multiple Arduino board variants if possible. We hope to enable some form of automated (or at least semi-automated) testing in the future, but for now any tests will need to be executed manually by the contributor and reviewers.
Use [Artistic Style](http://astyle.sourceforge.net/) (astyle) to format your code. Set the following rules for the astyle formatter:
```
style = ""
indent = "spaces"
indent-spaces = 2
indent-classes = true
indent-switches = true
indent-cases = true
indent-col1-comments = true
pad-oper = true
pad-header = true
keep-one-line-statements = true
```
If you happen to use Sublime Text, [this astyle plugin](https://github.com/timonwong/SublimeAStyleFormatter) is helpful. Set the above rules in the user settings file.

View File

@@ -0,0 +1,420 @@
/*
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2017 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: September 16th, 2017
*/
#include <ConfigurableFirmata.h>
#include "AccelStepperFirmata.h"
#include "utility/AccelStepper.h"
#include "utility/MultiStepper.h"
boolean AccelStepperFirmata::handlePinMode(byte pin, int mode)
{
if (mode == PIN_MODE_STEPPER) {
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
return true;
}
}
return false;
}
void AccelStepperFirmata::handleCapability(byte pin)
{
if (IS_PIN_DIGITAL(pin)) {
Firmata.write(PIN_MODE_STEPPER);
Firmata.write(21); //21 bits used for number of steps
}
}
// Send position data when it's requested or a move completes
void AccelStepperFirmata::reportPosition(byte deviceNum, bool complete)
{
if (stepper[deviceNum]) {
byte data[5];
long position = stepper[deviceNum]->currentPosition();
encode32BitSignedInteger(position, data);
Firmata.write(START_SYSEX);
Firmata.write(ACCELSTEPPER_DATA);
if (complete) {
Firmata.write(ACCELSTEPPER_MOVE_COMPLETE);
} else {
Firmata.write(ACCELSTEPPER_REPORT_POSITION);
}
Firmata.write(deviceNum);
Firmata.write(data[0]);
Firmata.write(data[1]);
Firmata.write(data[2]);
Firmata.write(data[3]);
Firmata.write(data[4]);
Firmata.write(END_SYSEX);
}
}
void AccelStepperFirmata::reportGroupComplete(byte deviceNum)
{
if (group[deviceNum]) {
Firmata.write(START_SYSEX);
Firmata.write(ACCELSTEPPER_DATA);
Firmata.write(MULTISTEPPER_MOVE_COMPLETE);
Firmata.write(deviceNum);
Firmata.write(END_SYSEX);
}
}
/*==============================================================================
* SYSEX-BASED commands
*============================================================================*/
boolean AccelStepperFirmata::handleSysex(byte command, byte argc, byte *argv)
{
if (command == ACCELSTEPPER_DATA) {
byte stepCommand, deviceNum, interface, wireCount, stepType;
byte stepOrMotorPin1, directionOrMotorPin2;
byte motorPin3 = 0, motorPin4 = 0, enablePin = 0, invertPins = 0;
long numSteps;
unsigned int index = 0;
stepCommand = argv[index++];
deviceNum = argv[index++];
if (deviceNum < MAX_ACCELSTEPPERS) {
if (stepCommand == ACCELSTEPPER_CONFIG) {
interface = argv[index++];
wireCount = (interface & 0x70) >> 4; // upper 3 bits are the wire count
stepType = (interface & 0x07) >> 1; // bits 4-6 are the step type
stepOrMotorPin1 = argv[index++]; // Step pin for driver or MotorPin1
directionOrMotorPin2 = argv[index++]; // Direction pin for driver or motorPin2
if (Firmata.getPinMode(directionOrMotorPin2) == PIN_MODE_IGNORE
|| Firmata.getPinMode(stepOrMotorPin1) == PIN_MODE_IGNORE) {
return false;
}
Firmata.setPinMode(stepOrMotorPin1, PIN_MODE_STEPPER);
Firmata.setPinMode(directionOrMotorPin2, PIN_MODE_STEPPER);
if (!stepper[deviceNum]) {
numSteppers++;
}
if (wireCount >= 3) {
motorPin3 = argv[index++];
if (Firmata.getPinMode(motorPin3) == PIN_MODE_IGNORE)
return false;
Firmata.setPinMode(motorPin3, PIN_MODE_STEPPER);
}
if (wireCount >= 4) {
motorPin4 = argv[index++];
if (Firmata.getPinMode(motorPin4) == PIN_MODE_IGNORE)
return false;
Firmata.setPinMode(motorPin4, PIN_MODE_STEPPER);
}
// If we have an enable pin
if (interface & 0x01) {
enablePin = argv[index++];
if (Firmata.getPinMode(enablePin) == PIN_MODE_IGNORE)
return false;
}
// Instantiate our stepper
if (wireCount == 1) {
stepper[deviceNum] = new AccelStepper(AccelStepper::DRIVER, stepOrMotorPin1, directionOrMotorPin2);
} else if (wireCount == 2) {
stepper[deviceNum] = new AccelStepper(AccelStepper::FULL2WIRE, stepOrMotorPin1, directionOrMotorPin2);
} else if (wireCount == 3 && stepType == STEP_TYPE_WHOLE) {
stepper[deviceNum] = new AccelStepper(AccelStepper::FULL3WIRE, stepOrMotorPin1, directionOrMotorPin2, motorPin3);
} else if (wireCount == 3 && stepType == STEP_TYPE_HALF) {
stepper[deviceNum] = new AccelStepper(AccelStepper::HALF3WIRE, stepOrMotorPin1, directionOrMotorPin2, motorPin3);
} else if (wireCount == 4 && stepType == STEP_TYPE_WHOLE) {
stepper[deviceNum] = new AccelStepper(AccelStepper::FULL4WIRE, stepOrMotorPin1, directionOrMotorPin2, motorPin3, motorPin4, false);
} else if (wireCount == 4 && stepType == STEP_TYPE_HALF) {
stepper[deviceNum] = new AccelStepper(AccelStepper::HALF4WIRE, stepOrMotorPin1, directionOrMotorPin2, motorPin3, motorPin4, false);
}
// If there is still another byte to read we must be inverting some pins
if (argc >= index) {
invertPins = argv[index];
if (wireCount == 1) {
stepper[deviceNum]->setPinsInverted(invertPins & 0x01, invertPins >> 1 & 0x01, invertPins >> 4 & 0x01);
} else {
stepper[deviceNum]->setPinsInverted(invertPins & 0x01, invertPins >> 1 & 0x01, invertPins >> 2 & 0x01, invertPins >> 3 & 0x01, invertPins >> 4 & 0x01);
}
}
if (interface & 0x01) {
stepper[deviceNum]->setEnablePin(enablePin);
}
/*
Default to no acceleration. We set the acceleration value high enough that our speed is
reached on the first step of a movement.
More info about this hack in ACCELSTEPPER_SET_ACCELERATION.
The lines where we are setting the speed twice are necessary because if the max speed doesn't change
from the default value then our time to next step does not get computed after raising the acceleration.
*/
stepper[deviceNum]->setMaxSpeed(2.0);
stepper[deviceNum]->setMaxSpeed(1.0);
stepper[deviceNum]->setAcceleration(MAX_ACCELERATION);
isRunning[deviceNum] = false;
}
else if (stepCommand == ACCELSTEPPER_STEP) {
numSteps = decode32BitSignedInteger(argv[2], argv[3], argv[4], argv[5], argv[6]);
if (stepper[deviceNum]) {
stepper[deviceNum]->move(numSteps);
isRunning[deviceNum] = true;
}
}
else if (stepCommand == ACCELSTEPPER_ZERO) {
if (stepper[deviceNum]) {
stepper[deviceNum]->setCurrentPosition(0);
}
}
else if (stepCommand == ACCELSTEPPER_TO) {
if (stepper[deviceNum]) {
numSteps = decode32BitSignedInteger(argv[2], argv[3], argv[4], argv[5], argv[6]);
stepper[deviceNum]->moveTo(numSteps);
isRunning[deviceNum] = true;
}
}
else if (stepCommand == ACCELSTEPPER_ENABLE) {
if (stepper[deviceNum]) {
if (argv[2] == 0x00) {
stepper[deviceNum]->disableOutputs();
} else {
stepper[deviceNum]->enableOutputs();
}
}
}
else if (stepCommand == ACCELSTEPPER_STOP) {
if (stepper[deviceNum]) {
stepper[deviceNum]->stop();
isRunning[deviceNum] = false;
reportPosition(deviceNum, true);
}
}
else if (stepCommand == ACCELSTEPPER_REPORT_POSITION) {
if (stepper[deviceNum]) {
reportPosition(deviceNum, false);
}
}
else if (stepCommand == ACCELSTEPPER_SET_ACCELERATION) {
float decodedAcceleration = decodeCustomFloat(argv[2], argv[3], argv[4], argv[5]);
if (stepper[deviceNum]) {
/*
<HACK>
All firmata instances of accelStepper have an acceleration value. If a user does not
want acceleration we just set the acceleration value high enough so
that the chosen speed will be realized by the first step of a movement.
This simplifies some of the logic in StepperFirmata and gives us more flexibility
should an alternative stepper library become available at a future date.
*/
if (decodedAcceleration == 0.0) {
stepper[deviceNum]->setAcceleration(MAX_ACCELERATION);
} else {
stepper[deviceNum]->setAcceleration(decodedAcceleration);
}
/*
</HACK>
*/
}
}
else if (stepCommand == ACCELSTEPPER_SET_SPEED) {
// Sets the maxSpeed for accelStepper. We do not use setSpeed here because
// all instances of accelStepper that have been created by firmata are
// using acceleration. More info about this hack in ACCELSTEPPER_SET_ACCELERATION.
float speed = decodeCustomFloat(argv[2], argv[3], argv[4], argv[5]);
if (stepper[deviceNum]) {
stepper[deviceNum]->setMaxSpeed(speed);
}
}
else if (stepCommand == MULTISTEPPER_CONFIG) {
if (!group[deviceNum]) {
numGroups++;
group[deviceNum] = new MultiStepper();
}
for (byte i = index; i < argc; i++) {
byte stepperNumber = argv[i];
if (stepper[stepperNumber]) {
groupStepperCount[deviceNum]++;
group[deviceNum]->addStepper(*stepper[stepperNumber]);
}
}
groupIsRunning[deviceNum] = false;
}
else if (stepCommand == MULTISTEPPER_TO) {
groupIsRunning[deviceNum] = true;
long positions[groupStepperCount[deviceNum]];
for (byte i = 0, offset = 0; i < groupStepperCount[deviceNum]; i++) {
offset = index + (i * 5);
positions[i] = decode32BitSignedInteger(argv[offset], argv[offset + 1], argv[offset + 2], argv[offset + 3], argv[offset + 4]);
}
group[deviceNum]->moveTo(positions);
}
else if (stepCommand == MULTISTEPPER_STOP) {
groupIsRunning[deviceNum] = false;
reportGroupComplete(deviceNum);
}
}
return true;
}
return false;
}
/*==============================================================================
* SETUP()
*============================================================================*/
void AccelStepperFirmata::reset()
{
for (byte i = 0; i < MAX_ACCELSTEPPERS; i++) {
if (stepper[i]) {
free(stepper[i]);
stepper[i] = 0;
}
}
numSteppers = 0;
for (byte i = 0; i < MAX_GROUPS; i++) {
if (group[i]) {
free(group[i]);
group[i] = 0;
}
}
numGroups = 0;
}
/*==============================================================================
* Helpers
*============================================================================*/
float AccelStepperFirmata::decodeCustomFloat(byte arg1, byte arg2, byte arg3, byte arg4)
{
long l4 = (long)arg4;
long significand = (long)arg1 | (long)arg2 << 7 | (long)arg3 << 14 | (l4 & 0x03) << 21;
float exponent = (float)(((l4 >> 2) & 0x0f) - 11);
bool sign = (bool)((l4 >> 6) & 0x01);
float result = (float)significand;
if (sign) {
result *= -1;
}
result = result * powf(10.0, exponent);
return result;
}
long AccelStepperFirmata::decode32BitSignedInteger(byte arg1, byte arg2, byte arg3, byte arg4, byte arg5)
{
long result = (long)arg1 | (long)arg2 << 7 | (long)arg3 << 14 | (long)arg4 << 21 | (((long)arg5 << 28) & 0x07);
if ((long)arg5 >> 3 == 0x01) {
result = result * -1;
}
return result;
}
void AccelStepperFirmata::encode32BitSignedInteger(long value, byte pdata[])
{
bool inv = false;
if (value < 0) {
inv = true;
value = value * -1;
}
pdata[0] = value & 0x7f;
pdata[1] = (value >> 7) & 0x7f;
pdata[2] = (value >> 14) & 0x7f;
pdata[3] = (value >> 21) & 0x7f;
pdata[4] = (value >> 28) & 0x7f;
if (inv == true) {
pdata[4] = pdata[4] | 0x08;
}
}
/*==============================================================================
* LOOP()
*============================================================================*/
void AccelStepperFirmata::update()
{
bool stepsLeft;
if (numGroups > 0) {
// if one or more groups exist, update their position
for (byte i = 0; i < MAX_GROUPS; i++) {
if (group[i] && groupIsRunning[i] == true) {
stepsLeft = group[i]->run();
// send command to client application when stepping is complete
if (stepsLeft != true) {
groupIsRunning[i] = false;
reportGroupComplete(i);
}
}
}
}
if (numSteppers > 0) {
// if one or more stepper motors are used, update their position
for (byte i = 0; i < MAX_ACCELSTEPPERS; i++) {
if (stepper[i] && isRunning[i] == true) {
stepsLeft = stepper[i]->run();
// send command to client application when stepping is complete
if (!stepsLeft) {
isRunning[i] = false;
reportPosition(i, true);
}
}
}
}
}

View File

@@ -0,0 +1,69 @@
/*
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2017 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.
*/
#ifndef AccelStepperFirmata_h
#define AccelStepperFirmata_h
#include <ConfigurableFirmata.h>
#include "utility/AccelStepper.h"
#include "utility/MultiStepper.h"
#include "FirmataFeature.h"
#define MAX_ACCELSTEPPERS 10 // arbitrary value... may need to adjust
#define MAX_GROUPS 5 // arbitrary value... may need to adjust
#define MAX_ACCELERATION 1000000 // 1000^2 so that full speed is reached on first step
#define STEP_TYPE_WHOLE 0x00
#define STEP_TYPE_HALF 0x01
#define ACCELSTEPPER_CONFIG 0x00
#define ACCELSTEPPER_ZERO 0x01
#define ACCELSTEPPER_STEP 0x02
#define ACCELSTEPPER_TO 0x03
#define ACCELSTEPPER_ENABLE 0x04
#define ACCELSTEPPER_STOP 0x05
#define ACCELSTEPPER_REPORT_POSITION 0x06
#define ACCELSTEPPER_LIMIT 0x07
#define ACCELSTEPPER_SET_ACCELERATION 0x08
#define ACCELSTEPPER_SET_SPEED 0x09
#define ACCELSTEPPER_MOVE_COMPLETE 0x0a
#define MULTISTEPPER_CONFIG 0x20
#define MULTISTEPPER_TO 0x21
#define MULTISTEPPER_STOP 0x23
#define MULTISTEPPER_MOVE_COMPLETE 0x24
class AccelStepperFirmata: public FirmataFeature
{
public:
boolean handlePinMode(byte pin, int mode);
void handleCapability(byte pin);
void reportPosition(byte deviceNum, bool complete);
void reportGroupComplete(byte deviceNum);
boolean handleSysex(byte command, byte argc, byte *argv);
float decodeCustomFloat(byte arg1, byte arg2, byte arg3, byte arg4);
long decode28BitUnsignedInteger(byte arg1, byte arg2, byte arg3, byte arg4);
long decode32BitSignedInteger(byte arg1, byte arg2, byte arg3, byte arg4, byte arg5);
void encode32BitSignedInteger(long value, byte pdata[]);
void update();
void reset();
private:
AccelStepper *stepper[MAX_ACCELSTEPPERS];
MultiStepper *group[MAX_GROUPS];
bool isRunning[MAX_ACCELSTEPPERS];
bool groupIsRunning[MAX_GROUPS];
byte numSteppers;
byte numGroups;
byte groupStepperCount[MAX_GROUPS];
};
#endif /* AccelStepperFirmata_h */

View File

@@ -0,0 +1,32 @@
/*
AnalogFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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.
*/
#include <ConfigurableFirmata.h>
#include "AnalogFirmata.h"
boolean handleAnalogFirmataSysex(byte command, byte argc, byte* argv)
{
if (command == 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);
return true;
}
return false;
}

View File

@@ -0,0 +1,24 @@
/*
AnalogFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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.
*/
#ifndef AnalogFirmata_h
#define AnalogFirmata_h
#include <ConfigurableFirmata.h>
boolean handleAnalogFirmataSysex(byte command, byte argc, byte* argv);
#endif

View File

@@ -0,0 +1,109 @@
/*
AnalogFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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: November 22nd, 2015
*/
#include <ConfigurableFirmata.h>
#include "AnalogFirmata.h"
#include "AnalogInputFirmata.h"
AnalogInputFirmata *AnalogInputFirmataInstance;
void reportAnalogInputCallback(byte analogPin, int value)
{
AnalogInputFirmataInstance->reportAnalog(analogPin, value);
}
AnalogInputFirmata::AnalogInputFirmata()
{
AnalogInputFirmataInstance = this;
analogInputsToReport = 0;
Firmata.attach(REPORT_ANALOG, reportAnalogInputCallback);
}
// -----------------------------------------------------------------------------
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
*/
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
//}
void AnalogInputFirmata::reportAnalog(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 (!Firmata.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
}
boolean AnalogInputFirmata::handlePinMode(byte pin, int mode)
{
if (IS_PIN_ANALOG(pin)) {
if (mode == PIN_MODE_ANALOG) {
reportAnalog(PIN_TO_ANALOG(pin), 1); // turn on reporting
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
}
return true;
} else {
reportAnalog(PIN_TO_ANALOG(pin), 0); // turn off reporting
}
}
return false;
}
void AnalogInputFirmata::handleCapability(byte pin)
{
if (IS_PIN_ANALOG(pin)) {
Firmata.write(PIN_MODE_ANALOG);
Firmata.write(10); // 10 = 10-bit resolution
}
}
boolean AnalogInputFirmata::handleSysex(byte command, byte argc, byte* argv)
{
return handleAnalogFirmataSysex(command, argc, argv);
}
void AnalogInputFirmata::reset()
{
// by default, do not report any analog inputs
analogInputsToReport = 0;
}
void AnalogInputFirmata::report()
{
byte pin, analogPin;
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
for (pin = 0; pin < TOTAL_PINS; pin++) {
if (IS_PIN_ANALOG(pin) && Firmata.getPinMode(pin) == PIN_MODE_ANALOG) {
analogPin = PIN_TO_ANALOG(pin);
if (analogInputsToReport & (1 << analogPin)) {
Firmata.sendAnalog(analogPin, analogRead(analogPin));
}
}
}
}

View File

@@ -0,0 +1,42 @@
/*
AnalogFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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.
*/
#ifndef AnalogInputFirmata_h
#define AnalogInputFirmata_h
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
#include "FirmataReporting.h"
void reportAnalogInputCallback(byte analogPin, int value);
class AnalogInputFirmata: public FirmataFeature
{
public:
AnalogInputFirmata();
void reportAnalog(byte analogPin, int value);
void handleCapability(byte pin);
boolean handlePinMode(byte pin, int mode);
boolean handleSysex(byte command, byte argc, byte* argv);
void reset();
void report();
private:
/* analog inputs */
int analogInputsToReport; // bitwise array to store pin reporting
};
#endif

View File

@@ -0,0 +1,65 @@
/*
AnalogFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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 December 23rd, 2016
*/
#include <ConfigurableFirmata.h>
#include "AnalogFirmata.h"
#include "AnalogOutputFirmata.h"
AnalogOutputFirmata::AnalogOutputFirmata()
{
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
}
void AnalogOutputFirmata::reset()
{
}
boolean AnalogOutputFirmata::handlePinMode(byte pin, int mode)
{
if (mode == PIN_MODE_PWM && IS_PIN_PWM(pin)) {
pinMode(PIN_TO_PWM(pin), OUTPUT);
analogWrite(PIN_TO_PWM(pin), 0);
return true;
}
return false;
}
void AnalogOutputFirmata::handleCapability(byte pin)
{
if (IS_PIN_PWM(pin)) {
Firmata.write(PIN_MODE_PWM);
Firmata.write(DEFAULT_PWM_RESOLUTION);
}
}
boolean AnalogOutputFirmata::handleSysex(byte command, byte argc, byte* argv)
{
if (command == 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);
return true;
}
return false;
} else {
return handleAnalogFirmataSysex(command, argc, argv);
}
}

View File

@@ -0,0 +1,38 @@
/*
AnalogFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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.
*/
#ifndef AnalogOutputFirmata_h
#define AnalogOutputFirmata_h
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
// analogWriteCallback is defined in AnalogWrite.h but must also be declared here in order
// for AnalogOutputFirmata to compile
void analogWriteCallback(byte pin, int value);
class AnalogOutputFirmata: public FirmataFeature
{
public:
AnalogOutputFirmata();
void handleCapability(byte pin);
boolean handlePinMode(byte pin, int mode);
boolean handleSysex(byte command, byte argc, byte* argv);
void reset();
private:
};
#endif

View File

@@ -0,0 +1,51 @@
/*
AnalogWrite.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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: November 15th, 2015
*/
#ifndef AnalogWrite_h
#define AnalogWrite_h
#include <ConfigurableFirmata.h>
#if defined AnalogOutputFirmata_h || defined ServoFirmata_h
void analogWriteCallback(byte pin, int value)
{
if (pin < TOTAL_PINS) {
switch (Firmata.getPinMode(pin)) {
#ifdef ServoFirmata_h
case PIN_MODE_SERVO:
if (IS_PIN_SERVO(pin)) {
servoAnalogWrite(pin, value);
Firmata.setPinState(pin, value);
}
break;
#endif
#ifdef AnalogOutputFirmata_h
case PIN_MODE_PWM:
if (IS_PIN_PWM(pin)) {
analogWrite(PIN_TO_PWM(pin), value);
Firmata.setPinState(pin, value);
}
break;
#endif
}
}
}
#endif
#endif

View File

@@ -0,0 +1,710 @@
/*
ConfigurableFirmata.pp - ConfigurableFirmata library v2.10.0 - 2017-6-16
Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (c) 2013 Norbert Truchsess. All rights reserved.
Copyright (c) 2013-2017 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.
*/
//******************************************************************************
//* Includes
//******************************************************************************
#include "ConfigurableFirmata.h"
#include "HardwareSerial.h"
extern "C" {
#include <string.h>
#include <stdlib.h>
}
//******************************************************************************
//* Support Functions
//******************************************************************************
/**
* Split a 16-bit byte into two 7-bit values and write each value.
* @param value The 16-bit value to be split and written separately.
*/
void FirmataClass::sendValueAsTwo7bitBytes(int value)
{
FirmataStream->write(value & B01111111); // LSB
FirmataStream->write(value >> 7 & B01111111); // MSB
}
/**
* A helper method to write the beginning of a Sysex message transmission.
*/
void FirmataClass::startSysex(void)
{
FirmataStream->write(START_SYSEX);
}
/**
* A helper method to write the end of a Sysex message transmission.
*/
void FirmataClass::endSysex(void)
{
FirmataStream->write(END_SYSEX);
}
//******************************************************************************
//* Constructors
//******************************************************************************
/**
* The Firmata class.
* An instance named "Firmata" is created automatically for the user.
*/
FirmataClass::FirmataClass()
{
firmwareVersionCount = 0;
firmwareVersionVector = 0;
blinkVersionDisabled = false;
systemReset();
}
//******************************************************************************
//* Public Methods
//******************************************************************************
/**
* Initialize the default Serial transport at the default baud of 57600.
*/
void FirmataClass::begin(void)
{
begin(57600);
}
/**
* Initialize the default Serial transport and override the default baud.
* Sends the protocol version to the host application followed by the firmware version and name.
* blinkVersion is also called. To skip the call to blinkVersion, call Firmata.disableBlinkVersion()
* before calling Firmata.begin(baud).
* @param speed The baud to use. 57600 baud is the default value.
*/
void FirmataClass::begin(long speed)
{
Serial.begin(speed);
FirmataStream = &Serial;
blinkVersion();
printVersion(); // send the protocol version
printFirmwareVersion(); // send the firmware name and version
}
/**
* Reassign the Firmata stream transport.
* @param s A reference to the Stream transport object. This can be any type of
* transport that implements the Stream interface. Some examples include Ethernet, WiFi
* and other UARTs on the board (Serial1, Serial2, etc).
*/
void FirmataClass::begin(Stream &s)
{
FirmataStream = &s;
// do not call blinkVersion() here because some hardware such as the
// Ethernet shield use pin 13
printVersion(); // send the protocol version
printFirmwareVersion(); // send the firmware name and version
}
/**
* Send the Firmata protocol version to the Firmata host application.
*/
void FirmataClass::printVersion(void)
{
FirmataStream->write(REPORT_VERSION);
FirmataStream->write(FIRMATA_PROTOCOL_MAJOR_VERSION);
FirmataStream->write(FIRMATA_PROTOCOL_MINOR_VERSION);
}
/**
* Blink the Firmata protocol version to the onboard LEDs (if the board has an onboard LED).
* If VERSION_BLINK_PIN is not defined in Boards.h for a particular board, then this method
* does nothing.
* The first series of flashes indicates the firmware major version (2 flashes = 2).
* The second series of flashes indicates the firmware minor version (5 flashes = 5).
*/
void FirmataClass::blinkVersion(void)
{
#if defined(VERSION_BLINK_PIN)
if (blinkVersionDisabled) return;
// flash the pin with the protocol version
pinMode(VERSION_BLINK_PIN, OUTPUT);
strobeBlinkPin(VERSION_BLINK_PIN, FIRMATA_FIRMWARE_MAJOR_VERSION, 40, 210);
delay(250);
strobeBlinkPin(VERSION_BLINK_PIN, FIRMATA_FIRMWARE_MINOR_VERSION, 40, 210);
delay(125);
#endif
}
/**
* Provides a means to disable the version blink sequence on the onboard LED, trimming startup
* time by a couple of seconds.
* Call this before Firmata.begin(). It only applies when using the default Serial transport.
*/
void FirmataClass::disableBlinkVersion()
{
blinkVersionDisabled = true;
}
/**
* Sends the firmware name and version to the Firmata host application. The major and minor version
* numbers are the first 2 bytes in the message. The following bytes are the characters of the
* firmware name.
*/
void FirmataClass::printFirmwareVersion(void)
{
byte i;
if (firmwareVersionCount) { // make sure that the name has been set before reporting
startSysex();
FirmataStream->write(REPORT_FIRMWARE);
FirmataStream->write(firmwareVersionVector[0]); // major version number
FirmataStream->write(firmwareVersionVector[1]); // minor version number
for (i = 2; i < firmwareVersionCount; ++i) {
sendValueAsTwo7bitBytes(firmwareVersionVector[i]);
}
endSysex();
}
}
/**
* Sets the name and version of the firmware. This is not the same version as the Firmata protocol
* (although at times the firmware version and protocol version may be the same number).
* @param name A pointer to the name char array
* @param major The major version number
* @param minor The minor version number
*/
void FirmataClass::setFirmwareNameAndVersion(const char *name, byte major, byte minor)
{
const char *firmwareName;
const char *extension;
// parse out ".cpp" and "applet/" that comes from using __FILE__
extension = strstr(name, ".cpp");
firmwareName = strrchr(name, '/');
if (!firmwareName) {
// windows
firmwareName = strrchr(name, '\\');
}
if (!firmwareName) {
// user passed firmware name
firmwareName = name;
} else {
firmwareName ++;
}
if (!extension) {
firmwareVersionCount = strlen(firmwareName) + 2;
} else {
firmwareVersionCount = extension - firmwareName + 2;
}
// in case anyone calls setFirmwareNameAndVersion more than once
free(firmwareVersionVector);
firmwareVersionVector = (byte *) malloc(firmwareVersionCount + 1);
firmwareVersionVector[firmwareVersionCount] = 0;
firmwareVersionVector[0] = major;
firmwareVersionVector[1] = minor;
strncpy((char *)firmwareVersionVector + 2, firmwareName, firmwareVersionCount - 2);
}
//------------------------------------------------------------------------------
// Input Stream Handling
/**
* A wrapper for Stream::available()
* @return The number of bytes remaining in the input stream buffer.
*/
int FirmataClass::available(void)
{
return FirmataStream->available();
}
/**
* Process incoming sysex messages. Handles REPORT_FIRMWARE and STRING_DATA internally.
* Calls callback function for STRING_DATA and all other sysex messages.
* @private
*/
void FirmataClass::processSysexMessage(void)
{
switch (storedInputData[0]) { //first byte in buffer is command
case REPORT_FIRMWARE:
printFirmwareVersion();
break;
case STRING_DATA:
if (currentStringCallback) {
byte bufferLength = (sysexBytesRead - 1) / 2;
byte i = 1;
byte j = 0;
while (j < bufferLength) {
// The string length will only be at most half the size of the
// stored input buffer so we can decode the string within the buffer.
storedInputData[j] = storedInputData[i];
i++;
storedInputData[j] += (storedInputData[i] << 7);
i++;
j++;
}
// Make sure string is null terminated. This may be the case for data
// coming from client libraries in languages that don't null terminate
// strings.
if (storedInputData[j - 1] != '\0') {
storedInputData[j] = '\0';
}
(*currentStringCallback)((char *)&storedInputData[0]);
}
break;
default:
if (currentSysexCallback)
(*currentSysexCallback)(storedInputData[0], sysexBytesRead - 1, storedInputData + 1);
}
}
/**
* Read a single int from the input stream. If the value is not = -1, pass it on to parse(byte)
*/
void FirmataClass::processInput(void)
{
int inputData = FirmataStream->read(); // this is 'int' to handle -1 when no data
if (inputData != -1) {
parse(inputData);
}
}
/**
* Parse data from the input stream.
* @param inputData A single byte to be added to the parser.
*/
void FirmataClass::parse(byte inputData)
{
int command;
// 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:
setPinMode(storedInputData[1], storedInputData[0]);
break;
case SET_DIGITAL_PIN_VALUE:
if (currentPinValueCallback)
(*currentPinValueCallback)(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:
case SET_DIGITAL_PIN_VALUE:
waitForData = 2; // two data bytes needed
executeMultiByteCommand = command;
break;
case REPORT_ANALOG:
case REPORT_DIGITAL:
waitForData = 1; // one data byte needed
executeMultiByteCommand = command;
break;
case START_SYSEX:
parsingSysex = true;
sysexBytesRead = 0;
break;
case SYSTEM_RESET:
systemReset();
break;
case REPORT_VERSION:
Firmata.printVersion();
break;
}
}
}
/**
* @return Returns true if the parser is actively parsing data.
*/
boolean FirmataClass::isParsingMessage(void)
{
return (waitForData > 0 || parsingSysex);
}
/**
* @return Returns true if the SYSTEM_RESET message is being executed
*/
boolean FirmataClass::isResetting(void)
{
return resetting;
}
//------------------------------------------------------------------------------
// Output Stream Handling
/**
* Send an analog message to the Firmata host application. The range of pins is limited to [0..15]
* when using the ANALOG_MESSAGE. The maximum value of the ANALOG_MESSAGE is limited to 14 bits
* (16384). To increase the pin range or value, see the documentation for the EXTENDED_ANALOG
* message.
* @param pin The analog pin to send the value of (limited to pins 0 - 15).
* @param value The value of the analog pin (0 - 1024 for 10-bit analog, 0 - 4096 for 12-bit, etc).
* The maximum value is 14-bits (16384).
*/
void FirmataClass::sendAnalog(byte pin, int value)
{
// pin can only be 0-15, so chop higher bits
FirmataStream->write(ANALOG_MESSAGE | (pin & 0xF));
sendValueAsTwo7bitBytes(value);
}
/* (intentionally left out asterix here)
* STUB - NOT IMPLEMENTED
* Send a single digital pin value to the Firmata host application.
* @param pin The digital pin to send the value of.
* @param value The value of the pin.
*/
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: 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();
}
/**
* Send an 8-bit port in a single digital message (protocol v2 and later).
* Send 14-bits in a single digital message (protocol v1).
* @param portNumber The port number to send. Note that this is not the same as a "port" on the
* physical microcontroller. Ports are defined in order per every 8 pins in ascending order
* of the Arduino digital pin numbering scheme. Port 0 = pins D0 - D7, port 1 = pins D8 - D15, etc.
* @param portData The value of the port. The value of each pin in the port is represented by a bit.
*/
void FirmataClass::sendDigitalPort(byte portNumber, int portData)
{
FirmataStream->write(DIGITAL_MESSAGE | (portNumber & 0xF));
FirmataStream->write((byte)portData % 128); // Tx bits 0-6
FirmataStream->write(portData >> 7); // Tx bits 7-13
}
/**
* Send a sysex message where all values after the command byte are packet as 2 7-bit bytes
* (this is not always the case so this function is not always used to send sysex messages).
* @param command The sysex command byte.
* @param bytec The number of data bytes in the message (excludes start, command and end bytes).
* @param bytev A pointer to the array of data bytes to send in the message.
*/
void FirmataClass::sendSysex(byte command, byte bytec, byte *bytev)
{
byte i;
startSysex();
FirmataStream->write(command);
for (i = 0; i < bytec; i++) {
sendValueAsTwo7bitBytes(bytev[i]);
}
endSysex();
}
/**
* Send a string to the Firmata host application.
* @param command Must be STRING_DATA
* @param string A pointer to the char string
*/
void FirmataClass::sendString(byte command, const char *string)
{
sendSysex(command, strlen(string), (byte *)string);
}
/**
* Send a string to the Firmata host application.
* @param string A pointer to the char string
*/
void FirmataClass::sendString(const char *string)
{
sendString(STRING_DATA, string);
}
/**
* A wrapper for Stream::available().
* Write a single byte to the output stream.
* @param c The byte to be written.
*/
void FirmataClass::write(byte c)
{
FirmataStream->write(c);
}
/**
* Attach a generic sysex callback function to a command (options are: ANALOG_MESSAGE,
* DIGITAL_MESSAGE, REPORT_ANALOG, REPORT DIGITAL, SET_PIN_MODE and SET_DIGITAL_PIN_VALUE).
* @param command The ID of the command to attach a callback function to.
* @param newFunction A reference to the callback function to attach.
*/
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;
case SET_DIGITAL_PIN_VALUE: currentPinValueCallback = newFunction; break;
}
}
/**
* Attach a callback function for the SYSTEM_RESET command.
* @param command Must be set to SYSTEM_RESET or it will be ignored.
* @param newFunction A reference to the system reset callback function to attach.
*/
void FirmataClass::attach(byte command, systemResetCallbackFunction newFunction)
{
switch (command) {
case SYSTEM_RESET: currentSystemResetCallback = newFunction; break;
}
}
/**
* Attach a callback function for the STRING_DATA command.
* @param command Must be set to STRING_DATA or it will be ignored.
* @param newFunction A reference to the string callback function to attach.
*/
void FirmataClass::attach(byte command, stringCallbackFunction newFunction)
{
switch (command) {
case STRING_DATA: currentStringCallback = newFunction; break;
}
}
/**
* Attach a generic sysex callback function to sysex command.
* @param command The ID of the command to attach a callback function to.
* @param newFunction A reference to the sysex callback function to attach.
*/
void FirmataClass::attach(byte command, sysexCallbackFunction newFunction)
{
currentSysexCallback = newFunction;
}
/**
* Detach a callback function for a specified command (such as SYSTEM_RESET, STRING_DATA,
* ANALOG_MESSAGE, DIGITAL_MESSAGE, etc).
* @param command The ID of the command to detatch the callback function from.
*/
void FirmataClass::detach(byte command)
{
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);
}
}
/**
* Detach a callback function for a delayed task when using FirmataScheduler
* @see FirmataScheduler
* @param newFunction A reference to the delay task callback function to attach.
*/
void FirmataClass::attachDelayTask(delayTaskCallbackFunction newFunction)
{
delayTaskCallback = newFunction;
}
/**
* Call the delayTask callback function when using FirmataScheduler. Must first attach a callback
* using attachDelayTask.
* @see FirmataScheduler
* @param delay The amount of time to delay in milliseconds.
*/
void FirmataClass::delayTask(long delay)
{
if (delayTaskCallback) {
(*delayTaskCallback)(delay);
}
}
/**
* @param pin The pin to get the configuration of.
* @return The configuration of the specified pin.
*/
byte FirmataClass::getPinMode(byte pin)
{
return pinConfig[pin];
}
/**
* Set the pin mode/configuration. The pin configuration (or mode) in Firmata represents the
* current function of the pin. Examples are digital input or output, analog input, pwm, i2c,
* serial (uart), etc.
* @param pin The pin to configure.
* @param config The configuration value for the specified pin.
*/
void FirmataClass::setPinMode(byte pin, byte config)
{
if (pinConfig[pin] == PIN_MODE_IGNORE)
return;
pinState[pin] = 0;
pinConfig[pin] = config;
if (currentPinModeCallback)
(*currentPinModeCallback)(pin, config);
}
/**
* @param pin The pin to get the state of.
* @return The state of the specified pin.
*/
int FirmataClass::getPinState(byte pin)
{
return pinState[pin];
}
/**
* Set the pin state. The pin state of an output pin is the pin value. The state of an
* input pin is 0, unless the pin has it's internal pull up resistor enabled, then the value is 1.
* @param pin The pin to set the state of
* @param state Set the state of the specified pin
*/
void FirmataClass::setPinState(byte pin, int state)
{
pinState[pin] = state;
}
// sysex callbacks
/*
* this is too complicated for analogReceive, but maybe for Sysex?
void FirmataClass::attachSysex(sysexFunction newFunction)
{
byte i;
byte tmpCount = analogReceiveFunctionCount;
analogReceiveFunction* tmpArray = analogReceiveFunctionArray;
analogReceiveFunctionCount++;
analogReceiveFunctionArray = (analogReceiveFunction*) calloc(analogReceiveFunctionCount, sizeof(analogReceiveFunction));
for(i = 0; i < tmpCount; i++) {
analogReceiveFunctionArray[i] = tmpArray[i];
}
analogReceiveFunctionArray[tmpCount] = newFunction;
free(tmpArray);
}
*/
//******************************************************************************
//* Private Methods
//******************************************************************************
/**
* Resets the system state upon a SYSTEM_RESET message from the host software.
* @private
*/
void FirmataClass::systemReset(void)
{
resetting = true;
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
for (i = 0; i < MAX_DATA_BYTES; i++) {
storedInputData[i] = 0;
}
parsingSysex = false;
sysexBytesRead = 0;
if (currentSystemResetCallback)
(*currentSystemResetCallback)();
resetting = false;
}
/**
* Flashing the pin for the version number
* @private
* @param pin The pin the LED is attached to.
* @param count The number of times to flash the LED.
* @param onInterval The number of milliseconds for the LED to be ON during each interval.
* @param offInterval The number of milliseconds for the LED to be OFF during each interval.
*/
void FirmataClass::strobeBlinkPin(byte pin, int count, int onInterval, int offInterval)
{
byte i;
for (i = 0; i < count; i++) {
delay(offInterval);
digitalWrite(pin, HIGH);
delay(onInterval);
digitalWrite(pin, LOW);
}
}
// make one instance for the user to use
FirmataClass Firmata;

View File

@@ -0,0 +1,241 @@
/*
ConfigurableFirmata.h - ConfigurableFirmata library v2.10.0 - 2017-6-16
Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (c) 2013 Norbert Truchsess. All rights reserved.
Copyright (c) 2013-2017 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.
*/
#ifndef Configurable_Firmata_h
#define Configurable_Firmata_h
#include "utility/Boards.h" /* Hardware Abstraction Layer + Wiring/Arduino */
/* Version numbers for the protocol. The protocol is still changing, so these
* version numbers are important.
* Query using the REPORT_VERSION message.
*/
#define FIRMATA_PROTOCOL_MAJOR_VERSION 2 // for non-compatible changes
#define FIRMATA_PROTOCOL_MINOR_VERSION 6 // for backwards compatible changes
#define FIRMATA_PROTOCOL_BUGFIX_VERSION 0 // for bugfix releases
/*
* Version numbers for the Firmata library.
* ConfigurableFirmata 2.10.0 implements version 2.6.0 of the Firmata protocol.
* The firmware version will not always equal the protocol version going forward.
* Query using the REPORT_FIRMWARE message.
*/
#define FIRMATA_FIRMWARE_MAJOR_VERSION 2 // for non-compatible changes
#define FIRMATA_FIRMWARE_MINOR_VERSION 10 // for backwards compatible changes
#define FIRMATA_FIRMWARE_BUGFIX_VERSION 0 // for bugfix releases
// DEPRECATED as of ConfigurableFirmata v2.8.1.
// Use FIRMATA_PROTOCOL_[MAJOR|MINOR|BUGFIX]_VERSION instead.
#define FIRMATA_MAJOR_VERSION 2
#define FIRMATA_MINOR_VERSION 6
#define FIRMATA_BUGFIX_VERSION 0
// DEPRECATED as of ConfigurableFirmata v2.8.1.
//Use FIRMATA_FIRMWARE_[MAJOR|MINOR|BUGFIX]_VERSION instead.
#define FIRMWARE_MAJOR_VERSION 2
#define FIRMWARE_MINOR_VERSION 10
#define FIRMWARE_BUGFIX_VERSION 0
#define MAX_DATA_BYTES 64 // max number of data bytes in incoming messages
// Arduino 101 also defines SET_PIN_MODE as a macro in scss_registers.h
#ifdef SET_PIN_MODE
#undef SET_PIN_MODE
#endif
// message command bytes (128-255/0x80-0xFF)
#define DIGITAL_MESSAGE 0x90 // send data for a digital pin
#define ANALOG_MESSAGE 0xE0 // send data for an analog pin (or PWM)
#define REPORT_ANALOG 0xC0 // enable analog input by pin #
#define REPORT_DIGITAL 0xD0 // enable digital input by port pair
//
#define SET_PIN_MODE 0xF4 // set a pin to INPUT/OUTPUT/PWM/etc
#define SET_DIGITAL_PIN_VALUE 0xF5 // set value of an individual digital pin
//
#define REPORT_VERSION 0xF9 // report protocol version
#define SYSTEM_RESET 0xFF // reset from MIDI
//
#define START_SYSEX 0xF0 // start a MIDI Sysex message
#define END_SYSEX 0xF7 // end a MIDI Sysex message
// extended command set using sysex (0-127/0x00-0x7F)
/* 0x00-0x0F reserved for user-defined commands */
#define SERIAL_MESSAGE 0x60 // communicate with serial devices, including other boards
#define ENCODER_DATA 0x61 // reply with encoders current positions
#define ACCELSTEPPER_DATA 0x62 // control a stepper motor
#define SERVO_CONFIG 0x70 // set max angle, minPulse, maxPulse, freq
#define STRING_DATA 0x71 // a string message with 14-bits per char
#define STEPPER_DATA 0x72 // control a stepper motor
#define ONEWIRE_DATA 0x73 // send an OneWire read/write/reset/select/skip/search request
#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 EXTENDED_ANALOG 0x6F // analog write (PWM, Servo, etc) to any pin
#define PIN_STATE_QUERY 0x6D // ask for a pin's current mode and value
#define PIN_STATE_RESPONSE 0x6E // reply with pin's current mode and value
#define CAPABILITY_QUERY 0x6B // ask for supported modes and resolution of all pins
#define CAPABILITY_RESPONSE 0x6C // reply with supported modes and resolution
#define ANALOG_MAPPING_QUERY 0x69 // ask for mapping of analog to pin numbers
#define ANALOG_MAPPING_RESPONSE 0x6A // reply with mapping info
#define REPORT_FIRMWARE 0x79 // report name and version of the firmware
#define SAMPLING_INTERVAL 0x7A // set the poll rate of the main loop
#define SCHEDULER_DATA 0x7B // send a createtask/deletetask/addtotask/schedule/querytasks/querytask request to the scheduler
#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 Arduino.h
//#define OUTPUT 0x01 // defined in Arduino.h
#define PIN_MODE_ANALOG 0x02 // analog pin in analogInput mode
#define PIN_MODE_PWM 0x03 // digital pin in PWM output mode
#define PIN_MODE_SERVO 0x04 // digital pin in Servo output mode
#define PIN_MODE_SHIFT 0x05 // shiftIn/shiftOut mode
#define PIN_MODE_I2C 0x06 // pin included in I2C setup
#define PIN_MODE_ONEWIRE 0x07 // pin configured for 1-wire
#define PIN_MODE_STEPPER 0x08 // pin configured for stepper motor
#define PIN_MODE_ENCODER 0x09 // pin configured for rotary encoders
#define PIN_MODE_SERIAL 0x0A // pin configured for serial communication
#define PIN_MODE_PULLUP 0x0B // enable internal pull-up resistor for pin
#define PIN_MODE_IGNORE 0x7F // pin configured to be ignored by digitalWrite and capabilityResponse
#define TOTAL_PIN_MODES 13
// DEPRECATED as of Firmata v2.5
#define ANALOG 0x02 // same as PIN_MODE_ANALOG
#define PWM 0x03 // same as PIN_MODE_PWM
#define SERVO 0x04 // same as PIN_MODE_SERVO
#define SHIFT 0x05 // same as PIN_MODE_SHIFT
#define I2C 0x06 // same as PIN_MODE_I2C
#define ONEWIRE 0x07 // same as PIN_MODE_ONEWIRE
#define STEPPER 0x08 // same as PIN_MODE_STEPPER
#define ENCODER 0x09 // same as PIN_MODE_ENCODER
#define IGNORE 0x7F // same as PIN_MODE_IGNORE
extern "C" {
// callback function types
typedef void (*callbackFunction)(byte, int);
typedef void (*systemResetCallbackFunction)(void);
typedef void (*stringCallbackFunction)(char *);
typedef void (*sysexCallbackFunction)(byte command, byte argc, byte *argv);
typedef void (*delayTaskCallbackFunction)(long delay);
}
// TODO make it a subclass of a generic Serial/Stream base class
class FirmataClass
{
public:
FirmataClass();
/* Arduino constructors */
void begin();
void begin(long);
void begin(Stream &s);
/* querying functions */
void printVersion(void);
void blinkVersion(void);
void printFirmwareVersion(void);
//void setFirmwareVersion(byte major, byte minor); // see macro below
void setFirmwareNameAndVersion(const char *name, byte major, byte minor);
void disableBlinkVersion();
/* serial receive handling */
int available(void);
void processInput(void);
void parse(unsigned char value);
boolean isParsingMessage(void);
boolean isResetting(void);
/* serial send handling */
void sendAnalog(byte pin, int value);
void sendDigital(byte pin, int value); // TODO implement this
void sendDigitalPort(byte portNumber, int portData);
void sendString(const char *string);
void sendString(byte command, const char *string);
void sendSysex(byte command, byte bytec, byte *bytev);
void write(byte c);
/* attach & detach callback functions to messages */
void attach(byte command, callbackFunction newFunction);
void attach(byte command, systemResetCallbackFunction newFunction);
void attach(byte command, stringCallbackFunction newFunction);
void attach(byte command, sysexCallbackFunction newFunction);
void detach(byte command);
/* delegate to Scheduler (if any) */
void attachDelayTask(delayTaskCallbackFunction newFunction);
void delayTask(long delay);
/* access pin config */
byte getPinMode(byte pin);
void setPinMode(byte pin, byte config);
/* access pin state */
int getPinState(byte pin);
void setPinState(byte pin, int state);
/* utility methods */
void sendValueAsTwo7bitBytes(int value);
void startSysex(void);
void endSysex(void);
private:
Stream *FirmataStream;
/* firmware name and version */
byte firmwareVersionCount;
byte *firmwareVersionVector;
/* input message handling */
byte waitForData; // this flag says the next serial input will be data
byte executeMultiByteCommand; // execute this after getting multi-byte data
byte multiByteChannel; // channel data for multiByteCommands
byte storedInputData[MAX_DATA_BYTES]; // multi-byte data
/* sysex */
boolean parsingSysex;
int sysexBytesRead;
/* pins configuration */
byte pinConfig[TOTAL_PINS]; // configuration of every pin
int pinState[TOTAL_PINS]; // any value that has been written
boolean resetting;
/* callback functions */
callbackFunction currentAnalogCallback;
callbackFunction currentDigitalCallback;
callbackFunction currentReportAnalogCallback;
callbackFunction currentReportDigitalCallback;
callbackFunction currentPinModeCallback;
callbackFunction currentPinValueCallback;
systemResetCallbackFunction currentSystemResetCallback;
stringCallbackFunction currentStringCallback;
sysexCallbackFunction currentSysexCallback;
delayTaskCallbackFunction delayTaskCallback;
boolean blinkVersionDisabled;
/* private methods ------------------------------ */
void processSysexMessage(void);
void systemReset(void);
void strobeBlinkPin(byte pin, int count, int onInterval, int offInterval);
};
extern FirmataClass Firmata;
/*==============================================================================
* MACROS
*============================================================================*/
/* shortcut for setFirmwareNameAndVersion() that uses __FILE__ to set the
* firmware name. It needs to be a macro so that __FILE__ is included in the
* firmware source file rather than the library source file.
*/
#define setFirmwareVersion(x, y) setFirmwareNameAndVersion(__FILE__, x, y)
#endif /* Configurable_Firmata_h */

View File

@@ -0,0 +1,127 @@
/*
DigitalInputFirmata.cpp - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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: November 15th, 2015
*/
#include <ConfigurableFirmata.h>
#include "DigitalInputFirmata.h"
DigitalInputFirmata *DigitalInputFirmataInstance;
void reportDigitalInputCallback(byte port, int value)
{
DigitalInputFirmataInstance->reportDigital(port, value);
}
DigitalInputFirmata::DigitalInputFirmata()
{
DigitalInputFirmataInstance = this;
Firmata.attach(REPORT_DIGITAL, reportDigitalInputCallback);
}
boolean DigitalInputFirmata::handleSysex(byte command, byte argc, byte* argv)
{
return false;
}
void DigitalInputFirmata::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 Serial output queue using Serial.print() */
void DigitalInputFirmata::report(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);
}
void DigitalInputFirmata::reportDigital(byte port, int value)
{
if (port < TOTAL_PORTS) {
reportPINs[port] = (byte)value;
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
}
boolean DigitalInputFirmata::handlePinMode(byte pin, int mode)
{
if (IS_PIN_DIGITAL(pin)) {
if (mode == INPUT || mode == PIN_MODE_PULLUP) {
portConfigInputs[pin / 8] |= (1 << (pin & 7));
if (mode == INPUT) {
pinMode(PIN_TO_DIGITAL(pin), INPUT);
} else {
pinMode(PIN_TO_DIGITAL(pin), INPUT_PULLUP);
Firmata.setPinState(pin, 1);
}
return true;
} else {
portConfigInputs[pin / 8] &= ~(1 << (pin & 7));
}
}
return false;
}
void DigitalInputFirmata::handleCapability(byte pin)
{
if (IS_PIN_DIGITAL(pin)) {
Firmata.write((byte)INPUT);
Firmata.write(1);
Firmata.write((byte)PIN_MODE_PULLUP);
Firmata.write(1);
}
}
void DigitalInputFirmata::reset()
{
for (byte i = 0; i < TOTAL_PORTS; i++) {
reportPINs[i] = false; // by default, reporting off
portConfigInputs[i] = 0; // until activated
previousPINs[i] = 0;
}
}

View File

@@ -0,0 +1,46 @@
/*
DigitalInputFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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.
*/
#ifndef DigitalInputFirmata_h
#define DigitalInputFirmata_h
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
void reportDigitalInputCallback(byte port, int value);
class DigitalInputFirmata: public FirmataFeature
{
public:
DigitalInputFirmata();
void reportDigital(byte port, int value);
void report(void);
void handleCapability(byte pin);
boolean handleSysex(byte command, byte argc, byte* argv);
boolean handlePinMode(byte pin, int mode);
void reset();
private:
/* digital input ports */
byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence
byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent
/* pins configuration */
byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else
void outputPort(byte portNumber, byte portValue, byte forceSend);
};
#endif

View File

@@ -0,0 +1,106 @@
/*
DigitalOutputFirmata.cpp - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated by Jeff Hoefs: February 16th, 2016
*/
#include <ConfigurableFirmata.h>
#include "DigitalOutputFirmata.h"
DigitalOutputFirmata *DigitalOutputFirmataInstance;
void digitalOutputWriteCallback(byte port, int value)
{
DigitalOutputFirmataInstance->digitalWritePort(port, value);
}
/*
* Sets the value of an individual pin. Useful if you want to set a pin value but
* are not tracking the digital port state.
* Can only be used on pins configured as OUTPUT.
* Cannot be used to enable pull-ups on Digital INPUT pins.
*/
void handleSetPinValueCallback(byte pin, int value)
{
if (pin < TOTAL_PINS && IS_PIN_DIGITAL(pin)) {
if (Firmata.getPinMode(pin) == OUTPUT) {
digitalWrite(pin, value);
Firmata.setPinState(pin, value);
}
}
}
DigitalOutputFirmata::DigitalOutputFirmata()
{
DigitalOutputFirmataInstance = this;
Firmata.attach(DIGITAL_MESSAGE, digitalOutputWriteCallback);
Firmata.attach(SET_DIGITAL_PIN_VALUE, handleSetPinValueCallback);
}
boolean DigitalOutputFirmata::handleSysex(byte command, byte argc, byte* argv)
{
return false;
}
void DigitalOutputFirmata::reset()
{
}
void DigitalOutputFirmata::digitalWritePort(byte port, int value)
{
byte pin, lastPin, pinValue, 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)) {
// do not touch pins in PWM, ANALOG, SERVO or other modes
if (Firmata.getPinMode(pin) == OUTPUT || Firmata.getPinMode(pin) == INPUT) {
pinValue = ((byte)value & mask) ? 1 : 0;
if (Firmata.getPinMode(pin) == OUTPUT) {
pinWriteMask |= mask;
} else if (Firmata.getPinMode(pin) == INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) {
pinMode(pin, INPUT_PULLUP);
}
Firmata.setPinState(pin, pinValue);
}
}
mask = mask << 1;
}
writePort(port, (byte)value, pinWriteMask);
}
}
boolean DigitalOutputFirmata::handlePinMode(byte pin, int mode)
{
if (IS_PIN_DIGITAL(pin) && mode == OUTPUT && Firmata.getPinMode(pin) != PIN_MODE_IGNORE) {
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
return true;
}
return false;
}
void DigitalOutputFirmata::handleCapability(byte pin)
{
if (IS_PIN_DIGITAL(pin)) {
Firmata.write((byte)OUTPUT);
Firmata.write(1);
}
}

View File

@@ -0,0 +1,40 @@
/*
DigitalOutputFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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: November 15th, 2015
*/
#ifndef DigitalOutputFirmata_h
#define DigitalOutputFirmata_h
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
void digitalOutputWriteCallback(byte port, int value);
void handleSetPinValueCallback(byte pin, int value);
class DigitalOutputFirmata: public FirmataFeature
{
public:
DigitalOutputFirmata();
void digitalWritePort(byte port, int value);
void handleCapability(byte pin);
boolean handleSysex(byte command, byte argc, byte* argv);
boolean handlePinMode(byte pin, int mode);
void reset();
private:
};
#endif

View File

@@ -0,0 +1,64 @@
/*
Encoder7Bit.cpp - Firmata library
Copyright (C) 2012-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.
*/
#include "Encoder7Bit.h"
#include <ConfigurableFirmata.h>
Encoder7BitClass::Encoder7BitClass()
{
previous = 0;
shift = 0;
}
void Encoder7BitClass::startBinaryWrite()
{
shift = 0;
}
void Encoder7BitClass::endBinaryWrite()
{
if (shift > 0) {
Firmata.write(previous);
}
}
void Encoder7BitClass::writeBinary(byte data)
{
if (shift == 0) {
Firmata.write(data & 0x7f);
shift++;
previous = data >> 7;
}
else {
Firmata.write(((data << shift) & 0x7f) | previous);
if (shift == 6) {
Firmata.write(data >> 1);
shift = 0;
}
else {
shift++;
previous = data >> (8 - shift);
}
}
}
void Encoder7BitClass::readBinary(int outBytes, byte *inData, byte *outData)
{
for (int i = 0; i < outBytes; i++) {
int j = i << 3;
int pos = j / 7;
byte shift = j % 7;
outData[i] = (inData[pos] >> shift) | ((inData[pos + 1] << (7 - shift)) & 0xFF);
}
}
Encoder7BitClass Encoder7Bit;

View File

@@ -0,0 +1,35 @@
/*
Encoder7Bit.h - Firmata library
Copyright (C) 2012-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.
*/
#ifndef Encoder7Bit_h
#define Encoder7Bit_h
#include <Arduino.h>
#define num7BitOutbytes(a)(((a)*7)>>3)
class Encoder7BitClass
{
public:
Encoder7BitClass();
void startBinaryWrite();
void endBinaryWrite();
void writeBinary(byte data);
void readBinary(int outBytes, byte *inData, byte *outData);
private:
byte previous;
int shift;
};
extern Encoder7BitClass Encoder7Bit;
#endif

View File

@@ -0,0 +1,3 @@
/*
* Implementation is in EthernetClientStream.h to avoid linker issues.
*/

View File

@@ -0,0 +1,141 @@
/*
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.
Last updated June 18th, 2016
*/
#ifndef ETHERNETCLIENTSTREAM_H
#define ETHERNETCLIENTSTREAM_H
#include <inttypes.h>
#include <Stream.h>
//#define SERIAL_DEBUG
#include "utility/firmataDebug.h"
#define MILLIS_RECONNECT 5000
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:
Client &client;
IPAddress localip;
IPAddress ip;
const char* host;
uint16_t port;
bool connected;
uint32_t time_connect;
bool maintain();
void stop();
};
/*
* EthernetClientStream.cpp
* Copied here as a hack to linker issues with 3rd party board packages that don't properly
* implement the Arduino network APIs.
*/
EthernetClientStream::EthernetClientStream(Client &client, IPAddress localip, IPAddress ip, const char* host, uint16_t port)
: client(client),
localip(localip),
ip(ip),
host(host),
port(port),
connected(false)
{
}
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)
{
// ensure the local IP is updated in the case that it is changed by the DHCP server
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();
}
// if the client is disconnected, attempt to reconnect every 5 seconds
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. attempting to reconnect...");
} else {
DEBUG_PRINTLN("connected");
}
}
return connected;
}
#endif /* ETHERNETCLIENTSTREAM_H */

View File

@@ -0,0 +1,117 @@
/*
FirmataExt.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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: November 15th, 2015
*/
#include <ConfigurableFirmata.h>
#include "FirmataExt.h"
FirmataExt *FirmataExtInstance;
void handleSetPinModeCallback(byte pin, int mode)
{
if (!FirmataExtInstance->handlePinMode(pin, mode) && mode != PIN_MODE_IGNORE) {
Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM
}
}
void handleSysexCallback(byte command, byte argc, byte* argv)
{
if (!FirmataExtInstance->handleSysex(command, argc, argv)) {
Firmata.sendString("Unhandled sysex command");
}
}
FirmataExt::FirmataExt()
{
FirmataExtInstance = this;
Firmata.attach(SET_PIN_MODE, handleSetPinModeCallback);
Firmata.attach((byte)START_SYSEX, handleSysexCallback);
numFeatures = 0;
}
void FirmataExt::handleCapability(byte pin)
{
}
boolean FirmataExt::handlePinMode(byte pin, int mode)
{
boolean known = false;
for (byte i = 0; i < numFeatures; i++) {
known |= features[i]->handlePinMode(pin, mode);
}
return known;
}
boolean FirmataExt::handleSysex(byte command, byte argc, byte* argv)
{
switch (command) {
case PIN_STATE_QUERY:
if (argc > 0) {
byte pin = argv[0];
if (pin < TOTAL_PINS) {
Firmata.write(START_SYSEX);
Firmata.write(PIN_STATE_RESPONSE);
Firmata.write(pin);
Firmata.write(Firmata.getPinMode(pin));
int pinState = Firmata.getPinState(pin);
Firmata.write((byte)pinState & 0x7F);
if (pinState & 0xFF80) Firmata.write((byte)(pinState >> 7) & 0x7F);
if (pinState & 0xC000) Firmata.write((byte)(pinState >> 14) & 0x7F);
Firmata.write(END_SYSEX);
return true;
}
}
break;
case CAPABILITY_QUERY:
Firmata.write(START_SYSEX);
Firmata.write(CAPABILITY_RESPONSE);
for (byte pin = 0; pin < TOTAL_PINS; pin++) {
if (Firmata.getPinMode(pin) != PIN_MODE_IGNORE) {
for (byte i = 0; i < numFeatures; i++) {
features[i]->handleCapability(pin);
}
}
Firmata.write(127);
}
Firmata.write(END_SYSEX);
return true;
default:
for (byte i = 0; i < numFeatures; i++) {
if (features[i]->handleSysex(command, argc, argv)) {
return true;
}
}
break;
}
return false;
}
void FirmataExt::addFeature(FirmataFeature &capability)
{
if (numFeatures < MAX_FEATURES) {
features[numFeatures++] = &capability;
}
}
void FirmataExt::reset()
{
for (byte i = 0; i < numFeatures; i++) {
features[i]->reset();
}
}

View File

@@ -0,0 +1,44 @@
/*
FirmataExt.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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.
*/
#ifndef FirmataExt_h
#define FirmataExt_h
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
#define MAX_FEATURES TOTAL_PIN_MODES + 1
void handleSetPinModeCallback(byte pin, int mode);
void handleSysexCallback(byte command, byte argc, byte* argv);
class FirmataExt: public FirmataFeature
{
public:
FirmataExt();
void handleCapability(byte pin); //empty method
boolean handlePinMode(byte pin, int mode);
boolean handleSysex(byte command, byte argc, byte* argv);
void addFeature(FirmataFeature &capability);
void reset();
private:
FirmataFeature *features[MAX_FEATURES];
byte numFeatures;
};
#endif

View File

@@ -0,0 +1,31 @@
/*
FirmataExt.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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.
*/
#ifndef FirmataFeature_h
#define FirmataFeature_h
#include <ConfigurableFirmata.h>
class FirmataFeature
{
public:
virtual void handleCapability(byte pin) = 0;
virtual boolean handlePinMode(byte pin, int mode) = 0;
virtual boolean handleSysex(byte command, byte argc, byte* argv) = 0;
virtual void reset() = 0;
};
#endif

View File

@@ -0,0 +1,66 @@
/*
FirmataReporting.cpp - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
*/
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
#include "FirmataReporting.h"
void FirmataReporting::setSamplingInterval(int interval)
{
samplingInterval = interval;
}
void FirmataReporting::handleCapability(byte pin)
{
}
boolean FirmataReporting::handlePinMode(byte pin, int mode)
{
return false;
}
boolean FirmataReporting::handleSysex(byte command, byte argc, byte* argv)
{
if (command == SAMPLING_INTERVAL) {
if (argc > 1) {
samplingInterval = argv[0] + (argv[1] << 7);
if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) {
samplingInterval = MINIMUM_SAMPLING_INTERVAL;
}
return true;
}
}
return false;
}
boolean FirmataReporting::elapsed()
{
currentMillis = millis();
if (currentMillis - previousMillis > samplingInterval) {
previousMillis += samplingInterval;
if (currentMillis - previousMillis > samplingInterval)
previousMillis = currentMillis - samplingInterval;
return true;
}
return false;
}
void FirmataReporting::reset()
{
previousMillis = millis();
samplingInterval = 19;
}

View File

@@ -0,0 +1,43 @@
/*
FirmataReporting.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated by Jeff Hoefs: January 23rd, 2016
*/
#ifndef FirmataReporting_h
#define FirmataReporting_h
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
#define MINIMUM_SAMPLING_INTERVAL 1
class FirmataReporting: public FirmataFeature
{
public:
void setSamplingInterval(int interval);
void handleCapability(byte pin); //empty method
boolean handlePinMode(byte pin, int mode); //empty method
boolean handleSysex(byte command, byte argc, byte* argv);
boolean elapsed();
void reset();
private:
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
unsigned int samplingInterval; // how often to run the main loop (in ms)
};
#endif

View File

@@ -0,0 +1,300 @@
/*
FirmataScheduler.cpp - Firmata library
Copyright (C) 2012-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.
*/
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
#include "Encoder7Bit.h"
#include "FirmataScheduler.h"
#include "FirmataExt.h"
FirmataScheduler *FirmataSchedulerInstance;
void delayTaskCallback(long delay)
{
FirmataSchedulerInstance->delayTask(delay);
}
FirmataScheduler::FirmataScheduler()
{
FirmataSchedulerInstance = this;
tasks = NULL;
running = NULL;
Firmata.attachDelayTask(delayTaskCallback);
}
void FirmataScheduler::handleCapability(byte pin)
{
}
boolean FirmataScheduler::handlePinMode(byte pin, int mode)
{
return false;
}
boolean FirmataScheduler::handleSysex(byte command, byte argc, byte* argv)
{
if (command == SCHEDULER_DATA) {
if (argc > 0) {
switch (argv[0]) {
case CREATE_FIRMATA_TASK:
{
if (argc == 4) {
createTask(argv[1], argv[2] | argv[3] << 7);
}
break;
}
case DELETE_FIRMATA_TASK:
{
if (argc == 2) {
deleteTask(argv[1]);
}
break;
}
case ADD_TO_FIRMATA_TASK:
{
if (argc > 2) {
int len = num7BitOutbytes(argc - 2);
Encoder7Bit.readBinary(len, argv + 2, argv + 2); //decode inplace
addToTask(argv[1], len, argv + 2); //addToTask copies data...
}
break;
}
case DELAY_FIRMATA_TASK:
{
if (argc == 6) {
argv++;
Encoder7Bit.readBinary(4, argv, argv); //decode inplace
delayTask(*(long*)((byte*)argv));
}
break;
}
case SCHEDULE_FIRMATA_TASK:
{
if (argc == 7) { //one byte taskid, 5 bytes to encode 4 bytes of long
Encoder7Bit.readBinary(4, argv + 2, argv + 2); //decode inplace
schedule(argv[1], *(long*)((byte*)argv + 2)); //argv[1] | argv[2]<<8 | argv[3]<<16 | argv[4]<<24
}
break;
}
case QUERY_ALL_FIRMATA_TASKS:
{
queryAllTasks();
break;
}
case QUERY_FIRMATA_TASK:
{
if (argc == 2) {
queryTask(argv[1]);
}
break;
}
case RESET_FIRMATA_TASKS:
{
reset();
}
}
}
return true;
}
return false;
};
void FirmataScheduler::createTask(byte id, int len)
{
firmata_task *existing = findTask(id);
if (existing) {
reportTask(id, existing, true);
}
else {
firmata_task *newTask = (firmata_task*)malloc(sizeof(firmata_task) + len);
newTask->id = id;
newTask->time_ms = 0;
newTask->len = len;
newTask->nextTask = tasks;
newTask->pos = 0;
tasks = newTask;
}
};
void FirmataScheduler::deleteTask(byte id)
{
firmata_task *current = tasks;
firmata_task *previous = NULL;
while (current) {
if (current->id == id) {
if (previous) {
previous->nextTask = current->nextTask;
}
else {
tasks = current->nextTask;
}
free (current);
return;
}
else {
previous = current;
current = current->nextTask;
}
}
};
void FirmataScheduler::addToTask(byte id, int additionalBytes, byte *message)
{
firmata_task *existing = findTask(id);
if (existing) { //task exists and has not been fully loaded yet
if (existing->pos + additionalBytes <= existing->len) {
for (int i = 0; i < additionalBytes; i++) {
existing->messages[existing->pos++] = message[i];
}
}
}
else {
reportTask(id, NULL, true);
}
};
void FirmataScheduler::schedule(byte id, long delay_ms)
{
firmata_task *existing = findTask(id);
if (existing) {
existing->pos = 0;
existing->time_ms = millis() + delay_ms;
}
else {
reportTask(id, NULL, true);
}
};
void FirmataScheduler::delayTask(long delay_ms)
{
if (running) {
long now = millis();
running->time_ms += delay_ms;
if (running->time_ms < now) { //if delay time allready passed by schedule to 'now'.
running->time_ms = now;
}
}
}
void FirmataScheduler::queryAllTasks()
{
Firmata.write(START_SYSEX);
Firmata.write(SCHEDULER_DATA);
Firmata.write(QUERY_ALL_TASKS_REPLY);
firmata_task *task = tasks;
while (task) {
Firmata.write(task->id);
task = task->nextTask;
}
Firmata.write(END_SYSEX);
};
void FirmataScheduler::queryTask(byte id)
{
firmata_task *task = findTask(id);
reportTask(id, task, false);
}
void FirmataScheduler::reportTask(byte id, firmata_task *task, boolean error)
{
Firmata.write(START_SYSEX);
Firmata.write(SCHEDULER_DATA);
if (error) {
Firmata.write(ERROR_TASK_REPLY);
} else {
Firmata.write(QUERY_TASK_REPLY);
}
Firmata.write(id);
if (task) {
Encoder7Bit.startBinaryWrite();
for (int i = 3; i < firmata_task_len(task); i++) {
Encoder7Bit.writeBinary(((byte *)task)[i]); //don't write first 3 bytes (firmata_task*, byte); makes use of AVR byteorder (LSB first)
}
Encoder7Bit.endBinaryWrite();
}
Firmata.write(END_SYSEX);
};
void FirmataScheduler::runTasks()
{
if (tasks) {
long now = millis();
firmata_task *current = tasks;
firmata_task *previous = NULL;
while (current) {
if (current->time_ms > 0 && current->time_ms < now) { // TODO handle overflow
if (execute(current)) {
previous = current;
current = current->nextTask;
}
else {
if (previous) {
previous->nextTask = current->nextTask;
free(current);
current = previous->nextTask;
}
else {
tasks = current->nextTask;
free(current);
current = tasks;
}
}
}
else {
current = current->nextTask;
}
}
}
};
void FirmataScheduler::reset()
{
while (tasks) {
firmata_task *nextTask = tasks->nextTask;
free(tasks);
tasks = nextTask;
}
};
//private
boolean FirmataScheduler::execute(firmata_task *task)
{
long start = task->time_ms;
int pos = task->pos;
int len = task->len;
byte *messages = task->messages;
running = task;
while (pos < len) {
Firmata.parse(messages[pos++]);
if (start != task->time_ms) { // return true if task got rescheduled during run.
task->pos = ( pos == len ? 0 : pos ); // last message executed? -> start over next time
running = NULL;
return true;
}
};
running = NULL;
return false;
}
firmata_task *FirmataScheduler::findTask(byte id)
{
firmata_task *currentTask = tasks;
while (currentTask) {
if (id == currentTask->id) {
return currentTask;
} else {
currentTask = currentTask->nextTask;
}
};
return NULL;
}

View File

@@ -0,0 +1,73 @@
/*
FirmataScheduler.h - Firmata library
Copyright (C) 2012-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.
*/
#ifndef FirmataScheduler_h
#define FirmataScheduler_h
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
#include "Encoder7Bit.h"
//subcommands
#define CREATE_FIRMATA_TASK 0
#define DELETE_FIRMATA_TASK 1
#define ADD_TO_FIRMATA_TASK 2
#define DELAY_FIRMATA_TASK 3
#define SCHEDULE_FIRMATA_TASK 4
#define QUERY_ALL_FIRMATA_TASKS 5
#define QUERY_FIRMATA_TASK 6
#define RESET_FIRMATA_TASKS 7
#define ERROR_TASK_REPLY 8
#define QUERY_ALL_TASKS_REPLY 9
#define QUERY_TASK_REPLY 10
#define firmata_task_len(a)(sizeof(firmata_task)+(a)->len)
void delayTaskCallback(long delay);
struct firmata_task
{
firmata_task *nextTask;
byte id; //only 7bits used -> supports 127 tasks
long time_ms;
int len;
int pos;
byte messages[];
};
class FirmataScheduler: public FirmataFeature
{
public:
FirmataScheduler();
void handleCapability(byte pin); //empty method
boolean handlePinMode(byte pin, int mode); //empty method
boolean handleSysex(byte command, byte argc, byte* argv);
void runTasks();
void reset();
void createTask(byte id, int len);
void deleteTask(byte id);
void addToTask(byte id, int len, byte *message);
void schedule(byte id, long time_ms);
void delayTask(long time_ms);
void queryAllTasks();
void queryTask(byte id);
private:
firmata_task *tasks;
firmata_task *running;
boolean execute(firmata_task *task);
firmata_task *findTask(byte id);
void reportTask(byte id, firmata_task *task, boolean error);
};
#endif

View File

@@ -0,0 +1,4 @@
/*
* Implementation is in I2CFirmata.h to avoid having to include Wire.h in all
* sketch files that include ConfigurableFirmata.h
*/

View File

@@ -0,0 +1,333 @@
/*
I2CFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
I2CFirmata.cpp has been merged into this header file as a hack to avoid having to
include Wire.h for every arduino sketch that includes ConfigurableFirmata.
Last updated by Jeff Hoefs: January 23rd, 2015
*/
#ifndef I2CFirmata_h
#define I2CFirmata_h
#include <Wire.h>
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
#include "FirmataReporting.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_END_TX_MASK B01000000
#define I2C_STOP_TX 1
#define I2C_RESTART_TX 0
#define I2C_MAX_QUERIES 8
#define I2C_REGISTER_NOT_SPECIFIED -1
/* i2c data */
struct i2c_device_info {
byte addr;
int reg;
byte bytes;
byte stopTX;
};
class I2CFirmata: public FirmataFeature
{
public:
I2CFirmata();
boolean handlePinMode(byte pin, int mode);
void handleCapability(byte pin);
boolean handleSysex(byte command, byte argc, byte* argv);
void reset();
void report();
private:
/* for i2c read continuous more */
i2c_device_info query[I2C_MAX_QUERIES];
byte i2cRxData[32];
boolean isI2CEnabled;
signed char queryIndex;
unsigned int i2cReadDelayTime; // default delay time between i2c read request and Wire.requestFrom()
void readAndReportData(byte address, int theRegister, byte numBytes, byte stopTX);
void handleI2CRequest(byte argc, byte *argv);
boolean handleI2CConfig(byte argc, byte *argv);
boolean enableI2CPins();
void disableI2CPins();
};
/*
* I2CFirmata.cpp
* Copied here as a hack to avoid having to include Wire.h in all sketch files that
* include ConfigurableFirmata.h
*/
I2CFirmata::I2CFirmata()
{
isI2CEnabled = false;
queryIndex = -1;
i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom()
}
void I2CFirmata::readAndReportData(byte address, int theRegister, byte numBytes, byte stopTX) {
// 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);
Wire.write((byte)theRegister);
Wire.endTransmission(stopTX); // default = true
// 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] = Wire.read();
}
// send slave address, register and received bytes
Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData);
}
boolean I2CFirmata::handlePinMode(byte pin, int mode)
{
if (IS_PIN_I2C(pin)) {
if (mode == PIN_MODE_I2C) {
// the user must call I2C_CONFIG to enable I2C for a device
return true;
} else if (isI2CEnabled) {
// disable i2c so pins can be used for other functions
// the following if statements should reconfigure the pins properly
if (Firmata.getPinMode(pin) == PIN_MODE_I2C) {
disableI2CPins();
}
}
}
return false;
}
void I2CFirmata::handleCapability(byte pin)
{
if (IS_PIN_I2C(pin)) {
Firmata.write(PIN_MODE_I2C);
Firmata.write(1); // TODO: could assign a number to map to SCL or SDA
}
}
boolean I2CFirmata::handleSysex(byte command, byte argc, byte *argv)
{
switch (command) {
case I2C_REQUEST:
if (isI2CEnabled) {
handleI2CRequest(argc, argv);
return true;
}
case I2C_CONFIG:
return handleI2CConfig(argc, argv);
}
return false;
}
void I2CFirmata::handleI2CRequest(byte argc, byte *argv)
{
byte mode;
byte stopTX;
byte slaveAddress;
byte data;
int slaveRegister;
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];
}
// need to invert the logic here since 0 will be default for client
// libraries that have not updated to add support for restart tx
if (argv[1] & I2C_END_TX_MASK) {
stopTX = I2C_RESTART_TX;
}
else {
stopTX = I2C_STOP_TX; // default
}
switch (mode) {
case I2C_WRITE:
Wire.beginTransmission(slaveAddress);
for (byte i = 2; i < argc; i += 2) {
data = argv[i] + (argv[i + 1] << 7);
Wire.write(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, stopTX);
break;
case I2C_READ_CONTINUOUSLY:
if ((queryIndex + 1) >= I2C_MAX_QUERIES) {
// too many queries, just ignore
Firmata.sendString("too many 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;
query[queryIndex].stopTX = stopTX;
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 {
queryIndexToSkip = 0;
// 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;
query[i].stopTX = query[i + 1].stopTX;
}
}
queryIndex--;
}
break;
default:
break;
}
}
boolean I2CFirmata::handleI2CConfig(byte argc, byte *argv)
{
unsigned int delayTime = (argv[0] + (argv[1] << 7));
if (delayTime > 0) {
i2cReadDelayTime = delayTime;
}
if (!isI2CEnabled) {
enableI2CPins();
}
return isI2CEnabled;
}
boolean I2CFirmata::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)) {
if (Firmata.getPinMode(i) == PIN_MODE_IGNORE) {
return false;
}
// mark pins as i2c so they are ignore in non i2c data requests
Firmata.setPinMode(i, PIN_MODE_I2C);
pinMode(i, PIN_MODE_I2C);
}
}
isI2CEnabled = true;
Wire.begin();
return true;
}
/* disable the i2c pins so they can be used for other functions */
void I2CFirmata::disableI2CPins()
{
isI2CEnabled = false;
// disable read continuous mode for all devices
queryIndex = -1;
// uncomment the following if or when the end() method is added to Wire library
// Wire.end();
}
void I2CFirmata::reset()
{
if (isI2CEnabled) {
disableI2CPins();
}
}
void I2CFirmata::report()
{
// 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, query[i].stopTX);
}
}
}
#endif /* I2CFirmata_h */

View File

@@ -0,0 +1,178 @@
/*
OneWireFirmata.cpp - Firmata library
Copyright (C) 2012-2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated by Jeff Hoefs: January 23rd, 2016
*/
#include <ConfigurableFirmata.h>
#include "OneWireFirmata.h"
#include "Encoder7Bit.h"
boolean OneWireFirmata::handlePinMode(byte pin, int mode)
{
if (IS_PIN_DIGITAL(pin) && mode == PIN_MODE_ONEWIRE) {
oneWireConfig(pin, ONEWIRE_POWER);
return true;
}
return false;
}
void OneWireFirmata::handleCapability(byte pin)
{
if (IS_PIN_DIGITAL(pin)) {
Firmata.write(PIN_MODE_ONEWIRE);
Firmata.write(1);
}
}
void OneWireFirmata::oneWireConfig(byte pin, boolean power)
{
ow_device_info *info = &pinOneWire[pin];
if (info->device == NULL) {
info->device = new OneWire(pin);
}
for (int i = 0; i < 8; i++) {
info->addr[i] = 0x0;
}
info->power = power;
}
boolean OneWireFirmata::handleSysex(byte command, byte argc, byte* argv)
{
if (command == ONEWIRE_DATA) {
if (argc > 1) {
byte subcommand = argv[0];
byte pin = argv[1];
ow_device_info *info = &pinOneWire[pin];
OneWire *device = info->device;
if (device || subcommand == ONEWIRE_CONFIG_REQUEST) {
switch (subcommand) {
case ONEWIRE_SEARCH_REQUEST:
case ONEWIRE_SEARCH_ALARMS_REQUEST:
{
device->reset_search();
Firmata.write(START_SYSEX);
Firmata.write(ONEWIRE_DATA);
boolean isAlarmSearch = (subcommand == ONEWIRE_SEARCH_ALARMS_REQUEST);
Firmata.write(isAlarmSearch ? (byte)ONEWIRE_SEARCH_ALARMS_REPLY : (byte)ONEWIRE_SEARCH_REPLY);
Firmata.write(pin);
Encoder7Bit.startBinaryWrite();
byte addrArray[8];
while (isAlarmSearch ? device->search(addrArray, false) : device->search(addrArray)) {
for (int i = 0; i < 8; i++) {
Encoder7Bit.writeBinary(addrArray[i]);
}
}
Encoder7Bit.endBinaryWrite();
Firmata.write(END_SYSEX);
break;
}
case ONEWIRE_CONFIG_REQUEST:
{
if (argc == 3 && Firmata.getPinMode(pin) != PIN_MODE_IGNORE) {
Firmata.setPinMode(pin, PIN_MODE_ONEWIRE);
oneWireConfig(pin, argv[2]); // this calls oneWireConfig again, this time setting the correct config (which doesn't cause harm though)
} else {
return false;
}
break;
}
default:
{
if (subcommand & ONEWIRE_RESET_REQUEST_BIT) {
device->reset();
for (int i = 0; i < 8; i++) {
info->addr[i] = 0x0;
}
}
if (subcommand & ONEWIRE_SKIP_REQUEST_BIT) {
device->skip();
for (byte i = 0; i < 8; i++) {
info->addr[i] = 0x0;
}
}
if (subcommand & ONEWIRE_WITHDATA_REQUEST_BITS) {
int numBytes = num7BitOutbytes(argc - 2);
int numReadBytes = 0;
int correlationId;
argv += 2;
Encoder7Bit.readBinary(numBytes, argv, argv); //decode inplace
if (subcommand & ONEWIRE_SELECT_REQUEST_BIT) {
if (numBytes < 8) break;
device->select(argv);
for (int i = 0; i < 8; i++) {
info->addr[i] = argv[i];
}
argv += 8;
numBytes -= 8;
}
if (subcommand & ONEWIRE_READ_REQUEST_BIT) {
if (numBytes < 4) break;
numReadBytes = *((int*)argv);
argv += 2;
correlationId = *((int*)argv);
argv += 2;
numBytes -= 4;
}
if (subcommand & ONEWIRE_DELAY_REQUEST_BIT) {
if (numBytes < 4) break;
Firmata.delayTask(*((long*)argv));
argv += 4;
numBytes -= 4;
}
if (subcommand & ONEWIRE_WRITE_REQUEST_BIT) {
for (int i = 0; i < numBytes; i++) {
info->device->write(argv[i], info->power);
}
}
if (numReadBytes > 0) {
Firmata.write(START_SYSEX);
Firmata.write(ONEWIRE_DATA);
Firmata.write(ONEWIRE_READ_REPLY);
Firmata.write(pin);
Encoder7Bit.startBinaryWrite();
Encoder7Bit.writeBinary(correlationId & 0xFF);
Encoder7Bit.writeBinary((correlationId >> 8) & 0xFF);
for (int i = 0; i < numReadBytes; i++) {
Encoder7Bit.writeBinary(device->read());
}
Encoder7Bit.endBinaryWrite();
Firmata.write(END_SYSEX);
}
}
}
}
}
return true;
}
}
return false;
}
void OneWireFirmata::reset()
{
for (int i = 0; i < TOTAL_PINS; i++) {
if (pinOneWire[i].device) {
free(pinOneWire[i].device);
pinOneWire[i].device = NULL;
}
for (int j = 0; j < 8; j++) {
pinOneWire[i].addr[j] = 0;
}
pinOneWire[i].power = false;
}
}

View File

@@ -0,0 +1,63 @@
/*
OneWireFirmata.h - Firmata library
Copyright (C) 2012-2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
*/
#ifndef OneWireFirmata_h
#define OneWireFirmata_h
#include <ConfigurableFirmata.h>
#include "utility/OneWire.h"
#include "FirmataFeature.h"
//subcommands:
#define ONEWIRE_SEARCH_REQUEST 0x40
#define ONEWIRE_CONFIG_REQUEST 0x41
#define ONEWIRE_SEARCH_REPLY 0x42
#define ONEWIRE_READ_REPLY 0x43
#define ONEWIRE_SEARCH_ALARMS_REQUEST 0x44
#define ONEWIRE_SEARCH_ALARMS_REPLY 0x45
#define ONEWIRE_RESET_REQUEST_BIT 0x01
#define ONEWIRE_SKIP_REQUEST_BIT 0x02
#define ONEWIRE_SELECT_REQUEST_BIT 0x04
#define ONEWIRE_READ_REQUEST_BIT 0x08
#define ONEWIRE_DELAY_REQUEST_BIT 0x10
#define ONEWIRE_WRITE_REQUEST_BIT 0x20
#define ONEWIRE_WITHDATA_REQUEST_BITS 0x3C
#define ONEWIRE_CRC 0 //for OneWire.h: crc-functions are not used by Firmata
//default value for power:
#define ONEWIRE_POWER 1
struct ow_device_info
{
OneWire* device;
byte addr[8];
boolean power;
};
class OneWireFirmata: public FirmataFeature
{
public:
boolean handlePinMode(byte pin, int mode);
void handleCapability(byte pin);
boolean handleSysex(byte command, byte argc, byte* argv);
void reset();
private:
ow_device_info pinOneWire[TOTAL_PINS];
void oneWireConfig(byte pin, boolean power);
};
#endif

View File

@@ -0,0 +1,360 @@
/*
SerialFirmata.cpp - Firmata library
Copyright (C) 2015-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated December 23rd, 2016
*/
#include "SerialFirmata.h"
SerialFirmata::SerialFirmata()
{
#if defined(SoftwareSerial_h)
swSerial0 = NULL;
swSerial1 = NULL;
swSerial2 = NULL;
swSerial3 = NULL;
#endif
serialIndex = -1;
}
boolean SerialFirmata::handlePinMode(byte pin, int mode)
{
if (mode == PIN_MODE_SERIAL) {
// nothing else to do here since the mode is set in SERIAL_CONFIG
return true;
}
return false;
}
void SerialFirmata::handleCapability(byte pin)
{
if (IS_PIN_SERIAL(pin)) {
Firmata.write(PIN_MODE_SERIAL);
Firmata.write(getSerialPinType(pin));
}
}
boolean SerialFirmata::handleSysex(byte command, byte argc, byte *argv)
{
if (command == SERIAL_MESSAGE) {
Stream *serialPort;
byte mode = argv[0] & SERIAL_MODE_MASK;
byte portId = argv[0] & SERIAL_PORT_ID_MASK;
if (portId >= SERIAL_READ_ARR_LEN) return false;
switch (mode) {
case SERIAL_CONFIG:
{
long baud = (long)argv[1] | ((long)argv[2] << 7) | ((long)argv[3] << 14);
serial_pins pins;
lastAvailableBytes[portId] = 0;
lastReceive[portId] = 0;
// this ifdef will be removed once a command to enable RX buffering has been added to the protocol
#if defined(FIRMATA_SERIAL_PORT_RX_BUFFERING)
// 8N1 = 10 bits per char, max. 50 bits -> 50000 = 50bits * 1000ms/s
// char delay value (ms) to detect the end of a message, defaults to 50 bits * 1000 / baud rate
// a value of 0 will disable RX buffering, resulting in single byte transfers to the host with
// baud rates below approximately 56k (varies with CPU speed)
maxCharDelay[portId] = 50000 / baud;
#else
maxCharDelay[portId] = 0;
#endif
if (portId < 8) {
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
pins = getSerialPinNumbers(portId);
if (pins.rx != 0 && pins.tx != 0) {
Firmata.setPinMode(pins.rx, PIN_MODE_SERIAL);
Firmata.setPinMode(pins.tx, PIN_MODE_SERIAL);
// Fixes an issue where some serial devices would not work properly with Arduino Due
// because all Arduino pins are set to OUTPUT by default in StandardFirmata.
pinMode(pins.rx, INPUT);
}
((HardwareSerial*)serialPort)->begin(baud);
}
} else {
#if defined(SoftwareSerial_h)
byte swTxPin, swRxPin;
if (argc > 4) {
swRxPin = argv[4];
swTxPin = argv[5];
} else {
// RX and TX pins must be specified when using software serial
Firmata.sendString("Specify serial RX and TX pins");
return false;
}
switch (portId) {
case SW_SERIAL0:
if (swSerial0 == NULL) {
swSerial0 = new SoftwareSerial(swRxPin, swTxPin);
}
break;
case SW_SERIAL1:
if (swSerial1 == NULL) {
swSerial1 = new SoftwareSerial(swRxPin, swTxPin);
}
break;
case SW_SERIAL2:
if (swSerial2 == NULL) {
swSerial2 = new SoftwareSerial(swRxPin, swTxPin);
}
break;
case SW_SERIAL3:
if (swSerial3 == NULL) {
swSerial3 = new SoftwareSerial(swRxPin, swTxPin);
}
break;
}
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
Firmata.setPinMode(swRxPin, PIN_MODE_SERIAL);
Firmata.setPinMode(swTxPin, PIN_MODE_SERIAL);
((SoftwareSerial*)serialPort)->begin(baud);
}
#endif
}
break; // SERIAL_CONFIG
}
case SERIAL_WRITE:
{
byte data;
serialPort = getPortFromId(portId);
if (serialPort == NULL) {
break;
}
for (byte i = 1; i < argc; i += 2) {
data = argv[i] + (argv[i + 1] << 7);
serialPort->write(data);
}
break; // SERIAL_WRITE
}
case SERIAL_READ:
if (argv[1] == SERIAL_READ_CONTINUOUSLY) {
if (serialIndex + 1 >= MAX_SERIAL_PORTS) {
break;
}
if (argc > 2) {
// maximum number of bytes to read from buffer per iteration of loop()
serialBytesToRead[portId] = (int)argv[2] | ((int)argv[3] << 7);
} else {
// read all available bytes per iteration of loop()
serialBytesToRead[portId] = 0;
}
serialIndex++;
reportSerial[serialIndex] = portId;
} else if (argv[1] == SERIAL_STOP_READING) {
byte serialIndexToSkip = 0;
if (serialIndex <= 0) {
serialIndex = -1;
} else {
for (byte i = 0; i < serialIndex + 1; i++) {
if (reportSerial[i] == portId) {
serialIndexToSkip = i;
break;
}
}
// shift elements over to fill space left by removed element
for (byte i = serialIndexToSkip; i < serialIndex + 1; i++) {
if (i < MAX_SERIAL_PORTS) {
reportSerial[i] = reportSerial[i + 1];
}
}
serialIndex--;
}
}
break; // SERIAL_READ
case SERIAL_CLOSE:
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
if (portId < 8) {
((HardwareSerial*)serialPort)->end();
} else {
#if defined(SoftwareSerial_h)
((SoftwareSerial*)serialPort)->end();
if (serialPort != NULL) {
free(serialPort);
serialPort = NULL;
}
#endif
}
}
break; // SERIAL_CLOSE
case SERIAL_FLUSH:
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
getPortFromId(portId)->flush();
}
break; // SERIAL_FLUSH
#if defined(SoftwareSerial_h)
case SERIAL_LISTEN:
// can only call listen() on software serial ports
if (portId > 7) {
serialPort = getPortFromId(portId);
if (serialPort != NULL) {
((SoftwareSerial*)serialPort)->listen();
}
}
break; // SERIAL_LISTEN
#endif
}
return true;
}
return false;
}
void SerialFirmata::update()
{
checkSerial();
}
void SerialFirmata::reset()
{
#if defined(SoftwareSerial_h)
Stream *serialPort;
// free memory allocated for SoftwareSerial ports
for (byte i = SW_SERIAL0; i < SW_SERIAL3 + 1; i++) {
serialPort = getPortFromId(i);
if (serialPort != NULL) {
free(serialPort);
serialPort = NULL;
}
}
#endif
serialIndex = -1;
for (byte i = 0; i < SERIAL_READ_ARR_LEN; i++) {
serialBytesToRead[i] = 0;
}
}
// get a pointer to the serial port associated with the specified port id
Stream* SerialFirmata::getPortFromId(byte portId)
{
switch (portId) {
case HW_SERIAL0:
// block use of Serial (typically pins 0 and 1) until ability to reclaim Serial is implemented
//return &Serial;
return NULL;
#if defined(PIN_SERIAL1_RX)
case HW_SERIAL1:
return &Serial1;
#endif
#if defined(PIN_SERIAL2_RX)
case HW_SERIAL2:
return &Serial2;
#endif
#if defined(PIN_SERIAL3_RX)
case HW_SERIAL3:
return &Serial3;
#endif
#if defined(SoftwareSerial_h)
case SW_SERIAL0:
if (swSerial0 != NULL) {
// instances of SoftwareSerial are already pointers so simply return the instance
return swSerial0;
}
break;
case SW_SERIAL1:
if (swSerial1 != NULL) {
return swSerial1;
}
break;
case SW_SERIAL2:
if (swSerial2 != NULL) {
return swSerial2;
}
break;
case SW_SERIAL3:
if (swSerial3 != NULL) {
return swSerial3;
}
break;
#endif
}
return NULL;
}
// Check serial ports that have READ_CONTINUOUS mode set and relay any data
// for each port to the device attached to that port.
void SerialFirmata::checkSerial()
{
byte portId, serialData;
int bytesToRead = 0;
int numBytesToRead = 0;
Stream* serialPort;
if (serialIndex > -1) {
unsigned long currentMillis = millis();
// loop through all reporting (READ_CONTINUOUS) serial ports
for (byte i = 0; i < serialIndex + 1; i++) {
portId = reportSerial[i];
bytesToRead = serialBytesToRead[portId];
serialPort = getPortFromId(portId);
if (serialPort == NULL) {
continue;
}
#if defined(SoftwareSerial_h)
// only the SoftwareSerial port that is "listening" can read data
if (portId > 7 && !((SoftwareSerial*)serialPort)->isListening()) {
continue;
}
#endif
int availableBytes = serialPort->available();
if (availableBytes > 0) {
bool read = true;
// check if reading should be delayed to collect some bytes before
// forwarding (for baud rates significantly below 57600 baud)
if (maxCharDelay[portId]) {
// inter character delay exceeded or more than 48 bytes available or more bytes available than required
read = (lastAvailableBytes[portId] > 0 && (currentMillis - lastReceive[portId]) >= maxCharDelay[portId])
|| (bytesToRead == 0 && availableBytes >= 48) || (bytesToRead > 0 && availableBytes >= bytesToRead);
if (availableBytes > lastAvailableBytes[portId]) {
lastReceive[portId] = currentMillis;
lastAvailableBytes[portId] = availableBytes;
}
}
if (read) {
Firmata.write(START_SYSEX);
Firmata.write(SERIAL_MESSAGE);
Firmata.write(SERIAL_REPLY | portId);
if (bytesToRead == 0 || (serialPort->available() <= bytesToRead)) {
numBytesToRead = serialPort->available();
} else {
numBytesToRead = bytesToRead;
}
if (lastAvailableBytes[portId] - numBytesToRead >= 0) {
lastAvailableBytes[portId] -= numBytesToRead;
} else {
lastAvailableBytes[portId] = 0;
}
// relay serial data to the serial device
while (numBytesToRead > 0) {
serialData = serialPort->read();
Firmata.write(serialData & 0x7F);
Firmata.write((serialData >> 7) & 0x7F);
numBytesToRead--;
}
Firmata.write(END_SYSEX);
}
}
}
}
}

View File

@@ -0,0 +1,166 @@
/*
SerialFirmata.h - Firmata library
Copyright (C) 2015-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated December 23rd, 2016
*/
#ifndef SerialFirmata_h
#define SerialFirmata_h
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
// SoftwareSerial is currently only supported for AVR-based boards and the Arduino 101.
// Limited to Arduino 1.6.6 or higher because Arduino builder cannot find SoftwareSerial
// prior to this release.
#if (ARDUINO > 10605) && (defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ARC32))
#include <SoftwareSerial.h>
#endif
// uncomment FIRMATA_SERIAL_PORT_RX_BUFFERING to collect bytes received by serial port until the
// receive buffer gets filled or a data gap is detected to avoid forwarding single bytes at baud
// rates below 50000
//#define FIRMATA_SERIAL_PORT_RX_BUFFERING
// Serial port Ids
#define HW_SERIAL0 0x00
#define HW_SERIAL1 0x01
#define HW_SERIAL2 0x02
#define HW_SERIAL3 0x03
// extensible up to 0x07
#define SW_SERIAL0 0x08
#define SW_SERIAL1 0x09
#define SW_SERIAL2 0x0A
#define SW_SERIAL3 0x0B
// extensible up to 0x0F
#define SERIAL_PORT_ID_MASK 0x0F
#define MAX_SERIAL_PORTS 8
#define SERIAL_READ_ARR_LEN 12
// map configuration query response resolution value to serial pin type
#define RES_RX1 0x02
#define RES_TX1 0x03
#define RES_RX2 0x04
#define RES_TX2 0x05
#define RES_RX3 0x06
#define RES_TX3 0x07
// Serial command bytes
#define SERIAL_CONFIG 0x10
#define SERIAL_WRITE 0x20
#define SERIAL_READ 0x30
#define SERIAL_REPLY 0x40
#define SERIAL_CLOSE 0x50
#define SERIAL_FLUSH 0x60
#define SERIAL_LISTEN 0x70
// Serial read modes
#define SERIAL_READ_CONTINUOUSLY 0x00
#define SERIAL_STOP_READING 0x01
#define SERIAL_MODE_MASK 0xF0
struct serial_pins {
uint8_t rx;
uint8_t tx;
};
/*
* Get the serial serial pin type (RX1, TX1, RX2, TX2, etc) for the specified pin.
*/
inline uint8_t getSerialPinType(uint8_t pin) {
#if defined(PIN_SERIAL_RX)
// TODO when use of HW_SERIAL0 is enabled
#endif
#if defined(PIN_SERIAL1_RX)
if (pin == PIN_SERIAL1_RX) return RES_RX1;
if (pin == PIN_SERIAL1_TX) return RES_TX1;
#endif
#if defined(PIN_SERIAL2_RX)
if (pin == PIN_SERIAL2_RX) return RES_RX2;
if (pin == PIN_SERIAL2_TX) return RES_TX2;
#endif
#if defined(PIN_SERIAL3_RX)
if (pin == PIN_SERIAL3_RX) return RES_RX3;
if (pin == PIN_SERIAL3_TX) return RES_TX3;
#endif
return 0;
}
/*
* Get the RX and TX pins numbers for the specified HW serial port.
*/
inline serial_pins getSerialPinNumbers(uint8_t portId) {
serial_pins pins;
switch (portId) {
#if defined(PIN_SERIAL_RX)
// case HW_SERIAL0:
// // TODO when use of HW_SERIAL0 is enabled
// break;
#endif
#if defined(PIN_SERIAL1_RX)
case HW_SERIAL1:
pins.rx = PIN_SERIAL1_RX;
pins.tx = PIN_SERIAL1_TX;
break;
#endif
#if defined(PIN_SERIAL2_RX)
case HW_SERIAL2:
pins.rx = PIN_SERIAL2_RX;
pins.tx = PIN_SERIAL2_TX;
break;
#endif
#if defined(PIN_SERIAL3_RX)
case HW_SERIAL3:
pins.rx = PIN_SERIAL3_RX;
pins.tx = PIN_SERIAL3_TX;
break;
#endif
default:
pins.rx = 0;
pins.tx = 0;
}
return pins;
}
class SerialFirmata: public FirmataFeature
{
public:
SerialFirmata();
boolean handlePinMode(byte pin, int mode);
void handleCapability(byte pin);
boolean handleSysex(byte command, byte argc, byte* argv);
void update();
void reset();
void checkSerial();
private:
byte reportSerial[MAX_SERIAL_PORTS];
int serialBytesToRead[SERIAL_READ_ARR_LEN];
signed char serialIndex;
unsigned long lastReceive[SERIAL_READ_ARR_LEN];
unsigned char maxCharDelay[SERIAL_READ_ARR_LEN];
int lastAvailableBytes[SERIAL_READ_ARR_LEN];
#if defined(SoftwareSerial_h)
Stream *swSerial0;
Stream *swSerial1;
Stream *swSerial2;
Stream *swSerial3;
#endif
Stream* getPortFromId(byte portId);
};
#endif /* SerialFirmata_h */

View File

@@ -0,0 +1,4 @@
/*
* Implementation is in ServoFirmata.h to avoid having to include Servo.h in all
* sketch files that include ConfigurableFirmata.h
*/

View File

@@ -0,0 +1,151 @@
/*
ServoFirmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 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.
ServoFirmata.cpp has been merged into this header file as a hack to avoid having to
include Servo.h for every arduino sketch that includes ConfigurableFirmata.
Last updated by Jeff Hoefs: November 15th, 2015
*/
#ifndef ServoFirmata_h
#define ServoFirmata_h
#include <Servo.h>
#include <ConfigurableFirmata.h>
#include "FirmataFeature.h"
void servoAnalogWrite(byte pin, int value);
class ServoFirmata: public FirmataFeature
{
public:
ServoFirmata();
boolean analogWrite(byte pin, int value);
boolean handlePinMode(byte pin, int mode);
void handleCapability(byte pin);
boolean handleSysex(byte command, byte argc, byte* argv);
void reset();
private:
Servo *servos[MAX_SERVOS];
void attach(byte pin, int minPulse, int maxPulse);
void detach(byte pin);
};
/*
* ServoFirmata.cpp
* Copied here as a hack to avoid having to include Servo.h in all sketch files that
* include ConfigurableFirmata.h
*/
ServoFirmata *ServoInstance;
void servoAnalogWrite(byte pin, int value)
{
ServoInstance->analogWrite(pin, value);
}
ServoFirmata::ServoFirmata()
{
ServoInstance = this;
}
boolean ServoFirmata::analogWrite(byte pin, int value)
{
if (IS_PIN_SERVO(pin)) {
Servo *servo = servos[PIN_TO_SERVO(pin)];
if (servo) {
servo->write(value);
return true;
}
}
return false;
}
boolean ServoFirmata::handlePinMode(byte pin, int mode)
{
if (IS_PIN_SERVO(pin)) {
if (mode == PIN_MODE_SERVO) {
attach(pin, -1, -1);
return true;
} else {
detach(pin);
}
}
return false;
}
void ServoFirmata::handleCapability(byte pin)
{
if (IS_PIN_SERVO(pin)) {
Firmata.write(PIN_MODE_SERVO);
Firmata.write(14); //14 bit resolution (Servo takes int as argument)
}
}
boolean ServoFirmata::handleSysex(byte command, byte argc, byte* argv)
{
if (command == SERVO_CONFIG) {
if (argc > 4) {
// these vars are here for clarity, they'll optimized away by the compiler
byte pin = argv[0];
if (IS_PIN_SERVO(pin) && Firmata.getPinMode(pin) != PIN_MODE_IGNORE) {
int minPulse = argv[1] + (argv[2] << 7);
int maxPulse = argv[3] + (argv[4] << 7);
Firmata.setPinMode(pin, PIN_MODE_SERVO);
attach(pin, minPulse, maxPulse);
return true;
}
}
}
return false;
}
void ServoFirmata::attach(byte pin, int minPulse, int maxPulse)
{
Servo *servo = servos[PIN_TO_SERVO(pin)];
if (!servo) {
servo = new Servo();
servos[PIN_TO_SERVO(pin)] = servo;
}
if (servo->attached())
servo->detach();
if (minPulse >= 0 || maxPulse >= 0)
servo->attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse);
else
servo->attach(PIN_TO_DIGITAL(pin));
}
void ServoFirmata::detach(byte pin)
{
Servo *servo = servos[PIN_TO_SERVO(pin)];
if (servo) {
if (servo->attached())
servo->detach();
free(servo);
servos[PIN_TO_SERVO(pin)] = NULL;
}
}
void ServoFirmata::reset()
{
for (byte pin = 0; pin < TOTAL_PINS; pin++) {
if (IS_PIN_SERVO(pin)) {
detach(pin);
}
}
}
#endif /* ServoFirmata_h */

View File

@@ -0,0 +1,151 @@
/*
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated by Jeff Hoefs: January 23rd, 2016
*/
#include <ConfigurableFirmata.h>
#include "StepperFirmata.h"
#include "utility/FirmataStepper.h"
boolean StepperFirmata::handlePinMode(byte pin, int mode)
{
if (mode == PIN_MODE_STEPPER) {
if (IS_PIN_DIGITAL(pin)) {
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
return true;
}
}
return false;
}
void StepperFirmata::handleCapability(byte pin)
{
if (IS_PIN_DIGITAL(pin)) {
Firmata.write(PIN_MODE_STEPPER);
Firmata.write(21); //21 bits used for number of steps
}
}
/*==============================================================================
* SYSEX-BASED commands
*============================================================================*/
boolean StepperFirmata::handleSysex(byte command, byte argc, byte *argv)
{
if (command == STEPPER_DATA) {
byte stepCommand, deviceNum, directionPin, stepPin, stepDirection;
byte interface, interfaceType;
byte motorPin3, motorPin4;
unsigned int stepsPerRev;
long numSteps;
int stepSpeed;
int accel;
int decel;
stepCommand = argv[0];
deviceNum = argv[1];
if (deviceNum < MAX_STEPPERS) {
if (stepCommand == STEPPER_CONFIG) {
interface = argv[2]; // upper 4 bits are the stepDelay, lower 4 bits are the interface type
interfaceType = interface & 0x0F; // the interface type is specified by the lower 4 bits
stepsPerRev = (argv[3] + (argv[4] << 7));
directionPin = argv[5]; // or motorPin1 for TWO_WIRE or FOUR_WIRE interface
stepPin = argv[6]; // // or motorPin2 for TWO_WIRE or FOUR_WIRE interface
if (Firmata.getPinMode(directionPin) == PIN_MODE_IGNORE || Firmata.getPinMode(stepPin) == PIN_MODE_IGNORE)
return false;
Firmata.setPinMode(directionPin, PIN_MODE_STEPPER);
Firmata.setPinMode(stepPin, PIN_MODE_STEPPER);
if (!stepper[deviceNum]) {
numSteppers++;
}
if (interfaceType == FirmataStepper::DRIVER || interfaceType == FirmataStepper::TWO_WIRE) {
stepper[deviceNum] = new FirmataStepper(interface, stepsPerRev, directionPin, stepPin);
} else if (interfaceType == FirmataStepper::FOUR_WIRE) {
motorPin3 = argv[7];
motorPin4 = argv[8];
if (Firmata.getPinMode(motorPin3) == PIN_MODE_IGNORE || Firmata.getPinMode(motorPin4) == PIN_MODE_IGNORE)
return false;
Firmata.setPinMode(motorPin3, PIN_MODE_STEPPER);
Firmata.setPinMode(motorPin4, PIN_MODE_STEPPER);
stepper[deviceNum] = new FirmataStepper(interface, stepsPerRev, directionPin, stepPin, motorPin3, motorPin4);
}
}
else if (stepCommand == STEPPER_STEP) {
stepDirection = argv[2];
numSteps = (long)argv[3] | ((long)argv[4] << 7) | ((long)argv[5] << 14);
stepSpeed = (argv[6] + (argv[7] << 7));
if (stepDirection == 0) {
numSteps *= -1;
}
if (stepper[deviceNum]) {
if (argc >= 8 && argc < 12) {
// num steps, speed (0.01*rad/sec)
stepper[deviceNum]->setStepsToMove(numSteps, stepSpeed);
} else if (argc == 12) {
accel = (argv[8] + (argv[9] << 7));
decel = (argv[10] + (argv[11] << 7));
// num steps, speed (0.01*rad/sec), accel (0.01*rad/sec^2), decel (0.01*rad/sec^2)
stepper[deviceNum]->setStepsToMove(numSteps, stepSpeed, accel, decel);
}
}
}
return true;
}
}
return false;
}
/*==============================================================================
* SETUP()
*============================================================================*/
void StepperFirmata::reset()
{
for (byte i = 0; i < MAX_STEPPERS; i++) {
if (stepper[i]) {
free(stepper[i]);
stepper[i] = 0;
}
}
numSteppers = 0;
}
/*==============================================================================
* LOOP()
*============================================================================*/
void StepperFirmata::update()
{
if (numSteppers > 0) {
// if one or more stepper motors are used, update their position
for (byte i = 0; i < MAX_STEPPERS; i++) {
if (stepper[i]) {
bool done = stepper[i]->update();
// send command to client application when stepping is complete
if (done) {
Firmata.write(START_SYSEX);
Firmata.write(STEPPER_DATA);
Firmata.write(i);
Firmata.write(END_SYSEX);
}
}
}
}
}

View File

@@ -0,0 +1,40 @@
/*
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2013 Norbert Truchsess. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
*/
#ifndef StepperFirmata_h
#define StepperFirmata_h
#include <ConfigurableFirmata.h>
#include "utility/FirmataStepper.h"
#include "FirmataFeature.h"
#define MAX_STEPPERS 6 // arbitrary value... may need to adjust
#define STEPPER_CONFIG 0
#define STEPPER_STEP 1
class StepperFirmata: public FirmataFeature
{
public:
boolean handlePinMode(byte pin, int mode);
void handleCapability(byte pin);
boolean handleSysex(byte command, byte argc, byte *argv);
void update();
void reset();
private:
FirmataStepper *stepper[MAX_STEPPERS];
byte numSteppers;
};
#endif

View File

@@ -0,0 +1,651 @@
// AccelStepper.cpp
//
// Copyright (C) 2009-2013 Mike McCauley
// $Id: AccelStepper.cpp,v 1.23 2016/08/09 00:39:10 mikem Exp $
#include "AccelStepper.h"
#if 0
// Some debugging assistance
void dump(uint8_t* p, int l)
{
int i;
for (i = 0; i < l; i++)
{
Serial.print(p[i], HEX);
Serial.print(" ");
}
Serial.println("");
}
#endif
void AccelStepper::moveTo(long absolute)
{
if (_targetPos != absolute)
{
_targetPos = absolute;
computeNewSpeed();
// compute new n?
}
}
void AccelStepper::move(long relative)
{
moveTo(_currentPos + relative);
}
// Implements steps according to the current step interval
// You must call this at least once per step
// returns true if a step occurred
boolean AccelStepper::runSpeed()
{
// Dont do anything unless we actually have a step interval
if (!_stepInterval)
return false;
unsigned long time = micros();
if (time - _lastStepTime >= _stepInterval)
{
if (_direction == DIRECTION_CW)
{
// Clockwise
_currentPos += 1;
}
else
{
// Anticlockwise
_currentPos -= 1;
}
step(_currentPos);
_lastStepTime = time; // Caution: does not account for costs in step()
return true;
}
else
{
return false;
}
}
long AccelStepper::distanceToGo()
{
return _targetPos - _currentPos;
}
long AccelStepper::targetPosition()
{
return _targetPos;
}
long AccelStepper::currentPosition()
{
return _currentPos;
}
// Useful during initialisations or after initial positioning
// Sets speed to 0
void AccelStepper::setCurrentPosition(long position)
{
_targetPos = _currentPos = position;
_n = 0;
_stepInterval = 0;
_speed = 0.0;
}
void AccelStepper::computeNewSpeed()
{
long distanceTo = distanceToGo(); // +ve is clockwise from curent location
long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16
if (distanceTo == 0 && stepsToStop <= 1)
{
// We are at the target and its time to stop
_stepInterval = 0;
_speed = 0.0;
_n = 0;
return;
}
if (distanceTo > 0)
{
// We are anticlockwise from the target
// Need to go clockwise from here, maybe decelerate now
if (_n > 0)
{
// Currently accelerating, need to decel now? Or maybe going the wrong way?
if ((stepsToStop >= distanceTo) || _direction == DIRECTION_CCW)
_n = -stepsToStop; // Start deceleration
}
else if (_n < 0)
{
// Currently decelerating, need to accel again?
if ((stepsToStop < distanceTo) && _direction == DIRECTION_CW)
_n = -_n; // Start accceleration
}
}
else if (distanceTo < 0)
{
// We are clockwise from the target
// Need to go anticlockwise from here, maybe decelerate
if (_n > 0)
{
// Currently accelerating, need to decel now? Or maybe going the wrong way?
if ((stepsToStop >= -distanceTo) || _direction == DIRECTION_CW)
_n = -stepsToStop; // Start deceleration
}
else if (_n < 0)
{
// Currently decelerating, need to accel again?
if ((stepsToStop < -distanceTo) && _direction == DIRECTION_CCW)
_n = -_n; // Start accceleration
}
}
// Need to accelerate or decelerate
if (_n == 0)
{
// First step from stopped
_cn = _c0;
_direction = (distanceTo > 0) ? DIRECTION_CW : DIRECTION_CCW;
}
else
{
// Subsequent step. Works for accel (n is +_ve) and decel (n is -ve).
_cn = _cn - ((2.0 * _cn) / ((4.0 * _n) + 1)); // Equation 13
_cn = max(_cn, _cmin);
}
_n++;
_stepInterval = _cn;
_speed = 1000000.0 / _cn;
if (_direction == DIRECTION_CCW)
_speed = -_speed;
#if 0
Serial.println(_speed);
Serial.println(_acceleration);
Serial.println(_cn);
Serial.println(_c0);
Serial.println(_n);
Serial.println(_stepInterval);
Serial.println(distanceTo);
Serial.println(stepsToStop);
Serial.println("-----");
#endif
}
// Run the motor to implement speed and acceleration in order to proceed to the target position
// You must call this at least once per step, preferably in your main loop
// If the motor is in the desired position, the cost is very small
// returns true if the motor is still running to the target position.
boolean AccelStepper::run()
{
if (runSpeed())
computeNewSpeed();
return _speed != 0.0 || distanceToGo() != 0;
}
AccelStepper::AccelStepper(uint8_t interface, uint8_t pin1, uint8_t pin2, uint8_t pin3, uint8_t pin4, bool enable)
{
_interface = interface;
_currentPos = 0;
_targetPos = 0;
_speed = 0.0;
_maxSpeed = 1.0;
_acceleration = 0.0;
_sqrt_twoa = 1.0;
_stepInterval = 0;
_minPulseWidth = 1;
_enablePin = 0xff;
_lastStepTime = 0;
_pin[0] = pin1;
_pin[1] = pin2;
_pin[2] = pin3;
_pin[3] = pin4;
// NEW
_n = 0;
_c0 = 0.0;
_cn = 0.0;
_cmin = 1.0;
_direction = DIRECTION_CCW;
int i;
for (i = 0; i < 4; i++)
_pinInverted[i] = 0;
if (enable)
enableOutputs();
// Some reasonable default
setAcceleration(1);
}
AccelStepper::AccelStepper(void (*forward)(), void (*backward)())
{
_interface = 0;
_currentPos = 0;
_targetPos = 0;
_speed = 0.0;
_maxSpeed = 1.0;
_acceleration = 0.0;
_sqrt_twoa = 1.0;
_stepInterval = 0;
_minPulseWidth = 1;
_enablePin = 0xff;
_lastStepTime = 0;
_pin[0] = 0;
_pin[1] = 0;
_pin[2] = 0;
_pin[3] = 0;
_forward = forward;
_backward = backward;
// NEW
_n = 0;
_c0 = 0.0;
_cn = 0.0;
_cmin = 1.0;
_direction = DIRECTION_CCW;
int i;
for (i = 0; i < 4; i++)
_pinInverted[i] = 0;
// Some reasonable default
setAcceleration(1);
}
void AccelStepper::setMaxSpeed(float speed)
{
if (speed < 0.0)
speed = -speed;
if (_maxSpeed != speed)
{
_maxSpeed = speed;
_cmin = 1000000.0 / speed;
// Recompute _n from current speed and adjust speed if accelerating or cruising
if (_n > 0)
{
_n = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16
computeNewSpeed();
}
}
}
float AccelStepper::maxSpeed()
{
return _maxSpeed;
}
void AccelStepper::setAcceleration(float acceleration)
{
if (acceleration == 0.0)
return;
if (acceleration < 0.0)
acceleration = -acceleration;
if (_acceleration != acceleration)
{
// Recompute _n per Equation 17
_n = _n * (_acceleration / acceleration);
// New c0 per Equation 7, with correction per Equation 15
_c0 = 0.676 * sqrt(2.0 / acceleration) * 1000000.0; // Equation 15
_acceleration = acceleration;
computeNewSpeed();
}
}
void AccelStepper::setSpeed(float speed)
{
if (speed == _speed)
return;
speed = constrain(speed, -_maxSpeed, _maxSpeed);
if (speed == 0.0)
_stepInterval = 0;
else
{
_stepInterval = fabs(1000000.0 / speed);
_direction = (speed > 0.0) ? DIRECTION_CW : DIRECTION_CCW;
}
_speed = speed;
}
float AccelStepper::speed()
{
return _speed;
}
// Subclasses can override
void AccelStepper::step(long step)
{
switch (_interface)
{
case FUNCTION:
step0(step);
break;
case DRIVER:
step1(step);
break;
case FULL2WIRE:
step2(step);
break;
case FULL3WIRE:
step3(step);
break;
case FULL4WIRE:
step4(step);
break;
case HALF3WIRE:
step6(step);
break;
case HALF4WIRE:
step8(step);
break;
}
}
// You might want to override this to implement eg serial output
// bit 0 of the mask corresponds to _pin[0]
// bit 1 of the mask corresponds to _pin[1]
// ....
void AccelStepper::setOutputPins(uint8_t mask)
{
uint8_t numpins = 2;
if (_interface == FULL4WIRE || _interface == HALF4WIRE)
numpins = 4;
else if (_interface == FULL3WIRE || _interface == HALF3WIRE)
numpins = 3;
uint8_t i;
for (i = 0; i < numpins; i++)
digitalWrite(_pin[i], (mask & (1 << i)) ? (HIGH ^ _pinInverted[i]) : (LOW ^ _pinInverted[i]));
}
// 0 pin step function (ie for functional usage)
void AccelStepper::step0(long step)
{
(void)(step); // Unused
if (_speed > 0)
_forward();
else
_backward();
}
// 1 pin step function (ie for stepper drivers)
// This is passed the current step number (0 to 7)
// Subclasses can override
void AccelStepper::step1(long step)
{
(void)(step); // Unused
// _pin[0] is step, _pin[1] is direction
setOutputPins(_direction ? 0b10 : 0b00); // Set direction first else get rogue pulses
setOutputPins(_direction ? 0b11 : 0b01); // step HIGH
// Caution 200ns setup time
// Delay the minimum allowed pulse width
delayMicroseconds(_minPulseWidth);
setOutputPins(_direction ? 0b10 : 0b00); // step LOW
}
// 2 pin step function
// This is passed the current step number (0 to 7)
// Subclasses can override
void AccelStepper::step2(long step)
{
switch (step & 0x3)
{
case 0: /* 01 */
setOutputPins(0b10);
break;
case 1: /* 11 */
setOutputPins(0b11);
break;
case 2: /* 10 */
setOutputPins(0b01);
break;
case 3: /* 00 */
setOutputPins(0b00);
break;
}
}
// 3 pin step function
// This is passed the current step number (0 to 7)
// Subclasses can override
void AccelStepper::step3(long step)
{
switch (step % 3)
{
case 0: // 100
setOutputPins(0b100);
break;
case 1: // 001
setOutputPins(0b001);
break;
case 2: //010
setOutputPins(0b010);
break;
}
}
// 4 pin step function for half stepper
// This is passed the current step number (0 to 7)
// Subclasses can override
void AccelStepper::step4(long step)
{
switch (step & 0x3)
{
case 0: // 1010
setOutputPins(0b0101);
break;
case 1: // 0110
setOutputPins(0b0110);
break;
case 2: //0101
setOutputPins(0b1010);
break;
case 3: //1001
setOutputPins(0b1001);
break;
}
}
// 3 pin half step function
// This is passed the current step number (0 to 7)
// Subclasses can override
void AccelStepper::step6(long step)
{
switch (step % 6)
{
case 0: // 100
setOutputPins(0b100);
break;
case 1: // 101
setOutputPins(0b101);
break;
case 2: // 001
setOutputPins(0b001);
break;
case 3: // 011
setOutputPins(0b011);
break;
case 4: // 010
setOutputPins(0b010);
break;
case 5: // 011
setOutputPins(0b110);
break;
}
}
// 4 pin half step function
// This is passed the current step number (0 to 7)
// Subclasses can override
void AccelStepper::step8(long step)
{
switch (step & 0x7)
{
case 0: // 1000
setOutputPins(0b0001);
break;
case 1: // 1010
setOutputPins(0b0101);
break;
case 2: // 0010
setOutputPins(0b0100);
break;
case 3: // 0110
setOutputPins(0b0110);
break;
case 4: // 0100
setOutputPins(0b0010);
break;
case 5: //0101
setOutputPins(0b1010);
break;
case 6: // 0001
setOutputPins(0b1000);
break;
case 7: //1001
setOutputPins(0b1001);
break;
}
}
// Prevents power consumption on the outputs
void AccelStepper::disableOutputs()
{
if (! _interface) return;
setOutputPins(0); // Handles inversion automatically
if (_enablePin != 0xff)
{
pinMode(_enablePin, OUTPUT);
digitalWrite(_enablePin, LOW ^ _enableInverted);
}
}
void AccelStepper::enableOutputs()
{
if (! _interface)
return;
pinMode(_pin[0], OUTPUT);
pinMode(_pin[1], OUTPUT);
if (_interface == FULL4WIRE || _interface == HALF4WIRE)
{
pinMode(_pin[2], OUTPUT);
pinMode(_pin[3], OUTPUT);
}
else if (_interface == FULL3WIRE || _interface == HALF3WIRE)
{
pinMode(_pin[2], OUTPUT);
}
if (_enablePin != 0xff)
{
pinMode(_enablePin, OUTPUT);
digitalWrite(_enablePin, HIGH ^ _enableInverted);
}
}
void AccelStepper::setMinPulseWidth(unsigned int minWidth)
{
_minPulseWidth = minWidth;
}
void AccelStepper::setEnablePin(uint8_t enablePin)
{
_enablePin = enablePin;
// This happens after construction, so init pin now.
if (_enablePin != 0xff)
{
pinMode(_enablePin, OUTPUT);
digitalWrite(_enablePin, HIGH ^ _enableInverted);
}
}
void AccelStepper::setPinsInverted(bool directionInvert, bool stepInvert, bool enableInvert)
{
_pinInverted[0] = stepInvert;
_pinInverted[1] = directionInvert;
_enableInverted = enableInvert;
}
void AccelStepper::setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert)
{
_pinInverted[0] = pin1Invert;
_pinInverted[1] = pin2Invert;
_pinInverted[2] = pin3Invert;
_pinInverted[3] = pin4Invert;
_enableInverted = enableInvert;
}
// Blocks until the target position is reached and stopped
void AccelStepper::runToPosition()
{
while (run())
;
}
boolean AccelStepper::runSpeedToPosition()
{
if (_targetPos == _currentPos)
return false;
if (_targetPos >_currentPos)
_direction = DIRECTION_CW;
else
_direction = DIRECTION_CCW;
return runSpeed();
}
// Blocks until the new target position is reached
void AccelStepper::runToNewPosition(long position)
{
moveTo(position);
runToPosition();
}
void AccelStepper::stop()
{
if (_speed != 0.0)
{
long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)) + 1; // Equation 16 (+integer rounding)
if (_speed > 0)
move(stepsToStop);
else
move(-stepsToStop);
}
}
bool AccelStepper::isRunning()
{
return !(_speed == 0.0 && _targetPos == _currentPos);
}

View File

@@ -0,0 +1,728 @@
// AccelStepper.h
//
/// \mainpage AccelStepper library for Arduino
///
/// This is the Arduino AccelStepper library.
/// It provides an object-oriented interface for 2, 3 or 4 pin stepper motors and motor drivers.
///
/// The standard Arduino IDE includes the Stepper library
/// (http://arduino.cc/en/Reference/Stepper) for stepper motors. It is
/// perfectly adequate for simple, single motor applications.
///
/// AccelStepper significantly improves on the standard Arduino Stepper library in several ways:
/// \li Supports acceleration and deceleration
/// \li Supports multiple simultaneous steppers, with independent concurrent stepping on each stepper
/// \li API functions never delay() or block
/// \li Supports 2, 3 and 4 wire steppers, plus 3 and 4 wire half steppers.
/// \li Supports alternate stepping functions to enable support of AFMotor (https://github.com/adafruit/Adafruit-Motor-Shield-library)
/// \li Supports stepper drivers such as the Sparkfun EasyDriver (based on 3967 driver chip)
/// \li Very slow speeds are supported
/// \li Extensive API
/// \li Subclass support
///
/// The latest version of this documentation can be downloaded from
/// http://www.airspayce.com/mikem/arduino/AccelStepper
/// The version of the package that this documentation refers to can be downloaded
/// from http://www.airspayce.com/mikem/arduino/AccelStepper/AccelStepper-1.57.zip
///
/// Example Arduino programs are included to show the main modes of use.
///
/// You can also find online help and discussion at http://groups.google.com/group/accelstepper
/// Please use that group for all questions and discussions on this topic.
/// Do not contact the author directly, unless it is to discuss commercial licensing.
/// Before asking a question or reporting a bug, please read
/// - http://en.wikipedia.org/wiki/Wikipedia:Reference_desk/How_to_ask_a_software_question
/// - http://www.catb.org/esr/faqs/smart-questions.html
/// - http://www.chiark.greenend.org.uk/~shgtatham/bugs.html
///
/// Tested on Arduino Diecimila and Mega with arduino-0018 & arduino-0021
/// on OpenSuSE 11.1 and avr-libc-1.6.1-1.15,
/// cross-avr-binutils-2.19-9.1, cross-avr-gcc-4.1.3_20080612-26.5.
/// Tested on Teensy http://www.pjrc.com/teensy including Teensy 3.1 built using Arduino IDE 1.0.5 with
/// teensyduino addon 1.18 and later.
///
/// \par Installation
///
/// Install in the usual way: unzip the distribution zip file to the libraries
/// sub-folder of your sketchbook.
///
/// \par Theory
///
/// This code uses speed calculations as described in
/// "Generate stepper-motor speed profiles in real time" by David Austin
/// http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf or
/// http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time or
/// http://web.archive.org/web/20140705143928/http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf
/// with the exception that AccelStepper uses steps per second rather than radians per second
/// (because we dont know the step angle of the motor)
/// An initial step interval is calculated for the first step, based on the desired acceleration
/// On subsequent steps, shorter step intervals are calculated based
/// on the previous step until max speed is achieved.
///
/// \par Adafruit Motor Shield V2
///
/// The included examples AFMotor_* are for Adafruit Motor Shield V1 and do not work with Adafruit Motor Shield V2.
/// See https://github.com/adafruit/Adafruit_Motor_Shield_V2_Library for examples that work with Adafruit Motor Shield V2.
///
/// \par Donations
///
/// This library is offered under a free GPL license for those who want to use it that way.
/// We try hard to keep it up to date, fix bugs
/// and to provide free support. If this library has helped you save time or money, please consider donating at
/// http://www.airspayce.com or here:
///
/// \htmlonly <form action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_donations" /> <input type="hidden" name="business" value="mikem@airspayce.com" /> <input type="hidden" name="lc" value="AU" /> <input type="hidden" name="item_name" value="Airspayce" /> <input type="hidden" name="item_number" value="AccelStepper" /> <input type="hidden" name="currency_code" value="USD" /> <input type="hidden" name="bn" value="PP-DonationsBF:btn_donateCC_LG.gif:NonHosted" /> <input type="image" alt="PayPal — The safer, easier way to pay online." name="submit" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donateCC_LG.gif" /> <img alt="" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" border="0" /></form> \endhtmlonly
///
/// \par Trademarks
///
/// AccelStepper is a trademark of AirSpayce Pty Ltd. The AccelStepper mark was first used on April 26 2010 for
/// international trade, and is used only in relation to motor control hardware and software.
/// It is not to be confused with any other similar marks covering other goods and services.
///
/// \par Copyright
///
/// This software is Copyright (C) 2010 Mike McCauley. Use is subject to license
/// conditions. The main licensing options available are GPL V2 or Commercial:
///
/// \par Open Source Licensing GPL V2
/// This is the appropriate option if you want to share the source code of your
/// application with everyone you distribute it to, and you also want to give them
/// the right to share who uses it. If you wish to use this software under Open
/// Source Licensing, you must contribute all your source code to the open source
/// community in accordance with the GPL Version 2 when your application is
/// distributed. See https://www.gnu.org/licenses/gpl-2.0.html
///
/// \par Commercial Licensing
/// This is the appropriate option if you are creating proprietary applications
/// and you are not prepared to distribute and share the source code of your
/// application. Purchase commercial licenses at http://airspayce.binpress.com/
///
/// \par Revision History
/// \version 1.0 Initial release
///
/// \version 1.1 Added speed() function to get the current speed.
/// \version 1.2 Added runSpeedToPosition() submitted by Gunnar Arndt.
/// \version 1.3 Added support for stepper drivers (ie with Step and Direction inputs) with _pins == 1
/// \version 1.4 Added functional contructor to support AFMotor, contributed by Limor, with example sketches.
/// \version 1.5 Improvements contributed by Peter Mousley: Use of microsecond steps and other speed improvements
/// to increase max stepping speed to about 4kHz. New option for user to set the min allowed pulse width.
/// Added checks for already running at max speed and skip further calcs if so.
/// \version 1.6 Fixed a problem with wrapping of microsecond stepping that could cause stepping to hang.
/// Reported by Sandy Noble.
/// Removed redundant _lastRunTime member.
/// \version 1.7 Fixed a bug where setCurrentPosition() did not always work as expected.
/// Reported by Peter Linhart.
/// \version 1.8 Added support for 4 pin half-steppers, requested by Harvey Moon
/// \version 1.9 setCurrentPosition() now also sets motor speed to 0.
/// \version 1.10 Builds on Arduino 1.0
/// \version 1.11 Improvments from Michael Ellison:
/// Added optional enable line support for stepper drivers
/// Added inversion for step/direction/enable lines for stepper drivers
/// \version 1.12 Announce Google Group
/// \version 1.13 Improvements to speed calculation. Cost of calculation is now less in the worst case,
/// and more or less constant in all cases. This should result in slightly beter high speed performance, and
/// reduce anomalous speed glitches when other steppers are accelerating.
/// However, its hard to see how to replace the sqrt() required at the very first step from 0 speed.
/// \version 1.14 Fixed a problem with compiling under arduino 0021 reported by EmbeddedMan
/// \version 1.15 Fixed a problem with runSpeedToPosition which did not correctly handle
/// running backwards to a smaller target position. Added examples
/// \version 1.16 Fixed some cases in the code where abs() was used instead of fabs().
/// \version 1.17 Added example ProportionalControl
/// \version 1.18 Fixed a problem: If one calls the funcion runSpeed() when Speed is zero, it makes steps
/// without counting. reported by Friedrich, Klappenbach.
/// \version 1.19 Added MotorInterfaceType and symbolic names for the number of pins to use
/// for the motor interface. Updated examples to suit.
/// Replaced individual pin assignment variables _pin1, _pin2 etc with array _pin[4].
/// _pins member changed to _interface.
/// Added _pinInverted array to simplify pin inversion operations.
/// Added new function setOutputPins() which sets the motor output pins.
/// It can be overridden in order to provide, say, serial output instead of parallel output
/// Some refactoring and code size reduction.
/// \version 1.20 Improved documentation and examples to show need for correctly
/// specifying AccelStepper::FULL4WIRE and friends.
/// \version 1.21 Fixed a problem where desiredSpeed could compute the wrong step acceleration
/// when _speed was small but non-zero. Reported by Brian Schmalz.
/// Precompute sqrt_twoa to improve performance and max possible stepping speed
/// \version 1.22 Added Bounce.pde example
/// Fixed a problem where calling moveTo(), setMaxSpeed(), setAcceleration() more
/// frequently than the step time, even
/// with the same values, would interfere with speed calcs. Now a new speed is computed
/// only if there was a change in the set value. Reported by Brian Schmalz.
/// \version 1.23 Rewrite of the speed algorithms in line with
/// http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf
/// Now expect smoother and more linear accelerations and decelerations. The desiredSpeed()
/// function was removed.
/// \version 1.24 Fixed a problem introduced in 1.23: with runToPosition, which did never returned
/// \version 1.25 Now ignore attempts to set acceleration to 0.0
/// \version 1.26 Fixed a problem where certina combinations of speed and accelration could cause
/// oscillation about the target position.
/// \version 1.27 Added stop() function to stop as fast as possible with current acceleration parameters.
/// Also added new Quickstop example showing its use.
/// \version 1.28 Fixed another problem where certain combinations of speed and accelration could cause
/// oscillation about the target position.
/// Added support for 3 wire full and half steppers such as Hard Disk Drive spindle.
/// Contributed by Yuri Ivatchkovitch.
/// \version 1.29 Fixed a problem that could cause a DRIVER stepper to continually step
/// with some sketches. Reported by Vadim.
/// \version 1.30 Fixed a problem that could cause stepper to back up a few steps at the end of
/// accelerated travel with certain speeds. Reported and patched by jolo.
/// \version 1.31 Updated author and distribution location details to airspayce.com
/// \version 1.32 Fixed a problem with enableOutputs() and setEnablePin on Arduino Due that
/// prevented the enable pin changing stae correctly. Reported by Duane Bishop.
/// \version 1.33 Fixed an error in example AFMotor_ConstantSpeed.pde did not setMaxSpeed();
/// Fixed a problem that caused incorrect pin sequencing of FULL3WIRE and HALF3WIRE.
/// Unfortunately this meant changing the signature for all step*() functions.
/// Added example MotorShield, showing how to use AdaFruit Motor Shield to control
/// a 3 phase motor such as a HDD spindle motor (and without using the AFMotor library.
/// \version 1.34 Added setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert)
/// to allow inversion of 2, 3 and 4 wire stepper pins. Requested by Oleg.
/// \version 1.35 Removed default args from setPinsInverted(bool, bool, bool, bool, bool) to prevent ambiguity with
/// setPinsInverted(bool, bool, bool). Reported by Mac Mac.
/// \version 1.36 Changed enableOutputs() and disableOutputs() to be virtual so can be overridden.
/// Added new optional argument 'enable' to constructor, which allows you toi disable the
/// automatic enabling of outputs at construction time. Suggested by Guido.
/// \version 1.37 Fixed a problem with step1 that could cause a rogue step in the
/// wrong direction (or not,
/// depending on the setup-time requirements of the connected hardware).
/// Reported by Mark Tillotson.
/// \version 1.38 run() function incorrectly always returned true. Updated function and doc so it returns true
/// if the motor is still running to the target position.
/// \version 1.39 Updated typos in keywords.txt, courtesey Jon Magill.
/// \version 1.40 Updated documentation, including testing on Teensy 3.1
/// \version 1.41 Fixed an error in the acceleration calculations, resulting in acceleration of haldf the intended value
/// \version 1.42 Improved support for FULL3WIRE and HALF3WIRE output pins. These changes were in Yuri's original
/// contribution but did not make it into production.<br>
/// \version 1.43 Added DualMotorShield example. Shows how to use AccelStepper to control 2 x 2 phase steppers using the
/// Itead Studio Arduino Dual Stepper Motor Driver Shield model IM120417015.<br>
/// \version 1.44 examples/DualMotorShield/DualMotorShield.ino examples/DualMotorShield/DualMotorShield.pde
/// was missing from the distribution.<br>
/// \version 1.45 Fixed a problem where if setAcceleration was not called, there was no default
/// acceleration. Reported by Michael Newman.<br>
/// \version 1.45 Fixed inaccuracy in acceleration rate by using Equation 15, suggested by Sebastian Gracki.<br>
/// Performance improvements in runSpeed suggested by Jaakko Fagerlund.<br>
/// \version 1.46 Fixed error in documentation for runToPosition().
/// Reinstated time calculations in runSpeed() since new version is reported
/// not to work correctly under some circumstances. Reported by Oleg V Gavva.<br>
/// \version 1.48 2015-08-25
/// Added new class MultiStepper that can manage multiple AccelSteppers,
/// and cause them all to move
/// to selected positions at such a (constant) speed that they all arrive at their
/// target position at the same time. Suitable for X-Y flatbeds etc.<br>
/// Added new method maxSpeed() to AccelStepper to return the currently configured maxSpeed.<br>
/// \version 1.49 2016-01-02
/// Testing with VID28 series instrument stepper motors and EasyDriver.
/// OK, although with light pointers
/// and slow speeds like 180 full steps per second the motor movement can be erratic,
/// probably due to some mechanical resonance. Best to accelerate through this speed.<br>
/// Added isRunning().<br>
/// \version 1.50 2016-02-25
/// AccelStepper::disableOutputs now sets the enable pion to OUTPUT mode if the enable pin is defined.
/// Patch from Piet De Jong.<br>
/// Added notes about the fact that AFMotor_* examples do not work with Adafruit Motor Shield V2.<br>
/// \version 1.51 2016-03-24
/// Fixed a problem reported by gregor: when resetting the stepper motor position using setCurrentPosition() the
/// stepper speed is reset by setting _stepInterval to 0, but _speed is not
/// reset. this results in the stepper motor not starting again when calling
/// setSpeed() with the same speed the stepper was set to before.
/// \version 1.52 2016-08-09
/// Added MultiStepper to keywords.txt.
/// Improvements to efficiency of AccelStepper::runSpeed() as suggested by David Grayson.
/// Improvements to speed accuracy as suggested by David Grayson.
/// \version 1.53 2016-08-14
/// Backed out Improvements to speed accuracy from 1.52 as it did not work correctly.
/// \version 1.54 2017-01-24
/// Fixed some warnings about unused arguments.
/// \version 1.55 2017-01-25
/// Fixed another warning in MultiStepper.cpp
/// \version 1.56 2017-02-03
/// Fixed minor documentation error with DIRECTION_CCW and DIRECTION_CW. Reported by David Mutterer.
/// Added link to Binpress commercial license purchasing.
/// \version 1.57 2017-03-28
/// _direction moved to protected at the request of Rudy Ercek.
/// setMaxSpeed() and setAcceleration() now correct negative values to be positive.
///
/// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS
// Copyright (C) 2009-2013 Mike McCauley
// $Id: AccelStepper.h,v 1.27 2016/08/14 10:26:54 mikem Exp mikem $
#ifndef AccelStepper_h
#define AccelStepper_h
#include <stdlib.h>
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#include <wiring.h>
#endif
// These defs cause trouble on some versions of Arduino
#undef round
/////////////////////////////////////////////////////////////////////
/// \class AccelStepper AccelStepper.h <AccelStepper.h>
/// \brief Support for stepper motors with acceleration etc.
///
/// This defines a single 2 or 4 pin stepper motor, or stepper moter with fdriver chip, with optional
/// acceleration, deceleration, absolute positioning commands etc. Multiple
/// simultaneous steppers are supported, all moving
/// at different speeds and accelerations.
///
/// \par Operation
/// This module operates by computing a step time in microseconds. The step
/// time is recomputed after each step and after speed and acceleration
/// parameters are changed by the caller. The time of each step is recorded in
/// microseconds. The run() function steps the motor once if a new step is due.
/// The run() function must be called frequently until the motor is in the
/// desired position, after which time run() will do nothing.
///
/// \par Positioning
/// Positions are specified by a signed long integer. At
/// construction time, the current position of the motor is consider to be 0. Positive
/// positions are clockwise from the initial position; negative positions are
/// anticlockwise. The current position can be altered for instance after
/// initialization positioning.
///
/// \par Caveats
/// This is an open loop controller: If the motor stalls or is oversped,
/// AccelStepper will not have a correct
/// idea of where the motor really is (since there is no feedback of the motor's
/// real position. We only know where we _think_ it is, relative to the
/// initial starting point).
///
/// \par Performance
/// The fastest motor speed that can be reliably supported is about 4000 steps per
/// second at a clock frequency of 16 MHz on Arduino such as Uno etc.
/// Faster processors can support faster stepping speeds.
/// However, any speed less than that
/// down to very slow speeds (much less than one per second) are also supported,
/// provided the run() function is called frequently enough to step the motor
/// whenever required for the speed set.
/// Calling setAcceleration() is expensive,
/// since it requires a square root to be calculated.
///
/// Gregor Christandl reports that with an Arduino Due and a simple test program,
/// he measured 43163 steps per second using runSpeed(),
/// and 16214 steps per second using run();
class AccelStepper
{
public:
/// \brief Symbolic names for number of pins.
/// Use this in the pins argument the AccelStepper constructor to
/// provide a symbolic name for the number of pins
/// to use.
typedef enum
{
FUNCTION = 0, ///< Use the functional interface, implementing your own driver functions (internal use only)
DRIVER = 1, ///< Stepper Driver, 2 driver pins required
FULL2WIRE = 2, ///< 2 wire stepper, 2 motor pins required
FULL3WIRE = 3, ///< 3 wire stepper, such as HDD spindle, 3 motor pins required
FULL4WIRE = 4, ///< 4 wire full stepper, 4 motor pins required
HALF3WIRE = 6, ///< 3 wire half stepper, such as HDD spindle, 3 motor pins required
HALF4WIRE = 8 ///< 4 wire half stepper, 4 motor pins required
} MotorInterfaceType;
/// Constructor. You can have multiple simultaneous steppers, all moving
/// at different speeds and accelerations, provided you call their run()
/// functions at frequent enough intervals. Current Position is set to 0, target
/// position is set to 0. MaxSpeed and Acceleration default to 1.0.
/// The motor pins will be initialised to OUTPUT mode during the
/// constructor by a call to enableOutputs().
/// \param[in] interface Number of pins to interface to. Integer values are
/// supported, but it is preferred to use the \ref MotorInterfaceType symbolic names.
/// AccelStepper::DRIVER (1) means a stepper driver (with Step and Direction pins).
/// If an enable line is also needed, call setEnablePin() after construction.
/// You may also invert the pins using setPinsInverted().
/// AccelStepper::FULL2WIRE (2) means a 2 wire stepper (2 pins required).
/// AccelStepper::FULL3WIRE (3) means a 3 wire stepper, such as HDD spindle (3 pins required).
/// AccelStepper::FULL4WIRE (4) means a 4 wire stepper (4 pins required).
/// AccelStepper::HALF3WIRE (6) means a 3 wire half stepper, such as HDD spindle (3 pins required)
/// AccelStepper::HALF4WIRE (8) means a 4 wire half stepper (4 pins required)
/// Defaults to AccelStepper::FULL4WIRE (4) pins.
/// \param[in] pin1 Arduino digital pin number for motor pin 1. Defaults
/// to pin 2. For a AccelStepper::DRIVER (interface==1),
/// this is the Step input to the driver. Low to high transition means to step)
/// \param[in] pin2 Arduino digital pin number for motor pin 2. Defaults
/// to pin 3. For a AccelStepper::DRIVER (interface==1),
/// this is the Direction input the driver. High means forward.
/// \param[in] pin3 Arduino digital pin number for motor pin 3. Defaults
/// to pin 4.
/// \param[in] pin4 Arduino digital pin number for motor pin 4. Defaults
/// to pin 5.
/// \param[in] enable If this is true (the default), enableOutputs() will be called to enable
/// the output pins at construction time.
AccelStepper(uint8_t interface = AccelStepper::FULL4WIRE, uint8_t pin1 = 2, uint8_t pin2 = 3, uint8_t pin3 = 4, uint8_t pin4 = 5, bool enable = true);
/// Alternate Constructor which will call your own functions for forward and backward steps.
/// You can have multiple simultaneous steppers, all moving
/// at different speeds and accelerations, provided you call their run()
/// functions at frequent enough intervals. Current Position is set to 0, target
/// position is set to 0. MaxSpeed and Acceleration default to 1.0.
/// Any motor initialization should happen before hand, no pins are used or initialized.
/// \param[in] forward void-returning procedure that will make a forward step
/// \param[in] backward void-returning procedure that will make a backward step
AccelStepper(void (*forward)(), void (*backward)());
/// Set the target position. The run() function will try to move the motor (at most one step per call)
/// from the current position to the target position set by the most
/// recent call to this function. Caution: moveTo() also recalculates the speed for the next step.
/// If you are trying to use constant speed movements, you should call setSpeed() after calling moveTo().
/// \param[in] absolute The desired absolute position. Negative is
/// anticlockwise from the 0 position.
void moveTo(long absolute);
/// Set the target position relative to the current position
/// \param[in] relative The desired position relative to the current position. Negative is
/// anticlockwise from the current position.
void move(long relative);
/// Poll the motor and step it if a step is due, implementing
/// accelerations and decelerations to acheive the target position. You must call this as
/// frequently as possible, but at least once per minimum step time interval,
/// preferably in your main loop. Note that each call to run() will make at most one step, and then only when a step is due,
/// based on the current speed and the time since the last step.
/// \return true if the motor is still running to the target position.
boolean run();
/// Poll the motor and step it if a step is due, implementing a constant
/// speed as set by the most recent call to setSpeed(). You must call this as
/// frequently as possible, but at least once per step interval,
/// \return true if the motor was stepped.
boolean runSpeed();
/// Sets the maximum permitted speed. The run() function will accelerate
/// up to the speed set by this function.
/// Caution: the maximum speed achievable depends on your processor and clock speed.
/// \param[in] speed The desired maximum speed in steps per second. Must
/// be > 0. Caution: Speeds that exceed the maximum speed supported by the processor may
/// Result in non-linear accelerations and decelerations.
void setMaxSpeed(float speed);
/// returns the maximum speed configured for this stepper
/// that was previously set by setMaxSpeed();
/// \return The currently configured maximum speed
float maxSpeed();
/// Sets the acceleration/deceleration rate.
/// \param[in] acceleration The desired acceleration in steps per second
/// per second. Must be > 0.0. This is an expensive call since it requires a square
/// root to be calculated. Dont call more ofthen than needed
void setAcceleration(float acceleration);
/// Sets the desired constant speed for use with runSpeed().
/// \param[in] speed The desired constant speed in steps per
/// second. Positive is clockwise. Speeds of more than 1000 steps per
/// second are unreliable. Very slow speeds may be set (eg 0.00027777 for
/// once per hour, approximately. Speed accuracy depends on the Arduino
/// crystal. Jitter depends on how frequently you call the runSpeed() function.
void setSpeed(float speed);
/// The most recently set speed
/// \return the most recent speed in steps per second
float speed();
/// The distance from the current position to the target position.
/// \return the distance from the current position to the target position
/// in steps. Positive is clockwise from the current position.
long distanceToGo();
/// The most recently set target position.
/// \return the target position
/// in steps. Positive is clockwise from the 0 position.
long targetPosition();
/// The currently motor position.
/// \return the current motor position
/// in steps. Positive is clockwise from the 0 position.
long currentPosition();
/// Resets the current position of the motor, so that wherever the motor
/// happens to be right now is considered to be the new 0 position. Useful
/// for setting a zero position on a stepper after an initial hardware
/// positioning move.
/// Has the side effect of setting the current motor speed to 0.
/// \param[in] position The position in steps of wherever the motor
/// happens to be right now.
void setCurrentPosition(long position);
/// Moves the motor (with acceleration/deceleration)
/// to the target position and blocks until it is at
/// position. Dont use this in event loops, since it blocks.
void runToPosition();
/// Runs at the currently selected speed until the target position is reached
/// Does not implement accelerations.
/// \return true if it stepped
boolean runSpeedToPosition();
/// Moves the motor (with acceleration/deceleration)
/// to the new target position and blocks until it is at
/// position. Dont use this in event loops, since it blocks.
/// \param[in] position The new target position.
void runToNewPosition(long position);
/// Sets a new target position that causes the stepper
/// to stop as quickly as possible, using the current speed and acceleration parameters.
void stop();
/// Disable motor pin outputs by setting them all LOW
/// Depending on the design of your electronics this may turn off
/// the power to the motor coils, saving power.
/// This is useful to support Arduino low power modes: disable the outputs
/// during sleep and then reenable with enableOutputs() before stepping
/// again.
/// If the enable Pin is defined, sets it to OUTPUT mode and clears the pin to disabled.
virtual void disableOutputs();
/// Enable motor pin outputs by setting the motor pins to OUTPUT
/// mode. Called automatically by the constructor.
/// If the enable Pin is defined, sets it to OUTPUT mode and sets the pin to enabled.
virtual void enableOutputs();
/// Sets the minimum pulse width allowed by the stepper driver. The minimum practical pulse width is
/// approximately 20 microseconds. Times less than 20 microseconds
/// will usually result in 20 microseconds or so.
/// \param[in] minWidth The minimum pulse width in microseconds.
void setMinPulseWidth(unsigned int minWidth);
/// Sets the enable pin number for stepper drivers.
/// 0xFF indicates unused (default).
/// Otherwise, if a pin is set, the pin will be turned on when
/// enableOutputs() is called and switched off when disableOutputs()
/// is called.
/// \param[in] enablePin Arduino digital pin number for motor enable
/// \sa setPinsInverted
void setEnablePin(uint8_t enablePin = 0xff);
/// Sets the inversion for stepper driver pins
/// \param[in] directionInvert True for inverted direction pin, false for non-inverted
/// \param[in] stepInvert True for inverted step pin, false for non-inverted
/// \param[in] enableInvert True for inverted enable pin, false (default) for non-inverted
void setPinsInverted(bool directionInvert = false, bool stepInvert = false, bool enableInvert = false);
/// Sets the inversion for 2, 3 and 4 wire stepper pins
/// \param[in] pin1Invert True for inverted pin1, false for non-inverted
/// \param[in] pin2Invert True for inverted pin2, false for non-inverted
/// \param[in] pin3Invert True for inverted pin3, false for non-inverted
/// \param[in] pin4Invert True for inverted pin4, false for non-inverted
/// \param[in] enableInvert True for inverted enable pin, false (default) for non-inverted
void setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert);
/// Checks to see if the motor is currently running to a target
/// \return true if the speed is not zero or not at the target position
bool isRunning();
protected:
/// \brief Direction indicator
/// Symbolic names for the direction the motor is turning
typedef enum
{
DIRECTION_CCW = 0, ///< Counter-Clockwise
DIRECTION_CW = 1 ///< Clockwise
} Direction;
/// Forces the library to compute a new instantaneous speed and set that as
/// the current speed. It is called by
/// the library:
/// \li after each step
/// \li after change to maxSpeed through setMaxSpeed()
/// \li after change to acceleration through setAcceleration()
/// \li after change to target position (relative or absolute) through
/// move() or moveTo()
void computeNewSpeed();
/// Low level function to set the motor output pins
/// bit 0 of the mask corresponds to _pin[0]
/// bit 1 of the mask corresponds to _pin[1]
/// You can override this to impment, for example serial chip output insted of using the
/// output pins directly
virtual void setOutputPins(uint8_t mask);
/// Called to execute a step. Only called when a new step is
/// required. Subclasses may override to implement new stepping
/// interfaces. The default calls step1(), step2(), step4() or step8() depending on the
/// number of pins defined for the stepper.
/// \param[in] step The current step phase number (0 to 7)
virtual void step(long step);
/// Called to execute a step using stepper functions (pins = 0) Only called when a new step is
/// required. Calls _forward() or _backward() to perform the step
/// \param[in] step The current step phase number (0 to 7)
virtual void step0(long step);
/// Called to execute a step on a stepper driver (ie where pins == 1). Only called when a new step is
/// required. Subclasses may override to implement new stepping
/// interfaces. The default sets or clears the outputs of Step pin1 to step,
/// and sets the output of _pin2 to the desired direction. The Step pin (_pin1) is pulsed for 1 microsecond
/// which is the minimum STEP pulse width for the 3967 driver.
/// \param[in] step The current step phase number (0 to 7)
virtual void step1(long step);
/// Called to execute a step on a 2 pin motor. Only called when a new step is
/// required. Subclasses may override to implement new stepping
/// interfaces. The default sets or clears the outputs of pin1 and pin2
/// \param[in] step The current step phase number (0 to 7)
virtual void step2(long step);
/// Called to execute a step on a 3 pin motor, such as HDD spindle. Only called when a new step is
/// required. Subclasses may override to implement new stepping
/// interfaces. The default sets or clears the outputs of pin1, pin2,
/// pin3
/// \param[in] step The current step phase number (0 to 7)
virtual void step3(long step);
/// Called to execute a step on a 4 pin motor. Only called when a new step is
/// required. Subclasses may override to implement new stepping
/// interfaces. The default sets or clears the outputs of pin1, pin2,
/// pin3, pin4.
/// \param[in] step The current step phase number (0 to 7)
virtual void step4(long step);
/// Called to execute a step on a 3 pin motor, such as HDD spindle. Only called when a new step is
/// required. Subclasses may override to implement new stepping
/// interfaces. The default sets or clears the outputs of pin1, pin2,
/// pin3
/// \param[in] step The current step phase number (0 to 7)
virtual void step6(long step);
/// Called to execute a step on a 4 pin half-steper motor. Only called when a new step is
/// required. Subclasses may override to implement new stepping
/// interfaces. The default sets or clears the outputs of pin1, pin2,
/// pin3, pin4.
/// \param[in] step The current step phase number (0 to 7)
virtual void step8(long step);
/// Current direction motor is spinning in
/// Protected because some peoples subclasses need it to be so
boolean _direction; // 1 == CW
private:
/// Number of pins on the stepper motor. Permits 2 or 4. 2 pins is a
/// bipolar, and 4 pins is a unipolar.
uint8_t _interface; // 0, 1, 2, 4, 8, See MotorInterfaceType
/// Arduino pin number assignments for the 2 or 4 pins required to interface to the
/// stepper motor or driver
uint8_t _pin[4];
/// Whether the _pins is inverted or not
uint8_t _pinInverted[4];
/// The current absolution position in steps.
long _currentPos; // Steps
/// The target position in steps. The AccelStepper library will move the
/// motor from the _currentPos to the _targetPos, taking into account the
/// max speed, acceleration and deceleration
long _targetPos; // Steps
/// The current motos speed in steps per second
/// Positive is clockwise
float _speed; // Steps per second
/// The maximum permitted speed in steps per second. Must be > 0.
float _maxSpeed;
/// The acceleration to use to accelerate or decelerate the motor in steps
/// per second per second. Must be > 0
float _acceleration;
float _sqrt_twoa; // Precomputed sqrt(2*_acceleration)
/// The current interval between steps in microseconds.
/// 0 means the motor is currently stopped with _speed == 0
unsigned long _stepInterval;
/// The last step time in microseconds
unsigned long _lastStepTime;
/// The minimum allowed pulse width in microseconds
unsigned int _minPulseWidth;
/// Is the direction pin inverted?
///bool _dirInverted; /// Moved to _pinInverted[1]
/// Is the step pin inverted?
///bool _stepInverted; /// Moved to _pinInverted[0]
/// Is the enable pin inverted?
bool _enableInverted;
/// Enable pin for stepper driver, or 0xFF if unused.
uint8_t _enablePin;
/// The pointer to a forward-step procedure
void (*_forward)();
/// The pointer to a backward-step procedure
void (*_backward)();
/// The step counter for speed calculations
long _n;
/// Initial step size in microseconds
float _c0;
/// Last step size in microseconds
float _cn;
/// Min step size in microseconds based on maxSpeed
float _cmin; // at max speed
};
/// @example Random.pde
/// Make a single stepper perform random changes in speed, position and acceleration
/// @example Overshoot.pde
/// Check overshoot handling
/// which sets a new target position and then waits until the stepper has
/// achieved it. This is used for testing the handling of overshoots
/// @example MultipleSteppers.pde
/// Shows how to multiple simultaneous steppers
/// Runs one stepper forwards and backwards, accelerating and decelerating
/// at the limits. Runs other steppers at the same time
/// @example ConstantSpeed.pde
/// Shows how to run AccelStepper in the simplest,
/// fixed speed mode with no accelerations
/// @example Blocking.pde
/// Shows how to use the blocking call runToNewPosition
/// Which sets a new target position and then waits until the stepper has
/// achieved it.
/// @example AFMotor_MultiStepper.pde
/// Control both Stepper motors at the same time with different speeds
/// and accelerations.
/// @example AFMotor_ConstantSpeed.pde
/// Shows how to run AccelStepper in the simplest,
/// fixed speed mode with no accelerations
/// @example ProportionalControl.pde
/// Make a single stepper follow the analog value read from a pot or whatever
/// The stepper will move at a constant speed to each newly set posiiton,
/// depending on the value of the pot.
/// @example Bounce.pde
/// Make a single stepper bounce from one limit to another, observing
/// accelrations at each end of travel
/// @example Quickstop.pde
/// Check stop handling.
/// Calls stop() while the stepper is travelling at full speed, causing
/// the stepper to stop as quickly as possible, within the constraints of the
/// current acceleration.
/// @example MotorShield.pde
/// Shows how to use AccelStepper to control a 3-phase motor, such as a HDD spindle motor
/// using the Adafruit Motor Shield http://www.ladyada.net/make/mshield/index.html.
/// @example DualMotorShield.pde
/// Shows how to use AccelStepper to control 2 x 2 phase steppers using the
/// Itead Studio Arduino Dual Stepper Motor Driver Shield
/// model IM120417015
#endif

View File

@@ -0,0 +1,3 @@
/*
* Implementation is in BLEStream.h to avoid linker issues.
*/

View File

@@ -0,0 +1,243 @@
/*
BLEStream.h
Based on BLESerial.cpp by Voita Molda
https://github.com/sandeepmistry/arduino-BLEPeripheral/blob/master/examples/serial/BLESerial.h
Last updated April 4th, 2016
*/
#ifndef _BLE_STREAM_H_
#define _BLE_STREAM_H_
#include <Arduino.h>
#if defined(_VARIANT_ARDUINO_101_X_)
#include <CurieBLE.h>
#define _MAX_ATTR_DATA_LEN_ BLE_MAX_ATTR_DATA_LEN
#else
#include <BLEPeripheral.h>
#define _MAX_ATTR_DATA_LEN_ BLE_ATTRIBUTE_MAX_VALUE_LENGTH
#endif
#define BLESTREAM_TXBUFFER_FLUSH_INTERVAL 80
#define BLESTREAM_MIN_FLUSH_INTERVAL 8 // minimum interval for flushing the TX buffer
// #define BLE_SERIAL_DEBUG
class BLEStream : public BLEPeripheral, public Stream
{
public:
BLEStream(unsigned char req = 0, unsigned char rdy = 0, unsigned char rst = 0);
void begin(...);
bool poll();
void end();
void setFlushInterval(int);
virtual int available(void);
virtual int peek(void);
virtual int read(void);
virtual void flush(void);
virtual size_t write(uint8_t byte);
using Print::write;
virtual operator bool();
private:
bool _connected;
unsigned long _flushed;
int _flushInterval;
static BLEStream* _instance;
size_t _rxHead;
size_t _rxTail;
size_t _rxCount() const;
unsigned char _rxBuffer[256];
size_t _txCount;
unsigned char _txBuffer[_MAX_ATTR_DATA_LEN_];
BLEService _uartService = BLEService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
BLEDescriptor _uartNameDescriptor = BLEDescriptor("2901", "UART");
BLECharacteristic _rxCharacteristic = BLECharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse, _MAX_ATTR_DATA_LEN_);
BLEDescriptor _rxNameDescriptor = BLEDescriptor("2901", "RX - Receive Data (Write)");
BLECharacteristic _txCharacteristic = BLECharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, _MAX_ATTR_DATA_LEN_);
BLEDescriptor _txNameDescriptor = BLEDescriptor("2901", "TX - Transfer Data (Notify)");
void _received(const unsigned char* data, size_t size);
static void _received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic);
};
/*
* BLEStream.cpp
* Copied here as a hack to avoid having to install the BLEPeripheral libarary even if it's
* not needed.
*/
BLEStream* BLEStream::_instance = NULL;
BLEStream::BLEStream(unsigned char req, unsigned char rdy, unsigned char rst) :
#if defined(_VARIANT_ARDUINO_101_X_)
BLEPeripheral()
#else
BLEPeripheral(req, rdy, rst)
#endif
{
this->_txCount = 0;
this->_rxHead = this->_rxTail = 0;
this->_flushed = 0;
this->_flushInterval = BLESTREAM_TXBUFFER_FLUSH_INTERVAL;
BLEStream::_instance = this;
addAttribute(this->_uartService);
addAttribute(this->_uartNameDescriptor);
setAdvertisedServiceUuid(this->_uartService.uuid());
addAttribute(this->_rxCharacteristic);
addAttribute(this->_rxNameDescriptor);
this->_rxCharacteristic.setEventHandler(BLEWritten, BLEStream::_received);
addAttribute(this->_txCharacteristic);
addAttribute(this->_txNameDescriptor);
}
void BLEStream::begin(...)
{
BLEPeripheral::begin();
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLEStream::begin()"));
#endif
}
bool BLEStream::poll()
{
// BLEPeripheral::poll is called each time connected() is called
this->_connected = BLEPeripheral::connected();
if (millis() > this->_flushed + this->_flushInterval) {
flush();
}
return this->_connected;
}
void BLEStream::end()
{
this->_rxCharacteristic.setEventHandler(BLEWritten, (void(*)(BLECentral&, BLECharacteristic&))NULL);
this->_rxHead = this->_rxTail = 0;
flush();
BLEPeripheral::disconnect();
}
int BLEStream::available(void)
{
// BLEPeripheral::poll only calls delay(1) in CurieBLE so skipping it here to avoid the delay
#ifndef _VARIANT_ARDUINO_101_X_
// TODO Need to do more testing to determine if all of these calls to BLEPeripheral::poll are
// actually necessary. Seems to run fine without them, but only minimal testing so far.
BLEPeripheral::poll();
#endif
int retval = (this->_rxHead - this->_rxTail + sizeof(this->_rxBuffer)) % sizeof(this->_rxBuffer);
#ifdef BLE_SERIAL_DEBUG
if (retval > 0) {
Serial.print(F("BLEStream::available() = "));
Serial.println(retval);
}
#endif
return retval;
}
int BLEStream::peek(void)
{
#ifndef _VARIANT_ARDUINO_101_X_
BLEPeripheral::poll();
#endif
if (this->_rxTail == this->_rxHead) return -1;
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::peek() = 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
int BLEStream::read(void)
{
#ifndef _VARIANT_ARDUINO_101_X_
BLEPeripheral::poll();
#endif
if (this->_rxTail == this->_rxHead) return -1;
this->_rxTail = (this->_rxTail + 1) % sizeof(this->_rxBuffer);
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::read() = 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
void BLEStream::flush(void)
{
if (this->_txCount == 0) return;
#ifndef _VARIANT_ARDUINO_101_X_
// ensure there are available packets before sending
while(!this->_txCharacteristic.canNotify()) {
BLEPeripheral::poll();
}
#endif
this->_txCharacteristic.setValue(this->_txBuffer, this->_txCount);
this->_flushed = millis();
this->_txCount = 0;
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLEStream::flush()"));
#endif
}
size_t BLEStream::write(uint8_t byte)
{
#ifndef _VARIANT_ARDUINO_101_X_
BLEPeripheral::poll();
#endif
if (this->_txCharacteristic.subscribed() == false) return 0;
this->_txBuffer[this->_txCount++] = byte;
if (this->_txCount == sizeof(this->_txBuffer)) flush();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::write( 0x"));
Serial.print(byte, HEX);
Serial.println(F(") = 1"));
#endif
return 1;
}
BLEStream::operator bool()
{
bool retval = this->_connected = BLEPeripheral::connected();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::operator bool() = "));
Serial.println(retval);
#endif
return retval;
}
void BLEStream::setFlushInterval(int interval)
{
if (interval > BLESTREAM_MIN_FLUSH_INTERVAL) {
this->_flushInterval = interval;
}
}
void BLEStream::_received(const unsigned char* data, size_t size)
{
for (size_t i = 0; i < size; i++) {
this->_rxHead = (this->_rxHead + 1) % sizeof(this->_rxBuffer);
this->_rxBuffer[this->_rxHead] = data[i];
}
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLEStream::received("));
for (int i = 0; i < size; i++) Serial.print(data[i], HEX);
Serial.println(F(")"));
#endif
}
void BLEStream::_received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic)
{
BLEStream::_instance->_received(rxCharacteristic.value(), rxCharacteristic.valueLength());
}
#endif // _BLE_STREAM_H_

View File

@@ -0,0 +1,697 @@
/*
Boards.h - Hardware Abstraction Layer for Firmata library
Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (c) 2013 Norbert Truchsess. All rights reserved.
Copyright (c) 2013-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated December 23rd, 2016
*/
#ifndef Firmata_Boards_h
#define Firmata_Boards_h
#include <inttypes.h>
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h" // for digitalRead, digitalWrite, etc
#else
#include "WProgram.h"
#endif
// Normally Servo.h must be included before Firmata.h (which then includes
// this file). If Servo.h wasn't included, this allows the code to still
// compile, but without support for any Servos. Hopefully that's what the
// user intended by not including Servo.h
#ifndef MAX_SERVOS
#define MAX_SERVOS 0
#endif
/*
Firmata Hardware Abstraction Layer
Firmata is built on top of the hardware abstraction functions of Arduino,
specifically digitalWrite, digitalRead, analogWrite, analogRead, and
pinMode. While these functions offer simple integer pin numbers, Firmata
needs more information than is provided by Arduino. This file provides
all other hardware specific details. To make Firmata support a new board,
only this file should require editing.
The key concept is every "pin" implemented by Firmata may be mapped to
any pin as implemented by Arduino. Usually a simple 1-to-1 mapping is
best, but such mapping should not be assumed. This hardware abstraction
layer allows Firmata to implement any number of pins which map onto the
Arduino implemented pins in almost any arbitrary way.
General Constants:
These constants provide basic information Firmata requires.
TOTAL_PINS: The total number of pins Firmata implemented by Firmata.
Usually this will match the number of pins the Arduino functions
implement, including any pins pins capable of analog or digital.
However, Firmata may implement any number of pins. For example,
on Arduino Mini with 8 analog inputs, 6 of these may be used
for digital functions, and 2 are analog only. On such boards,
Firmata can implement more pins than Arduino's pinMode()
function, in order to accommodate those special pins. The
Firmata protocol supports a maximum of 128 pins, so this
constant must not exceed 128.
TOTAL_ANALOG_PINS: The total number of analog input pins implemented.
The Firmata protocol allows up to 16 analog inputs, accessed
using offsets 0 to 15. Because Firmata presents the analog
inputs using different offsets than the actual pin numbers
(a legacy of Arduino's analogRead function, and the way the
analog input capable pins are physically labeled on all
Arduino boards), the total number of analog input signals
must be specified. 16 is the maximum.
VERSION_BLINK_PIN: When Firmata starts up, it will blink the version
number. This constant is the Arduino pin number where a
LED is connected.
Pin Mapping Macros:
These macros provide the mapping between pins as implemented by
Firmata protocol and the actual pin numbers used by the Arduino
functions. Even though such mappings are often simple, pin
numbers received by Firmata protocol should always be used as
input to these macros, and the result of the macro should be
used with any Arduino function.
When Firmata is extended to support a new pin mode or feature,
a pair of macros should be added and used for all hardware
access. For simple 1:1 mapping, these macros add no actual
overhead, yet their consistent use allows source code which
uses them consistently to be easily adapted to all other boards
with different requirements.
IS_PIN_XXXX(pin): The IS_PIN macros resolve to true or non-zero
if a pin as implemented by Firmata corresponds to a pin
that actually implements the named feature.
PIN_TO_XXXX(pin): The PIN_TO macros translate pin numbers as
implemented by Firmata to the pin numbers needed as inputs
to the Arduino functions. The corresponding IS_PIN macro
should always be tested before using a PIN_TO macro, so
these macros only need to handle valid Firmata pin
numbers for the named feature.
Port Access Inline Funtions:
For efficiency, Firmata protocol provides access to digital
input and output pins grouped by 8 bit ports. When these
groups of 8 correspond to actual 8 bit ports as implemented
by the hardware, these inline functions can provide high
speed direct port access. Otherwise, a default implementation
using 8 calls to digitalWrite or digitalRead is used.
When porting Firmata to a new board, it is recommended to
use the default functions first and focus only on the constants
and macros above. When those are working, if optimized port
access is desired, these inline functions may be extended.
The recommended approach defines a symbol indicating which
optimization to use, and then conditional complication is
used within these functions.
readPort(port, bitmask): Read an 8 bit port, returning the value.
port: The port number, Firmata pins port*8 to port*8+7
bitmask: The actual pins to read, indicated by 1 bits.
writePort(port, value, bitmask): Write an 8 bit port.
port: The port number, Firmata pins port*8 to port*8+7
value: The 8 bit value to write
bitmask: The actual pins to write, indicated by 1 bits.
*/
/*==============================================================================
* Board Specific Configuration
*============================================================================*/
#ifndef digitalPinHasPWM
#define digitalPinHasPWM(p) IS_PIN_DIGITAL(p)
#endif
#if defined(digitalPinToInterrupt) && defined(NOT_AN_INTERRUPT)
#define IS_PIN_INTERRUPT(p) (digitalPinToInterrupt(p) > NOT_AN_INTERRUPT)
#else
#define IS_PIN_INTERRUPT(p) (0)
#endif
// Arduino Duemilanove, Diecimila, and NG
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
#if defined(NUM_ANALOG_INPUTS) && NUM_ANALOG_INPUTS == 6
#define TOTAL_ANALOG_PINS 6
#define TOTAL_PINS 20 // 14 digital + 6 analog
#else
#define TOTAL_ANALOG_PINS 8
#define TOTAL_PINS 22 // 14 digital + 8 analog
#endif
#define VERSION_BLINK_PIN 13
#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) <= 19)
#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) < 14 + TOTAL_ANALOG_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) - 2 < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 18 || (p) == 19)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 14)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
#define ARDUINO_PINOUT_OPTIMIZE 1
// Wiring (and board)
#elif defined(WIRING)
#define VERSION_BLINK_PIN WLED
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= FIRST_ANALOG_PIN && (p) < (FIRST_ANALOG_PIN+TOTAL_ANALOG_PINS))
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - FIRST_ANALOG_PIN)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// old Arduinos
#elif defined(__AVR_ATmega8__)
#define TOTAL_ANALOG_PINS 6
#define TOTAL_PINS 20 // 14 digital + 6 analog
#define VERSION_BLINK_PIN 13
#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) <= 19)
#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) <= 19)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) - 2 < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 18 || (p) == 19)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 14)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
#define ARDUINO_PINOUT_OPTIMIZE 1
// Arduino Mega
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define TOTAL_ANALOG_PINS 16
#define TOTAL_PINS 70 // 54 digital + 16 analog
#define VERSION_BLINK_PIN 13
#define PIN_SERIAL1_RX 19
#define PIN_SERIAL1_TX 18
#define PIN_SERIAL2_RX 17
#define PIN_SERIAL2_TX 16
#define PIN_SERIAL3_RX 15
#define PIN_SERIAL3_TX 14
#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 54 && (p) < TOTAL_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 2 && (p) - 2 < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 20 || (p) == 21)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) > 13 && (p) < 20)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 54)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
// Arduino DUE
#elif defined(__SAM3X8E__)
#define TOTAL_ANALOG_PINS 12
#define TOTAL_PINS 66 // 54 digital + 12 analog
#define VERSION_BLINK_PIN 13
#define PIN_SERIAL1_RX 19
#define PIN_SERIAL1_TX 18
#define PIN_SERIAL2_RX 17
#define PIN_SERIAL2_TX 16
#define PIN_SERIAL3_RX 15
#define PIN_SERIAL3_TX 14
#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 54 && (p) < TOTAL_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 2 && (p) - 2 < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 20 || (p) == 21) // 70 71
#define IS_PIN_SERIAL(p) ((p) > 13 && (p) < 20)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 54)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
// Arduino/Genuino MKR1000
#elif defined(ARDUINO_SAMD_MKR1000)
#define TOTAL_ANALOG_PINS 7
#define TOTAL_PINS 22 // 8 digital + 3 spi + 2 i2c + 2 uart + 7 analog
#define IS_PIN_DIGITAL(p) (((p) >= 0 && (p) <= 21) && !IS_PIN_SERIAL(p))
#define IS_PIN_ANALOG(p) ((p) >= 15 && (p) < 15 + TOTAL_ANALOG_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS) // deprecated since v2.4
#define IS_PIN_I2C(p) ((p) == 11 || (p) == 12) // SDA = 11, SCL = 12
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == PIN_SERIAL1_RX || (p) == PIN_SERIAL1_TX) //defined in variant.h RX = 13, TX = 14
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 15)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p) // deprecated since v2.4
// Arduino MKRZero
#elif defined(ARDUINO_SAMD_MKRZERO)
#define TOTAL_ANALOG_PINS 7
#define TOTAL_PINS 34 // 8 digital + 3 spi + 2 i2c + 2 uart + 7 analog + 3 usb + 1 aref + 5 sd + 1 bottom pad + 1 led + 1 battery adc
#define IS_PIN_DIGITAL(p) ((((p) >= 0 && (p) <= 21) || (p) == 32) && !IS_PIN_SERIAL(p))
#define IS_PIN_ANALOG(p) (((p) >= 15 && (p) < 15 + TOTAL_ANALOG_PINS) || (p) == 33)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS) // deprecated since v2.4
#define IS_PIN_I2C(p) ((p) == 11 || (p) == 12) // SDA = 11, SCL = 12
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == PIN_SERIAL1_RX || (p) == PIN_SERIAL1_TX) //defined in variant.h RX = 13, TX = 14
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 15)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p) // deprecated since v2.4
// Arduino Zero
// Note this will work with an Arduino Zero Pro, but not with an Arduino M0 Pro
// Arduino M0 Pro does not properly map pins to the board labeled pin numbers
#elif defined(_VARIANT_ARDUINO_ZERO_)
#define TOTAL_ANALOG_PINS 6
#define TOTAL_PINS 25 // 14 digital + 6 analog + 2 i2c + 3 spi
#define TOTAL_PORTS 3 // set when TOTAL_PINS > num digitial I/O pins
#define VERSION_BLINK_PIN LED_BUILTIN
//#define PIN_SERIAL1_RX 0 // already defined in zero core variant.h
//#define PIN_SERIAL1_TX 1 // already defined in zero core variant.h
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) <= 19)
#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) < 14 + TOTAL_ANALOG_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS) // deprecated since v2.4
#define IS_PIN_I2C(p) ((p) == 20 || (p) == 21) // SDA = 20, SCL = 21
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK) // SS = A2
#define IS_PIN_SERIAL(p) ((p) == 0 || (p) == 1)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 14)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p) // deprecated since v2.4
// Arduino 101
#elif defined(_VARIANT_ARDUINO_101_X_)
#define TOTAL_ANALOG_PINS NUM_ANALOG_INPUTS
#define TOTAL_PINS NUM_DIGITAL_PINS // 15 digital (including ATN pin) + 6 analog
#define VERSION_BLINK_PIN LED_BUILTIN
#define PIN_SERIAL1_RX 0
#define PIN_SERIAL1_TX 1
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) <= 20)
#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) < 14 + TOTAL_ANALOG_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p) // 3, 5, 6, 9
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS) // deprecated since v2.4
#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL) // SDA = 18, SCL = 19
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == 0 || (p) == 1)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 14)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p) // deprecated since v2.4
// Teensy 1.0
#elif defined(__AVR_AT90USB162__)
#define TOTAL_ANALOG_PINS 0
#define TOTAL_PINS 21 // 21 digital + no analog
#define VERSION_BLINK_PIN 6
#define PIN_SERIAL1_RX 2
#define PIN_SERIAL1_TX 3
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) (0)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == 2 || (p) == 3)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) (0)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// Teensy 2.0
#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
#define TOTAL_ANALOG_PINS 12
#define TOTAL_PINS 25 // 11 digital + 12 analog
#define VERSION_BLINK_PIN 11
#define PIN_SERIAL1_RX 7
#define PIN_SERIAL1_TX 8
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 11 && (p) <= 22)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 5 || (p) == 6)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == 7 || (p) == 8)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) (((p) < 22) ? 21 - (p) : 11)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// Teensy 3.5 and 3.6
// reference: https://github.com/PaulStoffregen/cores/blob/master/teensy3/pins_arduino.h
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define TOTAL_ANALOG_PINS 27 // 3.5 has 27 and 3.6 has 25
#define TOTAL_PINS 70 // 43 digital + 21 analog-digital + 6 analog (64-69)
#define VERSION_BLINK_PIN 13
#define PIN_SERIAL1_RX 0
#define PIN_SERIAL1_TX 1
#define PIN_SERIAL2_RX 9
#define PIN_SERIAL2_TX 10
#define PIN_SERIAL3_RX 7
#define PIN_SERIAL3_TX 8
// The following 2 UARTs are not yet available via SerialFirmata
#define PIN_SERIAL4_RX 31
#define PIN_SERIAL5_TX 32
#define PIN_SERIAL6_RX 34
#define PIN_SERIAL6_TX 33
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) <= 63)
#define IS_PIN_ANALOG(p) (((p) >= 14 && (p) <= 23) || ((p) >= 31 && (p) <= 39) || ((p) >= 49 && (p) <= 50) || ((p) >= 64 && (p) <= 69))
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 18 || (p) == 19)
#define IS_PIN_SERIAL(p) (((p) > 6 && (p) < 11) || ((p) == 0 || (p) == 1))
#define PIN_TO_DIGITAL(p) (p)
// A0-A9 = D14-D23; A12-A20 = D31-D39; A23-A24 = D49-D50; A10-A11 = D64-D65; A21-A22 = D66-D67; A25-A26 = D68-D69
#define PIN_TO_ANALOG(p) (((p) <= 23) ? (p) - 14 : (((p) <= 39) ? (p) - 19 : (((p) <= 50) ? (p) - 26 : (((p) <= 65) ? (p) - 55 : (((p) <= 67) ? (p) - 45 : (p) - 43)))))
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// Teensy 3.0, 3.1 and 3.2
#elif defined(__MK20DX128__) || defined(__MK20DX256__)
#define TOTAL_ANALOG_PINS 14
#define TOTAL_PINS 38 // 24 digital + 10 analog-digital + 4 analog
#define VERSION_BLINK_PIN 13
#define PIN_SERIAL1_RX 0
#define PIN_SERIAL1_TX 1
#define PIN_SERIAL2_RX 9
#define PIN_SERIAL2_TX 10
#define PIN_SERIAL3_RX 7
#define PIN_SERIAL3_TX 8
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) <= 33)
#define IS_PIN_ANALOG(p) (((p) >= 14 && (p) <= 23) || ((p) >= 34 && (p) <= 38))
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 18 || (p) == 19)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) (((p) > 6 && (p) < 11) || ((p) == 0 || (p) == 1))
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) (((p) <= 23) ? (p) - 14 : (p) - 24)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// Teensy-LC
#elif defined(__MKL26Z64__)
#define TOTAL_ANALOG_PINS 13
#define TOTAL_PINS 27 // 27 digital + 13 analog-digital
#define VERSION_BLINK_PIN 13
#define PIN_SERIAL1_RX 0
#define PIN_SERIAL1_TX 1
#define PIN_SERIAL2_RX 9
#define PIN_SERIAL2_TX 10
#define PIN_SERIAL3_RX 7
#define PIN_SERIAL3_TX 8
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) <= 26)
#define IS_PIN_ANALOG(p) ((p) >= 14)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 18 || (p) == 19)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) (((p) > 6 && (p) < 11) || ((p) == 0 || (p) == 1))
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 14)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// Teensy++ 1.0 and 2.0
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
#define TOTAL_ANALOG_PINS 8
#define TOTAL_PINS 46 // 38 digital + 8 analog
#define VERSION_BLINK_PIN 6
#define PIN_SERIAL1_RX 2
#define PIN_SERIAL1_TX 3
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 38 && (p) < TOTAL_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 0 || (p) == 1)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == 2 || (p) == 3)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 38)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// Leonardo
#elif defined(__AVR_ATmega32U4__)
#define TOTAL_ANALOG_PINS 12
#define TOTAL_PINS 30 // 14 digital + 12 analog + 4 SPI (D14-D17 on ISP header)
#define VERSION_BLINK_PIN 13
#define PIN_SERIAL1_RX 0
#define PIN_SERIAL1_TX 1
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 18 && (p) < TOTAL_PINS)
#define IS_PIN_PWM(p) ((p) == 3 || (p) == 5 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 11 || (p) == 13)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 2 || (p) == 3)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == 0 || (p) == 1)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) (p) - 18
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// Intel Galileo Board (gen 1 and 2) and Intel Edison
#elif defined(ARDUINO_LINUX)
#define TOTAL_ANALOG_PINS 6
#define TOTAL_PINS 20 // 14 digital + 6 analog
#define VERSION_BLINK_PIN 13
#define PIN_SERIAL1_RX 0
#define PIN_SERIAL1_TX 1
#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) <= 19)
#define IS_PIN_ANALOG(p) ((p) >= 14 && (p) <= 19)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) - 2 < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == 0 || (p) == 1)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 14)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
// RedBearLab BLE Nano with factory switch settings (S1 - S10)
#elif defined(BLE_NANO)
#define TOTAL_ANALOG_PINS 6
#define TOTAL_PINS 15 // 9 digital + 3 analog
#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) <= 14)
#define IS_PIN_ANALOG(p) ((p) == 8 || (p) == 9 || (p) == 10 || (p) == 11 || (p) == 12 || (p) == 14) //A0~A5
#define IS_PIN_PWM(p) ((p) == 3 || (p) == 5 || (p) == 6)
#define IS_PIN_SERVO(p) ((p) >= 2 && (p) <= 7)
#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL)
#define IS_PIN_SPI(p) ((p) == CS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 8)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
// Pinoccio Scout
// Note: digital pins 9-16 are usable but not labeled on the board numerically.
// SS=9, MOSI=10, MISO=11, SCK=12, RX1=13, TX1=14, SCL=15, SDA=16
#elif defined(ARDUINO_PINOCCIO)
#define TOTAL_ANALOG_PINS 8
#define TOTAL_PINS NUM_DIGITAL_PINS // 32
#define VERSION_BLINK_PIN 23
#define PIN_SERIAL1_RX 13
#define PIN_SERIAL1_TX 14
#define IS_PIN_DIGITAL(p) (((p) >= 2) && ((p) <= 16)) || (((p) >= 24) && ((p) <= 31))
#define IS_PIN_ANALOG(p) ((p) >= 24 && (p) <= 31)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) IS_PIN_DIGITAL(p)
#define IS_PIN_I2C(p) ((p) == SCL || (p) == SDA)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_SERIAL(p) ((p) == 13 || (p) == 14)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 24)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
// Sanguino
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
#define TOTAL_ANALOG_PINS 8
#define TOTAL_PINS 32 // 24 digital + 8 analog
#define VERSION_BLINK_PIN 0
#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 24 && (p) < TOTAL_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 16 || (p) == 17)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 24)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
// Illuminato
#elif defined(__AVR_ATmega645__)
#define TOTAL_ANALOG_PINS 6
#define TOTAL_PINS 42 // 36 digital + 6 analog
#define VERSION_BLINK_PIN 13
#define IS_PIN_DIGITAL(p) ((p) >= 2 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 36 && (p) < TOTAL_PINS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == 4 || (p) == 5)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - 36)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
// ESP8266
// note: boot mode GPIOs 0, 2 and 15 can be used as outputs, GPIOs 6-11 are in use for flash IO
#elif defined(ESP8266)
#define TOTAL_ANALOG_PINS NUM_ANALOG_INPUTS
#define TOTAL_PINS A0 + NUM_ANALOG_INPUTS
#define PIN_SERIAL_RX 3
#define PIN_SERIAL_TX 1
#define IS_PIN_DIGITAL(p) (((p) >= 0 && (p) <= 5) || ((p) >= 12 && (p) < A0))
#define IS_PIN_ANALOG(p) ((p) >= A0 && (p) < A0 + NUM_ANALOG_INPUTS)
#define IS_PIN_PWM(p) digitalPinHasPWM(p)
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_INTERRUPT(p) (digitalPinToInterrupt(p) > NOT_AN_INTERRUPT)
#define IS_PIN_SERIAL(p) ((p) == PIN_SERIAL_RX || (p) == PIN_SERIAL_TX)
#define PIN_TO_DIGITAL(p) (p)
#define PIN_TO_ANALOG(p) ((p) - A0)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) (p)
#define DEFAULT_PWM_RESOLUTION 10
// anything else
#else
#error "Please edit Boards.h with a hardware abstraction for this board"
#endif
// as long this is not defined for all boards:
#ifndef IS_PIN_SPI
#define IS_PIN_SPI(p) (0)
#endif
#ifndef IS_PIN_SERIAL
#define IS_PIN_SERIAL(p) 0
#endif
#ifndef DEFAULT_PWM_RESOLUTION
#define DEFAULT_PWM_RESOLUTION 8
#endif
/*==============================================================================
* readPort() - Read an 8 bit port
*============================================================================*/
static inline unsigned char readPort(byte, byte) __attribute__((always_inline, unused));
static inline unsigned char readPort(byte port, byte bitmask)
{
#if defined(ARDUINO_PINOUT_OPTIMIZE)
if (port == 0) return (PIND & 0xFC) & bitmask; // ignore Rx/Tx 0/1
if (port == 1) return ((PINB & 0x3F) | ((PINC & 0x03) << 6)) & bitmask;
if (port == 2) return ((PINC & 0x3C) >> 2) & bitmask;
return 0;
#else
unsigned char out = 0, pin = port * 8;
if (IS_PIN_DIGITAL(pin + 0) && (bitmask & 0x01) && digitalRead(PIN_TO_DIGITAL(pin + 0))) out |= 0x01;
if (IS_PIN_DIGITAL(pin + 1) && (bitmask & 0x02) && digitalRead(PIN_TO_DIGITAL(pin + 1))) out |= 0x02;
if (IS_PIN_DIGITAL(pin + 2) && (bitmask & 0x04) && digitalRead(PIN_TO_DIGITAL(pin + 2))) out |= 0x04;
if (IS_PIN_DIGITAL(pin + 3) && (bitmask & 0x08) && digitalRead(PIN_TO_DIGITAL(pin + 3))) out |= 0x08;
if (IS_PIN_DIGITAL(pin + 4) && (bitmask & 0x10) && digitalRead(PIN_TO_DIGITAL(pin + 4))) out |= 0x10;
if (IS_PIN_DIGITAL(pin + 5) && (bitmask & 0x20) && digitalRead(PIN_TO_DIGITAL(pin + 5))) out |= 0x20;
if (IS_PIN_DIGITAL(pin + 6) && (bitmask & 0x40) && digitalRead(PIN_TO_DIGITAL(pin + 6))) out |= 0x40;
if (IS_PIN_DIGITAL(pin + 7) && (bitmask & 0x80) && digitalRead(PIN_TO_DIGITAL(pin + 7))) out |= 0x80;
return out;
#endif
}
/*==============================================================================
* writePort() - Write an 8 bit port, only touch pins specified by a bitmask
*============================================================================*/
static inline unsigned char writePort(byte, byte, byte) __attribute__((always_inline, unused));
static inline unsigned char writePort(byte port, byte value, byte bitmask)
{
#if defined(ARDUINO_PINOUT_OPTIMIZE)
if (port == 0) {
bitmask = bitmask & 0xFC; // do not touch Tx & Rx pins
byte valD = value & bitmask;
byte maskD = ~bitmask;
cli();
PORTD = (PORTD & maskD) | valD;
sei();
} else if (port == 1) {
byte valB = (value & bitmask) & 0x3F;
byte valC = (value & bitmask) >> 6;
byte maskB = ~(bitmask & 0x3F);
byte maskC = ~((bitmask & 0xC0) >> 6);
cli();
PORTB = (PORTB & maskB) | valB;
PORTC = (PORTC & maskC) | valC;
sei();
} else if (port == 2) {
bitmask = bitmask & 0x0F;
byte valC = (value & bitmask) << 2;
byte maskC = ~(bitmask << 2);
cli();
PORTC = (PORTC & maskC) | valC;
sei();
}
return 1;
#else
byte pin = port * 8;
if ((bitmask & 0x01)) digitalWrite(PIN_TO_DIGITAL(pin + 0), (value & 0x01));
if ((bitmask & 0x02)) digitalWrite(PIN_TO_DIGITAL(pin + 1), (value & 0x02));
if ((bitmask & 0x04)) digitalWrite(PIN_TO_DIGITAL(pin + 2), (value & 0x04));
if ((bitmask & 0x08)) digitalWrite(PIN_TO_DIGITAL(pin + 3), (value & 0x08));
if ((bitmask & 0x10)) digitalWrite(PIN_TO_DIGITAL(pin + 4), (value & 0x10));
if ((bitmask & 0x20)) digitalWrite(PIN_TO_DIGITAL(pin + 5), (value & 0x20));
if ((bitmask & 0x40)) digitalWrite(PIN_TO_DIGITAL(pin + 6), (value & 0x40));
if ((bitmask & 0x80)) digitalWrite(PIN_TO_DIGITAL(pin + 7), (value & 0x80));
return 1;
#endif
}
#ifndef TOTAL_PORTS
#define TOTAL_PORTS ((TOTAL_PINS + 7) / 8)
#endif
#endif /* Firmata_Boards_h */

View File

@@ -0,0 +1,403 @@
/**
FirmataStepper is a simple non-blocking stepper motor library
for 2 and 4 wire bipolar and unipolar stepper motor drive circuits
as well as EasyDriver (http://schmalzhaus.com/EasyDriver/) and
other step + direction drive circuits.
FirmataStepper (0.3) by Jeff Hoefs
EasyDriver support based on modifications by Chris Coleman
Acceleration / Deceleration algorithms and code based on:
app note: http://www.atmel.com/dyn/resources/prod_documents/doc8017.pdf
source code: http://www.atmel.com/dyn/resources/prod_documents/AVR446.zip
stepMotor function based on Stepper.cpp Stepper library for
Wiring/Arduino created by Tom Igoe, Sebastian Gassner
David Mellis and Noah Shibley.
Relevant notes from Stepper.cpp:
When wiring multiple stepper motors to a microcontroller,
you quickly run out of output pins, with each motor requiring 4 connections.
By making use of the fact that at any time two of the four motor
coils are the inverse of the other two, the number of
control connections can be reduced from 4 to 2.
A slightly modified circuit around a Darlington transistor array or an L293 H-bridge
connects to only 2 microcontroler pins, inverts the signals received,
and delivers the 4 (2 plus 2 inverted ones) output signals required
for driving a stepper motor.
The sequence of control signals for 4 control wires is as follows:
Step C0 C1 C2 C3
1 1 0 1 0
2 0 1 1 0
3 0 1 0 1
4 1 0 0 1
The sequence of controls signals for 2 control wires is as follows
(columns C1 and C2 from above):
Step C0 C1
1 0 1
2 1 1
3 1 0
4 0 0
The circuits can be found at
http://www.arduino.cc/en/Tutorial/Stepper
*/
#include "FirmataStepper.h"
/**
* Constructor.
*
* Configure a stepper for an EasyDriver or other step + direction interface or
* configure a bipolar or unipolar stepper motor for 2 wire drive mode.
* Configure a bipolar or unipolar stepper for 4 wire drive mode.
* @param interface Lower 3 bits:
* The interface type: FirmataStepper::DRIVER,
* FirmataStepper::TWO_WIRE or FirmataStepper::FOUR_WIRE
* Upper 4 bits: Any bits set = use 2 microsecond delay
* @param steps_per_rev The number of steps to make 1 revolution.
* @param first_pin The direction pin (EasyDriver) or the pin attached to the
* 1st motor coil (2 wire drive mode)
* @param second_pin The step pin (EasyDriver) or the pin attached to the 2nd
* motor coil (2 wire drive mode)
* @param motor_pin_3 The pin attached to the 3rd motor coil
* @param motor_pin_4 The pin attached to the 4th motor coil
*/
FirmataStepper::FirmataStepper(byte interface,
int steps_per_rev,
byte pin1,
byte pin2,
byte pin3,
byte pin4)
{
this->step_number = 0; // which step the motor is on
this->direction = 0; // motor direction
this->last_step_time = 0; // time stamp in ms of the last step taken
this->steps_per_rev = steps_per_rev; // total number of steps for this motor
this->running = false;
this->interface = interface & 0x0F; // default to Easy Stepper (or other step + direction driver)
// could update this in future to support additional delays if necessary
if (((interface & 0xF0) >> 4) > 0) {
// high current driver
this->stepDelay = 2; // microseconds
} else {
this->stepDelay = 1; // microseconds
}
this->motor_pin_1 = pin1;
this->motor_pin_2 = pin2;
this->dir_pin = pin1;
this->step_pin = pin2;
// setup the pins on the microcontroller:
pinMode(this->motor_pin_1, OUTPUT);
pinMode(this->motor_pin_2, OUTPUT);
if (this->interface == FirmataStepper::FOUR_WIRE) {
this->motor_pin_3 = pin3;
this->motor_pin_4 = pin4;
pinMode(this->motor_pin_3, OUTPUT);
pinMode(this->motor_pin_4, OUTPUT);
}
this->alpha = PI_2 / this->steps_per_rev;
this->at_x100 = (long)(this->alpha * T1_FREQ * 100);
this->ax20000 = (long)(this->alpha * 20000);
this->alpha_x2 = this->alpha * 2;
}
/**
* Move the stepper a given number of steps at the specified
* speed (rad/sec), acceleration (rad/sec^2) and deceleration (rad/sec^2).
*
* @param steps_to_move The number ofsteps to move the motor
* @param speed Max speed in 0.01*rad/sec
* @param accel [optional] Acceleration in 0.01*rad/sec^2
* @param decel [optional] Deceleration in 0.01*rad/sec^2
*/
void FirmataStepper::setStepsToMove(long steps_to_move, int speed, int accel, int decel)
{
unsigned long maxStepLimit;
unsigned long accelerationLimit;
this->step_number = 0;
this->lastAccelDelay = 0;
this->stepCount = 0;
this->rest = 0;
// ensure steps_to_move is always a positive value
if (steps_to_move < 0) {
this->direction = FirmataStepper::CCW;
steps_to_move = -steps_to_move;
} else {
this->direction = FirmataStepper::CW;
}
this->steps_to_move = steps_to_move;
// set max speed limit, by calc min_delay
// min_delay = (alpha / tt)/w
this->min_delay = this->at_x100 / speed;
// if acceleration or deceleration are not defined
// start in RUN state and do no decelerate
if (accel == 0 || decel == 0) {
this->step_delay = this->min_delay;
this->decel_start = steps_to_move;
this->run_state = FirmataStepper::RUN;
this->accel_count = 0;
this->running = true;
return;
}
// if only moving 1 step
if (steps_to_move == 1) {
// move one step
this->accel_count = -1;
this->run_state = FirmataStepper::DECEL;
this->step_delay = this->min_delay;
this->running = true;
} else if (steps_to_move != 0) {
// set initial step delay
// step_delay = 1/tt * sqrt(2*alpha/accel)
// step_delay = ( tfreq*0.676/100 )*100 * sqrt( (2*alpha*10000000000) / (accel*100) )/10000
this->step_delay = (long)((T1_FREQ_148 * sqrt(alpha_x2 / accel)) * 1000);
// find out after how many steps does the speed hit the max speed limit.
// maxSpeedLimit = speed^2 / (2*alpha*accel)
maxStepLimit = (long)speed * speed / (long)(((long)this->ax20000 * accel) / 100);
// if we hit max spped limit before 0.5 step it will round to 0.
// but in practice we need to move at least 1 step to get any speed at all.
if (maxStepLimit == 0) {
maxStepLimit = 1;
}
// find out after how many steps we must start deceleration.
// n1 = (n1+n2)decel / (accel + decel)
accelerationLimit = (long)((steps_to_move * decel) / (accel + decel));
// we must accelerate at least 1 step before we can start deceleration
if (accelerationLimit == 0) {
accelerationLimit = 1;
}
// use the limit we hit first to calc decel
if (accelerationLimit <= maxStepLimit) {
this->decel_val = accelerationLimit - steps_to_move;
} else {
this->decel_val = -(long)(maxStepLimit * accel) / decel;
}
// we must decelerate at least 1 step to stop
if (this->decel_val == 0) {
this->decel_val = -1;
}
// find step to start deceleration
this->decel_start = steps_to_move + this->decel_val;
// if the max speed is so low that we don't need to go via acceleration state.
if (this->step_delay <= this->min_delay) {
this->step_delay = this->min_delay;
this->run_state = FirmataStepper::RUN;
} else {
this->run_state = FirmataStepper::ACCEL;
}
// reset counter
this->accel_count = 0;
this->running = true;
}
}
bool FirmataStepper::update()
{
bool done = false;
unsigned long newStepDelay = 0;
unsigned long curTimeVal = micros();
unsigned long timeDiff = curTimeVal - this->last_step_time;
if (this->running == true &&
(timeDiff >= this->step_delay || this->run_state == FirmataStepper::STOP)) {
this->last_step_time = curTimeVal;
switch (this->run_state) {
case FirmataStepper::STOP:
this->stepCount = 0;
this->rest = 0;
done = true;
this->running = false;
break;
case FirmataStepper::ACCEL:
updateStepPosition();
this->stepCount++;
this->accel_count++;
newStepDelay = this->step_delay -
(((2 * (long)this->step_delay) + this->rest) / (4 * this->accel_count + 1));
// Keep track of remainder from new_step-delay calculation to incrase accurancy
this->rest = ((2 * (long)this->step_delay) + this->rest) % (4 * this->accel_count + 1);
// check if we should start deceleration
if (this->stepCount >= this->decel_start) {
this->accel_count = this->decel_val;
this->run_state = FirmataStepper::DECEL;
// check if we hit max speed
} else if (newStepDelay <= this->min_delay) {
this->lastAccelDelay = newStepDelay;
newStepDelay = this->min_delay;
this->rest = 0;
this->run_state = FirmataStepper::RUN;
}
break;
case FirmataStepper::RUN:
updateStepPosition();
this->stepCount++;
newStepDelay = this->min_delay;
// if no accel or decel was specified, go directly to STOP state
if ((long)this->stepCount >= this->steps_to_move) {
this->run_state = FirmataStepper::STOP;
// check if we should start deceleration
} else if (this->stepCount >= this->decel_start) {
this->accel_count = this->decel_val;
// start deceleration with same delay that accel ended with
newStepDelay = this->lastAccelDelay;
this->run_state = FirmataStepper::DECEL;
}
break;
case FirmataStepper::DECEL:
updateStepPosition();
this->stepCount++;
this->accel_count++;
newStepDelay = this->step_delay -
(((2 * (long)this->step_delay) + this->rest) / (4 * this->accel_count + 1));
this->rest = ((2 * (long)this->step_delay) + this->rest) % (4 * this->accel_count + 1);
// check if we ar at the last step
if (this->accel_count >= 0) {
this->run_state = FirmataStepper::STOP;
}
break;
}
this->step_delay = newStepDelay;
}
return done;
}
/**
* Update the step position.
* @private
*/
void FirmataStepper::updateStepPosition()
{
// increment or decrement the step number,
// depending on direction:
if (this->direction == FirmataStepper::CW) {
this->step_number++;
if (this->step_number >= this->steps_per_rev) {
this->step_number = 0;
}
} else {
if (this->step_number <= 0) {
this->step_number = this->steps_per_rev;
}
this->step_number--;
}
// step the motor to step number 0, 1, 2, or 3:
stepMotor(this->step_number % 4, this->direction);
}
/**
* Moves the motor forward or backwards.
* @param step_num For 2 or 4 wire configurations, this is the current step in
* the 2 or 4 step sequence.
* @param direction The direction of rotation
*/
void FirmataStepper::stepMotor(byte step_num, byte direction)
{
if (this->interface == FirmataStepper::DRIVER) {
digitalWrite(dir_pin, direction);
delayMicroseconds(this->stepDelay);
digitalWrite(step_pin, LOW);
delayMicroseconds(this->stepDelay);
digitalWrite(step_pin, HIGH);
} else if (this->interface == FirmataStepper::TWO_WIRE) {
switch (step_num) {
case 0: /* 01 */
digitalWrite(motor_pin_1, LOW);
digitalWrite(motor_pin_2, HIGH);
break;
case 1: /* 11 */
digitalWrite(motor_pin_1, HIGH);
digitalWrite(motor_pin_2, HIGH);
break;
case 2: /* 10 */
digitalWrite(motor_pin_1, HIGH);
digitalWrite(motor_pin_2, LOW);
break;
case 3: /* 00 */
digitalWrite(motor_pin_1, LOW);
digitalWrite(motor_pin_2, LOW);
break;
}
} else if (this->interface == FirmataStepper::FOUR_WIRE) {
switch (step_num) {
case 0: // 1010
digitalWrite(motor_pin_1, HIGH);
digitalWrite(motor_pin_2, LOW);
digitalWrite(motor_pin_3, HIGH);
digitalWrite(motor_pin_4, LOW);
break;
case 1: // 0110
digitalWrite(motor_pin_1, LOW);
digitalWrite(motor_pin_2, HIGH);
digitalWrite(motor_pin_3, HIGH);
digitalWrite(motor_pin_4, LOW);
break;
case 2: //0101
digitalWrite(motor_pin_1, LOW);
digitalWrite(motor_pin_2, HIGH);
digitalWrite(motor_pin_3, LOW);
digitalWrite(motor_pin_4, HIGH);
break;
case 3: //1001
digitalWrite(motor_pin_1, HIGH);
digitalWrite(motor_pin_2, LOW);
digitalWrite(motor_pin_3, LOW);
digitalWrite(motor_pin_4, HIGH);
break;
}
}
}
/**
* @return The version number of this library.
*/
byte FirmataStepper::version(void)
{
return 3;
}

Some files were not shown because too many files have changed in this diff Show More