1
0
Fork 0

arduino-0022

This commit is contained in:
Eve Entropia 2011-02-23 21:47:18 +01:00
parent 4f99742f03
commit a9ad0e80a0
803 changed files with 69785 additions and 33024 deletions

View file

@ -0,0 +1,715 @@
//************************************************************************
//* Arduino Test Suite
//* (C) 2010 by Mark Sproul
//* Open source as per standard Arduino code
//*
//* 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.
//*
//* This library 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.
//************************************************************************
//* Aug 31, 2010 <MLS> Started on TestArduino
//* Oct 18, 2010 <MLS> Added memory testing
//************************************************************************
#include <avr/pgmspace.h>
#include <avr/io.h>
#include <avr/eeprom.h>
#include "ArduinoTestSuite.h"
#include "WProgram.h"
#include "HardwareSerial.h"
#include "pins_arduino.h"
#include "avr_cpunames.h"
#if defined(USART3_RX_vect)
#define SERIAL_PORT_COUNT 4
#elif defined(USART1_RX_vect)
#define SERIAL_PORT_COUNT 2
#else
#define SERIAL_PORT_COUNT 1
#endif
//************************************************************************
enum
{
ATS_Manufacturer = 1,
ATS_CPU,
ATS_GCC_version,
ATS_LIBC_version,
ATS_CompiledDate,
ATS_TestSuiteName,
ATS_FreeMemory,
};
unsigned long gTestStartTime;
short gTagIndent;
int gYotalErrors;
int gTestCount;
prog_char gTextMsg_Manufacturer[] PROGMEM = "MANUFACTURER";
prog_char gTextMsg_CPUname[] PROGMEM = "CPU-NAME";
prog_char gTextMsg_GCC_VERSION[] PROGMEM = "GCC-Version";
prog_char gTextMsg_AVR_LIBC[] PROGMEM = "AVR-LibC-Ver";
prog_char gTextMsg_COMPILED_DATE[] PROGMEM = "Compiled-date";
prog_char gTextMsg_TEST_SUITE_NAME[] PROGMEM = "Test-Suite-Name";
prog_char gTextMsg_memoryUsage[] PROGMEM = "Free-memory";
prog_char gTextMsg_dotdotdot[] PROGMEM = "... ";
prog_char gTextMsg_ok[] PROGMEM = "ok";
prog_char gTextMsg_FAIL[] PROGMEM = "FAIL";
prog_char gTextMsg_spaceEqual[] PROGMEM = " = ";
prog_char gTextMsg_info[] PROGMEM = "info.";
prog_char gTextMsg_dashLine[] PROGMEM = "--------------------------";
prog_char gTextMsg_DigitalRW[] PROGMEM = "DigitalReadWrite_";
prog_char gTextMsg_PWMoutput[] PROGMEM = "PWMoutput_";
prog_char gTextMsg_AnalogInput[] PROGMEM = "AnalogInput_";
//************************************************************************
void Serial_print_P(prog_char *flashMemStr)
{
char theChar;
int ii;
ii = 0;
#if (FLASHEND > 0x10000)
while (theChar = pgm_read_byte_far(flashMemStr + ii++))
#else
while (theChar = pgm_read_byte_near(flashMemStr + ii++))
#endif
{
Serial.print(theChar);
}
}
//************************************************************************
void Serial_println_P(prog_char *flashMemStr)
{
Serial_print_P(flashMemStr);
Serial.println();
}
//************************************************************************
//* this is for internal use only, not made pubic to the API
static void ATS_PrintProperty( int propertyTagNum,
char *propertyName,
char *propertyValue)
{
char lineBuffer[64];
strcpy_P(lineBuffer, gTextMsg_info);
switch(propertyTagNum)
{
case 0:
strcat(lineBuffer, propertyName);
break;
case ATS_Manufacturer:
strcat_P(lineBuffer, gTextMsg_Manufacturer);
break;
case ATS_CPU:
strcat_P(lineBuffer, gTextMsg_CPUname);
break;
case ATS_GCC_version:
strcat_P(lineBuffer, gTextMsg_GCC_VERSION);
break;
case ATS_LIBC_version:
strcat_P(lineBuffer, gTextMsg_AVR_LIBC);
break;
case ATS_CompiledDate:
strcat_P(lineBuffer, gTextMsg_COMPILED_DATE);
break;
case ATS_TestSuiteName:
strcat_P(lineBuffer, gTextMsg_TEST_SUITE_NAME);
break;
case ATS_FreeMemory:
strcat_P(lineBuffer, gTextMsg_memoryUsage);
break;
}
while (strlen(lineBuffer) < 20)
{
strcat(lineBuffer, " ");
}
strcat_P(lineBuffer, gTextMsg_spaceEqual);
if (propertyValue != 0)
{
strcat(lineBuffer, propertyValue);
}
Serial.println(lineBuffer);
}
//************************************************************************
void ATS_begin(char *manufName, char *testSuiteName)
{
int freeMemory;
char memoryMsg[48];
gYotalErrors = 0;
gTestCount = 0;
Serial.begin(9600);
delay(1000);
gTestStartTime = millis();
Serial.println();
Serial.println();
Serial.println();
ATS_PrintProperty(ATS_Manufacturer, 0, manufName);
ATS_PrintProperty(ATS_CPU, 0, _AVR_CPU_NAME_);
ATS_PrintProperty(ATS_GCC_version, 0, __VERSION__);
ATS_PrintProperty(ATS_LIBC_version, 0, __AVR_LIBC_VERSION_STRING__);
ATS_PrintProperty(ATS_CompiledDate, 0, __DATE__);
ATS_PrintProperty(ATS_TestSuiteName, 0, testSuiteName);
freeMemory = ATS_GetFreeMemory();
sprintf(memoryMsg, "%d bytes", freeMemory);
ATS_PrintProperty(ATS_FreeMemory, 0, memoryMsg);
randomSeed(analogRead(0));
}
//************************************************************************
void ATS_end()
{
long seconds;
long milliSecs;
Serial_println_P(gTextMsg_dashLine);
// Ran 4 tests in 0.000s
Serial.print("Ran ");
Serial.print(gTestCount);
Serial.print(" tests in ");
seconds = millis() / 1000;
milliSecs = millis() % 1000;
Serial.print(seconds);
Serial.print('.');
Serial.print(milliSecs);
Serial.print('s');
Serial.println();
Serial.println();
if (gYotalErrors == 0)
{
Serial.print("OK");
}
else
{
Serial.print("FAILED (failures=");
Serial.print(gYotalErrors);
Serial.print(")");
}
Serial.println();
//* send control D to terminate (End Of File)
Serial.write(0x04);
}
//************************************************************************
void ATS_PrintTestStatus(char *testString, boolean passed)
{
int sLen;
Serial.print(testString);
sLen = strlen(testString);
while (sLen < 60)
{
Serial.print(' ');
sLen++;
}
Serial_print_P(gTextMsg_dotdotdot);
if (passed)
{
Serial_print_P(gTextMsg_ok);
}
else
{
Serial_print_P(gTextMsg_FAIL);
gYotalErrors++;
}
Serial.println();
gTestCount++;
}
//************************************************************************
//* returns true if no errors, false if there is an error
int ATS_Test_DigitalPinWithHelper(uint8_t digitalPinToTest, uint8_t helperpin)
{
boolean passedOK;
int pinValue;
char testName[64];
char numString[32];
strcpy_P(testName, gTextMsg_DigitalRW);
sprintf(numString, "%02d", digitalPinToTest);
strcat(testName, numString);
passedOK = true;
//* test senario 1
pinMode(digitalPinToTest, OUTPUT);
pinMode(helperpin, INPUT);
digitalWrite(digitalPinToTest, HIGH);
pinValue = digitalRead(helperpin);
if (pinValue != HIGH)
{
passedOK = false;
}
digitalWrite(digitalPinToTest, LOW);
pinValue = digitalRead(helperpin);
if (pinValue != LOW)
{
passedOK = false;
}
//* now reverse the input/output
pinMode(digitalPinToTest, INPUT);
pinMode(helperpin, OUTPUT);
digitalWrite(helperpin, HIGH);
pinValue = digitalRead(digitalPinToTest);
if (pinValue != HIGH)
{
passedOK = false;
}
digitalWrite(helperpin, LOW);
pinValue = digitalRead(digitalPinToTest);
if (pinValue != LOW)
{
passedOK = false;
}
if (! passedOK)
{
sprintf(numString, " (helper pin=%02d)", helperpin);
strcat(testName, numString);
}
ATS_PrintTestStatus(testName, passedOK);
return(passedOK);
}
//************************************************************************
boolean ATS_Test_DigitalPin(uint8_t digitalPinToTest)
{
boolean passedOK;
uint8_t helperpin;
if ((digitalPinToTest % 2) == 0)
{
//* if its EVEN, add 1
helperpin = digitalPinToTest + 1;
}
else
{
//* if its ODD
helperpin = digitalPinToTest - 1;
}
passedOK = ATS_Test_DigitalPinWithHelper(digitalPinToTest, helperpin);
return(passedOK);
}
//************************************************************************
//* returns true if no errors, false if there is an error
int ATS_TestTimer( uint8_t timerPinNumber,
uint8_t inputPin,
char *statusString,
char *errorString)
{
boolean passedOK;
unsigned long loopCounter;
unsigned long lowCount;
unsigned long highCount;
unsigned long startTime;
int percentLow;
int percentHigh;
int pinValue;
char numString[48];
int pwmValue;
pwmValue = 128;
loopCounter = 0;
lowCount = 0;
highCount = 0;
passedOK = true;
startTime = millis();
pinMode(inputPin, INPUT);
analogWrite(timerPinNumber, pwmValue);
while ((millis() - startTime) < 500)
{
pinValue = digitalRead(inputPin);
if (pinValue == HIGH)
{
highCount++;
}
else
{
lowCount++;
}
}
analogWrite(timerPinNumber, 0);
//* the difference should be about 50%
percentLow = lowCount / ((lowCount + highCount) / 100);
percentHigh = highCount / ((lowCount + highCount) / 100);
if ((percentLow > 45) && (percentLow < 55))
{
passedOK = true;
}
else
{
passedOK = false;
strcat(errorString, " PWM ERROR");
}
sprintf(numString, " (PWM=%02d %d%% LOW %d%% HIGH)", pwmValue, percentLow, percentHigh);
strcat(statusString, numString);
return(passedOK);
}
//************************************************************************
//* returns true if no errors, false if there is an error
boolean ATS_Test_PWMPinWithHelper(uint8_t pwmPinToTest, uint8_t helperpin)
{
boolean passedOK;
char testName[64];
char errorString[48];
char numString[8];
uint8_t timerNumber;
strcpy_P(testName, gTextMsg_PWMoutput);
sprintf(numString, "%02d", pwmPinToTest);
strcat(testName, numString);
passedOK = true;
errorString[0] = 0;
//* is pin1 a timer?
timerNumber = digitalPinToTimer(pwmPinToTest);
if (timerNumber != NOT_ON_TIMER)
{
passedOK = ATS_TestTimer(pwmPinToTest, helperpin, testName, errorString);
}
else
{
//* we should not get here
passedOK = false;
}
ATS_PrintTestStatus(testName, passedOK);
return(passedOK);
}
//************************************************************************
boolean ATS_Test_PWM_Pin(uint8_t pwmPinToTest)
{
boolean passedOK;
uint8_t helperpin;
if ((pwmPinToTest % 2) == 0)
{
//* if its EVEN, add 1
helperpin = pwmPinToTest + 1;
}
else
{
//* if its ODD
helperpin = pwmPinToTest - 1;
}
passedOK = ATS_Test_PWMPinWithHelper(pwmPinToTest, helperpin);
return(passedOK);
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define kAnalogPinOffset 54
#else
#define kAnalogPinOffset 14
#endif
//************************************************************************
boolean ATS_Test_AnalogInputWithHelper(uint8_t analogPintoTest, uint8_t helperPin)
{
boolean passedOK;
char testName[64];
char infoString[48];
int analogValueHigh;
int analogValueLow;
//* first we have to set the ANALOG pin to INPUT
pinMode(analogPintoTest + kAnalogPinOffset, INPUT);
passedOK = true;
strcpy_P(testName, gTextMsg_AnalogInput);
sprintf(infoString, "%02d", analogPintoTest);
strcat(testName, infoString);
pinMode(helperPin, OUTPUT);
digitalWrite(helperPin, LOW);
analogValueLow = analogRead(analogPintoTest);
if (analogValueLow > 100)
{
passedOK = false;
}
digitalWrite(helperPin, HIGH);
analogValueHigh = analogRead(analogPintoTest);
if (analogValueHigh < 1000)
{
passedOK = false;
}
sprintf(infoString, " (Low=%4d High=%4d helper pin=%d)", analogValueLow, analogValueHigh, helperPin);
strcat(testName, infoString);
ATS_PrintTestStatus(testName, passedOK);
return(passedOK);
}
//************************************************************************
boolean ATS_Test_AnalogInput(uint8_t analogPinToTest)
{
boolean passedOK;
uint8_t helperpin;
if ((analogPinToTest % 2) == 0)
{
//* if its EVEN, add 1
helperpin = kAnalogPinOffset + analogPinToTest + 1;
}
else
{
//* if its ODD
helperpin = kAnalogPinOffset + analogPinToTest - 1;
}
passedOK = ATS_Test_AnalogInputWithHelper(analogPinToTest, helperpin);
return(passedOK);
}
#define kSerialTestBaudRate 9600
#define kSerialTestDelay 3
#if (SERIAL_PORT_COUNT > 1) && !defined(__AVR_ATmega32U4__)
//************************************************************************
//* retunrs 0 if no errors, 1 if an error occured
short ATS_TestSerialLoopback(HardwareSerial *theSerialPort, char *serialPortName)
{
char xmitChar;
char rcvChar;
short ii;
short serialErrCt;
short timeOutLoopCtr;
serialErrCt = 1;
if (theSerialPort != 0)
{
serialErrCt = 0;
theSerialPort->begin(kSerialTestBaudRate);
for (ii=0; ii<150; ii++)
{
xmitChar = ii;
theSerialPort->print(xmitChar);
timeOutLoopCtr = 0;
//* wait for data to come back or timeout
while (!theSerialPort->available() && (timeOutLoopCtr < kSerialTestDelay))
{
delay(1);
timeOutLoopCtr++;
}
if (theSerialPort->available())
{
//* get the char
rcvChar = theSerialPort->read();
if (rcvChar != xmitChar)
{
serialErrCt = 1;
}
}
else
{
serialErrCt = 1;
}
}
theSerialPort->end();
if (serialErrCt == 0)
{
ATS_PrintTestStatus(serialPortName, PASSED);
}
else
{
ATS_PrintTestStatus(serialPortName, FAILED);
}
}
return(serialErrCt);
}
#endif
//************************************************************************
boolean ATS_Test_EEPROM(void)
{
boolean passedOK;
uint8_t dataByte;
uint8_t dataByteRead;
uint16_t dataWord;
uint16_t dataWordRead;
uint32_t dataLongWord;
uint32_t dataLongWordRead;
int addressPtr;
char reportString[48];
passedOK = true;
//* test BYTE read/write
addressPtr = random(E2END);
dataByte = 0x5A;
eeprom_write_byte((uint8_t *)addressPtr, dataByte);
dataByteRead = eeprom_read_byte((uint8_t *)addressPtr);
sprintf(reportString, "EEPROM_byte_rw (addr= 0x%04X)", addressPtr);
if (dataByteRead == dataByte)
{
ATS_PrintTestStatus(reportString, PASSED);
}
else
{
ATS_PrintTestStatus(reportString, FAILED);
passedOK = false;
}
//* test WORD read/write
addressPtr = random(E2END);
dataWord = 0xA55A;
eeprom_write_word((uint16_t *)addressPtr, dataWord);
dataWordRead = eeprom_read_word((uint16_t *)addressPtr);
sprintf(reportString, "EEPROM_word_rw (addr= 0x%04X)", addressPtr);
if (dataWordRead == dataWord)
{
ATS_PrintTestStatus(reportString, PASSED);
}
else
{
ATS_PrintTestStatus(reportString, FAILED);
passedOK = false;
}
//* test Long WORD read/write
addressPtr = random(E2END);
dataLongWord = 0x5AA5A55A;
eeprom_write_dword((uint32_t *)addressPtr, dataLongWord);
dataLongWordRead = eeprom_read_dword((uint32_t *)addressPtr);
sprintf(reportString, "EEPROM_dword_rw (addr= 0x%04X)", addressPtr);
if (dataLongWordRead == dataLongWord)
{
ATS_PrintTestStatus(reportString, PASSED);
}
else
{
ATS_PrintTestStatus(reportString, FAILED);
passedOK = false;
}
return(passedOK);
}
//************************************************************************
extern unsigned int __data_start;
extern unsigned int __data_end;
extern unsigned int __bss_start;
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;
//************************************************************************
int ATS_GetFreeMemory()
{
int free_memory;
if((int)__brkval == 0)
{
free_memory = ((int)&free_memory) - ((int)&__bss_end);
}
else
{
free_memory = ((int)&free_memory) - ((int)__brkval);
}
return free_memory;
}

View file

@ -0,0 +1,74 @@
//************************************************************************
//************************************************************************
//* Aug 31, 2010 <MLS> Started on TestArduino
//************************************************************************
#ifndef _AVR_IO_H_
#include <avr/io.h>
#endif
#ifndef WProgram_h
#include "WProgram.h"
#endif
#ifndef HardwareSerial_h
#include "HardwareSerial.h"
#endif
#if defined(USART3_RX_vect)
#define SERIAL_PORT_COUNT 4
#elif defined(USART1_RX_vect)
#define SERIAL_PORT_COUNT 2
#else
#define SERIAL_PORT_COUNT 1
#endif
void ATS_begin(char *manufName, char *testSuiteName);
void ATS_end();
void ATS_PrintTestStatus(char *testString, boolean passed);
boolean ATS_Test_DigitalPin(uint8_t digitalPinToTest);
boolean ATS_Test_PWM_Pin(uint8_t digitalPinToTest);
boolean ATS_Test_AnalogInput(uint8_t analogPintoTest);
boolean ATS_Test_EEPROM(void);
short ATS_TestSerialLoopback(HardwareSerial *theSerialPort, char *serialPortName);
int ATS_GetFreeMemory();
//************************************************************************
//* this has to be an inline function because calling subroutines affects free memory
inline void ATS_ReportMemoryUsage(int _memoryUsageAtStart)
{
int freeMemoryAtEnd;
int lostMemory;
boolean memoryOK;
char memoryUsage[48];
freeMemoryAtEnd = ATS_GetFreeMemory();
lostMemory = _memoryUsageAtStart - freeMemoryAtEnd;
if (lostMemory == 0)
{
strcpy(memoryUsage, "Memory Usage");
memoryOK = true;
}
else
{
sprintf(memoryUsage, "Memory Usage (lost %d bytes)", lostMemory);
memoryOK = false;
}
ATS_PrintTestStatus(memoryUsage, memoryOK);
}
extern unsigned long gTestStartTime;
extern int gYotalErrors;
extern int gTestCount;
#define PASSED true
#define FAILED false

View file

@ -0,0 +1,186 @@
//**************************************************************************************************
//*
//* Atmel AVR CPU name strings
//*
//**************************************************************************************************
//* Sep 19, 2010 <MLS> Started on avr_cpunames.h
//**************************************************************************************************
//#include "avr_cpunames.h"
//**************************************************************************************************
#if defined (__AVR_AT94K__)
#define _AVR_CPU_NAME_ "AT94k"
#elif defined (__AVR_AT43USB320__)
#elif defined (__AVR_AT43USB355__)
#elif defined (__AVR_AT76C711__)
#elif defined (__AVR_AT86RF401__)
#elif defined (__AVR_AT90PWM1__)
#elif defined (__AVR_AT90PWM2__)
#elif defined (__AVR_AT90PWM2B__)
#elif defined (__AVR_AT90PWM3__)
#elif defined (__AVR_AT90PWM3B__)
#elif defined (__AVR_AT90PWM216__)
#elif defined (__AVR_AT90PWM316__)
#elif defined (__AVR_ATmega32C1__)
#elif defined (__AVR_ATmega32M1__)
#elif defined (__AVR_ATmega32U4__)
#define _AVR_CPU_NAME_ "ATmega32U4"
#elif defined (__AVR_ATmega32U6__)
#define _AVR_CPU_NAME_ "ATmega32U6"
#elif defined (__AVR_ATmega128__)
#define _AVR_CPU_NAME_ "Atmega128"
#elif defined (__AVR_ATmega1280__)
#define _AVR_CPU_NAME_ "ATmega1280"
#elif defined (__AVR_ATmega1281__)
#define _AVR_CPU_NAME_ "ATmega1281"
#elif defined (__AVR_ATmega1284P__)
#define _AVR_CPU_NAME_ "ATmega1284"
#elif defined (__AVR_ATmega2560__)
#define _AVR_CPU_NAME_ "ATmega2560"
#elif defined (__AVR_ATmega2561__)
#define _AVR_CPU_NAME_ "ATmega2561"
#elif defined (__AVR_AT90CAN32__)
#define _AVR_CPU_NAME_ "AT90CAN32"
#elif defined (__AVR_AT90CAN64__)
#define _AVR_CPU_NAME_ "AT90CAN64"
#elif defined (__AVR_AT90CAN128__)
#define _AVR_CPU_NAME_ "AT90CAN128"
#elif defined (__AVR_AT90USB82__)
#define _AVR_CPU_NAME_ "AT90USB82"
#elif defined (__AVR_AT90USB162__)
#define _AVR_CPU_NAME_ "AT90USB162"
#elif defined (__AVR_AT90USB646__)
#define _AVR_CPU_NAME_ "AT90USB646"
#elif defined (__AVR_AT90USB647__)
#define _AVR_CPU_NAME_ "AT90USB647"
#elif defined (__AVR_AT90USB1286__)
#define _AVR_CPU_NAME_ "AT90USB1286"
#elif defined (__AVR_AT90USB1287__)
#define _AVR_CPU_NAME_ "AT90USB1287"
#elif defined (__AVR_ATmega64__)
#define _AVR_CPU_NAME_ "ATmega64"
#elif defined (__AVR_ATmega640__)
#define _AVR_CPU_NAME_ "ATmega640"
#elif defined (__AVR_ATmega644__)
#define _AVR_CPU_NAME_ "ATmega644"
#elif defined (__AVR_ATmega644P__)
#define _AVR_CPU_NAME_ "ATmega644P"
#elif defined (__AVR_ATmega645__)
#define _AVR_CPU_NAME_ "ATmega645"
#elif defined (__AVR_ATmega6450__)
#define _AVR_CPU_NAME_ "ATmega6450"
#elif defined (__AVR_ATmega649__)
#define _AVR_CPU_NAME_ "ATmega649"
#elif defined (__AVR_ATmega6490__)
#define _AVR_CPU_NAME_ "ATmega6490"
#elif defined (__AVR_ATmega103__)
#define _AVR_CPU_NAME_ "ATmega103"
#elif defined (__AVR_ATmega32__)
#define _AVR_CPU_NAME_ "Atmega32"
#elif defined (__AVR_ATmega323__)
#define _AVR_CPU_NAME_ "ATmega323"
#elif defined (__AVR_ATmega324P__)
#define _AVR_CPU_NAME_ "ATmega324P"
#elif defined (__AVR_ATmega325__)
#define _AVR_CPU_NAME_ "ATmega325"
#elif defined (__AVR_ATmega325P__)
#define _AVR_CPU_NAME_ "ATmega325P"
#elif defined (__AVR_ATmega3250__)
#define _AVR_CPU_NAME_ "ATmega3250"
#elif defined (__AVR_ATmega3250P__)
#define _AVR_CPU_NAME_ "ATmega3250P"
#elif defined (__AVR_ATmega328P__)
#define _AVR_CPU_NAME_ "ATmega328P"
#elif defined (__AVR_ATmega329__)
#define _AVR_CPU_NAME_ "ATmega329"
#elif defined (__AVR_ATmega329P__)
#define _AVR_CPU_NAME_ "ATmega329P"
#elif defined (__AVR_ATmega3290__)
#define _AVR_CPU_NAME_ "ATmega3290"
#elif defined (__AVR_ATmega3290P__)
#define _AVR_CPU_NAME_ "ATmega3290P"
#elif defined (__AVR_ATmega32HVB__)
#define _AVR_CPU_NAME_ "ATmega32HVB"
#elif defined (__AVR_ATmega406__)
#define _AVR_CPU_NAME_ "ATmega406"
#elif defined (__AVR_ATmega16__)
#define _AVR_CPU_NAME_ "Atmega16"
#elif defined (__AVR_ATmega161__)
#define _AVR_CPU_NAME_ "ATmega161"
#elif defined (__AVR_ATmega162__)
#define _AVR_CPU_NAME_ "ATmega162"
#elif defined (__AVR_ATmega163__)
#define _AVR_CPU_NAME_ "ATmega163"
#elif defined (__AVR_ATmega164P__)
#define _AVR_CPU_NAME_ "ATmega164P"
#elif defined (__AVR_ATmega165__)
#define _AVR_CPU_NAME_ "ATmega165"
#elif defined (__AVR_ATmega165P__)
#define _AVR_CPU_NAME_ "ATmega165P"
#elif defined (__AVR_ATmega168__)
#define _AVR_CPU_NAME_ "ATmega168"
#elif defined (__AVR_ATmega168P__)
#define _AVR_CPU_NAME_ "ATmega168P"
#elif defined (__AVR_ATmega169__)
#define _AVR_CPU_NAME_ "Atmega169"
#elif defined (__AVR_ATmega169P__)
#define _AVR_CPU_NAME_ "ATmega169P"
#elif defined (__AVR_ATmega8HVA__)
#define _AVR_CPU_NAME_ "ATmega8HVA"
#elif defined (__AVR_ATmega16HVA__)
#define _AVR_CPU_NAME_ "ATmega16HVA"
#elif defined (__AVR_ATmega8__)
#define _AVR_CPU_NAME_ "ATmega8"
#elif defined (__AVR_ATmega48__)
#define _AVR_CPU_NAME_ "ATmega48"
#elif defined (__AVR_ATmega48P__)
#define _AVR_CPU_NAME_ "ATmega48P"
#elif defined (__AVR_ATmega88__)
#define _AVR_CPU_NAME_ "ATmega88"
#elif defined (__AVR_ATmega88P__)
#define _AVR_CPU_NAME_ "ATmega88P"
#elif defined (__AVR_ATmega8515__)
#define _AVR_CPU_NAME_ "ATmega8515"
#elif defined (__AVR_ATmega8535__)
#define _AVR_CPU_NAME_ "ATmega8535"
#elif defined (__AVR_AT90S8535__)
#elif defined (__AVR_AT90C8534__)
#elif defined (__AVR_AT90S8515__)
#elif defined (__AVR_AT90S4434__)
#elif defined (__AVR_AT90S4433__)
#elif defined (__AVR_AT90S4414__)
#elif defined (__AVR_ATtiny22__)
#elif defined (__AVR_ATtiny26__)
#elif defined (__AVR_AT90S2343__)
#elif defined (__AVR_AT90S2333__)
#elif defined (__AVR_AT90S2323__)
#elif defined (__AVR_AT90S2313__)
#elif defined (__AVR_ATtiny2313__)
#define _AVR_CPU_NAME_ "ATtiny2313"
#elif defined (__AVR_ATtiny13__)
#elif defined (__AVR_ATtiny13A__)
#elif defined (__AVR_ATtiny25__)
#elif defined (__AVR_ATtiny45__)
#elif defined (__AVR_ATtiny85__)
#elif defined (__AVR_ATtiny24__)
#elif defined (__AVR_ATtiny44__)
#elif defined (__AVR_ATtiny84__)
#elif defined (__AVR_ATtiny261__)
#elif defined (__AVR_ATtiny461__)
#elif defined (__AVR_ATtiny861__)
#elif defined (__AVR_ATtiny43U__)
#elif defined (__AVR_ATtiny48__)
#elif defined (__AVR_ATtiny88__)
#elif defined (__AVR_ATtiny167__)
#else
#error cpu not defined
#endif
#if !defined (_AVR_CPU_NAME_)
// #define _AVR_CPU_NAME_ "UNKNOWN"
#endif

View file

@ -0,0 +1,76 @@
//************************************************************************
//* Arduino Test of Arduino Constants
//* (C) 2010 by Rick Anderson
//* Open source as per standard Arduino code
//*
//************************************************************************
//* Oct 16, 2010 <ROA> Test of Arduino Constants
//************************************************************************
#include "WProgram.h"
#include "HardwareSerial.h"
#include <ArduinoTestSuite.h>
//************************************************************************
void setup()
{
int startMemoryUsage;
//Start memory usage must be site prior to ATS_begin
startMemoryUsage = ATS_GetFreeMemory();
ATS_begin("Arduino", "Test of Arduino Constants");
/*
* Test Run Start
*/
//test true constant
ATS_PrintTestStatus("1. Test of true constant", true == 1);
//test false consts
ATS_PrintTestStatus( "2. Test of false constant", false == 0);
//Test of HIGH == 1
ATS_PrintTestStatus( "3. Test of HIGH == 1", HIGH == 1);
//Test of LOW == 0
ATS_PrintTestStatus( "4. Test of LOW == 0", LOW == 0);
//Test of INPUT == 1
ATS_PrintTestStatus( "5. Test of INPUT == 1", HIGH == 1);
//Test of OUTPUT == 0
ATS_PrintTestStatus( "6. Test of OUTPUT == 0", LOW == 0);
//test decimal
ATS_PrintTestStatus( "7. Test of decimal constant", 101 == ((1 * pow(10,2)) + (0 * pow(10,1)) + 1));
//test binary
ATS_PrintTestStatus( "8. Test of binary constant", B101 == 5);
//test octal
ATS_PrintTestStatus( "9. Test of octal constant", 0101 == 65);
//test hexadecimal
ATS_PrintTestStatus( "7. Test of hexadecimal constant", (0x101 == 257));
/*
* Test Run End
*/
ATS_ReportMemoryUsage(startMemoryUsage);
ATS_end();
}
//************************************************************************
void loop()
{
}

View file

@ -0,0 +1 @@
//************************************************************************ //* Arduino Test Suite //* ATS_ToneTest //* //* Copyright (c) 2010 Mark Sproul All right 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. //* //* This library 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 this library; if not, write to the Free Software //* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA //************************************************************************ //* Aug 31, 2010 <MLS> Started on TestArduino //* Oct 28, 2010 <MLS> Started on Delay //************************************************************************ #include "WProgram.h" #include "HardwareSerial.h" #include <ArduinoTestSuite.h> //************************************************************************ void setup() { short ii; short testNum; int startMemoryUsage; unsigned long startMillis; unsigned long endMillis; unsigned long deltaMillis; unsigned long errMillis; boolean passed; char testNameString[80]; startMemoryUsage = ATS_GetFreeMemory(); ATS_begin("Arduino", "DelayTest"); testNum = 1; //* we start at 2 because 0/1 are RXD/TXD for (ii=0; ii<1000; ii+= 15) { startMillis = millis(); delay(ii); endMillis = millis(); deltaMillis = endMillis - startMillis; if (deltaMillis >= ii) { errMillis = deltaMillis - ii; } else { errMillis = ii - deltaMillis; } if (errMillis <= 1) { passed = true; } else { passed = false; } sprintf(testNameString, "DelayTest.%02d (delay= %4d actual delay=%ld err=%ld)", testNum, ii, deltaMillis, errMillis); ATS_PrintTestStatus(testNameString, passed); testNum++; } ATS_ReportMemoryUsage(startMemoryUsage); ATS_end(); } //************************************************************************ void loop() { }

View file

@ -0,0 +1,94 @@
//************************************************************************
//* Arduino Test Suite
//* (C) 2010 by Mark Sproul
//* Open source as per standard Arduino code
//*
//************************************************************************
//* Aug 31, 2010 <MLS> Started on TestArduino
//* Oct 18, 2010 <MLS> Added memory testing
//************************************************************************
#include "WProgram.h"
#include "HardwareSerial.h"
#include "pins_arduino.h"
#include <ArduinoTestSuite.h>
#include "avr_cpunames.h"
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
#define kBoard_PinCount 20
#define kBoard_AnalogCount 6
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define kBoard_PinCount 70
#define kBoard_AnalogCount 16
#endif
//************************************************************************
void setup()
{
short ii;
uint8_t timerNumber;
int startMemoryUsage;
startMemoryUsage = ATS_GetFreeMemory();
ATS_begin("Arduino", "general");
//* test digital pins
//* we start at 2 because 0/1 are RXD/TXD
for (ii=2; ii<kBoard_PinCount; ii++)
{
ATS_Test_DigitalPin(ii);
}
//* test PWM pins
//* we start at 2 because 0/1 are RXD/TXD
for (ii=2; ii<kBoard_PinCount; ii++)
{
timerNumber = digitalPinToTimer(ii);
if (timerNumber != NOT_ON_TIMER)
{
ATS_Test_PWM_Pin(ii);
}
}
for (ii=0; ii<kBoard_AnalogCount; ii++)
{
ATS_Test_AnalogInput(ii);
}
#if (SERIAL_PORT_COUNT > 1)
ATS_TestSerialLoopback(&Serial1, "Serial1");
#endif
#if (SERIAL_PORT_COUNT > 2)
ATS_TestSerialLoopback(&Serial2, "Serial2");
#endif
#if (SERIAL_PORT_COUNT > 3)
ATS_TestSerialLoopback(&Serial3, "Serial3");
#endif
ATS_Test_EEPROM();
ATS_ReportMemoryUsage(startMemoryUsage);
ATS_end();
}
//************************************************************************
void loop()
{
}

View file

@ -0,0 +1,106 @@
// Tests writing to and reading from a file, in particular the
// the Stream implementation (e.g. read() and peek()).
#include <SD.h>
#include <ArduinoTestSuite.h>
void setup()
{
int startMemoryUsage = ATS_GetFreeMemory();
boolean b;
File f;
ATS_begin("Arduino", "SD Test");
ATS_PrintTestStatus("SD.begin()", b = SD.begin(4));
if (!b) goto done;
SD.remove("test.txt");
f = SD.open("test.txt", FILE_WRITE);
ATS_PrintTestStatus("SD.open()", f);
if (!f) goto done;
f.print("abc");
f.print("de");
f.close();
f = SD.open("test.txt", FILE_WRITE);
ATS_PrintTestStatus("SD.open()", f);
if (!f) goto done;
f.print("fgh");
f.close();
f = SD.open("test.txt");
ATS_PrintTestStatus("SD.open()", f);
if (!f) goto done;
ATS_PrintTestStatus("read()", f.read() == 'a');
ATS_PrintTestStatus("peek()", f.peek() == 'b');
ATS_PrintTestStatus("read()", f.read() == 'b');
ATS_PrintTestStatus("read()", f.read() == 'c');
ATS_PrintTestStatus("peek()", f.peek() == 'd');
ATS_PrintTestStatus("peek()", f.peek() == 'd');
ATS_PrintTestStatus("peek()", f.peek() == 'd');
ATS_PrintTestStatus("peek()", f.peek() == 'd');
ATS_PrintTestStatus("read()", f.read() == 'd');
ATS_PrintTestStatus("available()", f.available() != 0);
ATS_PrintTestStatus("read()", f.read() == 'e');
ATS_PrintTestStatus("available()", f.available() != 0);
ATS_PrintTestStatus("peek()", f.peek() == 'f');
ATS_PrintTestStatus("read()", f.read() == 'f');
ATS_PrintTestStatus("peek()", f.peek() == 'g');
ATS_PrintTestStatus("available()", f.available() != 0);
ATS_PrintTestStatus("peek()", f.peek() == 'g');
ATS_PrintTestStatus("read()", f.read() == 'g');
ATS_PrintTestStatus("available()", f.available() != 0);
ATS_PrintTestStatus("available()", f.available() != 0);
ATS_PrintTestStatus("available()", f.available() != 0);
ATS_PrintTestStatus("peek()", f.peek() == 'h');
ATS_PrintTestStatus("read()", f.read() == 'h');
ATS_PrintTestStatus("available()", f.available() == 0);
ATS_PrintTestStatus("peek()", f.peek() == -1);
ATS_PrintTestStatus("read()", f.read() == -1);
ATS_PrintTestStatus("peek()", f.peek() == -1);
ATS_PrintTestStatus("read()", f.read() == -1);
f.close();
SD.remove("test2.txt");
f = SD.open("test2.txt", FILE_WRITE);
ATS_PrintTestStatus("SD.open()", f);
if (!f) goto done;
f.print("ABC");
f.close();
f = SD.open("test.txt");
ATS_PrintTestStatus("SD.open()", f);
if (!f) goto done;
ATS_PrintTestStatus("peek()", f.peek() == 'a');
f.close();
f = SD.open("test2.txt");
ATS_PrintTestStatus("SD.open()", f);
if (!f) goto done;
ATS_PrintTestStatus("peek()", f.peek() == 'A');
ATS_PrintTestStatus("read()", f.read() == 'A');
f.close();
done:
ATS_ReportMemoryUsage(startMemoryUsage);
ATS_end();
}
void loop() {}

View file

@ -0,0 +1,78 @@
#include <SD.h>
#include <ArduinoTestSuite.h>
void setup()
{
int startMemoryUsage = ATS_GetFreeMemory();
boolean b;
File f;
ATS_begin("Arduino", "SD Files Test");
ATS_PrintTestStatus("SD.begin()", b = SD.begin(4));
if (!b) goto done;
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf.txt"));
ATS_PrintTestStatus("SD.open()", f = SD.open("asdf.txt", FILE_WRITE)); f.close();
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf.txt"));
ATS_PrintTestStatus("SD.exists()", SD.exists("/asdf.txt"));
ATS_PrintTestStatus("SD.remove()", SD.remove("asdf.txt"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf.txt"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf"));
ATS_PrintTestStatus("SD.mkdir()", SD.mkdir("asdf"));
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf"));
ATS_PrintTestStatus("SD.exists()", SD.exists("/asdf"));
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf/"));
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("asdf"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf"));
ATS_PrintTestStatus("SD.mkdir()", SD.mkdir("x/y/z"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x/"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y/"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y/z"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y/z/"));
ATS_PrintTestStatus("SD.exists()", SD.exists("/x/y/z/"));
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("x/y/z"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y/z"));
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("x/y/"));
ATS_PrintTestStatus("SD.exists()", SD.exists("x"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y/z"));
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("/x"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y/z"));
ATS_PrintTestStatus("!SD.open()", !(f = SD.open("asdf/asdf.txt", FILE_WRITE))); f.close();
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf.txt"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf/asdf.txt"));
ATS_PrintTestStatus("SD.mkdir()", SD.mkdir("asdf"));
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf"));
ATS_PrintTestStatus("SD.open()", f = SD.open("asdf/asdf.txt", FILE_WRITE)); f.close();
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf/asdf.txt"));
ATS_PrintTestStatus("!SD.rmdir()", !SD.rmdir("asdf"));
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf"));
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf/asdf.txt"));
ATS_PrintTestStatus("SD.remove()", SD.remove("asdf/asdf.txt"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf/asdf.txt"));
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf"));
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("asdf"));
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf"));
done:
ATS_ReportMemoryUsage(startMemoryUsage);
ATS_end();
}
void loop() {}

View file

@ -0,0 +1,109 @@
// Tests writing to and reading from a file, in particular the
// the Stream implementation (e.g. read() and peek()).
#include <SD.h>
#include <ArduinoTestSuite.h>
void setup()
{
int startMemoryUsage = ATS_GetFreeMemory();
boolean b;
File f;
ATS_begin("Arduino", "SD Test");
ATS_PrintTestStatus("SD.begin()", b = SD.begin(4));
if (!b) goto done;
SD.remove("test.txt");
f = SD.open("test.txt", FILE_WRITE);
ATS_PrintTestStatus("SD.open()", f);
if (!f) goto done;
ATS_PrintTestStatus("initial position", f.position() == 0);
ATS_PrintTestStatus("initial size", f.size() == 0);
f.print("0123456789");
ATS_PrintTestStatus("position after writing", f.position() == 10);
ATS_PrintTestStatus("size after writing", f.size() == 10);
f.seek(0);
ATS_PrintTestStatus("size after seek", f.size() == 10);
ATS_PrintTestStatus("position after seek", f.position() == 0);
f.seek(7);
ATS_PrintTestStatus("position after seek", f.position() == 7);
ATS_PrintTestStatus("reading after seek", f.read() == '7');
ATS_PrintTestStatus("position after reading after seeking", f.position() == 8);
ATS_PrintTestStatus("reading after reading after seeking", f.read() == '8');
f.seek(3);
ATS_PrintTestStatus("position after seeking", f.position() == 3);
ATS_PrintTestStatus("peeking after seeking", f.peek() == '3');
ATS_PrintTestStatus("position after peeking after seeking", f.position() == 3);
ATS_PrintTestStatus("peeking after peeking after seeking", f.peek() == '3');
ATS_PrintTestStatus("position after peeking after seeking", f.position() == 3);
ATS_PrintTestStatus("peeking after peeking after seeking", f.read() == '3');
ATS_PrintTestStatus("position after peeking after seeking", f.position() == 4);
f.seek(1);
ATS_PrintTestStatus("position after seeking", f.position() == 1);
ATS_PrintTestStatus("peeking after seeking", f.peek() == '1');
f.seek(4);
ATS_PrintTestStatus("position after seeking", f.position() == 4);
ATS_PrintTestStatus("peeking after seeking", f.peek() == '4');
f.seek(7);
ATS_PrintTestStatus("position()", f.position() == 7);
ATS_PrintTestStatus("read()", f.read() == '7');
f.seek(0);
f.peek();
f.print("AB");
ATS_PrintTestStatus("position()", f.position() == 2);
ATS_PrintTestStatus("size()", f.size() == 10);
ATS_PrintTestStatus("read()", f.read() == '2');
f.seek(0);
ATS_PrintTestStatus("read()", f.read() == 'A');
ATS_PrintTestStatus("read()", f.read() == 'B');
ATS_PrintTestStatus("read()", f.read() == '2');
f.close();
f = SD.open("test.txt");
ATS_PrintTestStatus("SD.open()", f);
if (!f) goto done;
ATS_PrintTestStatus("position()", f.position() == 0);
ATS_PrintTestStatus("size()", f.size() == 10);
ATS_PrintTestStatus("peek()", f.peek() == 'A');
ATS_PrintTestStatus("read()", f.read() == 'A');
f.seek(4);
ATS_PrintTestStatus("position()", f.position() == 4);
ATS_PrintTestStatus("size()", f.size() == 10);
ATS_PrintTestStatus("peek()", f.peek() == '4');
ATS_PrintTestStatus("read()", f.read() == '4');
f.close();
done:
ATS_ReportMemoryUsage(startMemoryUsage);
ATS_end();
}
void loop() {}

View file

@ -0,0 +1,52 @@
//************************************************************************
//* Arduino Test Example Skeleton
//* (C) 2010 by Rick Anderson
//* Open source as per standard Arduino code
//*
//************************************************************************
//* Oct 16, 2010 <ROA> Started on String Test
//************************************************************************
#include "WProgram.h"
#include "HardwareSerial.h"
#include <ArduinoTestSuite.h>
//************************************************************************
void setup()
{
int startMemoryUsage;
//startMemoryUsage must be set directly before ATS_begin
startMemoryUsage = ATS_GetFreeMemory();
ATS_begin("Arduino", "Skeleton Test");
/*
* Test Run Start
* Test one passes because result is set to true
* Test two fails becuase result is set to false
* You can test memory for any set of tests by using the ATS_ReportMemoryUsage test
* There is also a way to print current memeory for debugging
*/
ATS_PrintTestStatus("1. Test of true test status", true);
ATS_PrintTestStatus("2. Test of false test status, this will fail.", false);
ATS_ReportMemoryUsage(startMemoryUsage);
/*
* Test Run End
*/
ATS_end();
}
//************************************************************************
void loop()
{
}

View file

@ -0,0 +1,102 @@
//************************************************************************
//* Arduino Test Example Skeleton
//* (C) 2010 by Rick Anderson
//* Open source as per standard Arduino code
//*
//************************************************************************
//* Oct 16, 2010 <ROA> Started on String Test
//************************************************************************
#include "WProgram.h"
#include "HardwareSerial.h"
#include <ArduinoTestSuite.h>
//************************************************************************
void setup()
{
char testName[64];
int startMemoryUsage;
/*
* Create variable for the tests.
*/
String stringOne;
int firstClosingBracket;
int firstOpeningBracket;
int secondOpeningBracket;
int secondClosingBracket;
int bodyTag;
int firstListItem;
int secondListItem;
int lastOpeningBracket;
int lastListItem;
int lastParagraph;
int secondLastGraf;
/*;
* initiate the test run
*/
startMemoryUsage = ATS_GetFreeMemory();
ATS_begin("Arduino", "String Memory Test");
// indexOf() returns the position (i.e. index) of a particular character
// in a string. For example, if you were parsing HTML tags, you could use it:
stringOne = "<HTML><HEAD><BODY>";
firstClosingBracket = stringOne.indexOf('>');
Serial.println("The index of > in the string " + stringOne + " is " + firstClosingBracket);
stringOne = "<HTML><HEAD><BODY>";
secondOpeningBracket = firstClosingBracket + 1;
secondClosingBracket = stringOne.indexOf('>', secondOpeningBracket );
Serial.println("The index of the second > in the string " + stringOne + " is " + secondClosingBracket);
// you can also use indexOf() to search for Strings:
stringOne = "<HTML><HEAD><BODY>";
bodyTag = stringOne.indexOf("<BODY>");
Serial.println("The index of the body tag in the string " + stringOne + " is " + bodyTag);
stringOne = "<UL><LI>item<LI>item<LI>item</UL>";
firstListItem = stringOne.indexOf("<LI>");
secondListItem = stringOne.indexOf("item", firstListItem + 1 );
Serial.println("The index of the second list item in the string " + stringOne + " is " + secondClosingBracket);
// lastIndexOf() gives you the last occurrence of a character or string:
lastOpeningBracket = stringOne.lastIndexOf('<');
Serial.println("The index of the last < in the string " + stringOne + " is " + lastOpeningBracket);
lastListItem = stringOne.lastIndexOf("<LI>");
Serial.println("The index of the last list item in the string " + stringOne + " is " + lastListItem);
// lastIndexOf() can also search for a string:
stringOne = "<p>Lorem ipsum dolor sit amet</p><p>Ipsem</p><p>Quod</p>";
lastParagraph = stringOne.lastIndexOf("<p");
secondLastGraf = stringOne.lastIndexOf("<p", lastParagraph - 1);
Serial.println("The index of the second last paragraph tag " + stringOne + " is " + secondLastGraf);
ATS_ReportMemoryUsage(startMemoryUsage);
/*
* Test complete
*/
ATS_end();
}
//************************************************************************
void loop()
{
}

View file

@ -0,0 +1,184 @@
//************************************************************************
//* Arduino String Test
//* (C) 2010 by Rick Anderson
//* Open source as per standard Arduino code
//*
//************************************************************************
//* Oct 16, 2010 <ROA> Started on String Test
//************************************************************************
#include "WProgram.h"
#include "HardwareSerial.h"
#include <ArduinoTestSuite.h>
//************************************************************************
void setup()
{
int startMemoryUsage;
ATS_begin("Arduino", "Test of String Library");
/*
* Test Variable Setup
* Best practive set all your test variables prior to teseting.
* This is required for Memory tests.
*/
String stringOne = String("stringThree = ");
String stringTwo = String("this string");
String stringThree = String ();
char charResult[100];
/*
* Run the tests
*/
// adding a constant integer to a string:
stringThree = stringOne + 123;
//strcpy(charResult, "\0");
stringThree.toCharArray(charResult, sizeof(charResult));
ATS_PrintTestStatus("1. Adding a constant integer to a string:", strcmp(charResult,"stringThree = 123" ) == 0);
// adding a constant long interger to a string:
stringThree = stringOne + 123456789;
stringThree.toCharArray(charResult, sizeof(charResult));
ATS_PrintTestStatus("2. Adding a constant long interger to a string", strcmp(charResult,"stringThree = 123456789" ) == 0);
// adding a constant character to a string:
stringThree = stringOne + 'A';
stringThree.toCharArray(charResult, sizeof(charResult));
ATS_PrintTestStatus("3. Adding a constant character to a string", strcmp(charResult,"stringThree = A" ) == 0);
// adding a constant string to a string:
stringThree = stringOne + "abc";
stringThree.toCharArray(charResult, sizeof(charResult));
ATS_PrintTestStatus("4. Adding a constant string variable to a string", strcmp(charResult,"stringThree = abc" ) == 0);
//"5. Adding a constant long interger to a string"
stringThree = stringOne + stringTwo;
stringThree.toCharArray(charResult, sizeof(charResult));
ATS_PrintTestStatus("5. Adding a constant long interger to a string", strcmp(charResult,"stringThree = this string" ) == 0);
/*
* setup up String Comparison Operater Tests
*/
stringOne = String("this");
stringTwo = String("that");
// two strings equal:
ATS_PrintTestStatus("6. Two strings equal",stringOne == "this");
// two strings not equal:
ATS_PrintTestStatus("7. Two strings not equal",stringOne != stringTwo);
// two strings not equal (case sensitivity matters):
stringOne = "This";
stringTwo = "this";
ATS_PrintTestStatus("8. Two strings not equal [case sensitivity matters]", stringOne != stringTwo);
// you can also use equals() to see if two strings are the same:
stringOne = "this";
stringTwo = "this";
ATS_PrintTestStatus("9. Equals() method equals", stringOne.equals(stringTwo));
// you can also use not equals() to see if two strings are not the same:
stringOne = String("This");
stringTwo = String("this");
ATS_PrintTestStatus("10. Not equals() method equals", !stringOne.equals(stringTwo));
// or perhaps you want to ignore case:
ATS_PrintTestStatus("11. EqualsIgnoreCase() method equals", stringOne.equalsIgnoreCase(stringTwo));
// a numeric string compared to the number it represents:
stringOne = "1";
int numberOne = 1;
ATS_PrintTestStatus("12. A numeric string compared to the number it represents", stringOne == numberOne);
// two numeric strings compared:
stringOne = "2";
stringTwo = "1";
ATS_PrintTestStatus("13. Two numeric strings compared",stringOne >= stringTwo);
// comparison operators can be used to compare strings for alphabetic sorting too:
/*
stringOne = String("Brown");
ATS_PrintTestStatus("14. comparison operator < can be used to compare strings for alphabetic sorting ",stringOne < "Charles");
ATS_PrintTestStatus("15. comparison operator > can be used to compare strings for alphabetic sorting ",stringOne > "Adams");
ATS_PrintTestStatus("16. comparison operator <= can be used to compare strings for alphabetic sorting ",stringOne <= "Browne");
ATS_PrintTestStatus("17. comparison operator >= can be used to compare strings for alphabetic sorting ",stringOne >= "Brow");
*/
// the compareTo() operator also allows you to compare strings
stringOne = "Cucumber";
stringTwo = "Cucuracha";
ATS_PrintTestStatus("18. The compareTo() operator also allows you to compare strings", stringOne.compareTo(stringTwo) < 0);
// compareTo() String with numnber > String with number:
stringOne = "Sensor: 50";
stringTwo= "Sensor: 150";
ATS_PrintTestStatus("19. The compareTo() String with integers", stringOne.compareTo(stringTwo) < 0);
// compareTo() String with numnber > String with number append integer, matches example code:
stringOne = "Sensor: ";
stringTwo= "Sensor: ";
stringOne += 50;
stringTwo += 150;
ATS_PrintTestStatus("20. The compareTo() compare strings with appended integers", stringOne.compareTo(stringTwo) < 0);
/*
* setup up String Append Operation Tests
*/
// Serious awful problem here
stringOne = String("Sensor ");
stringTwo = String("value");
stringOne += stringTwo;
ATS_PrintTestStatus("21. Adding string to string += ", stringOne.equals("Sensor value"));
ATS_PrintTestStatus("22. The compareTo() compare strings with appended integers", stringOne.compareTo(stringTwo) < 0);
/*
* Test complete
*/
ATS_end();
}
//************************************************************************
void loop()
{
}

View file

@ -0,0 +1,250 @@
//************************************************************************
//* Arduino Test Suite
//* ATS_ToneTest
//*
//* Copyright (c) 2010 Mark Sproul All right 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.
//*
//* This library 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 this library; if not, write to the Free Software
//* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//************************************************************************
//* Aug 31, 2010 <MLS> Started on TestArduino
//* Oct 23, 2010 <MLS> Started on ToneTest
//************************************************************************
#include "WProgram.h"
#include "HardwareSerial.h"
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
#define kBoard_PinCount 20
#define kBoard_AnalogCount 6
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define kBoard_PinCount 70
#define kBoard_AnalogCount 16
#endif
#include <ArduinoTestSuite.h>
//************************************************************************
void TestTonePin(uint8_t toneOutputPinNumber)
{
uint8_t helperpin;
unsigned long startMilliSecs;
unsigned long highCount, lowCount;
int previousState;
int currentState;
char testNameString[80];
long outputFreq;
long measuredFreq;
boolean passed;
long percentError;
long deltaFreq;
if ((toneOutputPinNumber % 2) == 0)
{
//* if its EVEN, add 1
helperpin = toneOutputPinNumber + 1;
}
else
{
//* if its ODD
helperpin = toneOutputPinNumber - 1;
}
//* dont set the mode of the OUTPUT pin, the tone command does that
pinMode(helperpin, INPUT);
previousState = digitalRead(helperpin);
startMilliSecs = millis();
highCount = 0;
lowCount = 0;
measuredFreq = 0;
//* we are going to watch for one second
outputFreq = random(200, 2000);
tone(toneOutputPinNumber, outputFreq);
while ((millis() - startMilliSecs) < 1000)
{
currentState = digitalRead(helperpin);
if (currentState == HIGH)
{
highCount++;
}
else
{
lowCount++;
}
//* check to see if it changed state
if ((currentState == HIGH) && (previousState == LOW))
{
measuredFreq++;
}
previousState = currentState;
}
noTone(toneOutputPinNumber);
deltaFreq = abs(measuredFreq - outputFreq);
percentError = 100 - abs(((outputFreq - deltaFreq) * 100) / outputFreq);
sprintf(testNameString, "ToneTest.%02d (out freq= %4ld measured freq= %4ld err= %ld%%)", toneOutputPinNumber, outputFreq, measuredFreq, percentError);
if (percentError < 5)
{
passed = true;
}
else
{
passed = false;
}
ATS_PrintTestStatus(testNameString, passed);
}
//************************************************************************
//* this test to make sure the duration option works
void TestToneDuration(uint8_t toneOutputPinNumber)
{
uint8_t helperpin;
unsigned long startMilliSecs;
unsigned long highCount, lowCount;
int previousState;
int currentState;
char testNameString[80];
long outputFreq;
long measuredFreq;
boolean passed;
long percentError;
long deltaFreq;
long durationTime;
if ((toneOutputPinNumber % 2) == 0)
{
//* if its EVEN, add 1
helperpin = toneOutputPinNumber + 1;
}
else
{
//* if its ODD
helperpin = toneOutputPinNumber - 1;
}
//* dont set the mode of the OUTPUT pin, the tone command does that
pinMode(helperpin, INPUT);
previousState = digitalRead(helperpin);
startMilliSecs = millis();
highCount = 0;
lowCount = 0;
measuredFreq = 0;
durationTime = 0;
//* we are going to watch for one second
outputFreq = random(500, 2000);
tone(toneOutputPinNumber, outputFreq, 1000);
while ((millis() - startMilliSecs) < 2000)
{
currentState = digitalRead(helperpin);
if (currentState == HIGH)
{
highCount++;
}
else
{
lowCount++;
}
//* count the freq
if ((currentState == HIGH) && (previousState == LOW))
{
measuredFreq++;
}
//* check to see if it changed state
if (currentState != previousState)
{
durationTime = millis() - startMilliSecs;
}
previousState = currentState;
}
deltaFreq = abs(measuredFreq - outputFreq);
percentError = 100 - abs(((outputFreq - deltaFreq) * 100) / outputFreq);
sprintf(testNameString, "ToneTesDurationt.%02d (durationTime =%4ld/1000 freq err= %ld%%)", toneOutputPinNumber, durationTime, percentError);
if ((durationTime > 990) && (durationTime < 1010) && (percentError < 5))
{
passed = true;
}
else
{
passed = false;
}
noTone(toneOutputPinNumber);
ATS_PrintTestStatus(testNameString, passed);
}
//************************************************************************
void setup()
{
short ii;
uint8_t timerNumber;
int startMemoryUsage;
startMemoryUsage = ATS_GetFreeMemory();
ATS_begin("Arduino", "ToneTest");
//* we start at 2 because 0/1 are RXD/TXD
for (ii=2; ii<kBoard_PinCount; ii++)
{
TestTonePin(ii);
}
//* we dont need to test every pin
for (ii=2; ii<kBoard_PinCount; ii += 5)
{
TestToneDuration(ii);
}
ATS_ReportMemoryUsage(startMemoryUsage);
ATS_end();
}
//************************************************************************
void loop()
{
}

View file

@ -0,0 +1,50 @@
/*
EEPROM.cpp - EEPROM library
Copyright (c) 2006 David A. Mellis. All right 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.
This library 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 this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/******************************************************************************
* Includes
******************************************************************************/
#include <avr/eeprom.h>
#include "WConstants.h"
#include "EEPROM.h"
/******************************************************************************
* Definitions
******************************************************************************/
/******************************************************************************
* Constructors
******************************************************************************/
/******************************************************************************
* User API
******************************************************************************/
uint8_t EEPROMClass::read(int address)
{
return eeprom_read_byte((unsigned char *) address);
}
void EEPROMClass::write(int address, uint8_t value)
{
eeprom_write_byte((unsigned char *) address, value);
}
EEPROMClass EEPROM;

View file

@ -0,0 +1,35 @@
/*
EEPROM.h - EEPROM library
Copyright (c) 2006 David A. Mellis. All right 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.
This library 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 this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef EEPROM_h
#define EEPROM_h
#include <inttypes.h>
class EEPROMClass
{
public:
uint8_t read(int);
void write(int, uint8_t);
};
extern EEPROMClass EEPROM;
#endif

View file

@ -0,0 +1,23 @@
/*
* EEPROM Clear
*
* Sets all of the bytes of the EEPROM to 0.
* This example code is in the public domain.
*/
#include <EEPROM.h>
void setup()
{
// write a 0 to all 512 bytes of the EEPROM
for (int i = 0; i < 512; i++)
EEPROM.write(i, 0);
// turn the LED on when we're done
digitalWrite(13, HIGH);
}
void loop()
{
}

View file

@ -0,0 +1,39 @@
/*
* EEPROM Read
*
* Reads the value of each byte of the EEPROM and prints it
* to the computer.
* This example code is in the public domain.
*/
#include <EEPROM.h>
// start reading from the first byte (address 0) of the EEPROM
int address = 0;
byte value;
void setup()
{
Serial.begin(9600);
}
void loop()
{
// read a byte from the current address of the EEPROM
value = EEPROM.read(address);
Serial.print(address);
Serial.print("\t");
Serial.print(value, DEC);
Serial.println();
// advance to the next address of the EEPROM
address = address + 1;
// there are only 512 bytes of EEPROM, from 0 to 511, so if we're
// on address 512, wrap around to address 0
if (address == 512)
address = 0;
delay(500);
}

View file

@ -0,0 +1,38 @@
/*
* EEPROM Write
*
* Stores values read from analog input 0 into the EEPROM.
* These values will stay in the EEPROM when the board is
* turned off and may be retrieved later by another sketch.
*/
#include <EEPROM.h>
// the current address in the EEPROM (i.e. which byte
// we're going to write to next)
int addr = 0;
void setup()
{
}
void loop()
{
// need to divide by 4 because analog inputs range from
// 0 to 1023 and each byte of the EEPROM can only hold a
// value from 0 to 255.
int val = analogRead(0) / 4;
// write the value to the appropriate byte of the EEPROM.
// these values will remain there when the board is
// turned off.
EEPROM.write(addr, val);
// advance to the next address. there are 512 bytes in
// the EEPROM, so go back to 0 when we hit 512.
addr = addr + 1;
if (addr == 512)
addr = 0;
delay(100);
}

View file

@ -0,0 +1,18 @@
#######################################
# Syntax Coloring Map For Ultrasound
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
EEPROM KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################

View file

@ -0,0 +1,147 @@
#include "w5100.h"
#include "socket.h"
extern "C" {
#include "string.h"
}
#include "WProgram.h"
#include "Ethernet.h"
#include "Client.h"
#include "Server.h"
uint16_t Client::_srcport = 1024;
Client::Client(uint8_t sock) : _sock(sock) {
}
Client::Client(uint8_t *ip, uint16_t port) : _ip(ip), _port(port), _sock(MAX_SOCK_NUM) {
}
uint8_t Client::connect() {
if (_sock != MAX_SOCK_NUM)
return 0;
for (int i = 0; i < MAX_SOCK_NUM; i++) {
uint8_t s = W5100.readSnSR(i);
if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) {
_sock = i;
break;
}
}
if (_sock == MAX_SOCK_NUM)
return 0;
_srcport++;
if (_srcport == 0) _srcport = 1024;
socket(_sock, SnMR::TCP, _srcport, 0);
if (!::connect(_sock, _ip, _port)) {
_sock = MAX_SOCK_NUM;
return 0;
}
while (status() != SnSR::ESTABLISHED) {
delay(1);
if (status() == SnSR::CLOSED) {
_sock = MAX_SOCK_NUM;
return 0;
}
}
return 1;
}
void Client::write(uint8_t b) {
if (_sock != MAX_SOCK_NUM)
send(_sock, &b, 1);
}
void Client::write(const char *str) {
if (_sock != MAX_SOCK_NUM)
send(_sock, (const uint8_t *)str, strlen(str));
}
void Client::write(const uint8_t *buf, size_t size) {
if (_sock != MAX_SOCK_NUM)
send(_sock, buf, size);
}
int Client::available() {
if (_sock != MAX_SOCK_NUM)
return W5100.getRXReceivedSize(_sock);
return 0;
}
int Client::read() {
uint8_t b;
if (!available())
return -1;
recv(_sock, &b, 1);
return b;
}
int Client::peek() {
uint8_t b;
if (!available())
return -1;
::peek(_sock, &b);
return b;
}
void Client::flush() {
while (available())
read();
}
void Client::stop() {
if (_sock == MAX_SOCK_NUM)
return;
// attempt to close the connection gracefully (send a FIN to other side)
disconnect(_sock);
unsigned long start = millis();
// wait a second for the connection to close
while (status() != SnSR::CLOSED && millis() - start < 1000)
delay(1);
// if it hasn't closed, close it forcefully
if (status() != SnSR::CLOSED)
close(_sock);
EthernetClass::_server_port[_sock] = 0;
_sock = MAX_SOCK_NUM;
}
uint8_t Client::connected() {
if (_sock == MAX_SOCK_NUM) return 0;
uint8_t s = status();
return !(s == SnSR::LISTEN || s == SnSR::CLOSED || s == SnSR::FIN_WAIT ||
(s == SnSR::CLOSE_WAIT && !available()));
}
uint8_t Client::status() {
if (_sock == MAX_SOCK_NUM) return SnSR::CLOSED;
return W5100.readSnSR(_sock);
}
// the next three functions are a hack so we can compare the client returned
// by Server::available() to null, or use it as the condition in an
// if-statement. this lets us stay compatible with the Processing network
// library.
uint8_t Client::operator==(int p) {
return _sock == MAX_SOCK_NUM;
}
uint8_t Client::operator!=(int p) {
return _sock != MAX_SOCK_NUM;
}
Client::operator bool() {
return _sock != MAX_SOCK_NUM;
}

View file

@ -0,0 +1,37 @@
#ifndef client_h
#define client_h
#include "WProgram.h"
#include "Print.h"
class Client : public Stream {
public:
Client();
Client(uint8_t);
Client(uint8_t *, uint16_t);
uint8_t status();
uint8_t connect();
virtual void write(uint8_t);
virtual void write(const char *str);
virtual void write(const uint8_t *buf, size_t size);
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
void stop();
uint8_t connected();
uint8_t operator==(int);
uint8_t operator!=(int);
operator bool();
friend class Server;
private:
static uint16_t _srcport;
uint8_t _sock;
uint8_t *_ip;
uint16_t _port;
};
#endif

View file

@ -0,0 +1,36 @@
#include "w5100.h"
#include "Ethernet.h"
// XXX: don't make assumptions about the value of MAX_SOCK_NUM.
uint8_t EthernetClass::_state[MAX_SOCK_NUM] = {
0, 0, 0, 0 };
uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = {
0, 0, 0, 0 };
void EthernetClass::begin(uint8_t *mac, uint8_t *ip)
{
uint8_t gateway[4];
gateway[0] = ip[0];
gateway[1] = ip[1];
gateway[2] = ip[2];
gateway[3] = 1;
begin(mac, ip, gateway);
}
void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway)
{
uint8_t subnet[] = {
255, 255, 255, 0 };
begin(mac, ip, gateway, subnet);
}
void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway, uint8_t *subnet)
{
W5100.init();
W5100.setMACAddress(mac);
W5100.setIPAddress(ip);
W5100.setGatewayIp(gateway);
W5100.setSubnetMask(subnet);
}
EthernetClass Ethernet;

View file

@ -0,0 +1,25 @@
#ifndef ethernet_h
#define ethernet_h
#include <inttypes.h>
//#include "w5100.h"
#include "Client.h"
#include "Server.h"
#define MAX_SOCK_NUM 4
class EthernetClass {
private:
public:
static uint8_t _state[MAX_SOCK_NUM];
static uint16_t _server_port[MAX_SOCK_NUM];
void begin(uint8_t *, uint8_t *);
void begin(uint8_t *, uint8_t *, uint8_t *);
void begin(uint8_t *, uint8_t *, uint8_t *, uint8_t *);
friend class Client;
friend class Server;
};
extern EthernetClass Ethernet;
#endif

View file

@ -0,0 +1,92 @@
#include "w5100.h"
#include "socket.h"
extern "C" {
#include "string.h"
}
#include "Ethernet.h"
#include "Client.h"
#include "Server.h"
Server::Server(uint16_t port)
{
_port = port;
}
void Server::begin()
{
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
Client client(sock);
if (client.status() == SnSR::CLOSED) {
socket(sock, SnMR::TCP, _port, 0);
listen(sock);
EthernetClass::_server_port[sock] = _port;
break;
}
}
}
void Server::accept()
{
int listening = 0;
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
Client client(sock);
if (EthernetClass::_server_port[sock] == _port) {
if (client.status() == SnSR::LISTEN) {
listening = 1;
}
else if (client.status() == SnSR::CLOSE_WAIT && !client.available()) {
client.stop();
}
}
}
if (!listening) {
begin();
}
}
Client Server::available()
{
accept();
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
Client client(sock);
if (EthernetClass::_server_port[sock] == _port &&
(client.status() == SnSR::ESTABLISHED ||
client.status() == SnSR::CLOSE_WAIT)) {
if (client.available()) {
// XXX: don't always pick the lowest numbered socket.
return client;
}
}
}
return Client(MAX_SOCK_NUM);
}
void Server::write(uint8_t b)
{
write(&b, 1);
}
void Server::write(const char *str)
{
write((const uint8_t *)str, strlen(str));
}
void Server::write(const uint8_t *buffer, size_t size)
{
accept();
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
Client client(sock);
if (EthernetClass::_server_port[sock] == _port &&
client.status() == SnSR::ESTABLISHED) {
client.write(buffer, size);
}
}
}

View file

@ -0,0 +1,22 @@
#ifndef server_h
#define server_h
#include "Print.h"
class Client;
class Server :
public Print {
private:
uint16_t _port;
void accept();
public:
Server(uint16_t);
Client available();
void begin();
virtual void write(uint8_t);
virtual void write(const char *str);
virtual void write(const uint8_t *buf, size_t size);
};
#endif

View file

@ -0,0 +1,136 @@
/*
* Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
* This version only offers minimal wrapping of socket.c/socket.h
* Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/
*
* MIT License:
* Copyright (c) 2008 Bjoern Hartmann
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* bjoern@cs.stanford.edu 12/30/2008
*/
#include "w5100.h"
#include "socket.h"
#include "Ethernet.h"
#include "Udp.h"
/* Start UDP socket, listening at local port PORT */
void UdpClass::begin(uint16_t port) {
_port = port;
_sock = 0; //TODO: should not be hardcoded
socket(_sock, SnMR::UDP, _port, 0);
}
/* Send packet contained in buf of length len to peer at specified ip, and port */
/* Use this function to transmit binary data that might contain 0x00 bytes*/
/* This function returns sent data size for success else -1. */
uint16_t UdpClass::sendPacket(uint8_t * buf, uint16_t len, uint8_t * ip, uint16_t port){
return sendto(_sock,(const uint8_t *)buf,len,ip,port);
}
/* Send zero-terminated string str as packet to peer at specified ip, and port */
/* This function returns sent data size for success else -1. */
uint16_t UdpClass::sendPacket(const char str[], uint8_t * ip, uint16_t port){
// compute strlen
const char *s;
for(s = str; *s; ++s);
uint16_t len = (s-str);
// send packet
return sendto(_sock,(const uint8_t *)str,len,ip,port);
}
/* Is data available in rx buffer? Returns 0 if no, number of available bytes if yes.
* returned value includes 8 byte UDP header!*/
int UdpClass::available() {
return W5100.getRXReceivedSize(_sock);
}
/* Read a received packet into buffer buf (which is of maximum length len); */
/* store calling ip and port as well. Call available() to make sure data is ready first. */
/* NOTE: I don't believe len is ever checked in implementation of recvfrom(),*/
/* so it's easy to overflow buffer. so we check and truncate. */
/* returns number of bytes read, or negative number of bytes we would have needed if we truncated */
int UdpClass::readPacket(uint8_t * buf, uint16_t bufLen, uint8_t *ip, uint16_t *port) {
int packetLen = available()-8; //skip UDP header;
if(packetLen < 0 ) return 0; // no real data here
if(packetLen > (int)bufLen) {
//packet is too large - truncate
//HACK - hand-parse the UDP packet using TCP recv method
uint8_t tmpBuf[8];
int i;
//read 8 header bytes and get IP and port from it
recv(_sock,tmpBuf,8);
ip[0] = tmpBuf[0];
ip[1] = tmpBuf[1];
ip[2] = tmpBuf[2];
ip[3] = tmpBuf[3];
*port = tmpBuf[4];
*port = (*port << 8) + tmpBuf[5];
//now copy first (bufLen) bytes into buf
for(i=0;i<(int)bufLen;i++) {
recv(_sock,tmpBuf,1);
buf[i]=tmpBuf[0];
}
//and just read the rest byte by byte and throw it away
while(available()) {
recv(_sock,tmpBuf,1);
}
return (-1*packetLen);
//ALTERNATIVE: requires stdlib - takes a bunch of space
/*//create new buffer and read everything into it
uint8_t * tmpBuf = (uint8_t *)malloc(packetLen);
recvfrom(_sock,tmpBuf,packetLen,ip,port);
if(!tmpBuf) return 0; //couldn't allocate
// copy first bufLen bytes
for(unsigned int i=0; i<bufLen; i++) {
buf[i]=tmpBuf[i];
}
//free temp buffer
free(tmpBuf);
*/
}
return recvfrom(_sock,buf,bufLen,ip,port);
}
/* Read a received packet, throw away peer's ip and port. See note above. */
int UdpClass::readPacket(uint8_t * buf, uint16_t len) {
uint8_t ip[4];
uint16_t port[1];
return recvfrom(_sock,buf,len,ip,port);
}
int UdpClass::readPacket(char * buf, uint16_t bufLen, uint8_t *ip, uint16_t &port) {
uint16_t myPort;
uint16_t ret = readPacket( (byte*)buf, bufLen, ip, &myPort);
port = myPort;
return ret;
}
/* Create one global object */
UdpClass Udp;

View file

@ -0,0 +1,63 @@
/*
* Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
* This version only offers minimal wrapping of socket.c/socket.h
* Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/
*
* NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
* 1) UDP does not guarantee the order in which assembled UDP packets are received. This
* might not happen often in practice, but in larger network topologies, a UDP
* packet can be received out of sequence.
* 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being
* aware of it. Again, this may not be a concern in practice on small local networks.
* For more information, see http://www.cafeaulait.org/course/week12/35.html
*
* MIT License:
* Copyright (c) 2008 Bjoern Hartmann
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* bjoern@cs.stanford.edu 12/30/2008
*/
#ifndef udp_h
#define udp_h
#define UDP_TX_PACKET_MAX_SIZE 24
class UdpClass {
private:
uint8_t _sock; // socket ID for Wiz5100
uint16_t _port; // local port to listen on
public:
void begin(uint16_t); // initialize, start listening on specified port
int available(); // has data been received?
// C-style buffer-oriented functions
uint16_t sendPacket(uint8_t *, uint16_t, uint8_t *, uint16_t); //send a packet to specified peer
uint16_t sendPacket(const char[], uint8_t *, uint16_t); //send a string as a packet to specified peer
int readPacket(uint8_t *, uint16_t); // read a received packet
int readPacket(uint8_t *, uint16_t, uint8_t *, uint16_t *); // read a received packet, also return sender's ip and port
// readPacket that fills a character string buffer
int readPacket(char *, uint16_t, uint8_t *, uint16_t &);
};
extern UdpClass Udp;
#endif

View file

@ -0,0 +1,225 @@
/*
SCP1000 Barometric Pressure Sensor Display
Serves the output of a Barometric Pressure Sensor as a web page.
Uses the SPI library. For details on the sensor, see:
http://www.sparkfun.com/commerce/product_info.php?products_id=8161
http://www.vti.fi/en/support/obsolete_products/pressure_sensors/
This sketch adapted from Nathan Seidle's SCP1000 example for PIC:
http://www.sparkfun.com/datasheets/Sensors/SCP1000-Testing.zip
Circuit:
SCP1000 sensor attached to pins 6,7, and 11 - 13:
DRDY: pin 6
CSB: pin 7
MOSI: pin 11
MISO: pin 12
SCK: pin 13
created 31 July 2010
by Tom Igoe
*/
#include <Ethernet.h>
// the sensor communicates using SPI, so include the library:
#include <SPI.h>
// assign a MAC address for the ethernet controller.
// fill in your address here:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// assign an IP address for the controller:
byte ip[] = {
192,168,1,20 };
byte gateway[] = {
192,168,1,1};
byte subnet[] = {
255, 255, 255, 0 };
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
Server server(80);
//Sensor's memory register addresses:
const int PRESSURE = 0x1F; //3 most significant bits of pressure
const int PRESSURE_LSB = 0x20; //16 least significant bits of pressure
const int TEMPERATURE = 0x21; //16 bit temperature reading
// pins used for the connection with the sensor
// the others you need are controlled by the SPI library):
const int dataReadyPin = 6;
const int chipSelectPin = 7;
float temperature = 0.0;
long pressure = 0;
long lastReadingTime = 0;
void setup() {
// start the SPI library:
SPI.begin();
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
// initalize the data ready and chip select pins:
pinMode(dataReadyPin, INPUT);
pinMode(chipSelectPin, OUTPUT);
Serial.begin(9600);
//Configure SCP1000 for low noise configuration:
writeRegister(0x02, 0x2D);
writeRegister(0x01, 0x03);
writeRegister(0x03, 0x02);
// give the sensor and Ethernet shield time to set up:
delay(1000);
//Set the sensor to high resolution mode tp start readings:
writeRegister(0x03, 0x0A);
}
void loop() {
// check for a reading no more than once a second.
if (millis() - lastReadingTime > 1000){
// if there's a reading ready, read it:
// don't do anything until the data ready pin is high:
if (digitalRead(dataReadyPin) == HIGH) {
getData();
// timestamp the last time you got a reading:
lastReadingTime = millis();
}
}
// listen for incoming Ethernet connections:
listenForClients();
}
void getData() {
Serial.println("Getting reading");
//Read the temperature data
int tempData = readRegister(0x21, 2);
// convert the temperature to celsius and display it:
temperature = (float)tempData / 20.0;
//Read the pressure data highest 3 bits:
byte pressureDataHigh = readRegister(0x1F, 1);
pressureDataHigh &= 0b00000111; //you only needs bits 2 to 0
//Read the pressure data lower 16 bits:
unsigned int pressureDataLow = readRegister(0x20, 2);
//combine the two parts into one 19-bit number:
pressure = ((pressureDataHigh << 16) | pressureDataLow)/4;
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.println(" degrees C");
Serial.print("Pressure: " + String(pressure));
Serial.println(" Pa");
}
void listenForClients() {
// listen for incoming clients
Client client = server.available();
if (client) {
Serial.println("Got a client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
// print the current readings, in HTML format:
client.print("Temperature: ");
client.print(temperature);
client.print(" degrees C");
client.println("<br />");
client.print("Pressure: " + String(pressure));
client.print(" Pa");
client.println("<br />");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
}
}
//Send a write command to SCP1000
void writeRegister(byte registerName, byte registerValue) {
// SCP1000 expects the register name in the upper 6 bits
// of the byte:
registerName <<= 2;
// command (read or write) goes in the lower two bits:
registerName |= 0b00000010; //Write command
// take the chip select low to select the device:
digitalWrite(chipSelectPin, LOW);
SPI.transfer(registerName); //Send register location
SPI.transfer(registerValue); //Send value to record into register
// take the chip select high to de-select:
digitalWrite(chipSelectPin, HIGH);
}
//Read register from the SCP1000:
unsigned int readRegister(byte registerName, int numBytes) {
byte inByte = 0; // incoming from the SPI read
unsigned int result = 0; // result to return
// SCP1000 expects the register name in the upper 6 bits
// of the byte:
registerName <<= 2;
// command (read or write) goes in the lower two bits:
registerName &= 0b11111100; //Read command
// take the chip select low to select the device:
digitalWrite(chipSelectPin, LOW);
// send the device the register you want to read:
int command = SPI.transfer(registerName);
// send a value of 0 to read the first byte returned:
inByte = SPI.transfer(0x00);
result = inByte;
// if there's more than one byte returned,
// shift the first byte then get the second byte:
if (numBytes > 1){
result = inByte << 8;
inByte = SPI.transfer(0x00);
result = result |inByte;
}
// take the chip select high to de-select:
digitalWrite(chipSelectPin, HIGH);
// return the result:
return(result);
}

View file

@ -0,0 +1,63 @@
/*
Chat Server
A simple server that distributes any incoming messages to all
connected clients. To use telnet to your device's IP address and type.
You can see the client's input in the serial monitor as well.
Using an Arduino Wiznet Ethernet shield.
Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
* Analog inputs attached to pins A0 through A5 (optional)
created 18 Dec 2009
by David A. Mellis
modified 10 August 2010
by Tom Igoe
*/
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1, 177 };
byte gateway[] = { 192,168,1, 1 };
byte subnet[] = { 255, 255, 0, 0 };
// telnet defaults to port 23
Server server(23);
boolean gotAMessage = false; // whether or not you got a message from the client yet
void setup() {
// initialize the ethernet device
Ethernet.begin(mac, ip, gateway, subnet);
// start listening for clients
server.begin();
// open the serial port
Serial.begin(9600);
}
void loop() {
// wait for a new client:
Client client = server.available();
// when the client sends the first byte, say hello:
if (client) {
if (!gotAMessage) {
Serial.println("We have a new client");
client.println("Hello, client!");
gotAMessage = true;
}
// read the bytes incoming from the client:
char thisChar = client.read();
// echo the bytes back to the client:
server.write(thisChar);
// echo the bytes to the server as well:
Serial.print(thisChar);
}
}

View file

@ -0,0 +1,139 @@
/*
Pachube sensor client
This sketch connects an analog sensor to Pachube (http://www.pachube.com)
using a Wiznet Ethernet shield. You can use the Arduino Ethernet shield, or
the Adafruit Ethernet shield, either one will work, as long as it's got
a Wiznet Ethernet module on board.
Circuit:
* Analog sensor attached to analog in 0
* Ethernet shield attached to pins 10, 11, 12, 13
created 15 March 2010
updated 4 Sep 2010
by Tom Igoe
http://www.tigoe.net/pcomp/code/category/arduinowiring/873
This code is in the public domain.
*/
#include <SPI.h>
#include <Ethernet.h>
// assign a MAC address for the ethernet controller.
// fill in your address here:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// assign an IP address for the controller:
byte ip[] = {
192,169,1,20 };
byte gateway[] = {
192,168,1,1};
byte subnet[] = {
255, 255, 255, 0 };
// The address of the server you want to connect to (pachube.com):
byte server[] = {
209,40,205,190 };
// initialize the library instance:
Client client(server, 80);
long lastConnectionTime = 0; // last time you connected to the server, in milliseconds
boolean lastConnected = false; // state of the connection last time through the main loop
const int postingInterval = 10000; //delay between updates to Pachube.com
void setup() {
// start the ethernet connection and serial port:
Ethernet.begin(mac, ip);
Serial.begin(9600);
// give the ethernet module time to boot up:
delay(1000);
}
void loop() {
// read the analog sensor:
int sensorReading = analogRead(A0);
// if there's incoming data from the net connection.
// send it out the serial port. This is for debugging
// purposes only:
if (client.available()) {
char c = client.read();
Serial.print(c);
}
// if there's no net connection, but there was one last time
// through the loop, then stop the client:
if (!client.connected() && lastConnected) {
Serial.println();
Serial.println("disconnecting.");
client.stop();
}
// if you're not connected, and ten seconds have passed since
// your last connection, then connect again and send data:
if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) {
sendData(sensorReading);
}
// store the state of the connection for next time through
// the loop:
lastConnected = client.connected();
}
// this method makes a HTTP connection to the server:
void sendData(int thisData) {
// if there's a successful connection:
if (client.connect()) {
Serial.println("connecting...");
// send the HTTP PUT request.
// fill in your feed address here:
client.print("PUT /api/YOUR_FEED_HERE.csv HTTP/1.1\n");
client.print("Host: www.pachube.com\n");
// fill in your Pachube API key here:
client.print("X-PachubeApiKey: YOUR_KEY_HERE\n");
client.print("Content-Length: ");
// calculate the length of the sensor reading in bytes:
int thisLength = getLength(thisData);
client.println(thisLength, DEC);
// last pieces of the HTTP PUT request:
client.print("Content-Type: text/csv\n");
client.println("Connection: close\n");
// here's the actual content of the PUT request:
client.println(thisData, DEC);
// note the time that the connection was made:
lastConnectionTime = millis();
}
else {
// if you couldn't make a connection:
Serial.println("connection failed");
}
}
// This method calculates the number of digits in the
// sensor reading. Since each digit of the ASCII decimal
// representation is a byte, the number of digits equals
// the number of bytes:
int getLength(int someValue) {
// there's at least one byte:
int digits = 1;
// continually divide the value by ten,
// adding one to the digit count for each
// time you divide, until you're at 0:
int dividend = someValue /10;
while (dividend > 0) {
dividend = dividend /10;
digits++;
}
// return the number of digits:
return digits;
}

View file

@ -0,0 +1,124 @@
/*
Pachube sensor client with Strings
This sketch connects an analog sensor to Pachube (http://www.pachube.com)
using a Wiznet Ethernet shield. You can use the Arduino Ethernet shield, or
the Adafruit Ethernet shield, either one will work, as long as it's got
a Wiznet Ethernet module on board.
This example uses the String library, which is part of the Arduino core from
version 0019.
Circuit:
* Analog sensor attached to analog in 0
* Ethernet shield attached to pins 10, 11, 12, 13
created 15 March 2010
updated 4 Sep 2010
by Tom Igoe
This code is in the public domain.
*/
#include <SPI.h>
#include <Ethernet.h>
// assign a MAC address for the ethernet controller.
// fill in your address here:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// assign an IP address for the controller:
byte ip[] = {
192,169,1,20 };
byte gateway[] = {
192,168,1,1};
byte subnet[] = {
255, 255, 255, 0 };
// The address of the server you want to connect to (pachube.com):
byte server[] = {
209,40,205,190 };
// initialize the library instance:
Client client(server, 80);
long lastConnectionTime = 0; // last time you connected to the server, in milliseconds
boolean lastConnected = false; // state of the connection last time through the main loop
const int postingInterval = 10000; //delay between updates to Pachube.com
void setup() {
// start the ethernet connection and serial port:
Ethernet.begin(mac, ip);
Serial.begin(9600);
// give the ethernet module time to boot up:
delay(1000);
}
void loop() {
// read the analog sensor:
int sensorReading = analogRead(A0);
// convert the data to a String to send it:
String dataString = String(sensorReading);
// you can append multiple readings to this String if your
// pachube feed is set up to handle multiple values:
int otherSensorReading = analogRead(A1);
dataString += ",";
dataString += String(otherSensorReading);
// if there's incoming data from the net connection.
// send it out the serial port. This is for debugging
// purposes only:
if (client.available()) {
char c = client.read();
Serial.print(c);
}
// if there's no net connection, but there was one last time
// through the loop, then stop the client:
if (!client.connected() && lastConnected) {
Serial.println();
Serial.println("disconnecting.");
client.stop();
}
// if you're not connected, and ten seconds have passed since
// your last connection, then connect again and send data:
if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) {
sendData(dataString);
}
// store the state of the connection for next time through
// the loop:
lastConnected = client.connected();
}
// this method makes a HTTP connection to the server:
void sendData(String thisData) {
// if there's a successful connection:
if (client.connect()) {
Serial.println("connecting...");
// send the HTTP PUT request.
// fill in your feed address here:
client.print("PUT /api/YOUR_FEED_HERE.csv HTTP/1.1\n");
client.print("Host: www.pachube.com\n");
// fill in your Pachube API key here:
client.print("X-PachubeApiKey: YOUR_KEY_HERE\n");
client.print("Content-Length: ");
client.println(thisData.length(), DEC);
// last pieces of the HTTP PUT request:
client.print("Content-Type: text/csv\n");
client.println("Connection: close\n");
// here's the actual content of the PUT request:
client.println(thisData);
// note the time that the connection was made:
lastConnectionTime = millis();
}
else {
// if you couldn't make a connection:
Serial.println("connection failed");
}
}

View file

@ -0,0 +1,89 @@
/*
Telnet client
This sketch connects to a a telnet server (http://www.google.com)
using an Arduino Wiznet Ethernet shield. You'll need a telnet server
to test this with.
Processing's ChatServer example (part of the network library) works well,
running on port 10002. It can be found as part of the examples
in the Processing application, available at
http://processing.org/
Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
created 14 Sep 2010
by Tom Igoe
*/
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,177 };
// Enter the IP address of the server you're connecting to:
byte server[] = {
1,1,1,1 };
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 23 is default for telnet;
// if you're using Processing's ChatServer, use port 10002):
Client client(server, 10002);
void setup() {
// start the Ethernet connection:
Ethernet.begin(mac, ip);
// start the serial library:
Serial.begin(9600);
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println("connecting...");
// if you get a connection, report back via serial:
if (client.connect()) {
Serial.println("connected");
}
else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
}
}
void loop()
{
// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()) {
char c = client.read();
Serial.print(c);
}
// as long as there are bytes in the serial queue,
// read them and send them out the socket if it's open:
while (Serial.available() > 0) {
char inChar = Serial.read();
if (client.connected()) {
client.print(inChar);
}
}
// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting.");
client.stop();
// do nothing:
while(true);
}
}

View file

@ -0,0 +1,107 @@
/*
UDPSendReceive.pde:
This sketch receives UDP message strings, prints them to the serial port
and sends an "acknowledge" string back to the sender
A Processing sketch is included at the end of file that can be used to send
and received messages for testing with a computer.
created 21 Aug 2010
by Michael Margolis
This code is in the public domain.
*/
#include <SPI.h> // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <Udp.h> // UDP library from: bjoern@cs.stanford.edu 12/30/2008
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,177 };
unsigned int localPort = 8888; // local port to listen on
// the next two variables are set when a packet is received
byte remoteIp[4]; // holds received packet's originating IP
unsigned int remotePort; // holds received packet's originating port
// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
char ReplyBuffer[] = "acknowledged"; // a string to send back
void setup() {
// start the Ethernet and UDP:
Ethernet.begin(mac,ip);
Udp.begin(localPort);
Serial.begin(9600);
}
void loop() {
// if there's data available, read a packet
int packetSize = Udp.available(); // note that this includes the UDP header
if(packetSize)
{
packetSize = packetSize - 8; // subtract the 8 byte header
Serial.print("Received packet of size ");
Serial.println(packetSize);
// read the packet into packetBufffer and get the senders IP addr and port number
Udp.readPacket(packetBuffer,UDP_TX_PACKET_MAX_SIZE, remoteIp, remotePort);
Serial.println("Contents:");
Serial.println(packetBuffer);
Udp.sendPacket( ReplyBuffer, remoteIp, remotePort);
}
delay(10);
}
/*
Processing sketch to run with this example
=====================================================
// Processing UDP example to send and receive string data from Arduino
// press any key to send the "Hello Arduino" message
import hypermedia.net.*;
UDP udp; // define the UDP object
void setup() {
udp = new UDP( this, 6000 ); // create a new datagram connection on port 6000
//udp.log( true ); // <-- printout the connection activity
udp.listen( true ); // and wait for incoming message
}
void draw()
{
}
void keyPressed() {
String ip = "192.168.1.177"; // the remote IP address
int port = 8888; // the destination port
udp.send("Hello World", ip, port ); // the message to send
}
void receive( byte[] data ) { // <-- default handler
//void receive( byte[] data, String ip, int port ) { // <-- extended handler
for(int i=0; i < data.length; i++)
print(char(data[i]));
println();
}
*/

View file

@ -0,0 +1,121 @@
/*
Udp NTP Client
Get the time from a Network Time Protocol (NTP) time server
Demonstrates use of UDP sendPacket and ReceivePacket
For more on NTP time servers and the messages needed to communicate with them,
see http://en.wikipedia.org/wiki/Network_Time_Protocol
created 4 Sep 2010
by Michael Margolis
modified 17 Sep 2010
by Tom Igoe
This code is in the public domain.
*/
#include <SPI.h>
#include <Ethernet.h>
#include <Udp.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,177 };
unsigned int localPort = 8888; // local port to listen for UDP packets
byte timeServer[] = {
192, 43, 244, 18}; // time.nist.gov NTP server
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
void setup()
{
// start Ethernet and UDP
Ethernet.begin(mac,ip);
Udp.begin(localPort);
Serial.begin(9600);
}
void loop()
{
sendNTPpacket(timeServer); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
if ( Udp.available() ) {
Udp.readPacket(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print("Seconds since Jan 1 1900 = " );
Serial.println(secsSince1900);
// now convert NTP time into everyday time:
Serial.print("Unix time = ");
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// print Unix time:
Serial.println(epoch);
// print the hour, minute and second:
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
Serial.print(':');
Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute)
Serial.print(':');
Serial.println(epoch %60); // print the second
}
// wait ten seconds before asking for the time again
delay(10000);
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE, address, 123); //NTP requests are to port 123
}

View file

@ -0,0 +1,71 @@
/*
Web client
This sketch connects to a website (http://www.google.com)
using an Arduino Wiznet Ethernet shield.
Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
created 18 Dec 2009
by David A. Mellis
*/
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1,177 };
byte server[] = { 173,194,33,104 }; // Google
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
Client client(server, 80);
void setup() {
// start the Ethernet connection:
Ethernet.begin(mac, ip);
// start the serial library:
Serial.begin(9600);
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println("connecting...");
// if you get a connection, report back via serial:
if (client.connect()) {
Serial.println("connected");
// Make a HTTP request:
client.println("GET /search?q=arduino HTTP/1.0");
client.println();
}
else {
// kf you didn't get a connection to the server:
Serial.println("connection failed");
}
}
void loop()
{
// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()) {
char c = client.read();
Serial.print(c);
}
// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting.");
client.stop();
// do nothing forevermore:
for(;;)
;
}
}

View file

@ -0,0 +1,82 @@
/*
Web Server
A simple web server that shows the value of the analog input pins.
using an Arduino Wiznet Ethernet shield.
Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
* Analog inputs attached to pins A0 through A5 (optional)
created 18 Dec 2009
by David A. Mellis
modified 4 Sep 2010
by Tom Igoe
*/
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1, 177 };
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
Server server(80);
void setup()
{
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
}
void loop()
{
// listen for incoming clients
Client client = server.available();
if (client) {
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
client.print("analog input ");
client.print(analogChannel);
client.print(" is ");
client.print(analogRead(analogChannel));
client.println("<br />");
}
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
}
}

View file

@ -0,0 +1,30 @@
#######################################
# Syntax Coloring Map For Ethernet
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Ethernet KEYWORD1
Client KEYWORD1
Server KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
status KEYWORD2
connect KEYWORD2
write KEYWORD2
available KEYWORD2
read KEYWORD2
flush KEYWORD2
stop KEYWORD2
connected KEYWORD2
begin KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View file

@ -0,0 +1,327 @@
#include "w5100.h"
#include "socket.h"
static uint16_t local_port;
/**
* @brief This Socket function initialize the channel in perticular mode, and set the port and wait for W5100 done it.
* @return 1 for success else 0.
*/
uint8_t socket(SOCKET s, uint8_t protocol, uint16_t port, uint8_t flag)
{
uint8_t ret;
if ((protocol == SnMR::TCP) || (protocol == SnMR::UDP) || (protocol == SnMR::IPRAW) || (protocol == SnMR::MACRAW) || (protocol == SnMR::PPPOE))
{
close(s);
W5100.writeSnMR(s, protocol | flag);
if (port != 0) {
W5100.writeSnPORT(s, port);
}
else {
local_port++; // if don't set the source port, set local_port number.
W5100.writeSnPORT(s, local_port);
}
W5100.execCmdSn(s, Sock_OPEN);
return 1;
}
return 0;
}
/**
* @brief This function close the socket and parameter is "s" which represent the socket number
*/
void close(SOCKET s)
{
W5100.execCmdSn(s, Sock_CLOSE);
W5100.writeSnIR(s, 0xFF);
}
/**
* @brief This function established the connection for the channel in passive (server) mode. This function waits for the request from the peer.
* @return 1 for success else 0.
*/
uint8_t listen(SOCKET s)
{
if (W5100.readSnSR(s) != SnSR::INIT)
return 0;
W5100.execCmdSn(s, Sock_LISTEN);
return 1;
}
/**
* @brief This function established the connection for the channel in Active (client) mode.
* This function waits for the untill the connection is established.
*
* @return 1 for success else 0.
*/
uint8_t connect(SOCKET s, uint8_t * addr, uint16_t port)
{
if
(
((addr[0] == 0xFF) && (addr[1] == 0xFF) && (addr[2] == 0xFF) && (addr[3] == 0xFF)) ||
((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
(port == 0x00)
)
return 0;
// set destination IP
W5100.writeSnDIPR(s, addr);
W5100.writeSnDPORT(s, port);
W5100.execCmdSn(s, Sock_CONNECT);
return 1;
}
/**
* @brief This function used for disconnect the socket and parameter is "s" which represent the socket number
* @return 1 for success else 0.
*/
void disconnect(SOCKET s)
{
W5100.execCmdSn(s, Sock_DISCON);
}
/**
* @brief This function used to send the data in TCP mode
* @return 1 for success else 0.
*/
uint16_t send(SOCKET s, const uint8_t * buf, uint16_t len)
{
uint8_t status=0;
uint16_t ret=0;
uint16_t freesize=0;
if (len > W5100.SSIZE)
ret = W5100.SSIZE; // check size not to exceed MAX size.
else
ret = len;
// if freebuf is available, start.
do
{
freesize = W5100.getTXFreeSize(s);
status = W5100.readSnSR(s);
if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT))
{
ret = 0;
break;
}
}
while (freesize < ret);
// copy data
W5100.send_data_processing(s, (uint8_t *)buf, ret);
W5100.execCmdSn(s, Sock_SEND);
/* +2008.01 bj */
while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK )
{
/* m2008.01 [bj] : reduce code */
if ( W5100.readSnSR(s) == SnSR::CLOSED )
{
close(s);
return 0;
}
}
/* +2008.01 bj */
W5100.writeSnIR(s, SnIR::SEND_OK);
return ret;
}
/**
* @brief This function is an application I/F function which is used to receive the data in TCP mode.
* It continues to wait for data as much as the application wants to receive.
*
* @return received data size for success else -1.
*/
uint16_t recv(SOCKET s, uint8_t *buf, uint16_t len)
{
uint16_t ret=0;
if ( len > 0 )
{
W5100.recv_data_processing(s, buf, len);
W5100.execCmdSn(s, Sock_RECV);
ret = len;
}
return ret;
}
/**
* @brief Returns the first byte in the receive queue (no checking)
*
* @return
*/
uint16_t peek(SOCKET s, uint8_t *buf)
{
W5100.recv_data_processing(s, buf, 1, 1);
return 1;
}
/**
* @brief This function is an application I/F function which is used to send the data for other then TCP mode.
* Unlike TCP transmission, The peer's destination address and the port is needed.
*
* @return This function return send data size for success else -1.
*/
uint16_t sendto(SOCKET s, const uint8_t *buf, uint16_t len, uint8_t *addr, uint16_t port)
{
uint16_t ret=0;
if (len > W5100.SSIZE) ret = W5100.SSIZE; // check size not to exceed MAX size.
else ret = len;
if
(
((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
((port == 0x00)) ||(ret == 0)
)
{
/* +2008.01 [bj] : added return value */
ret = 0;
}
else
{
W5100.writeSnDIPR(s, addr);
W5100.writeSnDPORT(s, port);
// copy data
W5100.send_data_processing(s, (uint8_t *)buf, ret);
W5100.execCmdSn(s, Sock_SEND);
/* +2008.01 bj */
while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK )
{
if (W5100.readSnIR(s) & SnIR::TIMEOUT)
{
/* +2008.01 [bj]: clear interrupt */
W5100.writeSnIR(s, (SnIR::SEND_OK | SnIR::TIMEOUT)); /* clear SEND_OK & TIMEOUT */
return 0;
}
}
/* +2008.01 bj */
W5100.writeSnIR(s, SnIR::SEND_OK);
}
return ret;
}
/**
* @brief This function is an application I/F function which is used to receive the data in other then
* TCP mode. This function is used to receive UDP, IP_RAW and MAC_RAW mode, and handle the header as well.
*
* @return This function return received data size for success else -1.
*/
uint16_t recvfrom(SOCKET s, uint8_t *buf, uint16_t len, uint8_t *addr, uint16_t *port)
{
uint8_t head[8];
uint16_t data_len=0;
uint16_t ptr=0;
if ( len > 0 )
{
ptr = W5100.readSnRX_RD(s);
switch (W5100.readSnMR(s) & 0x07)
{
case SnMR::UDP :
W5100.read_data(s, (uint8_t *)ptr, head, 0x08);
ptr += 8;
// read peer's IP address, port number.
addr[0] = head[0];
addr[1] = head[1];
addr[2] = head[2];
addr[3] = head[3];
*port = head[4];
*port = (*port << 8) + head[5];
data_len = head[6];
data_len = (data_len << 8) + head[7];
W5100.read_data(s, (uint8_t *)ptr, buf, data_len); // data copy.
ptr += data_len;
W5100.writeSnRX_RD(s, ptr);
break;
case SnMR::IPRAW :
W5100.read_data(s, (uint8_t *)ptr, head, 0x06);
ptr += 6;
addr[0] = head[0];
addr[1] = head[1];
addr[2] = head[2];
addr[3] = head[3];
data_len = head[4];
data_len = (data_len << 8) + head[5];
W5100.read_data(s, (uint8_t *)ptr, buf, data_len); // data copy.
ptr += data_len;
W5100.writeSnRX_RD(s, ptr);
break;
case SnMR::MACRAW:
W5100.read_data(s,(uint8_t*)ptr,head,2);
ptr+=2;
data_len = head[0];
data_len = (data_len<<8) + head[1] - 2;
W5100.read_data(s,(uint8_t*) ptr,buf,data_len);
ptr += data_len;
W5100.writeSnRX_RD(s, ptr);
break;
default :
break;
}
W5100.execCmdSn(s, Sock_RECV);
}
return data_len;
}
uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len)
{
uint8_t status=0;
uint16_t ret=0;
if (len > W5100.SSIZE)
ret = W5100.SSIZE; // check size not to exceed MAX size.
else
ret = len;
if (ret == 0)
return 0;
W5100.send_data_processing(s, (uint8_t *)buf, ret);
W5100.execCmdSn(s, Sock_SEND);
while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK )
{
status = W5100.readSnSR(s);
if (W5100.readSnIR(s) & SnIR::TIMEOUT)
{
/* in case of igmp, if send fails, then socket closed */
/* if you want change, remove this code. */
close(s);
return 0;
}
}
W5100.writeSnIR(s, SnIR::SEND_OK);
return ret;
}

View file

@ -0,0 +1,20 @@
#ifndef _SOCKET_H_
#define _SOCKET_H_
#include "w5100.h"
extern uint8_t socket(SOCKET s, uint8_t protocol, uint16_t port, uint8_t flag); // Opens a socket(TCP or UDP or IP_RAW mode)
extern void close(SOCKET s); // Close socket
extern uint8_t connect(SOCKET s, uint8_t * addr, uint16_t port); // Establish TCP connection (Active connection)
extern void disconnect(SOCKET s); // disconnect the connection
extern uint8_t listen(SOCKET s); // Establish TCP connection (Passive connection)
extern uint16_t send(SOCKET s, const uint8_t * buf, uint16_t len); // Send data (TCP)
extern uint16_t recv(SOCKET s, uint8_t * buf, uint16_t len); // Receive data (TCP)
extern uint16_t peek(SOCKET s, uint8_t *buf);
extern uint16_t sendto(SOCKET s, const uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port); // Send data (UDP/IP RAW)
extern uint16_t recvfrom(SOCKET s, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port); // Receive data (UDP/IP RAW)
extern uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len);
#endif
/* _SOCKET_H_ */

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either the GNU General Public License version 2
* or the GNU Lesser General Public License version 2.1, both as
* published by the Free Software Foundation.
*/
#include <stdio.h>
#include <string.h>
#include <avr/interrupt.h>
#include "w5100.h"
// W5100 controller instance
W5100Class W5100;
#define TX_RX_MAX_BUF_SIZE 2048
#define TX_BUF 0x1100
#define RX_BUF (TX_BUF + TX_RX_MAX_BUF_SIZE)
#define TXBUF_BASE 0x4000
#define RXBUF_BASE 0x6000
void W5100Class::init(void)
{
delay(300);
SPI.begin();
initSS();
writeMR(1<<RST);
writeTMSR(0x55);
writeRMSR(0x55);
for (int i=0; i<MAX_SOCK_NUM; i++) {
SBASE[i] = TXBUF_BASE + SSIZE * i;
RBASE[i] = RXBUF_BASE + RSIZE * i;
}
}
uint16_t W5100Class::getTXFreeSize(SOCKET s)
{
uint16_t val=0, val1=0;
do {
val1 = readSnTX_FSR(s);
if (val1 != 0)
val = readSnTX_FSR(s);
}
while (val != val1);
return val;
}
uint16_t W5100Class::getRXReceivedSize(SOCKET s)
{
uint16_t val=0,val1=0;
do {
val1 = readSnRX_RSR(s);
if (val1 != 0)
val = readSnRX_RSR(s);
}
while (val != val1);
return val;
}
void W5100Class::send_data_processing(SOCKET s, uint8_t *data, uint16_t len)
{
uint16_t ptr = readSnTX_WR(s);
uint16_t offset = ptr & SMASK;
uint16_t dstAddr = offset + SBASE[s];
if (offset + len > SSIZE)
{
// Wrap around circular buffer
uint16_t size = SSIZE - offset;
write(dstAddr, data, size);
write(SBASE[s], data + size, len - size);
}
else {
write(dstAddr, data, len);
}
ptr += len;
writeSnTX_WR(s, ptr);
}
void W5100Class::recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek)
{
uint16_t ptr;
ptr = readSnRX_RD(s);
read_data(s, (uint8_t *)ptr, data, len);
if (!peek)
{
ptr += len;
writeSnRX_RD(s, ptr);
}
}
void W5100Class::read_data(SOCKET s, volatile uint8_t *src, volatile uint8_t *dst, uint16_t len)
{
uint16_t size;
uint16_t src_mask;
uint16_t src_ptr;
src_mask = (uint16_t)src & RMASK;
src_ptr = RBASE[s] + src_mask;
if( (src_mask + len) > RSIZE )
{
size = RSIZE - src_mask;
read(src_ptr, (uint8_t *)dst, size);
dst += size;
read(RBASE[s], (uint8_t *) dst, len - size);
}
else
read(src_ptr, (uint8_t *) dst, len);
}
uint8_t W5100Class::write(uint16_t _addr, uint8_t _data)
{
setSS();
SPI.transfer(0xF0);
SPI.transfer(_addr >> 8);
SPI.transfer(_addr & 0xFF);
SPI.transfer(_data);
resetSS();
return 1;
}
uint16_t W5100Class::write(uint16_t _addr, uint8_t *_buf, uint16_t _len)
{
for (int i=0; i<_len; i++)
{
setSS();
SPI.transfer(0xF0);
SPI.transfer(_addr >> 8);
SPI.transfer(_addr & 0xFF);
_addr++;
SPI.transfer(_buf[i]);
resetSS();
}
return _len;
}
uint8_t W5100Class::read(uint16_t _addr)
{
setSS();
SPI.transfer(0x0F);
SPI.transfer(_addr >> 8);
SPI.transfer(_addr & 0xFF);
uint8_t _data = SPI.transfer(0);
resetSS();
return _data;
}
uint16_t W5100Class::read(uint16_t _addr, uint8_t *_buf, uint16_t _len)
{
for (int i=0; i<_len; i++)
{
setSS();
SPI.transfer(0x0F);
SPI.transfer(_addr >> 8);
SPI.transfer(_addr & 0xFF);
_addr++;
_buf[i] = SPI.transfer(0);
resetSS();
}
return _len;
}
void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) {
// Send command to socket
writeSnCR(s, _cmd);
// Wait for command to complete
while (readSnCR(s))
;
}

View file

@ -0,0 +1,381 @@
/*
* Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either the GNU General Public License version 2
* or the GNU Lesser General Public License version 2.1, both as
* published by the Free Software Foundation.
*/
#ifndef W5100_H_INCLUDED
#define W5100_H_INCLUDED
#include <avr/pgmspace.h>
#include <SPI.h>
#define MAX_SOCK_NUM 4
typedef uint8_t SOCKET;
#define IDM_OR 0x8000
#define IDM_AR0 0x8001
#define IDM_AR1 0x8002
#define IDM_DR 0x8003
/*
class MR {
public:
static const uint8_t RST = 0x80;
static const uint8_t PB = 0x10;
static const uint8_t PPPOE = 0x08;
static const uint8_t LB = 0x04;
static const uint8_t AI = 0x02;
static const uint8_t IND = 0x01;
};
*/
/*
class IR {
public:
static const uint8_t CONFLICT = 0x80;
static const uint8_t UNREACH = 0x40;
static const uint8_t PPPoE = 0x20;
static const uint8_t SOCK0 = 0x01;
static const uint8_t SOCK1 = 0x02;
static const uint8_t SOCK2 = 0x04;
static const uint8_t SOCK3 = 0x08;
static inline uint8_t SOCK(SOCKET ch) { return (0x01 << ch); };
};
*/
class SnMR {
public:
static const uint8_t CLOSE = 0x00;
static const uint8_t TCP = 0x01;
static const uint8_t UDP = 0x02;
static const uint8_t IPRAW = 0x03;
static const uint8_t MACRAW = 0x04;
static const uint8_t PPPOE = 0x05;
static const uint8_t ND = 0x20;
static const uint8_t MULTI = 0x80;
};
enum SockCMD {
Sock_OPEN = 0x01,
Sock_LISTEN = 0x02,
Sock_CONNECT = 0x04,
Sock_DISCON = 0x08,
Sock_CLOSE = 0x10,
Sock_SEND = 0x20,
Sock_SEND_MAC = 0x21,
Sock_SEND_KEEP = 0x22,
Sock_RECV = 0x40
};
/*class SnCmd {
public:
static const uint8_t OPEN = 0x01;
static const uint8_t LISTEN = 0x02;
static const uint8_t CONNECT = 0x04;
static const uint8_t DISCON = 0x08;
static const uint8_t CLOSE = 0x10;
static const uint8_t SEND = 0x20;
static const uint8_t SEND_MAC = 0x21;
static const uint8_t SEND_KEEP = 0x22;
static const uint8_t RECV = 0x40;
};
*/
class SnIR {
public:
static const uint8_t SEND_OK = 0x10;
static const uint8_t TIMEOUT = 0x08;
static const uint8_t RECV = 0x04;
static const uint8_t DISCON = 0x02;
static const uint8_t CON = 0x01;
};
class SnSR {
public:
static const uint8_t CLOSED = 0x00;
static const uint8_t INIT = 0x13;
static const uint8_t LISTEN = 0x14;
static const uint8_t SYNSENT = 0x15;
static const uint8_t SYNRECV = 0x16;
static const uint8_t ESTABLISHED = 0x17;
static const uint8_t FIN_WAIT = 0x18;
static const uint8_t CLOSING = 0x1A;
static const uint8_t TIME_WAIT = 0x1B;
static const uint8_t CLOSE_WAIT = 0x1C;
static const uint8_t LAST_ACK = 0x1D;
static const uint8_t UDP = 0x22;
static const uint8_t IPRAW = 0x32;
static const uint8_t MACRAW = 0x42;
static const uint8_t PPPOE = 0x5F;
};
class IPPROTO {
public:
static const uint8_t IP = 0;
static const uint8_t ICMP = 1;
static const uint8_t IGMP = 2;
static const uint8_t GGP = 3;
static const uint8_t TCP = 6;
static const uint8_t PUP = 12;
static const uint8_t UDP = 17;
static const uint8_t IDP = 22;
static const uint8_t ND = 77;
static const uint8_t RAW = 255;
};
class W5100Class {
public:
void init();
/**
* @brief This function is being used for copy the data form Receive buffer of the chip to application buffer.
*
* It calculate the actual physical address where one has to read
* the data from Receive buffer. Here also take care of the condition while it exceed
* the Rx memory uper-bound of socket.
*/
void read_data(SOCKET s, volatile uint8_t * src, volatile uint8_t * dst, uint16_t len);
/**
* @brief This function is being called by send() and sendto() function also.
*
* This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer
* register. User should read upper byte first and lower byte later to get proper value.
*/
void send_data_processing(SOCKET s, uint8_t *data, uint16_t len);
/**
* @brief This function is being called by recv() also.
*
* This function read the Rx read pointer register
* and after copy the data from receive buffer update the Rx write pointer register.
* User should read upper byte first and lower byte later to get proper value.
*/
void recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek = 0);
inline void setGatewayIp(uint8_t *_addr);
inline void getGatewayIp(uint8_t *_addr);
inline void setSubnetMask(uint8_t *_addr);
inline void getSubnetMask(uint8_t *_addr);
inline void setMACAddress(uint8_t * addr);
inline void getMACAddress(uint8_t * addr);
inline void setIPAddress(uint8_t * addr);
inline void getIPAddress(uint8_t * addr);
inline void setRetransmissionTime(uint16_t timeout);
inline void setRetransmissionCount(uint8_t _retry);
void execCmdSn(SOCKET s, SockCMD _cmd);
uint16_t getTXFreeSize(SOCKET s);
uint16_t getRXReceivedSize(SOCKET s);
// W5100 Registers
// ---------------
private:
static uint8_t write(uint16_t _addr, uint8_t _data);
static uint16_t write(uint16_t addr, uint8_t *buf, uint16_t len);
static uint8_t read(uint16_t addr);
static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);
#define __GP_REGISTER8(name, address) \
static inline void write##name(uint8_t _data) { \
write(address, _data); \
} \
static inline uint8_t read##name() { \
return read(address); \
}
#define __GP_REGISTER16(name, address) \
static void write##name(uint16_t _data) { \
write(address, _data >> 8); \
write(address+1, _data & 0xFF); \
} \
static uint16_t read##name() { \
uint16_t res = read(address); \
res = (res << 8) + read(address + 1); \
return res; \
}
#define __GP_REGISTER_N(name, address, size) \
static uint16_t write##name(uint8_t *_buff) { \
return write(address, _buff, size); \
} \
static uint16_t read##name(uint8_t *_buff) { \
return read(address, _buff, size); \
}
public:
__GP_REGISTER8 (MR, 0x0000); // Mode
__GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address
__GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address
__GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address
__GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address
__GP_REGISTER8 (IR, 0x0015); // Interrupt
__GP_REGISTER8 (IMR, 0x0016); // Interrupt Mask
__GP_REGISTER16(RTR, 0x0017); // Timeout address
__GP_REGISTER8 (RCR, 0x0019); // Retry count
__GP_REGISTER8 (RMSR, 0x001A); // Receive memory size
__GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size
__GP_REGISTER8 (PATR, 0x001C); // Authentication type address in PPPoE mode
__GP_REGISTER8 (PTIMER, 0x0028); // PPP LCP Request Timer
__GP_REGISTER8 (PMAGIC, 0x0029); // PPP LCP Magic Number
__GP_REGISTER_N(UIPR, 0x002A, 4); // Unreachable IP address in UDP mode
__GP_REGISTER16(UPORT, 0x002E); // Unreachable Port address in UDP mode
#undef __GP_REGISTER8
#undef __GP_REGISTER16
#undef __GP_REGISTER_N
// W5100 Socket registers
// ----------------------
private:
static inline uint8_t readSn(SOCKET _s, uint16_t _addr);
static inline uint8_t writeSn(SOCKET _s, uint16_t _addr, uint8_t _data);
static inline uint16_t readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len);
static inline uint16_t writeSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len);
static const uint16_t CH_BASE = 0x0400;
static const uint16_t CH_SIZE = 0x0100;
#define __SOCKET_REGISTER8(name, address) \
static inline void write##name(SOCKET _s, uint8_t _data) { \
writeSn(_s, address, _data); \
} \
static inline uint8_t read##name(SOCKET _s) { \
return readSn(_s, address); \
}
#define __SOCKET_REGISTER16(name, address) \
static void write##name(SOCKET _s, uint16_t _data) { \
writeSn(_s, address, _data >> 8); \
writeSn(_s, address+1, _data & 0xFF); \
} \
static uint16_t read##name(SOCKET _s) { \
uint16_t res = readSn(_s, address); \
res = (res << 8) + readSn(_s, address + 1); \
return res; \
}
#define __SOCKET_REGISTER_N(name, address, size) \
static uint16_t write##name(SOCKET _s, uint8_t *_buff) { \
return writeSn(_s, address, _buff, size); \
} \
static uint16_t read##name(SOCKET _s, uint8_t *_buff) { \
return readSn(_s, address, _buff, size); \
}
public:
__SOCKET_REGISTER8(SnMR, 0x0000) // Mode
__SOCKET_REGISTER8(SnCR, 0x0001) // Command
__SOCKET_REGISTER8(SnIR, 0x0002) // Interrupt
__SOCKET_REGISTER8(SnSR, 0x0003) // Status
__SOCKET_REGISTER16(SnPORT, 0x0004) // Source Port
__SOCKET_REGISTER_N(SnDHAR, 0x0006, 6) // Destination Hardw Addr
__SOCKET_REGISTER_N(SnDIPR, 0x000C, 4) // Destination IP Addr
__SOCKET_REGISTER16(SnDPORT, 0x0010) // Destination Port
__SOCKET_REGISTER16(SnMSSR, 0x0012) // Max Segment Size
__SOCKET_REGISTER8(SnPROTO, 0x0014) // Protocol in IP RAW Mode
__SOCKET_REGISTER8(SnTOS, 0x0015) // IP TOS
__SOCKET_REGISTER8(SnTTL, 0x0016) // IP TTL
__SOCKET_REGISTER16(SnTX_FSR, 0x0020) // TX Free Size
__SOCKET_REGISTER16(SnTX_RD, 0x0022) // TX Read Pointer
__SOCKET_REGISTER16(SnTX_WR, 0x0024) // TX Write Pointer
__SOCKET_REGISTER16(SnRX_RSR, 0x0026) // RX Free Size
__SOCKET_REGISTER16(SnRX_RD, 0x0028) // RX Read Pointer
__SOCKET_REGISTER16(SnRX_WR, 0x002A) // RX Write Pointer (supported?)
#undef __SOCKET_REGISTER8
#undef __SOCKET_REGISTER16
#undef __SOCKET_REGISTER_N
private:
static const uint8_t RST = 7; // Reset BIT
static const int SOCKETS = 4;
static const uint16_t SMASK = 0x07FF; // Tx buffer MASK
static const uint16_t RMASK = 0x07FF; // Rx buffer MASK
public:
static const uint16_t SSIZE = 2048; // Max Tx buffer size
private:
static const uint16_t RSIZE = 2048; // Max Rx buffer size
uint16_t SBASE[SOCKETS]; // Tx buffer base address
uint16_t RBASE[SOCKETS]; // Rx buffer base address
private:
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
inline static void initSS() { DDRB |= _BV(4); };
inline static void setSS() { PORTB &= ~_BV(4); };
inline static void resetSS() { PORTB |= _BV(4); };
#else
inline static void initSS() { DDRB |= _BV(2); };
inline static void setSS() { PORTB &= ~_BV(2); };
inline static void resetSS() { PORTB |= _BV(2); };
#endif
};
extern W5100Class W5100;
uint8_t W5100Class::readSn(SOCKET _s, uint16_t _addr) {
return read(CH_BASE + _s * CH_SIZE + _addr);
}
uint8_t W5100Class::writeSn(SOCKET _s, uint16_t _addr, uint8_t _data) {
return write(CH_BASE + _s * CH_SIZE + _addr, _data);
}
uint16_t W5100Class::readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t _len) {
return read(CH_BASE + _s * CH_SIZE + _addr, _buf, _len);
}
uint16_t W5100Class::writeSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t _len) {
return write(CH_BASE + _s * CH_SIZE + _addr, _buf, _len);
}
void W5100Class::getGatewayIp(uint8_t *_addr) {
readGAR(_addr);
}
void W5100Class::setGatewayIp(uint8_t *_addr) {
writeGAR(_addr);
}
void W5100Class::getSubnetMask(uint8_t *_addr) {
readSUBR(_addr);
}
void W5100Class::setSubnetMask(uint8_t *_addr) {
writeSUBR(_addr);
}
void W5100Class::getMACAddress(uint8_t *_addr) {
readSHAR(_addr);
}
void W5100Class::setMACAddress(uint8_t *_addr) {
writeSHAR(_addr);
}
void W5100Class::getIPAddress(uint8_t *_addr) {
readSIPR(_addr);
}
void W5100Class::setIPAddress(uint8_t *_addr) {
writeSIPR(_addr);
}
void W5100Class::setRetransmissionTime(uint16_t _timeout) {
writeRTR(_timeout);
}
void W5100Class::setRetransmissionCount(uint8_t _retry) {
writeRCR(_retry);
}
#endif

View file

@ -0,0 +1,335 @@
/* Boards.h - Hardware Abstraction Layer for Firmata library */
#ifndef Firmata_Boards_h
#define Firmata_Boards_h
#include <WProgram.h> // for digitalRead, digitalWrite, etc
// 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
*============================================================================*/
// Arduino Duemilanove, Diecimila, and NG
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
#define TOTAL_ANALOG_PINS 8
#define TOTAL_PINS 24 // 14 digital + 2 unused + 8 analog
#define VERSION_BLINK_PIN 13
#define IS_PIN_DIGITAL(p) (((p) >= 2 && (p) <= 13) || ((p) >= 16 && (p) <= 21))
#define IS_PIN_ANALOG(p) ((p) >= 16 && (p) <= 23)
#define IS_PIN_PWM(p) IS_PIN_DIGITAL(p)
#define IS_PIN_SERVO(p) ((p) >= 2 && (p) <= 13 && (p) - 2 < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#define PIN_TO_DIGITAL(p) (((p) < 16) ? (p) : (p) - 2)
#define PIN_TO_ANALOG(p) ((p) - 16)
#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p)
#define PIN_TO_SERVO(p) ((p) - 2)
#define ARDUINO_PINOUT_OPTIMIZE 1
// old Arduinos
#elif defined(__AVR_ATmega8__)
#define TOTAL_ANALOG_PINS 6
#define TOTAL_PINS 22 // 14 digital + 2 unused + 6 analog
#define VERSION_BLINK_PIN 13
#define IS_PIN_DIGITAL(p) (((p) >= 2 && (p) <= 13) || ((p) >= 16 && (p) <= 21))
#define IS_PIN_ANALOG(p) ((p) >= 16 && (p) <= 21)
#define IS_PIN_PWM(p) IS_PIN_DIGITAL(p)
#define IS_PIN_SERVO(p) ((p) >= 2 && (p) <= 13 && (p) - 2 < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#define PIN_TO_DIGITAL(p) (((p) < 16) ? (p) : (p) - 2)
#define PIN_TO_ANALOG(p) ((p) - 16)
#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 IS_PIN_DIGITAL(p) ((p) >= 2 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 54 && (p) < TOTAL_PINS)
#define IS_PIN_PWM(p) IS_PIN_DIGITAL(p)
#define IS_PIN_SERVO(p) ((p) >= 2 && (p) - 2 < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#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)
// Wiring
#elif defined(__AVR_ATmega128__)
#define TOTAL_ANALOG_PINS 8
#define TOTAL_PINS 51
#define VERSION_BLINK_PIN 48
// TODO: hardware abstraction for wiring board
// 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 IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) (0)
#define IS_PIN_PWM(p) IS_PIN_DIGITAL(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#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__)
#define TOTAL_ANALOG_PINS 12
#define TOTAL_PINS 25 // 11 digital + 12 analog
#define VERSION_BLINK_PIN 11
#define IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 11 && (p) <= 22)
#define IS_PIN_PWM(p) IS_PIN_DIGITAL(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#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++ 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 IS_PIN_DIGITAL(p) ((p) >= 0 && (p) < TOTAL_PINS)
#define IS_PIN_ANALOG(p) ((p) >= 38 && (p) < TOTAL_PINS)
#define IS_PIN_PWM(p) IS_PIN_DIGITAL(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#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)
// 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) IS_PIN_DIGITAL(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#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) IS_PIN_DIGITAL(p)
#define IS_PIN_SERVO(p) ((p) >= 0 && (p) < MAX_SERVOS)
#define IS_PIN_I2C(p) (0)
#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)
// anything else
#else
#error "Please edit Boards.h with a hardware abstraction for this board"
#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 & B11111100 & bitmask; // ignore Rx/Tx 0/1
if (port == 1) return PINB & B00111111 & bitmask; // pins 8-13 (14,15 are disabled for the crystal)
if (port == 2) return PINC & 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; // Tx & Rx pins
cli();
PORTD = (PORTD & ~bitmask) | (bitmask & value);
sei();
} else if (port == 1) {
cli();
PORTB = (PORTB & ~bitmask) | (bitmask & value);
sei();
} else if (port == 2) {
cli();
PORTC = (PORTC & ~bitmask) | (bitmask & value);
sei();
}
#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));
#endif
}
#ifndef TOTAL_PORTS
#define TOTAL_PORTS ((TOTAL_PINS + 7) / 8)
#endif
#endif /* Firmata_Boards_h */

View file

@ -0,0 +1,442 @@
/*
Firmata.cpp - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
*/
//******************************************************************************
//* Includes
//******************************************************************************
#include "WProgram.h"
#include "HardwareSerial.h"
#include "Firmata.h"
extern "C" {
#include <string.h>
#include <stdlib.h>
}
//******************************************************************************
//* Support Functions
//******************************************************************************
void sendValueAsTwo7bitBytes(int value)
{
Serial.print(value & B01111111, BYTE); // LSB
Serial.print(value >> 7 & B01111111, BYTE); // MSB
}
void startSysex(void)
{
Serial.print(START_SYSEX, BYTE);
}
void endSysex(void)
{
Serial.print(END_SYSEX, BYTE);
}
//******************************************************************************
//* Constructors
//******************************************************************************
FirmataClass::FirmataClass(void)
{
firmwareVersionCount = 0;
systemReset();
}
//******************************************************************************
//* Public Methods
//******************************************************************************
/* begin method for overriding default serial bitrate */
void FirmataClass::begin(void)
{
begin(57600);
}
/* begin method for overriding default serial bitrate */
void FirmataClass::begin(long speed)
{
#if defined(__AVR_ATmega128__) // Wiring
Serial.begin((uint32_t)speed);
#else
Serial.begin(speed);
#endif
blinkVersion();
delay(300);
printVersion();
printFirmwareVersion();
}
// output the protocol version message to the serial port
void FirmataClass::printVersion(void) {
Serial.print(REPORT_VERSION, BYTE);
Serial.print(FIRMATA_MAJOR_VERSION, BYTE);
Serial.print(FIRMATA_MINOR_VERSION, BYTE);
}
void FirmataClass::blinkVersion(void)
{
// flash the pin with the protocol version
pinMode(VERSION_BLINK_PIN,OUTPUT);
pin13strobe(FIRMATA_MAJOR_VERSION, 200, 400);
delay(300);
pin13strobe(2,1,4); // separator, a quick burst
delay(300);
pin13strobe(FIRMATA_MINOR_VERSION, 200, 400);
}
void FirmataClass::printFirmwareVersion(void)
{
byte i;
if(firmwareVersionCount) { // make sure that the name has been set before reporting
startSysex();
Serial.print(REPORT_FIRMWARE, BYTE);
Serial.print(firmwareVersionVector[0]); // major version number
Serial.print(firmwareVersionVector[1]); // minor version number
for(i=2; i<firmwareVersionCount; ++i) {
sendValueAsTwo7bitBytes(firmwareVersionVector[i]);
}
endSysex();
}
}
void 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 FirmataClass::available(void)
{
return Serial.available();
}
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;
char *buffer = (char*)malloc(bufferLength * sizeof(char));
byte i = 1;
byte j = 0;
while(j < bufferLength) {
buffer[j] = (char)storedInputData[i];
i++;
buffer[j] += (char)(storedInputData[i] << 7);
i++;
j++;
}
(*currentStringCallback)(buffer);
}
break;
default:
if(currentSysexCallback)
(*currentSysexCallback)(storedInputData[0], sysexBytesRead - 1, storedInputData + 1);
}
}
void FirmataClass::processInput(void)
{
int inputData = Serial.read(); // this is 'int' to handle -1 when no data
int command;
// TODO make sure it handles -1 properly
if (parsingSysex) {
if(inputData == END_SYSEX) {
//stop sysex byte
parsingSysex = false;
//fire off handler function
processSysexMessage();
} else {
//normal data byte - add to buffer
storedInputData[sysexBytesRead] = inputData;
sysexBytesRead++;
}
} else if( (waitForData > 0) && (inputData < 128) ) {
waitForData--;
storedInputData[waitForData] = inputData;
if( (waitForData==0) && executeMultiByteCommand ) { // got the whole message
switch(executeMultiByteCommand) {
case ANALOG_MESSAGE:
if(currentAnalogCallback) {
(*currentAnalogCallback)(multiByteChannel,
(storedInputData[0] << 7)
+ storedInputData[1]);
}
break;
case DIGITAL_MESSAGE:
if(currentDigitalCallback) {
(*currentDigitalCallback)(multiByteChannel,
(storedInputData[0] << 7)
+ storedInputData[1]);
}
break;
case SET_PIN_MODE:
if(currentPinModeCallback)
(*currentPinModeCallback)(storedInputData[1], storedInputData[0]);
break;
case REPORT_ANALOG:
if(currentReportAnalogCallback)
(*currentReportAnalogCallback)(multiByteChannel,storedInputData[0]);
break;
case REPORT_DIGITAL:
if(currentReportDigitalCallback)
(*currentReportDigitalCallback)(multiByteChannel,storedInputData[0]);
break;
}
executeMultiByteCommand = 0;
}
} else {
// remove channel info from command byte if less than 0xF0
if(inputData < 0xF0) {
command = inputData & 0xF0;
multiByteChannel = inputData & 0x0F;
} else {
command = inputData;
// commands in the 0xF* range don't use channel data
}
switch (command) {
case ANALOG_MESSAGE:
case DIGITAL_MESSAGE:
case SET_PIN_MODE:
waitForData = 2; // two data bytes needed
executeMultiByteCommand = command;
break;
case REPORT_ANALOG:
case REPORT_DIGITAL:
waitForData = 1; // two data bytes needed
executeMultiByteCommand = command;
break;
case START_SYSEX:
parsingSysex = true;
sysexBytesRead = 0;
break;
case SYSTEM_RESET:
systemReset();
break;
case REPORT_VERSION:
Firmata.printVersion();
break;
}
}
}
//------------------------------------------------------------------------------
// Serial Send Handling
// send an analog message
void FirmataClass::sendAnalog(byte pin, int value)
{
// pin can only be 0-15, so chop higher bits
Serial.print(ANALOG_MESSAGE | (pin & 0xF), BYTE);
sendValueAsTwo7bitBytes(value);
}
// send a single digital pin in a digital message
void FirmataClass::sendDigital(byte pin, int value)
{
/* TODO add single pin digital messages to the protocol, this needs to
* track the last digital data sent so that it can be sure to change just
* one bit in the packet. This is complicated by the fact that the
* numbering of the pins will probably differ on Arduino, Wiring, and
* other boards. The DIGITAL_MESSAGE sends 14 bits at a time, but it is
* probably easier to send 8 bit ports for any board with more than 14
* digital pins.
*/
// TODO: 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 FirmataClass::sendDigitalPort(byte portNumber, int portData)
{
Serial.print(DIGITAL_MESSAGE | (portNumber & 0xF),BYTE);
Serial.print((byte)portData % 128, BYTE); // Tx bits 0-6
Serial.print(portData >> 7, BYTE); // Tx bits 7-13
}
void FirmataClass::sendSysex(byte command, byte bytec, byte* bytev)
{
byte i;
startSysex();
Serial.print(command, BYTE);
for(i=0; i<bytec; i++) {
sendValueAsTwo7bitBytes(bytev[i]);
}
endSysex();
}
void FirmataClass::sendString(byte command, const char* string)
{
sendSysex(command, strlen(string), (byte *)string);
}
// send a string as the protocol string type
void FirmataClass::sendString(const char* string)
{
sendString(STRING_DATA, string);
}
// Internal Actions/////////////////////////////////////////////////////////////
// generic callbacks
void FirmataClass::attach(byte command, callbackFunction newFunction)
{
switch(command) {
case ANALOG_MESSAGE: currentAnalogCallback = newFunction; break;
case DIGITAL_MESSAGE: currentDigitalCallback = newFunction; break;
case REPORT_ANALOG: currentReportAnalogCallback = newFunction; break;
case REPORT_DIGITAL: currentReportDigitalCallback = newFunction; break;
case SET_PIN_MODE: currentPinModeCallback = newFunction; break;
}
}
void FirmataClass::attach(byte command, systemResetCallbackFunction newFunction)
{
switch(command) {
case SYSTEM_RESET: currentSystemResetCallback = newFunction; break;
}
}
void FirmataClass::attach(byte command, stringCallbackFunction newFunction)
{
switch(command) {
case STRING_DATA: currentStringCallback = newFunction; break;
}
}
void FirmataClass::attach(byte command, sysexCallbackFunction newFunction)
{
currentSysexCallback = newFunction;
}
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);
}
}
// 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
void 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
}
// =============================================================================
// used for flashing the pin for the version number
void FirmataClass::pin13strobe(int count, int onInterval, int offInterval)
{
byte i;
pinMode(VERSION_BLINK_PIN, OUTPUT);
for(i=0; i<count; i++) {
delay(offInterval);
digitalWrite(VERSION_BLINK_PIN, HIGH);
delay(onInterval);
digitalWrite(VERSION_BLINK_PIN, LOW);
}
}
// make one instance for the user to use
FirmataClass Firmata;

View file

@ -0,0 +1,162 @@
/*
Firmata.h - Firmata library
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
*/
#ifndef Firmata_h
#define Firmata_h
#include <WProgram.h>
#include <inttypes.h>
/* 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 2 // for backwards compatible changes
#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 FirmataClass
{
public:
FirmataClass();
/* Arduino constructors */
void begin();
void begin(long);
/* 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);
void 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);
private:
/* 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);
};
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)
/* Hardware Abstraction Layer */
#include "Boards.h"
#endif /* Firmata_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,14 @@
- make Firmata a subclass of HardwareSerial
- per-pin digital callback, since the per-port callback is a bit complicated
for beginners (maybe Firmata is not for beginners...)
- simplify SimpleDigitalFirmata, take out the code that checks to see if the
data has changed, since it is a bit complicated for this example. Ideally
this example would be based on a call
- turn current SimpleDigitalFirmata into DigitalPortFirmata for a more complex
example using the code which checks for changes before doing anything
- test integration with Wiring

View file

@ -0,0 +1,79 @@
/*
* This firmware reads all inputs and sends them as fast as it can. It was
* inspired by the ease-of-use of the Arduino2Max program.
*
* This example code is in the public domain.
*/
#include <Firmata.h>
byte pin;
int analogValue;
int previousAnalogValues[TOTAL_ANALOG_PINS];
byte portStatus[TOTAL_PORTS]; // each bit: 1=pin is digital input, 0=other/ignore
byte previousPINs[TOTAL_PORTS];
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
/* make sure that the FTDI buffer doesn't go over 60 bytes, otherwise you
get long, random delays. So only read analogs every 20ms or so */
int samplingInterval = 19; // how often to run the main loop (in ms)
void sendPort(byte portNumber, byte portValue)
{
portValue = portValue & portStatus[portNumber];
if(previousPINs[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
}
}
void setup()
{
byte i, port, status;
Firmata.setFirmwareVersion(0, 1);
for(pin = 0; pin < TOTAL_PINS; pin++) {
if IS_PIN_DIGITAL(pin) pinMode(PIN_TO_DIGITAL(pin), INPUT);
}
for (port=0; port<TOTAL_PORTS; port++) {
status = 0;
for (i=0; i<8; i++) {
if (IS_PIN_DIGITAL(port * 8 + i)) status |= (1 << i);
}
portStatus[port] = status;
}
Firmata.begin(57600);
}
void loop()
{
byte i;
for (i=0; i<TOTAL_PORTS; i++) {
sendPort(i, readPort(i));
}
/* make sure that the FTDI buffer doesn't go over 60 bytes, otherwise you
get long, random delays. So only read analogs every 20ms or so */
currentMillis = millis();
if(currentMillis - previousMillis > samplingInterval) {
previousMillis += samplingInterval;
while(Firmata.available()) {
Firmata.processInput();
}
for(pin = 0; pin < TOTAL_ANALOG_PINS; pin++) {
analogValue = analogRead(pin);
if(analogValue != previousAnalogValues[pin]) {
Firmata.sendAnalog(pin, analogValue);
previousAnalogValues[pin] = analogValue;
}
}
}
}

View file

@ -0,0 +1,83 @@
/* This firmware supports as many analog ports as possible, all analog inputs,
* four PWM outputs, and two with servo support.
*
* This example code is in the public domain.
*/
#include <Servo.h>
#include <Firmata.h>
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
/* servos */
Servo servo9, servo10; // one instance per pin
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
int analogPin = 0; // counter for reading analog pins
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void analogWriteCallback(byte pin, int value)
{
switch(pin) {
case 9: servo9.write(value); break;
case 10: servo10.write(value); break;
case 3:
case 5:
case 6:
case 11: // PWM pins
analogWrite(pin, value);
break;
}
}
// -----------------------------------------------------------------------------
// sets bits in a bit array (int) to toggle the reporting of the analogIns
void reportAnalogCallback(byte pin, int value)
{
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << pin);
}
else { // everything but 0 enables reporting of that pin
analogInputsToReport = analogInputsToReport | (1 << pin);
}
// TODO: save status to EEPROM here, if changed
}
/*==============================================================================
* SETUP()
*============================================================================*/
void setup()
{
Firmata.setFirmwareVersion(0, 2);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
servo9.attach(9);
servo10.attach(10);
Firmata.begin(57600);
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
while(Firmata.available())
Firmata.processInput();
currentMillis = millis();
if(currentMillis - previousMillis > 20) {
previousMillis += 20; // run this every 20ms
for(analogPin=0;analogPin<TOTAL_ANALOG_PINS;analogPin++) {
if( analogInputsToReport & (1 << analogPin) )
Firmata.sendAnalog(analogPin, analogRead(analogPin));
}
}
}

View file

@ -0,0 +1,263 @@
# 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
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)/Servo \
-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)/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
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
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) --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

View file

@ -0,0 +1,40 @@
/* This sketch accepts strings and raw sysex messages and echos them back.
*
* This example code is in the public domain.
*/
#include <Firmata.h>
byte analogPin;
void stringCallback(char *myString)
{
Firmata.sendString(myString);
}
void sysexCallback(byte command, byte argc, byte*argv)
{
Serial.print(START_SYSEX, BYTE);
Serial.print(command, BYTE);
for(byte i=0; i<argc; i++) {
Serial.print(argv[i], BYTE);
}
Serial.print(END_SYSEX, BYTE);
}
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(STRING_DATA, stringCallback);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.begin(57600);
}
void loop()
{
while(Firmata.available()) {
Firmata.processInput();
}
}

View file

@ -0,0 +1,263 @@
# 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
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)/Servo \
-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)/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
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
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) --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

View file

@ -0,0 +1,217 @@
/*
Copyright (C) 2009 Jeff Hoefs. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
*/
#include <Wire.h>
#include <Firmata.h>
#define I2C_WRITE B00000000
#define I2C_READ B00001000
#define I2C_READ_CONTINUOUSLY B00010000
#define I2C_STOP_READING B00011000
#define I2C_READ_WRITE_MODE_MASK B00011000
#define MAX_QUERIES 8
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
unsigned int samplingInterval = 32; // default sampling interval is 33ms
unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom()
unsigned int powerPinsEnabled = 0; // use as boolean to prevent enablePowerPins from being called more than once
#define MINIMUM_SAMPLING_INTERVAL 10
#define REGISTER_NOT_SPECIFIED -1
struct i2c_device_info {
byte addr;
byte reg;
byte bytes;
};
i2c_device_info query[MAX_QUERIES];
byte i2cRxData[32];
boolean readingContinuously = false;
byte queryIndex = 0;
void readAndReportData(byte address, int theRegister, byte numBytes)
{
if (theRegister != REGISTER_NOT_SPECIFIED) {
Wire.beginTransmission(address);
Wire.send((byte)theRegister);
Wire.endTransmission();
delayMicroseconds(i2cReadDelayTime); // delay is necessary for some devices such as WiiNunchuck
}
else {
theRegister = 0; // fill the register with a dummy value
}
Wire.requestFrom(address, numBytes);
// check to be sure correct number of bytes were returned by slave
if(numBytes == Wire.available()) {
i2cRxData[0] = address;
i2cRxData[1] = theRegister;
for (int i = 0; i < numBytes; i++) {
i2cRxData[2 + i] = Wire.receive();
}
// send slave address, register and received bytes
Firmata.sendSysex(I2C_REPLY, numBytes + 2, i2cRxData);
}
else {
if(numBytes > Wire.available()) {
Firmata.sendString("I2C Read Error: Too many bytes received");
} else {
Firmata.sendString("I2C Read Error: Too few bytes received");
}
}
}
void sysexCallback(byte command, byte argc, byte *argv)
{
byte mode;
byte slaveAddress;
byte slaveRegister;
byte data;
int delayTime;
if (command == I2C_REQUEST) {
mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
slaveAddress = argv[0];
switch(mode) {
case I2C_WRITE:
Wire.beginTransmission(slaveAddress);
for (byte i = 2; i < argc; i += 2) {
data = argv[i] + (argv[i + 1] << 7);
Wire.send(data);
}
Wire.endTransmission();
delayMicroseconds(70); // TODO is this needed?
break;
case I2C_READ:
if (argc == 6) {
// a slave register is specified
slaveRegister = argv[2] + (argv[3] << 7);
data = argv[4] + (argv[5] << 7); // bytes to read
readAndReportData(slaveAddress, (int)slaveRegister, data);
}
else {
// a slave register is NOT specified
data = argv[2] + (argv[3] << 7); // bytes to read
readAndReportData(slaveAddress, (int)REGISTER_NOT_SPECIFIED, data);
}
break;
case I2C_READ_CONTINUOUSLY:
if ((queryIndex + 1) >= MAX_QUERIES) {
// too many queries, just ignore
Firmata.sendString("too many queries");
break;
}
query[queryIndex].addr = slaveAddress;
query[queryIndex].reg = argv[2] + (argv[3] << 7);
query[queryIndex].bytes = argv[4] + (argv[5] << 7);
readingContinuously = true;
queryIndex++;
break;
case I2C_STOP_READING:
readingContinuously = false;
queryIndex = 0;
break;
default:
break;
}
}
else if (command == SAMPLING_INTERVAL) {
samplingInterval = argv[0] + (argv[1] << 7);
if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) {
samplingInterval = MINIMUM_SAMPLING_INTERVAL;
}
samplingInterval -= 1;
Firmata.sendString("sampling interval");
}
else if (command == I2C_CONFIG) {
delayTime = (argv[4] + (argv[5] << 7)); // MSB
delayTime = (delayTime << 8) + (argv[2] + (argv[3] << 7)); // add LSB
if((argv[0] + (argv[1] << 7)) > 0) {
enablePowerPins(PORTC3, PORTC2);
}
if(delayTime > 0) {
i2cReadDelayTime = delayTime;
}
if(argc > 6) {
// If you extend I2C_Config, handle your data here
}
}
}
void systemResetCallback()
{
readingContinuously = false;
queryIndex = 0;
}
/* reference: BlinkM_funcs.h by Tod E. Kurt, ThingM, http://thingm.com/ */
// Enables Pins A2 and A3 to be used as GND and Power
// so that I2C devices can be plugged directly
// into Arduino header (pins A2 - A5)
static void enablePowerPins(byte pwrpin, byte gndpin)
{
if(powerPinsEnabled == 0) {
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |= _BV(pwrpin);
powerPinsEnabled = 1;
Firmata.sendString("Power pins enabled");
delay(100);
}
}
void setup()
{
Firmata.setFirmwareVersion(2, 0);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.attach(SYSTEM_RESET, systemResetCallback);
for (int i = 0; i < TOTAL_PINS; ++i) {
pinMode(i, OUTPUT);
}
Firmata.begin(57600);
Wire.begin();
}
void loop()
{
while (Firmata.available()) {
Firmata.processInput();
}
currentMillis = millis();
if (currentMillis - previousMillis > samplingInterval) {
previousMillis += samplingInterval;
for (byte i = 0; i < queryIndex; 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,228 @@
/*
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
*/
/*
* This is an old version of StandardFirmata (v2.0). It is kept here because
* its the last version that works on an ATMEGA8 chip. Also, it can be used
* for host software that has not been updated to a newer version of the
* protocol. It also uses the old baud rate of 115200 rather than 57600.
*/
#include <EEPROM.h>
#include <Firmata.h>
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
int analogPin = 0; // counter for reading analog pins
/* digital pins */
byte reportPINs[TOTAL_PORTS]; // PIN == input port
byte previousPINs[TOTAL_PORTS]; // PIN == input port
byte pinStatus[TOTAL_PINS]; // store pin status, default OUTPUT
byte portStatus[TOTAL_PORTS];
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void outputPort(byte portNumber, byte portValue)
{
portValue = portValue &~ portStatus[portNumber];
if(previousPINs[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
Firmata.sendDigitalPort(portNumber, portValue);
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using Serial.print() */
void checkDigitalInputs(void)
{
byte i, tmp;
for(i=0; i < TOTAL_PORTS; i++) {
if(reportPINs[i]) {
switch(i) {
case 0: outputPort(0, PIND &~ B00000011); break; // ignore Rx/Tx 0/1
case 1: outputPort(1, PINB); break;
case 2: outputPort(2, PINC); break;
}
}
}
}
// -----------------------------------------------------------------------------
/* 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) {
byte port = 0;
byte offset = 0;
if (pin < 8) {
port = 0;
offset = 0;
} else if (pin < 14) {
port = 1;
offset = 8;
} else if (pin < 22) {
port = 2;
offset = 14;
}
if(pin > 1) { // ignore RxTx (pins 0 and 1)
pinStatus[pin] = mode;
switch(mode) {
case INPUT:
pinMode(pin, INPUT);
portStatus[port] = portStatus[port] &~ (1 << (pin - offset));
break;
case OUTPUT:
digitalWrite(pin, LOW); // disable PWM
case PWM:
pinMode(pin, OUTPUT);
portStatus[port] = portStatus[port] | (1 << (pin - offset));
break;
//case ANALOG: // TODO figure this out
default:
Firmata.sendString("");
}
// TODO: save status to EEPROM here, if changed
}
}
void analogWriteCallback(byte pin, int value)
{
setPinModeCallback(pin,PWM);
analogWrite(pin, value);
}
void digitalWriteCallback(byte port, int value)
{
switch(port) {
case 0: // pins 2-7 (don't change Rx/Tx, pins 0 and 1)
// 0xFF03 == B1111111100000011 0x03 == B00000011
PORTD = (value &~ 0xFF03) | (PORTD & 0x03);
break;
case 1: // pins 8-13 (14,15 are disabled for the crystal)
PORTB = (byte)value;
break;
case 2: // analog pins used as digital
PORTC = (byte)value;
break;
}
}
// -----------------------------------------------------------------------------
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
*/
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
//}
void reportAnalogCallback(byte pin, int value)
{
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << pin);
}
else { // everything but 0 enables reporting of that pin
analogInputsToReport = analogInputsToReport | (1 << pin);
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
reportPINs[port] = (byte)value;
if(port == 2) // turn off analog reporting when used as digital
analogInputsToReport = 0;
}
/*==============================================================================
* SETUP()
*============================================================================*/
void setup()
{
byte i;
Firmata.setFirmwareVersion(2, 0);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
portStatus[0] = B00000011; // ignore Tx/RX pins
portStatus[1] = B11000000; // ignore 14/15 pins
portStatus[2] = B00000000;
// for(i=0; i<TOTAL_PINS; ++i) { // TODO make this work with analogs
for(i=0; i<14; ++i) {
setPinModeCallback(i,OUTPUT);
}
// set all outputs to 0 to make sure internal pull-up resistors are off
PORTB = 0; // pins 8-15
PORTC = 0; // analog port
PORTD = 0; // pins 0-7
// TODO rethink the init, perhaps it should report analog on default
for(i=0; i<TOTAL_PORTS; ++i) {
reportPINs[i] = false;
}
// TODO: load state from EEPROM here
/* send digital inputs here, if enabled, to set the initial state on the
* host computer, since once in the loop(), this firmware will only send
* digital data on change. */
if(reportPINs[0]) outputPort(0, PIND &~ B00000011); // ignore Rx/Tx 0/1
if(reportPINs[1]) outputPort(1, PINB);
if(reportPINs[2]) outputPort(2, PINC);
Firmata.begin(115200);
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* FTDI buffer using Serial.print() */
checkDigitalInputs();
currentMillis = millis();
if(currentMillis - previousMillis > 20) {
previousMillis += 20; // run this every 20ms
/* SERIALREAD - Serial.read() uses a 128 byte circular buffer, so handle
* all serialReads at once, i.e. empty the buffer */
while(Firmata.available())
Firmata.processInput();
/* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over
* 60 bytes. use a timer to sending an event character every 4 ms to
* trigger the buffer to dump. */
/* ANALOGREAD - right after the event character, do all of the
* analogReads(). These only need to be done every 4ms. */
for(analogPin=0;analogPin<TOTAL_ANALOG_PINS;analogPin++) {
if( analogInputsToReport & (1 << analogPin) ) {
Firmata.sendAnalog(analogPin, analogRead(analogPin));
}
}
}
}

View file

@ -0,0 +1,263 @@
# 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
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)/Servo \
-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)/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
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
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) --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

View file

@ -0,0 +1,42 @@
/* This firmware supports as many servos as possible using the Servo library
* included in Arduino 0017
*
* TODO add message to configure minPulse/maxPulse/degrees
*
* This example code is in the public domain.
*/
#include <Servo.h>
#include <Firmata.h>
Servo servos[MAX_SERVOS];
void analogWriteCallback(byte pin, int value)
{
if (IS_PIN_SERVO(pin)) {
servos[PIN_TO_SERVO(pin)].write(value);
}
}
void setup()
{
byte pin;
Firmata.setFirmwareVersion(0, 2);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
for (pin=0; pin < TOTAL_PINS; pin++) {
if (IS_PIN_SERVO(pin)) {
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));
}
}
Firmata.begin(57600);
}
void loop()
{
while(Firmata.available())
Firmata.processInput();
}

View file

@ -0,0 +1,263 @@
# 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
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)/Servo \
-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)/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
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
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) --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

View file

@ -0,0 +1,35 @@
/* Supports as many analog inputs and analog PWM outputs as possible.
*
* This example code is in the public domain.
*/
#include <Firmata.h>
byte analogPin = 0;
void analogWriteCallback(byte pin, int value)
{
if (IS_PIN_PWM(pin)) {
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
analogWrite(PIN_TO_PWM(pin), value);
}
}
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.begin(57600);
}
void loop()
{
while(Firmata.available()) {
Firmata.processInput();
}
// do one analogRead per loop, so if PC is sending a lot of
// analog write messages, we will only delay 1 analogRead
Firmata.sendAnalog(analogPin, analogRead(analogPin));
analogPin = analogPin + 1;
if (analogPin >= TOTAL_ANALOG_PINS) analogPin = 0;
}

View file

@ -0,0 +1,263 @@
# 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
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)/Servo \
-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)/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
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
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) --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

View file

@ -0,0 +1,61 @@
/* Supports as many digital inputs and outputs as possible.
*
* This example code is in the public domain.
*/
#include <Firmata.h>
byte previousPIN[TOTAL_PORTS]; // PIN means PORT for input
byte previousPORT[TOTAL_PORTS];
void outputPort(byte portNumber, byte portValue)
{
// only send the data when it changes, otherwise you get too many messages!
if (previousPIN[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPIN[portNumber] = portValue;
}
}
void setPinModeCallback(byte pin, int mode) {
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), mode);
}
}
void digitalWriteCallback(byte port, int value)
{
byte i;
byte currentPinValue, previousPinValue;
if (port < TOTAL_PORTS && value != previousPORT[port]) {
for(i=0; i<8; i++) {
currentPinValue = (byte) value & (1 << i);
previousPinValue = previousPORT[port] & (1 << i);
if(currentPinValue != previousPinValue) {
digitalWrite(i + (port*8), currentPinValue);
}
}
previousPORT[port] = value;
}
}
void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.begin(57600);
}
void loop()
{
byte i;
for (i=0; i<TOTAL_PORTS; i++) {
outputPort(i, readPort(i));
}
while(Firmata.available()) {
Firmata.processInput();
}
}

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,398 @@
/*
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
formatted using the GNU C formatting and indenting
*/
/*
* TODO: use Program Control to load stored profiles from EEPROM
*/
#include <Servo.h>
#include <Firmata.h>
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
/* digital input ports */
byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence
byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent
/* pins configuration */
byte pinConfig[TOTAL_PINS]; // configuration of every pin
byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else
int pinState[TOTAL_PINS]; // any value that has been written
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
int samplingInterval = 19; // how often to run the main loop (in ms)
Servo servos[MAX_SERVOS];
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void outputPort(byte portNumber, byte portValue, byte forceSend)
{
// pins not configured as INPUT are cleared to zeros
portValue = portValue & portConfigInputs[portNumber];
// only send if the value is different than previously sent
if(forceSend || previousPINs[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using Serial.print() */
void checkDigitalInputs(void)
{
/* Using non-looping code allows constants to be given to readPort().
* The compiler will apply substantial optimizations if the inputs
* to readPort() are compile-time constants. */
if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false);
if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false);
if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false);
if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false);
if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false);
if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false);
if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false);
if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false);
if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false);
if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false);
if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false);
if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false);
if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false);
if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false);
if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false);
if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false);
}
// -----------------------------------------------------------------------------
/* sets the pin mode to the correct state and sets the relevant bits in the
* two bit-arrays that track Digital I/O and PWM status
*/
void setPinModeCallback(byte pin, int mode)
{
if (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));
}
}
pinState[pin] = 0;
switch(mode) {
case ANALOG:
if (IS_PIN_ANALOG(pin)) {
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
}
pinConfig[pin] = ANALOG;
}
break;
case INPUT:
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
pinConfig[pin] = INPUT;
}
break;
case OUTPUT:
if (IS_PIN_DIGITAL(pin)) {
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
pinConfig[pin] = OUTPUT;
}
break;
case PWM:
if (IS_PIN_PWM(pin)) {
pinMode(PIN_TO_PWM(pin), OUTPUT);
analogWrite(PIN_TO_PWM(pin), 0);
pinConfig[pin] = PWM;
}
break;
case SERVO:
if (IS_PIN_SERVO(pin)) {
pinConfig[pin] = SERVO;
if (!servos[PIN_TO_SERVO(pin)].attached()) {
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));
} else {
Firmata.sendString("Servo only on pins from 2 to 13");
}
}
break;
case I2C:
pinConfig[pin] = mode;
Firmata.sendString("I2C mode not yet supported");
break;
default:
Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM
}
// TODO: save status to EEPROM here, if changed
}
void analogWriteCallback(byte pin, int value)
{
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;
}
}
mask = mask << 1;
}
writePort(port, (byte)value, pinWriteMask);
}
}
// -----------------------------------------------------------------------------
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
*/
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
//}
void reportAnalogCallback(byte analogPin, int value)
{
if (analogPin < TOTAL_ANALOG_PINS) {
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << analogPin);
} else {
analogInputsToReport = analogInputsToReport | (1 << analogPin);
}
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
if (port < TOTAL_PORTS) {
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)
{
switch(command) {
case SERVO_CONFIG:
if(argc > 4) {
// these vars are here for clarity, they'll optimized away by the compiler
byte pin = argv[0];
int minPulse = argv[1] + (argv[2] << 7);
int maxPulse = argv[3] + (argv[4] << 7);
if (IS_PIN_SERVO(pin)) {
// servos are pins from 2 to 13, so offset for array
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);
else
Firmata.sendString("Not enough data");
break;
case EXTENDED_ANALOG:
if (argc > 1) {
int val = argv[1];
if (argc > 2) val |= (argv[2] << 7);
if (argc > 3) val |= (argv[3] << 14);
analogWriteCallback(argv[0], val);
}
break;
case CAPABILITY_QUERY:
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);
}
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;
}
}
/*==============================================================================
* SETUP()
*============================================================================*/
void setup()
{
byte i;
Firmata.setFirmwareVersion(2, 2);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.attach(START_SYSEX, sysexCallback);
// TODO: load state from EEPROM here
/* these are initialized to zero by the compiler startup code
for (i=0; i < TOTAL_PORTS; i++) {
reportPINs[i] = false;
portConfigInputs[i] = 0;
previousPINs[i] = 0;
}
*/
for (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, OUTPUT);
}
}
// by defult, do not report any analog inputs
analogInputsToReport = 0;
Firmata.begin(57600);
/* send digital inputs to set the initial state on the host computer,
* since once in the loop(), this firmware will only send on change */
for (i=0; i < TOTAL_PORTS; i++) {
outputPort(i, readPort(i, portConfigInputs[i]), true);
}
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
byte pin, analogPin;
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* FTDI buffer using Serial.print() */
checkDigitalInputs();
/* SERIALREAD - processing incoming messagse as soon as possible, while still
* checking digital inputs. */
while(Firmata.available())
Firmata.processInput();
/* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over
* 60 bytes. use a timer to sending an event character every 4 ms to
* trigger the buffer to dump. */
currentMillis = millis();
if (currentMillis - previousMillis > samplingInterval) {
previousMillis += samplingInterval;
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
for(pin=0; pin<TOTAL_PINS; pin++) {
if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) {
analogPin = PIN_TO_ANALOG(pin);
if (analogInputsToReport & (1 << analogPin)) {
Firmata.sendAnalog(analogPin, analogRead(analogPin));
}
}
}
}
}

View file

@ -0,0 +1,436 @@
/*
This introduces modifications on the normal Firmata made for Arduino so that the LED
blinks until receiving the first command over serial.
Copyright (C) 2010 David Cuartielles. All rights reserved.
based at 99.9% on Firmata by HC Steiner according to the following license terms:
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
formatted using the GNU C formatting and indenting
*/
/*
* TODO: use Program Control to load stored profiles from EEPROM
*/
#include <Servo.h>
#include <Firmata.h>
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
/* has the command arrived? */
boolean firstCommand = false;
int dataOnSerial = 0;
boolean statusLed = false;
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
/* digital input ports */
byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence
byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent
/* pins configuration */
byte pinConfig[TOTAL_PINS]; // configuration of every pin
byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else
int pinState[TOTAL_PINS]; // any value that has been written
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
int samplingInterval = 19; // how often to run the main loop (in ms)
unsigned long toggleMillis;
Servo servos[MAX_SERVOS];
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void toggleLed()
{
if (millis() - toggleMillis > 500) {
statusLed = !statusLed;
digitalWrite(13, statusLed);
toggleMillis = millis();
}
}
void outputPort(byte portNumber, byte portValue, byte forceSend)
{
// pins not configured as INPUT are cleared to zeros
portValue = portValue & portConfigInputs[portNumber];
// only send if the value is different than previously sent
if(forceSend || previousPINs[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using Serial.print() */
void checkDigitalInputs(void)
{
/* Using non-looping code allows constants to be given to readPort().
* The compiler will apply substantial optimizations if the inputs
* to readPort() are compile-time constants. */
if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false);
if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false);
if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false);
if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false);
if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false);
if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false);
if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false);
if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false);
if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false);
if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false);
if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false);
if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false);
if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false);
if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false);
if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false);
if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false);
}
// -----------------------------------------------------------------------------
/* sets the pin mode to the correct state and sets the relevant bits in the
* two bit-arrays that track Digital I/O and PWM status
*/
void setPinModeCallback(byte pin, int mode)
{
if (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));
}
}
pinState[pin] = 0;
switch(mode) {
case ANALOG:
if (IS_PIN_ANALOG(pin)) {
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
}
pinConfig[pin] = ANALOG;
}
break;
case INPUT:
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
pinConfig[pin] = INPUT;
}
break;
case OUTPUT:
if (IS_PIN_DIGITAL(pin)) {
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
pinConfig[pin] = OUTPUT;
}
break;
case PWM:
if (IS_PIN_PWM(pin)) {
pinMode(PIN_TO_PWM(pin), OUTPUT);
analogWrite(PIN_TO_PWM(pin), 0);
pinConfig[pin] = PWM;
}
break;
case SERVO:
if (IS_PIN_SERVO(pin)) {
pinConfig[pin] = SERVO;
if (!servos[PIN_TO_SERVO(pin)].attached()) {
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));
} else {
Firmata.sendString("Servo only on pins from 2 to 13");
}
}
break;
case I2C:
pinConfig[pin] = mode;
Firmata.sendString("I2C mode not yet supported");
break;
default:
Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM
}
// TODO: save status to EEPROM here, if changed
}
void analogWriteCallback(byte pin, int value)
{
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;
}
}
mask = mask << 1;
}
writePort(port, (byte)value, pinWriteMask);
}
}
// -----------------------------------------------------------------------------
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
*/
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
//}
void reportAnalogCallback(byte analogPin, int value)
{
if (analogPin < TOTAL_ANALOG_PINS) {
if(value == 0) {
analogInputsToReport = analogInputsToReport &~ (1 << analogPin);
} else {
analogInputsToReport = analogInputsToReport | (1 << analogPin);
}
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
if (port < TOTAL_PORTS) {
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)
{
switch(command) {
case SERVO_CONFIG:
if(argc > 4) {
// these vars are here for clarity, they'll optimized away by the compiler
byte pin = argv[0];
int minPulse = argv[1] + (argv[2] << 7);
int maxPulse = argv[3] + (argv[4] << 7);
if (IS_PIN_SERVO(pin)) {
// servos are pins from 2 to 13, so offset for array
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);
else
Firmata.sendString("Not enough data");
break;
case EXTENDED_ANALOG:
if (argc > 1) {
int val = argv[1];
if (argc > 2) val |= (argv[2] << 7);
if (argc > 3) val |= (argv[3] << 14);
analogWriteCallback(argv[0], val);
}
break;
case CAPABILITY_QUERY:
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);
}
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;
}
}
/*==============================================================================
* SETUP()
*============================================================================*/
void setup()
{
byte i;
Firmata.setFirmwareVersion(2, 2);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.attach(START_SYSEX, sysexCallback);
// TODO: load state from EEPROM here
/* these are initialized to zero by the compiler startup code
for (i=0; i < TOTAL_PORTS; i++) {
reportPINs[i] = false;
portConfigInputs[i] = 0;
previousPINs[i] = 0;
}
*/
for (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, OUTPUT);
}
}
// by defult, do not report any analog inputs
analogInputsToReport = 0;
Firmata.begin(57600);
/* send digital inputs to set the initial state on the host computer,
* since once in the loop(), this firmware will only send on change */
for (i=0; i < TOTAL_PORTS; i++) {
outputPort(i, readPort(i, portConfigInputs[i]), true);
}
/* init the toggleLed counter */
toggleMillis = millis();
pinMode(13, OUTPUT);
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
byte pin, analogPin;
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* FTDI buffer using Serial.print() */
checkDigitalInputs();
//XXX: hack Firmata to blink until serial command arrives
dataOnSerial = Firmata.available();
if (dataOnSerial > 0 && !firstCommand) {
firstCommand = true;
}
//XXX: do the blink if the first command hasn't arrived yet
// configures pin 13 as output and then back as input
if (!firstCommand) {
toggleLed();
}
/* SERIALREAD - processing incoming messagse as soon as possible, while still
* checking digital inputs. */
while(dataOnSerial) {
Firmata.processInput();
dataOnSerial = Firmata.available();
}
/* 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. */
currentMillis = millis();
if (currentMillis - previousMillis > samplingInterval) {
previousMillis += samplingInterval;
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
for(pin=0; pin<TOTAL_PINS; pin++) {
if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) {
analogPin = PIN_TO_ANALOG(pin);
if (analogInputsToReport & (1 << analogPin)) {
Firmata.sendAnalog(analogPin, analogRead(analogPin));
}
}
}
}
}

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,309 @@
#include "LiquidCrystal.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "WProgram.h"
// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set:
// DL = 1; 8-bit interface data
// N = 0; 1-line display
// F = 0; 5x8 dot character font
// 3. Display on/off control:
// D = 0; Display off
// C = 0; Cursor off
// B = 0; Blinking off
// 4. Entry mode set:
// I/D = 1; Increment by 1
// S = 0; No shift
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts (and the
// LiquidCrystal constructor is called).
LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}
LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}
LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}
LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}
void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
_rs_pin = rs;
_rw_pin = rw;
_enable_pin = enable;
_data_pins[0] = d0;
_data_pins[1] = d1;
_data_pins[2] = d2;
_data_pins[3] = d3;
_data_pins[4] = d4;
_data_pins[5] = d5;
_data_pins[6] = d6;
_data_pins[7] = d7;
pinMode(_rs_pin, OUTPUT);
// we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
if (_rw_pin != 255) {
pinMode(_rw_pin, OUTPUT);
}
pinMode(_enable_pin, OUTPUT);
if (fourbitmode)
_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
else
_displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;
begin(16, 1);
}
void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
if (lines > 1) {
_displayfunction |= LCD_2LINE;
}
_numlines = lines;
_currline = 0;
// for some 1 line displays you can select a 10 pixel high font
if ((dotsize != 0) && (lines == 1)) {
_displayfunction |= LCD_5x10DOTS;
}
// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
// according to datasheet, we need at least 40ms after power rises above 2.7V
// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
delayMicroseconds(50000);
// Now we pull both RS and R/W low to begin commands
digitalWrite(_rs_pin, LOW);
digitalWrite(_enable_pin, LOW);
if (_rw_pin != 255) {
digitalWrite(_rw_pin, LOW);
}
//put the LCD into 4 bit or 8 bit mode
if (! (_displayfunction & LCD_8BITMODE)) {
// this is according to the hitachi HD44780 datasheet
// figure 24, pg 46
// we start in 8bit mode, try to set 4 bit mode
write4bits(0x03);
delayMicroseconds(4500); // wait min 4.1ms
// second try
write4bits(0x03);
delayMicroseconds(4500); // wait min 4.1ms
// third go!
write4bits(0x03);
delayMicroseconds(150);
// finally, set to 4-bit interface
write4bits(0x02);
} else {
// this is according to the hitachi HD44780 datasheet
// page 45 figure 23
// Send function set command sequence
command(LCD_FUNCTIONSET | _displayfunction);
delayMicroseconds(4500); // wait more than 4.1ms
// second try
command(LCD_FUNCTIONSET | _displayfunction);
delayMicroseconds(150);
// third go
command(LCD_FUNCTIONSET | _displayfunction);
}
// finally, set # lines, font size, etc.
command(LCD_FUNCTIONSET | _displayfunction);
// turn the display on with no cursor or blinking default
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
display();
// clear it off
clear();
// Initialize to default text direction (for romance languages)
_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
// set the entry mode
command(LCD_ENTRYMODESET | _displaymode);
}
/********** high level commands, for the user! */
void LiquidCrystal::clear()
{
command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
delayMicroseconds(2000); // this command takes a long time!
}
void LiquidCrystal::home()
{
command(LCD_RETURNHOME); // set cursor position to zero
delayMicroseconds(2000); // this command takes a long time!
}
void LiquidCrystal::setCursor(uint8_t col, uint8_t row)
{
int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
if ( row > _numlines ) {
row = _numlines-1; // we count rows starting w/0
}
command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}
// Turn the display on/off (quickly)
void LiquidCrystal::noDisplay() {
_displaycontrol &= ~LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::display() {
_displaycontrol |= LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// Turns the underline cursor on/off
void LiquidCrystal::noCursor() {
_displaycontrol &= ~LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::cursor() {
_displaycontrol |= LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// Turn on and off the blinking cursor
void LiquidCrystal::noBlink() {
_displaycontrol &= ~LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::blink() {
_displaycontrol |= LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// These commands scroll the display without changing the RAM
void LiquidCrystal::scrollDisplayLeft(void) {
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LiquidCrystal::scrollDisplayRight(void) {
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}
// This is for text that flows Left to Right
void LiquidCrystal::leftToRight(void) {
_displaymode |= LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET | _displaymode);
}
// This is for text that flows Right to Left
void LiquidCrystal::rightToLeft(void) {
_displaymode &= ~LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET | _displaymode);
}
// This will 'right justify' text from the cursor
void LiquidCrystal::autoscroll(void) {
_displaymode |= LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}
// This will 'left justify' text from the cursor
void LiquidCrystal::noAutoscroll(void) {
_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}
// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
location &= 0x7; // we only have 8 locations 0-7
command(LCD_SETCGRAMADDR | (location << 3));
for (int i=0; i<8; i++) {
write(charmap[i]);
}
}
/*********** mid level commands, for sending data/cmds */
inline void LiquidCrystal::command(uint8_t value) {
send(value, LOW);
}
inline void LiquidCrystal::write(uint8_t value) {
send(value, HIGH);
}
/************ low level data pushing commands **********/
// write either command or data, with automatic 4/8-bit selection
void LiquidCrystal::send(uint8_t value, uint8_t mode) {
digitalWrite(_rs_pin, mode);
// if there is a RW pin indicated, set it low to Write
if (_rw_pin != 255) {
digitalWrite(_rw_pin, LOW);
}
if (_displayfunction & LCD_8BITMODE) {
write8bits(value);
} else {
write4bits(value>>4);
write4bits(value);
}
}
void LiquidCrystal::pulseEnable(void) {
digitalWrite(_enable_pin, LOW);
delayMicroseconds(1);
digitalWrite(_enable_pin, HIGH);
delayMicroseconds(1); // enable pulse must be >450ns
digitalWrite(_enable_pin, LOW);
delayMicroseconds(100); // commands need > 37us to settle
}
void LiquidCrystal::write4bits(uint8_t value) {
for (int i = 0; i < 4; i++) {
pinMode(_data_pins[i], OUTPUT);
digitalWrite(_data_pins[i], (value >> i) & 0x01);
}
pulseEnable();
}
void LiquidCrystal::write8bits(uint8_t value) {
for (int i = 0; i < 8; i++) {
pinMode(_data_pins[i], OUTPUT);
digitalWrite(_data_pins[i], (value >> i) & 0x01);
}
pulseEnable();
}

View file

@ -0,0 +1,104 @@
#ifndef LiquidCrystal_h
#define LiquidCrystal_h
#include <inttypes.h>
#include "Print.h"
// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80
// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00
// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00
// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
class LiquidCrystal : public Print {
public:
LiquidCrystal(uint8_t rs, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
LiquidCrystal(uint8_t rs, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);
void clear();
void home();
void noDisplay();
void display();
void noBlink();
void blink();
void noCursor();
void cursor();
void scrollDisplayLeft();
void scrollDisplayRight();
void leftToRight();
void rightToLeft();
void autoscroll();
void noAutoscroll();
void createChar(uint8_t, uint8_t[]);
void setCursor(uint8_t, uint8_t);
virtual void write(uint8_t);
void command(uint8_t);
private:
void send(uint8_t, uint8_t);
void write4bits(uint8_t);
void write8bits(uint8_t);
void pulseEnable();
uint8_t _rs_pin; // LOW: command. HIGH: character.
uint8_t _rw_pin; // LOW: write to LCD. HIGH: read from LCD.
uint8_t _enable_pin; // activated by a HIGH pulse.
uint8_t _data_pins[8];
uint8_t _displayfunction;
uint8_t _displaycontrol;
uint8_t _displaymode;
uint8_t _initialized;
uint8_t _numlines,_currline;
};
#endif

View file

@ -0,0 +1,73 @@
/*
LiquidCrystal Library - Autoscroll
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch demonstrates the use of the autoscroll()
and noAutoscroll() functions to make new text scroll or not.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16,2);
}
void loop() {
// set the cursor to (0,0):
lcd.setCursor(0, 0);
// print from 0 to 9:
for (int thisChar = 0; thisChar < 10; thisChar++) {
lcd.print(thisChar);
delay(500);
}
// set the cursor to (16,1):
lcd.setCursor(16,1);
// set the display to automatically scroll:
lcd.autoscroll();
// print from 0 to 9:
for (int thisChar = 0; thisChar < 10; thisChar++) {
lcd.print(thisChar);
delay(500);
}
// turn off automatic scrolling
lcd.noAutoscroll();
// clear screen for the next loop:
lcd.clear();
}

View file

@ -0,0 +1,61 @@
/*
LiquidCrystal Library - Blink
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch prints "Hello World!" to the LCD and makes the
cursor block blink.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
}
void loop() {
// Turn off the blinking cursor:
lcd.noBlink();
delay(3000);
// Turn on the blinking cursor:
lcd.blink();
delay(3000);
}

View file

@ -0,0 +1,60 @@
/*
LiquidCrystal Library - Cursor
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch prints "Hello World!" to the LCD and
uses the cursor() and noCursor() methods to turn
on and off the cursor.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
}
void loop() {
// Turn off the cursor:
lcd.noCursor();
delay(500);
// Turn on the cursor:
lcd.cursor();
delay(500);
}

View file

@ -0,0 +1,60 @@
/*
LiquidCrystal Library - display() and noDisplay()
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch prints "Hello World!" to the LCD and uses the
display() and noDisplay() functions to turn on and off
the display.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
}
void loop() {
// Turn off the display:
lcd.noDisplay();
delay(500);
// Turn on the display:
lcd.display();
delay(500);
}

View file

@ -0,0 +1,58 @@
/*
LiquidCrystal Library - Hello World
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch prints "Hello World!" to the LCD
and shows the time.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 1);
// print the number of seconds since reset:
lcd.print(millis()/1000);
}

View file

@ -0,0 +1,85 @@
/*
LiquidCrystal Library - scrollDisplayLeft() and scrollDisplayRight()
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch prints "Hello World!" to the LCD and uses the
scrollDisplayLeft() and scrollDisplayRight() methods to scroll
the text.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
delay(1000);
}
void loop() {
// scroll 13 positions (string length) to the left
// to move it offscreen left:
for (int positionCounter = 0; positionCounter < 13; positionCounter++) {
// scroll one position left:
lcd.scrollDisplayLeft();
// wait a bit:
delay(150);
}
// scroll 29 positions (string length + display length) to the right
// to move it offscreen right:
for (int positionCounter = 0; positionCounter < 29; positionCounter++) {
// scroll one position right:
lcd.scrollDisplayRight();
// wait a bit:
delay(150);
}
// scroll 16 positions (display length + string length) to the left
// to move it back to center:
for (int positionCounter = 0; positionCounter < 16; positionCounter++) {
// scroll one position left:
lcd.scrollDisplayLeft();
// wait a bit:
delay(150);
}
// delay at the end of the full loop:
delay(1000);
}

View file

@ -0,0 +1,65 @@
/*
LiquidCrystal Library - Serial Input
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch displays text sent over the serial port
(e.g. from the Serial Monitor) on an attached LCD.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup(){
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// initialize the serial communications:
Serial.begin(9600);
}
void loop()
{
// when characters arrive over the serial port...
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(100);
// clear the screen
lcd.clear();
// read all the available characters
while (Serial.available() > 0) {
// display each character to the LCD
lcd.write(Serial.read());
}
}
}

View file

@ -0,0 +1,87 @@
/*
LiquidCrystal Library - TextDirection
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch demonstrates how to use leftToRight() and rightToLeft()
to move the cursor.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int thisChar = 'a';
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// turn on the cursor:
lcd.cursor();
Serial.begin(9600);
}
void loop() {
// reverse directions at 'm':
if (thisChar == 'm') {
// go right for the next letter
lcd.rightToLeft();
}
// reverse again at 's':
if (thisChar == 's') {
// go left for the next letter
lcd.leftToRight();
}
// reset at 'z':
if (thisChar > 'z') {
// go to (0,0):
lcd.home();
// start again at 0
thisChar = 'a';
}
// print the character
lcd.print(thisChar, BYTE);
// wait a second:
delay(1000);
// increment the letter:
thisChar++;
}

View file

@ -0,0 +1,71 @@
/*
LiquidCrystal Library - setCursor
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch prints to all the positions of the LCD using the
setCursor(0 method:
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// these constants won't change. But you can change the size of
// your LCD using them:
const int numRows = 2;
const int numCols = 16;
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(numCols,numRows);
}
void loop() {
// loop from ASCII 'a' to ASCII 'z':
for (int thisLetter = 'a'; thisLetter <= 'z'; thisLetter++) {
// loop over the columns:
for (int thisCol = 0; thisCol < numRows; thisCol++) {
// loop over the rows:
for (int thisRow = 0; thisRow < numCols; thisRow++) {
// set the cursor position:
lcd.setCursor(thisRow,thisCol);
// print the letter:
lcd.print(thisLetter, BYTE);
delay(200);
}
}
}
}

View file

@ -0,0 +1,37 @@
#######################################
# Syntax Coloring Map For LiquidCrystal
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
LiquidCrystal KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
clear KEYWORD2
home KEYWORD2
print KEYWORD2
setCursor KEYWORD2
cursor KEYWORD2
noCursor KEYWORD2
blink KEYWORD2
noBlink KEYWORD2
display KEYWORD2
noDisplay KEYWORD2
autoscroll KEYWORD2
noAutoscroll KEYWORD2
leftToRight KEYWORD2
rightToLeft KEYWORD2
scrollDisplayLeft KEYWORD2
scrollDisplayRight KEYWORD2
createChar KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View file

@ -0,0 +1,229 @@
/*
Matrix.cpp - Max7219 LED Matrix library for Arduino & Wiring
Copyright (c) 2006 Nicholas Zambetti. All right 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.
This library 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 this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// TODO: Support segment displays in api?
// TODO: Support varying vendor layouts?
/******************************************************************************
* Includes
******************************************************************************/
extern "C" {
// AVR LibC Includes
#include <inttypes.h>
#include <stdlib.h>
// Wiring Core Includes
#undef abs
#include "WConstants.h"
// Wiring Core Prototypes
//void pinMode(uint8_t, uint8_t);
//void digitalWrite(int, uint8_t);
}
#include "Sprite.h"
#include "Matrix.h"
/******************************************************************************
* Definitions
******************************************************************************/
// Matrix registers
#define REG_NOOP 0x00
#define REG_DIGIT0 0x01
#define REG_DIGIT1 0x02
#define REG_DIGIT2 0x03
#define REG_DIGIT3 0x04
#define REG_DIGIT4 0x05
#define REG_DIGIT5 0x06
#define REG_DIGIT6 0x07
#define REG_DIGIT7 0x08
#define REG_DECODEMODE 0x09
#define REG_INTENSITY 0x0A
#define REG_SCANLIMIT 0x0B
#define REG_SHUTDOWN 0x0C
#define REG_DISPLAYTEST 0x0F
/******************************************************************************
* Constructors
******************************************************************************/
Matrix::Matrix(uint8_t data, uint8_t clock, uint8_t load, uint8_t screens /* = 1 */)
{
// record pins for sw spi
_pinData = data;
_pinClock = clock;
_pinLoad = load;
// set ddr for sw spi pins
pinMode(_pinClock, OUTPUT);
pinMode(_pinData, OUTPUT);
pinMode(_pinLoad, OUTPUT);
// allocate screenbuffers
_screens = screens;
_buffer = (uint8_t*)calloc(_screens, 64);
_maximumX = (_screens * 8);
// initialize registers
clear(); // clear display
setScanLimit(0x07); // use all rows/digits
setBrightness(0x0F); // maximum brightness
setRegister(REG_SHUTDOWN, 0x01); // normal operation
setRegister(REG_DECODEMODE, 0x00); // pixels not integers
setRegister(REG_DISPLAYTEST, 0x00); // not in test mode
}
/******************************************************************************
* MAX7219 SPI
******************************************************************************/
// sends a single byte by sw spi (no latching)
void Matrix::putByte(uint8_t data)
{
uint8_t i = 8;
uint8_t mask;
while(i > 0) {
mask = 0x01 << (i - 1); // get bitmask
digitalWrite(_pinClock, LOW); // tick
if (data & mask){ // choose bit
digitalWrite(_pinData, HIGH); // set 1
}else{
digitalWrite(_pinData, LOW); // set 0
}
digitalWrite(_pinClock, HIGH); // tock
--i; // move to lesser bit
}
}
// sets register to a byte value for all screens
void Matrix::setRegister(uint8_t reg, uint8_t data)
{
digitalWrite(_pinLoad, LOW); // begin
for(uint8_t i = 0; i < _screens; ++i){
putByte(reg); // specify register
putByte(data); // send data
}
digitalWrite(_pinLoad, HIGH); // latch in data
digitalWrite(_pinLoad, LOW); // end
}
// syncs row of display with buffer
void Matrix::syncRow(uint8_t row)
{
if (!_buffer) return;
// uint8_t's can't be negative, so don't test for negative row
if (row >= 8) return;
digitalWrite(_pinLoad, LOW); // begin
for(uint8_t i = 0; i < _screens; ++i){
putByte(8 - row); // specify register
putByte(_buffer[row + (8 * i)]); // send data
}
digitalWrite(_pinLoad, HIGH); // latch in data
digitalWrite(_pinLoad, LOW); // end
}
/******************************************************************************
* MAX7219 Configuration
******************************************************************************/
// sets how many digits are displayed
void Matrix::setScanLimit(uint8_t value)
{
setRegister(REG_SCANLIMIT, value & 0x07);
}
// sets brightness of the display
void Matrix::setBrightness(uint8_t value)
{
setRegister(REG_INTENSITY, value & 0x0F);
}
/******************************************************************************
* Helper Functions
******************************************************************************/
void Matrix::buffer(uint8_t x, uint8_t y, uint8_t value)
{
if (!_buffer) return;
// uint8_t's can't be negative, so don't test for negative x and y.
if (x >= _maximumX || y >= 8) return;
uint8_t offset = x; // record x
x %= 8; // make x relative to a single matrix
offset -= x; // calculate buffer offset
// wrap shift relative x for nexus module layout
if (x == 0){
x = 8;
}
--x;
// record value in buffer
if(value){
_buffer[y + offset] |= 0x01 << x;
}else{
_buffer[y + offset] &= ~(0x01 << x);
}
}
/******************************************************************************
* User API
******************************************************************************/
// buffers and writes to screen
void Matrix::write(uint8_t x, uint8_t y, uint8_t value)
{
buffer(x, y, value);
// update affected row
syncRow(y);
}
void Matrix::write(uint8_t x, uint8_t y, Sprite sprite)
{
for (uint8_t i = 0; i < sprite.height(); i++){
for (uint8_t j = 0; j < sprite.width(); j++)
buffer(x + j, y + i, sprite.read(j, i));
syncRow(y + i);
}
}
// clears screens and buffers
void Matrix::clear(void)
{
if (!_buffer) return;
// clear buffer
for(uint8_t i = 0; i < 8; ++i){
for(uint8_t j = 0; j < _screens; ++j){
_buffer[i + (8 * j)] = 0x00;
}
}
// clear registers
for(uint8_t i = 0; i < 8; ++i){
syncRow(i);
}
}

View file

@ -0,0 +1,54 @@
/*
Matrix.h - Max7219 LED Matrix library for Arduino & Wiring
Copyright (c) 2006 Nicholas Zambetti. All right 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.
This library 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 this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef Matrix_h
#define Matrix_h
#include <inttypes.h>
class Sprite;
class Matrix
{
private:
uint8_t _pinData;
uint8_t _pinClock;
uint8_t _pinLoad;
uint8_t* _buffer;
uint8_t _screens;
uint8_t _maximumX;
void putByte(uint8_t);
void setRegister(uint8_t, uint8_t);
void syncRow(uint8_t);
void setScanLimit(uint8_t);
void buffer(uint8_t, uint8_t, uint8_t);
public:
Matrix(uint8_t, uint8_t, uint8_t, uint8_t = 1);
void setBrightness(uint8_t);
void write(uint8_t, uint8_t, uint8_t);
void write(uint8_t, uint8_t, Sprite);
void clear(void);
};
#endif

View file

@ -0,0 +1,42 @@
#include <Sprite.h>
#include <Matrix.h>
// Hello Matrix
// by Nicholas Zambetti <http://www.zambetti.com>
// Demonstrates the use of the Matrix library
// For MAX7219 LED Matrix Controllers
// Blinks welcoming face on screen
// Created 13 February 2006
/* create a new Matrix instance
pin 0: data (din)
pin 1: load (load)
pin 2: clock (clk)
*/
Matrix myMatrix = Matrix(0, 2, 1);
void setup()
{
}
void loop()
{
myMatrix.clear(); // clear display
delay(1000);
// turn some pixels on
myMatrix.write(1, 5, HIGH);
myMatrix.write(2, 2, HIGH);
myMatrix.write(2, 6, HIGH);
myMatrix.write(3, 6, HIGH);
myMatrix.write(4, 6, HIGH);
myMatrix.write(5, 2, HIGH);
myMatrix.write(5, 6, HIGH);
myMatrix.write(6, 5, HIGH);
delay(1000);
}

View file

@ -0,0 +1,48 @@
#include <Sprite.h>
#include <Matrix.h>
// Sprite Animation
// by Nicholas Zambetti <http://www.zambetti.com>
// Demonstrates the use of the Matrix & Sprite libraries
// Displays animated waveform graphic on screen
// Created 29 March 2006
/* create a new Matrix instance
pin 0: data (din)
pin 1: load (load)
pin 2: clock (clk)
*/
Matrix myMatrix = Matrix(0, 2, 1);
/* create a new Sprite instance
8 pixels wide, 4 pixels tall
*/
Sprite wave = Sprite(
8, 4,
B00011000,
B00100100,
B01000010,
B10000001
);
void setup()
{
}
int x = 0;
void loop()
{
myMatrix.write(x, 2, wave); // place sprite on screen
myMatrix.write(x - 8, 2, wave); // place sprite again, elsewhere on screen
delay(75); // wait a little bit
myMatrix.clear(); // clear the screen for next animation frame
if(x == 8) // if reached end of animation sequence
{
x = 0; // start from beginning
}
x++; // advance x coordinate to the right
}

View file

@ -0,0 +1,22 @@
#######################################
# Syntax Coloring Map For Matrix
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Matrix KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
setBrightness KEYWORD2
write KEYWORD2
clear KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View file

@ -0,0 +1,65 @@
/*
SD - a slightly more friendly wrapper for sdfatlib
This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
*/
#include <SD.h>
void File::write(uint8_t val) {
SD.file.write(val);
}
void File::write(const char *str) {
SD.file.write(str);
}
void File::write(const uint8_t *buf, size_t size) {
SD.file.write(buf, size);
}
int File::peek() {
char c = SD.file.read();
if (c != -1) SD.file.seekCur(-1);
return c;
}
int File::read() {
return SD.file.read();
}
int File::available() {
return size() - position();
}
void File::flush() {
SD.file.sync();
}
boolean File::seek(uint32_t pos) {
return SD.file.seekSet(pos);
}
uint32_t File::position() {
return SD.file.curPosition();
}
uint32_t File::size() {
return SD.file.fileSize();
}
void File::close() {
SD.file.close();
}
File::operator bool() {
return SD.file.isOpen();
}

View file

@ -0,0 +1,11 @@
** SD - a slightly more friendly wrapper for sdfatlib **
This library aims to expose a subset of SD card functionality in the
form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics

View file

@ -0,0 +1,439 @@
/*
SD - a slightly more friendly wrapper for sdfatlib
This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
This library provides four key benefits:
* Including `SD.h` automatically creates a global
`SD` object which can be interacted with in a similar
manner to other standard global objects like `Serial` and `Ethernet`.
* Boilerplate initialisation code is contained in one method named
`begin` and no further objects need to be created in order to access
the SD card.
* Calls to `open` can supply a full path name including parent
directories which simplifies interacting with files in subdirectories.
* Utility methods are provided to determine whether a file exists
and to create a directory heirarchy.
Note however that not all functionality provided by the underlying
sdfatlib library is exposed.
*/
/*
Implementation Notes
In order to handle multi-directory path traversal, functionality that
requires this ability is implemented as callback functions.
Individual methods call the `walkPath` function which performs the actual
directory traversal (swapping between two different directory/file handles
along the way) and at each level calls the supplied callback function.
Some types of functionality will take an action at each level (e.g. exists
or make directory) which others will only take an action at the bottom
level (e.g. open).
*/
#include "SD.h"
// Used by `getNextPathComponent`
#define MAX_COMPONENT_LEN 12 // What is max length?
#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1
bool getNextPathComponent(char *path, unsigned int *p_offset,
char *buffer) {
/*
Parse individual path components from a path.
e.g. after repeated calls '/foo/bar/baz' will be split
into 'foo', 'bar', 'baz'.
This is similar to `strtok()` but copies the component into the
supplied buffer rather than modifying the original string.
`buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.
`p_offset` needs to point to an integer of the offset at
which the previous path component finished.
Returns `true` if more components remain.
Returns `false` if this is the last component.
(This means path ended with 'foo' or 'foo/'.)
*/
// TODO: Have buffer local to this function, so we know it's the
// correct length?
int bufferOffset = 0;
int offset = *p_offset;
// Skip root or other separator
if (path[offset] == '/') {
offset++;
}
// Copy the next next path segment
while (bufferOffset < MAX_COMPONENT_LEN
&& (path[offset] != '/')
&& (path[offset] != '\0')) {
buffer[bufferOffset++] = path[offset++];
}
buffer[bufferOffset] = '\0';
// Skip trailing separator so we can determine if this
// is the last component in the path or not.
if (path[offset] == '/') {
offset++;
}
*p_offset = offset;
return (path[offset] != '\0');
}
boolean walkPath(char *filepath, SdFile& parentDir,
boolean (*callback)(SdFile& parentDir,
char *filePathComponent,
boolean isLastComponent,
void *object),
void *object = NULL) {
/*
When given a file path (and parent directory--normally root),
this function traverses the directories in the path and at each
level calls the supplied callback function while also providing
the supplied object for context if required.
e.g. given the path '/foo/bar/baz'
the callback would be called at the equivalent of
'/foo', '/foo/bar' and '/foo/bar/baz'.
The implementation swaps between two different directory/file
handles as it traverses the directories and does not use recursion
in an attempt to use memory efficiently.
If a callback wishes to stop the directory traversal it should
return false--in this case the function will stop the traversal,
tidy up and return false.
If a directory path doesn't exist at some point this function will
also return false and not subsequently call the callback.
If a directory path specified is complete, valid and the callback
did not indicate the traversal should be interrupted then this
function will return true.
*/
SdFile subfile1;
SdFile subfile2;
char buffer[PATH_COMPONENT_BUFFER_LEN];
unsigned int offset = 0;
SdFile *p_parent;
SdFile *p_child;
SdFile *p_tmp_sdfile;
p_child = &subfile1;
p_parent = &parentDir;
while (true) {
boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);
boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);
if (!shouldContinue) {
// TODO: Don't repeat this code?
// If it's one we've created then we
// don't need the parent handle anymore.
if (p_parent != &parentDir) {
(*p_parent).close();
}
return false;
}
if (!moreComponents) {
break;
}
boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);
// If it's one we've created then we
// don't need the parent handle anymore.
if (p_parent != &parentDir) {
(*p_parent).close();
}
// Handle case when it doesn't exist and we can't continue...
if (exists) {
// We alternate between two file handles as we go down
// the path.
if (p_parent == &parentDir) {
p_parent = &subfile2;
}
p_tmp_sdfile = p_parent;
p_parent = p_child;
p_child = p_tmp_sdfile;
} else {
return false;
}
}
if (p_parent != &parentDir) {
(*p_parent).close(); // TODO: Return/ handle different?
}
return true;
}
/*
The callbacks used to implement various functionality follow.
Each callback is supplied with a parent directory handle,
character string with the name of the current file path component,
a flag indicating if this component is the last in the path and
a pointer to an arbitrary object used for context.
*/
boolean callback_pathExists(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
/*
Callback used to determine if a file/directory exists in parent
directory.
Returns true if file path exists.
*/
SdFile child;
boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);
if (exists) {
child.close();
}
return exists;
}
boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
/*
Callback used to create a directory in the parent directory if
it does not already exist.
Returns true if a directory was created or it already existed.
*/
boolean result = false;
SdFile child;
result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
if (!result) {
result = child.makeDir(parentDir, filePathComponent);
}
return result;
}
boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
/*
Callback used to open a file specified by a filepath that may
specify one or more directories above it.
Expects the context object to be an instance of `SDClass` and
will use the `file` property of the instance to open the requested
file/directory with the associated file open mode property.
Always returns true if the directory traversal hasn't reached the
bottom of the directory heirarchy.
Returns false once the file has been opened--to prevent the traversal
from descending further. (This may be unnecessary.)
*/
if (isLastComponent) {
SDClass *p_SD = static_cast<SDClass*>(object);
p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
if (p_SD->fileOpenMode == FILE_WRITE) {
p_SD->file.seekSet(p_SD->file.fileSize());
}
// TODO: Return file open result?
return false;
}
return true;
}
boolean callback_remove(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
if (isLastComponent) {
return SdFile::remove(parentDir, filePathComponent);
}
return true;
}
boolean callback_rmdir(SdFile& parentDir, char *filePathComponent,
boolean isLastComponent, void *object) {
if (isLastComponent) {
SdFile f;
if (!f.open(parentDir, filePathComponent, O_READ)) return false;
return f.rmDir();
}
return true;
}
/* Implementation of class used to create `SDCard` object. */
boolean SDClass::begin(uint8_t csPin) {
/*
Performs the initialisation required by the sdfatlib library.
Return true if initialization succeeds, false otherwise.
*/
return card.init(SPI_HALF_SPEED, csPin) &&
volume.init(card) &&
root.openRoot(volume);
}
File SDClass::open(char *filepath, uint8_t mode) {
/*
Open the supplied file path for reading or writing.
The file content can be accessed via the `file` property of
the `SDClass` object--this property is currently
a standard `SdFile` object from `sdfatlib`.
Defaults to read only.
If `write` is true, default action (when `append` is true) is to
append data to the end of the file.
If `append` is false then the file will be truncated first.
If the file does not exist and it is opened for writing the file
will be created.
An attempt to open a file for reading that does not exist is an
error.
*/
// TODO: Allow for read&write? (Possibly not, as it requires seek.)
fileOpenMode = mode;
walkPath(filepath, root, callback_openPath, this);
return File();
}
//boolean SDClass::close() {
// /*
//
// Closes the file opened by the `open` method.
//
// */
// file.close();
//}
boolean SDClass::exists(char *filepath) {
/*
Returns true if the supplied file path exists.
*/
return walkPath(filepath, root, callback_pathExists);
}
//boolean SDClass::exists(char *filepath, SdFile& parentDir) {
// /*
//
// Returns true if the supplied file path rooted at `parentDir`
// exists.
//
// */
// return walkPath(filepath, parentDir, callback_pathExists);
//}
boolean SDClass::mkdir(char *filepath) {
/*
Makes a single directory or a heirarchy of directories.
A rough equivalent to `mkdir -p`.
*/
return walkPath(filepath, root, callback_makeDirPath);
}
boolean SDClass::rmdir(char *filepath) {
/*
Makes a single directory or a heirarchy of directories.
A rough equivalent to `mkdir -p`.
*/
return walkPath(filepath, root, callback_rmdir);
}
boolean SDClass::remove(char *filepath) {
return walkPath(filepath, root, callback_remove);
}
SDClass SD;

View file

@ -0,0 +1,88 @@
/*
SD - a slightly more friendly wrapper for sdfatlib
This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
*/
#ifndef __SD_H__
#define __SD_H__
#include <WProgram.h>
#include <utility/SdFat.h>
#include <utility/SdFatUtil.h>
#define FILE_READ O_READ
#define FILE_WRITE (O_READ | O_WRITE | O_CREAT | O_SYNC)
class File : public Stream {
public:
virtual void write(uint8_t);
virtual void write(const char *str);
virtual void write(const uint8_t *buf, size_t size);
virtual int read();
virtual int peek();
virtual int available();
virtual void flush();
boolean seek(uint32_t pos);
uint32_t position();
uint32_t size();
void close();
operator bool();
};
class SDClass {
private:
// These are required for initialisation and use of sdfatlib
Sd2Card card;
SdVolume volume;
SdFile root;
public:
// This needs to be called to set up the connection to the SD card
// before other methods are used.
boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN);
// Open the specified file/directory with the supplied mode (e.g. read or
// write, etc). Returns a File object for interacting with the file.
// Note that currently only one file can be open at a time.
File open(char *filename, uint8_t mode = FILE_READ);
// Methods to determine if the requested file path exists.
boolean exists(char *filepath);
// Create the requested directory heirarchy--if intermediate directories
// do not exist they will be created.
boolean mkdir(char *filepath);
// Delete the file.
boolean remove(char *filepath);
boolean rmdir(char *filepath);
private:
SdFile file;
// This is used to determine the mode used to open a file
// it's here because it's the easiest place to pass the
// information through the directory walking function. But
// it's probably not the best place for it.
// It shouldn't be set directly--it is set via the parameters to `open`.
int fileOpenMode;
friend class File;
friend boolean callback_openPath(SdFile&, char *, boolean, void *);
};
extern SDClass SD;
#endif

View file

@ -0,0 +1,86 @@
/*
SD card datalogger
This example shows how to log data from three analog sensors
to an SD card using the SD library.
The circuit:
* analog sensors on analog ins 0, 1, and 2
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4
created 24 Nov 2010
updated 2 Dec 2010
by Tom Igoe
This example code is in the public domain.
*/
#include <SD.h>
// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 4;
void setup()
{
Serial.begin(9600);
Serial.print("Initializing SD card...");
// make sure that the default chip select pin is set to
// output, even if you don't use it:
pinMode(10, OUTPUT);
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
}
void loop()
{
// make a string for assembling the data to log:
String dataString = "";
// read three sensors and append to the string:
for (int analogPin = 0; analogPin < 3; analogPin++) {
int sensor = analogRead(analogPin);
dataString += String(sensor);
if (analogPin < 2) {
dataString += ",";
}
}
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("datalog.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFile) {
dataFile.println(dataString);
dataFile.close();
// print to the serial port too:
Serial.println(dataString);
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
}

View file

@ -0,0 +1,64 @@
/*
SD card file dump
This example shows how to read a file from the SD card using the
SD library and send it over the serial port.
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4
created 22 December 2010
This example code is in the public domain.
*/
#include <SD.h>
// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 4;
void setup()
{
Serial.begin(9600);
Serial.print("Initializing SD card...");
// make sure that the default chip select pin is set to
// output, even if you don't use it:
pinMode(10, OUTPUT);
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("datalog.txt");
// if the file is available, write to it:
if (dataFile) {
while (dataFile.available()) {
Serial.write(dataFile.read());
}
dataFile.close();
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
}
void loop()
{
}

View file

@ -0,0 +1,78 @@
/*
SD card basic file example
This example shows how to create and destroy an SD card file
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4
created Nov 2010
by David A. Mellis
updated 2 Dec 2010
by Tom Igoe
This example code is in the public domain.
*/
#include <SD.h>
File myFile;
void setup()
{
Serial.begin(9600);
Serial.print("Initializing SD card...");
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.
// Note that even if it's not used as the CS pin, the hardware SS pin
// (10 on most Arduino boards, 53 on the Mega) must be left as an output
// or the SD library functions will not work.
pinMode(10, OUTPUT);
if (!SD.begin(4)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
}
else {
Serial.println("example.txt doesn't exist.");
}
// open a new file and immediately close it:
Serial.println("Creating example.txt...");
myFile = SD.open("example.txt", FILE_WRITE);
myFile.close();
// Check to see if the file exists:
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
}
else {
Serial.println("example.txt doesn't exist.");
}
// delete the file:
Serial.println("Removing example.txt...");
SD.remove("example.txt");
if (SD.exists("example.txt")){
Serial.println("example.txt exists.");
}
else {
Serial.println("example.txt doesn't exist.");
}
}
void loop()
{
// nothing happens after setup finishes.
}

View file

@ -0,0 +1,79 @@
/*
SD card read/write
This example shows how to read and write data to and from an SD card file
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4
created Nov 2010
by David A. Mellis
updated 2 Dec 2010
by Tom Igoe
This example code is in the public domain.
*/
#include <SD.h>
File myFile;
void setup()
{
Serial.begin(9600);
Serial.print("Initializing SD card...");
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.
// Note that even if it's not used as the CS pin, the hardware SS pin
// (10 on most Arduino boards, 53 on the Mega) must be left as an output
// or the SD library functions will not work.
pinMode(10, OUTPUT);
if (!SD.begin(4)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
myFile = SD.open("test.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
Serial.print("Writing to test.txt...");
myFile.println("testing 1, 2, 3.");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
// re-open the file for reading:
myFile = SD.open("test.txt");
if (myFile) {
Serial.println("test.txt:");
// read from the file until there's nothing else in it:
while (myFile.available()) {
Serial.write(myFile.read());
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}
void loop()
{
// nothing happens after setup
}

View file

@ -0,0 +1,30 @@
#######################################
# Syntax Coloring Map SD
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SD KEYWORD1
File KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
exists KEYWORD2
mkdir KEYWORD2
remove KEYWORD2
rmdir KEYWORD2
open KEYWORD2
close KEYWORD2
seek KEYWORD2
position KEYWORD2
size KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
FILE_READ LITERAL1
FILE_WRITE LITERAL1

View file

@ -0,0 +1,418 @@
/* Arduino SdFat Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino SdFat Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino SdFat Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef FatStructs_h
#define FatStructs_h
/**
* \file
* FAT file structures
*/
/*
* mostly from Microsoft document fatgen103.doc
* http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
*/
//------------------------------------------------------------------------------
/** Value for byte 510 of boot block or MBR */
uint8_t const BOOTSIG0 = 0X55;
/** Value for byte 511 of boot block or MBR */
uint8_t const BOOTSIG1 = 0XAA;
//------------------------------------------------------------------------------
/**
* \struct partitionTable
* \brief MBR partition table entry
*
* A partition table entry for a MBR formatted storage device.
* The MBR partition table has four entries.
*/
struct partitionTable {
/**
* Boot Indicator . Indicates whether the volume is the active
* partition. Legal values include: 0X00. Do not use for booting.
* 0X80 Active partition.
*/
uint8_t boot;
/**
* Head part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t beginHead;
/**
* Sector part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned beginSector : 6;
/** High bits cylinder for first block in partition. */
unsigned beginCylinderHigh : 2;
/**
* Combine beginCylinderLow with beginCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
uint8_t beginCylinderLow;
/**
* Partition type. See defines that begin with PART_TYPE_ for
* some Microsoft partition types.
*/
uint8_t type;
/**
* head part of cylinder-head-sector address of the last sector in the
* partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t endHead;
/**
* Sector part of cylinder-head-sector address of the last sector in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned endSector : 6;
/** High bits of end cylinder */
unsigned endCylinderHigh : 2;
/**
* Combine endCylinderLow with endCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
uint8_t endCylinderLow;
/** Logical block address of the first block in the partition. */
uint32_t firstSector;
/** Length of the partition, in blocks. */
uint32_t totalSectors;
};
/** Type name for partitionTable */
typedef struct partitionTable part_t;
//------------------------------------------------------------------------------
/**
* \struct masterBootRecord
*
* \brief Master Boot Record
*
* The first block of a storage device that is formatted with a MBR.
*/
struct masterBootRecord {
/** Code Area for master boot program. */
uint8_t codeArea[440];
/** Optional WindowsNT disk signature. May contain more boot code. */
uint32_t diskSignature;
/** Usually zero but may be more boot code. */
uint16_t usuallyZero;
/** Partition tables. */
part_t part[4];
/** First MBR signature byte. Must be 0X55 */
uint8_t mbrSig0;
/** Second MBR signature byte. Must be 0XAA */
uint8_t mbrSig1;
};
/** Type name for masterBootRecord */
typedef struct masterBootRecord mbr_t;
//------------------------------------------------------------------------------
/**
* \struct biosParmBlock
*
* \brief BIOS parameter block
*
* The BIOS parameter block describes the physical layout of a FAT volume.
*/
struct biosParmBlock {
/**
* Count of bytes per sector. This value may take on only the
* following values: 512, 1024, 2048 or 4096
*/
uint16_t bytesPerSector;
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128.
*/
uint8_t sectorsPerCluster;
/**
* Number of sectors before the first FAT.
* This value must not be zero.
*/
uint16_t reservedSectorCount;
/** The count of FAT data structures on the volume. This field should
* always contain the value 2 for any FAT volume of any type.
*/
uint8_t fatCount;
/**
* For FAT12 and FAT16 volumes, this field contains the count of
* 32-byte directory entries in the root directory. For FAT32 volumes,
* this field must be set to 0. For FAT12 and FAT16 volumes, this
* value should always specify a count that when multiplied by 32
* results in a multiple of bytesPerSector. FAT16 volumes should
* use the value 512.
*/
uint16_t rootDirEntryCount;
/**
* This field is the old 16-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then totalSectors32
* must be non-zero. For FAT32 volumes, this field must be 0. For
* FAT12 and FAT16 volumes, this field contains the sector count, and
* totalSectors32 is 0 if the total sector count fits
* (is less than 0x10000).
*/
uint16_t totalSectors16;
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (non-removable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
uint8_t mediaType;
/**
* Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
uint16_t sectorsPerFat16;
/** Sectors per track for interrupt 0x13. Not used otherwise. */
uint16_t sectorsPerTrtack;
/** Number of heads for interrupt 0x13. Not used otherwise. */
uint16_t headCount;
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint32_t hidddenSectors;
/**
* This field is the new 32-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then
* totalSectors16 must be non-zero.
*/
uint32_t totalSectors32;
/**
* Count of sectors occupied by one FAT on FAT32 volumes.
*/
uint32_t sectorsPerFat32;
/**
* This field is only defined for FAT32 media and does not exist on
* FAT12 and FAT16 media.
* Bits 0-3 -- Zero-based number of active FAT.
* Only valid if mirroring is disabled.
* Bits 4-6 -- Reserved.
* Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
* -- 1 means only one FAT is active; it is the one referenced in bits 0-3.
* Bits 8-15 -- Reserved.
*/
uint16_t fat32Flags;
/**
* FAT32 version. High byte is major revision number.
* Low byte is minor revision number. Only 0.0 define.
*/
uint16_t fat32Version;
/**
* Cluster number of the first cluster of the root directory for FAT32.
* This usually 2 but not required to be 2.
*/
uint32_t fat32RootCluster;
/**
* Sector number of FSINFO structure in the reserved area of the
* FAT32 volume. Usually 1.
*/
uint16_t fat32FSInfo;
/**
* If non-zero, indicates the sector number in the reserved area
* of the volume of a copy of the boot record. Usually 6.
* No value other than 6 is recommended.
*/
uint16_t fat32BackBootBlock;
/**
* Reserved for future expansion. Code that formats FAT32 volumes
* should always set all of the bytes of this field to 0.
*/
uint8_t fat32Reserved[12];
};
/** Type name for biosParmBlock */
typedef struct biosParmBlock bpb_t;
//------------------------------------------------------------------------------
/**
* \struct fat32BootSector
*
* \brief Boot sector for a FAT16 or FAT32 volume.
*
*/
struct fat32BootSector {
/** X86 jmp to boot program */
uint8_t jmpToBootCode[3];
/** informational only - don't depend on it */
char oemName[8];
/** BIOS Parameter Block */
bpb_t bpb;
/** for int0x13 use value 0X80 for hard drive */
uint8_t driveNumber;
/** used by Windows NT - should be zero for FAT */
uint8_t reserved1;
/** 0X29 if next three fields are valid */
uint8_t bootSignature;
/** usually generated by combining date and time */
uint32_t volumeSerialNumber;
/** should match volume label in root dir */
char volumeLabel[11];
/** informational only - don't depend on it */
char fileSystemType[8];
/** X86 boot code */
uint8_t bootCode[420];
/** must be 0X55 */
uint8_t bootSectorSig0;
/** must be 0XAA */
uint8_t bootSectorSig1;
};
//------------------------------------------------------------------------------
// End Of Chain values for FAT entries
/** FAT16 end of chain value used by Microsoft. */
uint16_t const FAT16EOC = 0XFFFF;
/** Minimum value for FAT16 EOC. Use to test for EOC. */
uint16_t const FAT16EOC_MIN = 0XFFF8;
/** FAT32 end of chain value used by Microsoft. */
uint32_t const FAT32EOC = 0X0FFFFFFF;
/** Minimum value for FAT32 EOC. Use to test for EOC. */
uint32_t const FAT32EOC_MIN = 0X0FFFFFF8;
/** Mask a for FAT32 entry. Entries are 28 bits. */
uint32_t const FAT32MASK = 0X0FFFFFFF;
/** Type name for fat32BootSector */
typedef struct fat32BootSector fbs_t;
//------------------------------------------------------------------------------
/**
* \struct directoryEntry
* \brief FAT short directory entry
*
* Short means short 8.3 name, not the entry size.
*
* Date Format. A FAT directory entry date stamp is a 16-bit field that is
* basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the
* format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
* 16-bit word):
*
* Bits 9-15: Count of years from 1980, valid value range 0-127
* inclusive (1980-2107).
*
* Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
*
* Bits 0-4: Day of month, valid value range 1-31 inclusive.
*
* Time Format. A FAT directory entry time stamp is a 16-bit field that has
* a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
* 16-bit word, bit 15 is the MSB of the 16-bit word).
*
* Bits 11-15: Hours, valid value range 0-23 inclusive.
*
* Bits 5-10: Minutes, valid value range 0-59 inclusive.
*
* Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
*
* The valid time range is from Midnight 00:00:00 to 23:59:58.
*/
struct directoryEntry {
/**
* Short 8.3 name.
* The first eight bytes contain the file name with blank fill.
* The last three bytes contain the file extension with blank fill.
*/
uint8_t name[11];
/** Entry attributes.
*
* The upper two bits of the attribute byte are reserved and should
* always be set to 0 when a file is created and never modified or
* looked at after that. See defines that begin with DIR_ATT_.
*/
uint8_t attributes;
/**
* Reserved for use by Windows NT. Set value to 0 when a file is
* created and never modify or look at it after that.
*/
uint8_t reservedNT;
/**
* The granularity of the seconds part of creationTime is 2 seconds
* so this field is a count of tenths of a second and its valid
* value range is 0-199 inclusive. (WHG note - seems to be hundredths)
*/
uint8_t creationTimeTenths;
/** Time file was created. */
uint16_t creationTime;
/** Date file was created. */
uint16_t creationDate;
/**
* Last access date. Note that there is no last access time, only
* a date. This is the date of last read or write. In the case of
* a write, this should be set to the same date as lastWriteDate.
*/
uint16_t lastAccessDate;
/**
* High word of this entry's first cluster number (always 0 for a
* FAT12 or FAT16 volume).
*/
uint16_t firstClusterHigh;
/** Time of last write. File creation is considered a write. */
uint16_t lastWriteTime;
/** Date of last write. File creation is considered a write. */
uint16_t lastWriteDate;
/** Low word of this entry's first cluster number. */
uint16_t firstClusterLow;
/** 32-bit unsigned holding this file's size in bytes. */
uint32_t fileSize;
};
//------------------------------------------------------------------------------
// Definitions for directory entries
//
/** Type name for directoryEntry */
typedef struct directoryEntry dir_t;
/** escape for name[0] = 0XE5 */
uint8_t const DIR_NAME_0XE5 = 0X05;
/** name[0] value for entry that is free after being "deleted" */
uint8_t const DIR_NAME_DELETED = 0XE5;
/** name[0] value for entry that is free and no allocated entries follow */
uint8_t const DIR_NAME_FREE = 0X00;
/** file is read-only */
uint8_t const DIR_ATT_READ_ONLY = 0X01;
/** File should hidden in directory listings */
uint8_t const DIR_ATT_HIDDEN = 0X02;
/** Entry is for a system file */
uint8_t const DIR_ATT_SYSTEM = 0X04;
/** Directory entry contains the volume label */
uint8_t const DIR_ATT_VOLUME_ID = 0X08;
/** Entry is for a directory */
uint8_t const DIR_ATT_DIRECTORY = 0X10;
/** Old DOS archive bit for backup support */
uint8_t const DIR_ATT_ARCHIVE = 0X20;
/** Test value for long name entry. Test is
(d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */
uint8_t const DIR_ATT_LONG_NAME = 0X0F;
/** Test mask for long name entry */
uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F;
/** defined attribute bits */
uint8_t const DIR_ATT_DEFINED_BITS = 0X3F;
/** Directory entry is part of a long name */
static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
}
/** Mask for file/subdirectory tests */
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
/** Directory entry is for a file */
static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
}
/** Directory entry is for a subdirectory */
static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
}
/** Directory entry is for a file or subdirectory */
static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
}
#endif // FatStructs_h

View file

@ -0,0 +1,644 @@
/* Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino Sd2Card Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <WProgram.h>
#include "Sd2Card.h"
//------------------------------------------------------------------------------
#ifndef SOFTWARE_SPI
// functions for hardware SPI
/** Send a byte to the card */
static void spiSend(uint8_t b) {
SPDR = b;
while (!(SPSR & (1 << SPIF)));
}
/** Receive a byte from the card */
static uint8_t spiRec(void) {
spiSend(0XFF);
return SPDR;
}
#else // SOFTWARE_SPI
//------------------------------------------------------------------------------
/** nop to tune soft SPI timing */
#define nop asm volatile ("nop\n\t")
//------------------------------------------------------------------------------
/** Soft SPI receive */
uint8_t spiRec(void) {
uint8_t data = 0;
// no interrupts during byte receive - about 8 us
cli();
// output pin high - like sending 0XFF
fastDigitalWrite(SPI_MOSI_PIN, HIGH);
for (uint8_t i = 0; i < 8; i++) {
fastDigitalWrite(SPI_SCK_PIN, HIGH);
// adjust so SCK is nice
nop;
nop;
data <<= 1;
if (fastDigitalRead(SPI_MISO_PIN)) data |= 1;
fastDigitalWrite(SPI_SCK_PIN, LOW);
}
// enable interrupts
sei();
return data;
}
//------------------------------------------------------------------------------
/** Soft SPI send */
void spiSend(uint8_t data) {
// no interrupts during byte send - about 8 us
cli();
for (uint8_t i = 0; i < 8; i++) {
fastDigitalWrite(SPI_SCK_PIN, LOW);
fastDigitalWrite(SPI_MOSI_PIN, data & 0X80);
data <<= 1;
fastDigitalWrite(SPI_SCK_PIN, HIGH);
}
// hold SCK high for a few ns
nop;
nop;
nop;
nop;
fastDigitalWrite(SPI_SCK_PIN, LOW);
// enable interrupts
sei();
}
#endif // SOFTWARE_SPI
//------------------------------------------------------------------------------
// send command and return error code. Return zero for OK
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
// end read if in partialBlockRead mode
readEnd();
// select card
chipSelectLow();
// wait up to 300 ms if busy
waitNotBusy(300);
// send command
spiSend(cmd | 0x40);
// send argument
for (int8_t s = 24; s >= 0; s -= 8) spiSend(arg >> s);
// send CRC
uint8_t crc = 0XFF;
if (cmd == CMD0) crc = 0X95; // correct crc for CMD0 with arg 0
if (cmd == CMD8) crc = 0X87; // correct crc for CMD8 with arg 0X1AA
spiSend(crc);
// wait for response
for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++);
return status_;
}
//------------------------------------------------------------------------------
/**
* Determine the size of an SD flash memory card.
*
* \return The number of 512 byte data blocks in the card
* or zero if an error occurs.
*/
uint32_t Sd2Card::cardSize(void) {
csd_t csd;
if (!readCSD(&csd)) return 0;
if (csd.v1.csd_ver == 0) {
uint8_t read_bl_len = csd.v1.read_bl_len;
uint16_t c_size = (csd.v1.c_size_high << 10)
| (csd.v1.c_size_mid << 2) | csd.v1.c_size_low;
uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1)
| csd.v1.c_size_mult_low;
return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7);
} else if (csd.v2.csd_ver == 1) {
uint32_t c_size = ((uint32_t)csd.v2.c_size_high << 16)
| (csd.v2.c_size_mid << 8) | csd.v2.c_size_low;
return (c_size + 1) << 10;
} else {
error(SD_CARD_ERROR_BAD_CSD);
return 0;
}
}
//------------------------------------------------------------------------------
void Sd2Card::chipSelectHigh(void) {
digitalWrite(chipSelectPin_, HIGH);
}
//------------------------------------------------------------------------------
void Sd2Card::chipSelectLow(void) {
digitalWrite(chipSelectPin_, LOW);
}
//------------------------------------------------------------------------------
/** Erase a range of blocks.
*
* \param[in] firstBlock The address of the first block in the range.
* \param[in] lastBlock The address of the last block in the range.
*
* \note This function requests the SD card to do a flash erase for a
* range of blocks. The data on the card after an erase operation is
* either 0 or 1, depends on the card vendor. The card must support
* single block erase.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
if (!eraseSingleBlockEnable()) {
error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK);
goto fail;
}
if (type_ != SD_CARD_TYPE_SDHC) {
firstBlock <<= 9;
lastBlock <<= 9;
}
if (cardCommand(CMD32, firstBlock)
|| cardCommand(CMD33, lastBlock)
|| cardCommand(CMD38, 0)) {
error(SD_CARD_ERROR_ERASE);
goto fail;
}
if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
error(SD_CARD_ERROR_ERASE_TIMEOUT);
goto fail;
}
chipSelectHigh();
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Determine if card supports single block erase.
*
* \return The value one, true, is returned if single block erase is supported.
* The value zero, false, is returned if single block erase is not supported.
*/
uint8_t Sd2Card::eraseSingleBlockEnable(void) {
csd_t csd;
return readCSD(&csd) ? csd.v1.erase_blk_en : 0;
}
//------------------------------------------------------------------------------
/**
* Initialize an SD flash memory card.
*
* \param[in] sckRateID SPI clock rate selector. See setSckRate().
* \param[in] chipSelectPin SD chip select pin number.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure. The reason for failure
* can be determined by calling errorCode() and errorData().
*/
uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0;
chipSelectPin_ = chipSelectPin;
// 16-bit init start time allows over a minute
uint16_t t0 = (uint16_t)millis();
uint32_t arg;
// set pin modes
pinMode(chipSelectPin_, OUTPUT);
chipSelectHigh();
pinMode(SPI_MISO_PIN, INPUT);
pinMode(SPI_MOSI_PIN, OUTPUT);
pinMode(SPI_SCK_PIN, OUTPUT);
#ifndef SOFTWARE_SPI
// SS must be in output mode even it is not chip select
pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin
// Enable SPI, Master, clock rate f_osc/128
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
// clear double speed
SPSR &= ~(1 << SPI2X);
#endif // SOFTWARE_SPI
// must supply min of 74 clock cycles with CS high.
for (uint8_t i = 0; i < 10; i++) spiSend(0XFF);
chipSelectLow();
// command to go idle in SPI mode
while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
error(SD_CARD_ERROR_CMD0);
goto fail;
}
}
// check SD version
if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
type(SD_CARD_TYPE_SD1);
} else {
// only need last byte of r7 response
for (uint8_t i = 0; i < 4; i++) status_ = spiRec();
if (status_ != 0XAA) {
error(SD_CARD_ERROR_CMD8);
goto fail;
}
type(SD_CARD_TYPE_SD2);
}
// initialize card and send host supports SDHC if SD2
arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0;
while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
// check for timeout
if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) {
error(SD_CARD_ERROR_ACMD41);
goto fail;
}
}
// if SD2 read OCR register to check for SDHC card
if (type() == SD_CARD_TYPE_SD2) {
if (cardCommand(CMD58, 0)) {
error(SD_CARD_ERROR_CMD58);
goto fail;
}
if ((spiRec() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC);
// discard rest of ocr - contains allowed voltage range
for (uint8_t i = 0; i < 3; i++) spiRec();
}
chipSelectHigh();
#ifndef SOFTWARE_SPI
return setSckRate(sckRateID);
#else // SOFTWARE_SPI
return true;
#endif // SOFTWARE_SPI
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/**
* Enable or disable partial block reads.
*
* Enabling partial block reads improves performance by allowing a block
* to be read over the SPI bus as several sub-blocks. Errors may occur
* if the time between reads is too long since the SD card may timeout.
* The SPI SS line will be held low until the entire block is read or
* readEnd() is called.
*
* Use this for applications like the Adafruit Wave Shield.
*
* \param[in] value The value TRUE (non-zero) or FALSE (zero).)
*/
void Sd2Card::partialBlockRead(uint8_t value) {
readEnd();
partialBlockRead_ = value;
}
//------------------------------------------------------------------------------
/**
* Read a 512 byte block from an SD card device.
*
* \param[in] block Logical block to be read.
* \param[out] dst Pointer to the location that will receive the data.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::readBlock(uint32_t block, uint8_t* dst) {
return readData(block, 0, 512, dst);
}
//------------------------------------------------------------------------------
/**
* Read part of a 512 byte block from an SD card.
*
* \param[in] block Logical block to be read.
* \param[in] offset Number of bytes to skip at start of block
* \param[out] dst Pointer to the location that will receive the data.
* \param[in] count Number of bytes to read
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::readData(uint32_t block,
uint16_t offset, uint16_t count, uint8_t* dst) {
uint16_t n;
if (count == 0) return true;
if ((count + offset) > 512) {
goto fail;
}
if (!inBlock_ || block != block_ || offset < offset_) {
block_ = block;
// use address if not SDHC card
if (type()!= SD_CARD_TYPE_SDHC) block <<= 9;
if (cardCommand(CMD17, block)) {
error(SD_CARD_ERROR_CMD17);
goto fail;
}
if (!waitStartBlock()) {
goto fail;
}
offset_ = 0;
inBlock_ = 1;
}
#ifdef OPTIMIZE_HARDWARE_SPI
// start first spi transfer
SPDR = 0XFF;
// skip data before offset
for (;offset_ < offset; offset_++) {
while (!(SPSR & (1 << SPIF)));
SPDR = 0XFF;
}
// transfer data
n = count - 1;
for (uint16_t i = 0; i < n; i++) {
while (!(SPSR & (1 << SPIF)));
dst[i] = SPDR;
SPDR = 0XFF;
}
// wait for last byte
while (!(SPSR & (1 << SPIF)));
dst[n] = SPDR;
#else // OPTIMIZE_HARDWARE_SPI
// skip data before offset
for (;offset_ < offset; offset_++) {
spiRec();
}
// transfer data
for (uint16_t i = 0; i < count; i++) {
dst[i] = spiRec();
}
#endif // OPTIMIZE_HARDWARE_SPI
offset_ += count;
if (!partialBlockRead_ || offset_ >= 512) {
// read rest of data, checksum and set chip select high
readEnd();
}
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Skip remaining data in a block when in partial block read mode. */
void Sd2Card::readEnd(void) {
if (inBlock_) {
// skip data and crc
#ifdef OPTIMIZE_HARDWARE_SPI
// optimize skip for hardware
SPDR = 0XFF;
while (offset_++ < 513) {
while (!(SPSR & (1 << SPIF)));
SPDR = 0XFF;
}
// wait for last crc byte
while (!(SPSR & (1 << SPIF)));
#else // OPTIMIZE_HARDWARE_SPI
while (offset_++ < 514) spiRec();
#endif // OPTIMIZE_HARDWARE_SPI
chipSelectHigh();
inBlock_ = 0;
}
}
//------------------------------------------------------------------------------
/** read CID or CSR register */
uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG);
goto fail;
}
if (!waitStartBlock()) goto fail;
// transfer data
for (uint16_t i = 0; i < 16; i++) dst[i] = spiRec();
spiRec(); // get first crc byte
spiRec(); // get second crc byte
chipSelectHigh();
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/**
* Set the SPI clock rate.
*
* \param[in] sckRateID A value in the range [0, 6].
*
* The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum
* SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128
* for \a scsRateID = 6.
*
* \return The value one, true, is returned for success and the value zero,
* false, is returned for an invalid value of \a sckRateID.
*/
uint8_t Sd2Card::setSckRate(uint8_t sckRateID) {
if (sckRateID > 6) {
error(SD_CARD_ERROR_SCK_RATE);
return false;
}
// see avr processor datasheet for SPI register bit definitions
if ((sckRateID & 1) || sckRateID == 6) {
SPSR &= ~(1 << SPI2X);
} else {
SPSR |= (1 << SPI2X);
}
SPCR &= ~((1 <<SPR1) | (1 << SPR0));
SPCR |= (sckRateID & 4 ? (1 << SPR1) : 0)
| (sckRateID & 2 ? (1 << SPR0) : 0);
return true;
}
//------------------------------------------------------------------------------
// wait for card to go not busy
uint8_t Sd2Card::waitNotBusy(uint16_t timeoutMillis) {
uint16_t t0 = millis();
do {
if (spiRec() == 0XFF) return true;
}
while (((uint16_t)millis() - t0) < timeoutMillis);
return false;
}
//------------------------------------------------------------------------------
/** Wait for start block token */
uint8_t Sd2Card::waitStartBlock(void) {
uint16_t t0 = millis();
while ((status_ = spiRec()) == 0XFF) {
if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) {
error(SD_CARD_ERROR_READ_TIMEOUT);
goto fail;
}
}
if (status_ != DATA_START_BLOCK) {
error(SD_CARD_ERROR_READ);
goto fail;
}
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/**
* Writes a 512 byte block to an SD card.
*
* \param[in] blockNumber Logical block to be written.
* \param[in] src Pointer to the location of the data to be written.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
#if SD_PROTECT_BLOCK_ZERO
// don't allow write to first block
if (blockNumber == 0) {
error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
goto fail;
}
#endif // SD_PROTECT_BLOCK_ZERO
// use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (cardCommand(CMD24, blockNumber)) {
error(SD_CARD_ERROR_CMD24);
goto fail;
}
if (!writeData(DATA_START_BLOCK, src)) goto fail;
// wait for flash programming to complete
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
error(SD_CARD_ERROR_WRITE_TIMEOUT);
goto fail;
}
// response is r2 so get and check two bytes for nonzero
if (cardCommand(CMD13, 0) || spiRec()) {
error(SD_CARD_ERROR_WRITE_PROGRAMMING);
goto fail;
}
chipSelectHigh();
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Write one data block in a multiple block write sequence */
uint8_t Sd2Card::writeData(const uint8_t* src) {
// wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE);
chipSelectHigh();
return false;
}
return writeData(WRITE_MULTIPLE_TOKEN, src);
}
//------------------------------------------------------------------------------
// send one block of data for write block or write multiple blocks
uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) {
#ifdef OPTIMIZE_HARDWARE_SPI
// send data - optimized loop
SPDR = token;
// send two byte per iteration
for (uint16_t i = 0; i < 512; i += 2) {
while (!(SPSR & (1 << SPIF)));
SPDR = src[i];
while (!(SPSR & (1 << SPIF)));
SPDR = src[i+1];
}
// wait for last data byte
while (!(SPSR & (1 << SPIF)));
#else // OPTIMIZE_HARDWARE_SPI
spiSend(token);
for (uint16_t i = 0; i < 512; i++) {
spiSend(src[i]);
}
#endif // OPTIMIZE_HARDWARE_SPI
spiSend(0xff); // dummy crc
spiSend(0xff); // dummy crc
status_ = spiRec();
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
error(SD_CARD_ERROR_WRITE);
chipSelectHigh();
return false;
}
return true;
}
//------------------------------------------------------------------------------
/** Start a write multiple blocks sequence.
*
* \param[in] blockNumber Address of first block in sequence.
* \param[in] eraseCount The number of blocks to be pre-erased.
*
* \note This function is used with writeData() and writeStop()
* for optimized multiple block writes.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
#if SD_PROTECT_BLOCK_ZERO
// don't allow write to first block
if (blockNumber == 0) {
error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
goto fail;
}
#endif // SD_PROTECT_BLOCK_ZERO
// send pre-erase count
if (cardAcmd(ACMD23, eraseCount)) {
error(SD_CARD_ERROR_ACMD23);
goto fail;
}
// use address if not SDHC card
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (cardCommand(CMD25, blockNumber)) {
error(SD_CARD_ERROR_CMD25);
goto fail;
}
return true;
fail:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** End a write multiple blocks sequence.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8_t Sd2Card::writeStop(void) {
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
spiSend(STOP_TRAN_TOKEN);
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
chipSelectHigh();
return true;
fail:
error(SD_CARD_ERROR_STOP_TRAN);
chipSelectHigh();
return false;
}

View file

@ -0,0 +1,233 @@
/* Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino Sd2Card Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef Sd2Card_h
#define Sd2Card_h
/**
* \file
* Sd2Card class
*/
#include "Sd2PinMap.h"
#include "SdInfo.h"
/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */
uint8_t const SPI_FULL_SPEED = 0;
/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */
uint8_t const SPI_HALF_SPEED = 1;
/** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */
uint8_t const SPI_QUARTER_SPEED = 2;
/**
* Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos.
* Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
*
* MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used
* on Mega Arduinos. Software SPI works well with GPS Shield V1.1
* but many SD cards will fail with GPS Shield V1.0.
*/
#define MEGA_SOFT_SPI 0
//------------------------------------------------------------------------------
#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__))
#define SOFTWARE_SPI
#endif // MEGA_SOFT_SPI
//------------------------------------------------------------------------------
// SPI pin definitions
//
#ifndef SOFTWARE_SPI
// hardware pin defs
/**
* SD Chip Select pin
*
* Warning if this pin is redefined the hardware SS will pin will be enabled
* as an output by init(). An avr processor will not function as an SPI
* master unless SS is set to output mode.
*/
/** The default chip select pin for the SD card is SS. */
uint8_t const SD_CHIP_SELECT_PIN = SS_PIN;
// The following three pins must not be redefined for hardware SPI.
/** SPI Master Out Slave In pin */
uint8_t const SPI_MOSI_PIN = MOSI_PIN;
/** SPI Master In Slave Out pin */
uint8_t const SPI_MISO_PIN = MISO_PIN;
/** SPI Clock pin */
uint8_t const SPI_SCK_PIN = SCK_PIN;
/** optimize loops for hardware SPI */
#define OPTIMIZE_HARDWARE_SPI
#else // SOFTWARE_SPI
// define software SPI pins so Mega can use unmodified GPS Shield
/** SPI chip select pin */
uint8_t const SD_CHIP_SELECT_PIN = 10;
/** SPI Master Out Slave In pin */
uint8_t const SPI_MOSI_PIN = 11;
/** SPI Master In Slave Out pin */
uint8_t const SPI_MISO_PIN = 12;
/** SPI Clock pin */
uint8_t const SPI_SCK_PIN = 13;
#endif // SOFTWARE_SPI
//------------------------------------------------------------------------------
/** Protect block zero from write if nonzero */
#define SD_PROTECT_BLOCK_ZERO 1
/** init timeout ms */
uint16_t const SD_INIT_TIMEOUT = 2000;
/** erase timeout ms */
uint16_t const SD_ERASE_TIMEOUT = 10000;
/** read timeout ms */
uint16_t const SD_READ_TIMEOUT = 300;
/** write time out ms */
uint16_t const SD_WRITE_TIMEOUT = 600;
//------------------------------------------------------------------------------
// SD card errors
/** timeout error for command CMD0 */
uint8_t const SD_CARD_ERROR_CMD0 = 0X1;
/** CMD8 was not accepted - not a valid SD card*/
uint8_t const SD_CARD_ERROR_CMD8 = 0X2;
/** card returned an error response for CMD17 (read block) */
uint8_t const SD_CARD_ERROR_CMD17 = 0X3;
/** card returned an error response for CMD24 (write block) */
uint8_t const SD_CARD_ERROR_CMD24 = 0X4;
/** WRITE_MULTIPLE_BLOCKS command failed */
uint8_t const SD_CARD_ERROR_CMD25 = 0X05;
/** card returned an error response for CMD58 (read OCR) */
uint8_t const SD_CARD_ERROR_CMD58 = 0X06;
/** SET_WR_BLK_ERASE_COUNT failed */
uint8_t const SD_CARD_ERROR_ACMD23 = 0X07;
/** card's ACMD41 initialization process timeout */
uint8_t const SD_CARD_ERROR_ACMD41 = 0X08;
/** card returned a bad CSR version field */
uint8_t const SD_CARD_ERROR_BAD_CSD = 0X09;
/** erase block group command failed */
uint8_t const SD_CARD_ERROR_ERASE = 0X0A;
/** card not capable of single block erase */
uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0X0B;
/** Erase sequence timed out */
uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0X0C;
/** card returned an error token instead of read data */
uint8_t const SD_CARD_ERROR_READ = 0X0D;
/** read CID or CSD failed */
uint8_t const SD_CARD_ERROR_READ_REG = 0X0E;
/** timeout while waiting for start of read data */
uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X0F;
/** card did not accept STOP_TRAN_TOKEN */
uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X10;
/** card returned an error token as a response to a write operation */
uint8_t const SD_CARD_ERROR_WRITE = 0X11;
/** attempt to write protected block zero */
uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X12;
/** card did not go ready for a multiple block write */
uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X13;
/** card returned an error to a CMD13 status check after a write */
uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X14;
/** timeout occurred during write programming */
uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X15;
/** incorrect rate selected */
uint8_t const SD_CARD_ERROR_SCK_RATE = 0X16;
//------------------------------------------------------------------------------
// card types
/** Standard capacity V1 SD card */
uint8_t const SD_CARD_TYPE_SD1 = 1;
/** Standard capacity V2 SD card */
uint8_t const SD_CARD_TYPE_SD2 = 2;
/** High Capacity SD card */
uint8_t const SD_CARD_TYPE_SDHC = 3;
//------------------------------------------------------------------------------
/**
* \class Sd2Card
* \brief Raw access to SD and SDHC flash memory cards.
*/
class Sd2Card {
public:
/** Construct an instance of Sd2Card. */
Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {}
uint32_t cardSize(void);
uint8_t erase(uint32_t firstBlock, uint32_t lastBlock);
uint8_t eraseSingleBlockEnable(void);
/**
* \return error code for last error. See Sd2Card.h for a list of error codes.
*/
uint8_t errorCode(void) const {return errorCode_;}
/** \return error data for last error. */
uint8_t errorData(void) const {return status_;}
/**
* Initialize an SD flash memory card with default clock rate and chip
* select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
*/
uint8_t init(void) {
return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN);
}
/**
* Initialize an SD flash memory card with the selected SPI clock rate
* and the default SD chip select pin.
* See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
*/
uint8_t init(uint8_t sckRateID) {
return init(sckRateID, SD_CHIP_SELECT_PIN);
}
uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin);
void partialBlockRead(uint8_t value);
/** Returns the current value, true or false, for partial block read. */
uint8_t partialBlockRead(void) const {return partialBlockRead_;}
uint8_t readBlock(uint32_t block, uint8_t* dst);
uint8_t readData(uint32_t block,
uint16_t offset, uint16_t count, uint8_t* dst);
/**
* Read a cards CID register. The CID contains card identification
* information such as Manufacturer ID, Product name, Product serial
* number and Manufacturing date. */
uint8_t readCID(cid_t* cid) {
return readRegister(CMD10, cid);
}
/**
* Read a cards CSD register. The CSD contains Card-Specific Data that
* provides information regarding access to the card's contents. */
uint8_t readCSD(csd_t* csd) {
return readRegister(CMD9, csd);
}
void readEnd(void);
uint8_t setSckRate(uint8_t sckRateID);
/** Return the card type: SD V1, SD V2 or SDHC */
uint8_t type(void) const {return type_;}
uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src);
uint8_t writeData(const uint8_t* src);
uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount);
uint8_t writeStop(void);
private:
uint32_t block_;
uint8_t chipSelectPin_;
uint8_t errorCode_;
uint8_t inBlock_;
uint16_t offset_;
uint8_t partialBlockRead_;
uint8_t status_;
uint8_t type_;
// private functions
uint8_t cardAcmd(uint8_t cmd, uint32_t arg) {
cardCommand(CMD55, 0);
return cardCommand(cmd, arg);
}
uint8_t cardCommand(uint8_t cmd, uint32_t arg);
void error(uint8_t code) {errorCode_ = code;}
uint8_t readRegister(uint8_t cmd, void* buf);
uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount);
void chipSelectHigh(void);
void chipSelectLow(void);
void type(uint8_t value) {type_ = value;}
uint8_t waitNotBusy(uint16_t timeoutMillis);
uint8_t writeData(uint8_t token, const uint8_t* src);
uint8_t waitStartBlock(void);
};
#endif // Sd2Card_h

View file

@ -0,0 +1,353 @@
/* Arduino SdFat Library
* Copyright (C) 2010 by William Greiman
*
* This file is part of the Arduino SdFat Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino SdFat Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
// Warning this file was generated by a program.
#ifndef Sd2PinMap_h
#define Sd2PinMap_h
#include <avr/io.h>
//------------------------------------------------------------------------------
/** struct for mapping digital pins */
struct pin_map_t {
volatile uint8_t* ddr;
volatile uint8_t* pin;
volatile uint8_t* port;
uint8_t bit;
};
//------------------------------------------------------------------------------
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Mega
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 20;
uint8_t const SCL_PIN = 21;
// SPI port
uint8_t const SS_PIN = 53;
uint8_t const MOSI_PIN = 51;
uint8_t const MISO_PIN = 50;
uint8_t const SCK_PIN = 52;
static const pin_map_t digitalPinMap[] = {
{&DDRE, &PINE, &PORTE, 0}, // E0 0
{&DDRE, &PINE, &PORTE, 1}, // E1 1
{&DDRE, &PINE, &PORTE, 4}, // E4 2
{&DDRE, &PINE, &PORTE, 5}, // E5 3
{&DDRG, &PING, &PORTG, 5}, // G5 4
{&DDRE, &PINE, &PORTE, 3}, // E3 5
{&DDRH, &PINH, &PORTH, 3}, // H3 6
{&DDRH, &PINH, &PORTH, 4}, // H4 7
{&DDRH, &PINH, &PORTH, 5}, // H5 8
{&DDRH, &PINH, &PORTH, 6}, // H6 9
{&DDRB, &PINB, &PORTB, 4}, // B4 10
{&DDRB, &PINB, &PORTB, 5}, // B5 11
{&DDRB, &PINB, &PORTB, 6}, // B6 12
{&DDRB, &PINB, &PORTB, 7}, // B7 13
{&DDRJ, &PINJ, &PORTJ, 1}, // J1 14
{&DDRJ, &PINJ, &PORTJ, 0}, // J0 15
{&DDRH, &PINH, &PORTH, 1}, // H1 16
{&DDRH, &PINH, &PORTH, 0}, // H0 17
{&DDRD, &PIND, &PORTD, 3}, // D3 18
{&DDRD, &PIND, &PORTD, 2}, // D2 19
{&DDRD, &PIND, &PORTD, 1}, // D1 20
{&DDRD, &PIND, &PORTD, 0}, // D0 21
{&DDRA, &PINA, &PORTA, 0}, // A0 22
{&DDRA, &PINA, &PORTA, 1}, // A1 23
{&DDRA, &PINA, &PORTA, 2}, // A2 24
{&DDRA, &PINA, &PORTA, 3}, // A3 25
{&DDRA, &PINA, &PORTA, 4}, // A4 26
{&DDRA, &PINA, &PORTA, 5}, // A5 27
{&DDRA, &PINA, &PORTA, 6}, // A6 28
{&DDRA, &PINA, &PORTA, 7}, // A7 29
{&DDRC, &PINC, &PORTC, 7}, // C7 30
{&DDRC, &PINC, &PORTC, 6}, // C6 31
{&DDRC, &PINC, &PORTC, 5}, // C5 32
{&DDRC, &PINC, &PORTC, 4}, // C4 33
{&DDRC, &PINC, &PORTC, 3}, // C3 34
{&DDRC, &PINC, &PORTC, 2}, // C2 35
{&DDRC, &PINC, &PORTC, 1}, // C1 36
{&DDRC, &PINC, &PORTC, 0}, // C0 37
{&DDRD, &PIND, &PORTD, 7}, // D7 38
{&DDRG, &PING, &PORTG, 2}, // G2 39
{&DDRG, &PING, &PORTG, 1}, // G1 40
{&DDRG, &PING, &PORTG, 0}, // G0 41
{&DDRL, &PINL, &PORTL, 7}, // L7 42
{&DDRL, &PINL, &PORTL, 6}, // L6 43
{&DDRL, &PINL, &PORTL, 5}, // L5 44
{&DDRL, &PINL, &PORTL, 4}, // L4 45
{&DDRL, &PINL, &PORTL, 3}, // L3 46
{&DDRL, &PINL, &PORTL, 2}, // L2 47
{&DDRL, &PINL, &PORTL, 1}, // L1 48
{&DDRL, &PINL, &PORTL, 0}, // L0 49
{&DDRB, &PINB, &PORTB, 3}, // B3 50
{&DDRB, &PINB, &PORTB, 2}, // B2 51
{&DDRB, &PINB, &PORTB, 1}, // B1 52
{&DDRB, &PINB, &PORTB, 0}, // B0 53
{&DDRF, &PINF, &PORTF, 0}, // F0 54
{&DDRF, &PINF, &PORTF, 1}, // F1 55
{&DDRF, &PINF, &PORTF, 2}, // F2 56
{&DDRF, &PINF, &PORTF, 3}, // F3 57
{&DDRF, &PINF, &PORTF, 4}, // F4 58
{&DDRF, &PINF, &PORTF, 5}, // F5 59
{&DDRF, &PINF, &PORTF, 6}, // F6 60
{&DDRF, &PINF, &PORTF, 7}, // F7 61
{&DDRK, &PINK, &PORTK, 0}, // K0 62
{&DDRK, &PINK, &PORTK, 1}, // K1 63
{&DDRK, &PINK, &PORTK, 2}, // K2 64
{&DDRK, &PINK, &PORTK, 3}, // K3 65
{&DDRK, &PINK, &PORTK, 4}, // K4 66
{&DDRK, &PINK, &PORTK, 5}, // K5 67
{&DDRK, &PINK, &PORTK, 6}, // K6 68
{&DDRK, &PINK, &PORTK, 7} // K7 69
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
// Sanguino
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 17;
uint8_t const SCL_PIN = 18;
// SPI port
uint8_t const SS_PIN = 4;
uint8_t const MOSI_PIN = 5;
uint8_t const MISO_PIN = 6;
uint8_t const SCK_PIN = 7;
static const pin_map_t digitalPinMap[] = {
{&DDRB, &PINB, &PORTB, 0}, // B0 0
{&DDRB, &PINB, &PORTB, 1}, // B1 1
{&DDRB, &PINB, &PORTB, 2}, // B2 2
{&DDRB, &PINB, &PORTB, 3}, // B3 3
{&DDRB, &PINB, &PORTB, 4}, // B4 4
{&DDRB, &PINB, &PORTB, 5}, // B5 5
{&DDRB, &PINB, &PORTB, 6}, // B6 6
{&DDRB, &PINB, &PORTB, 7}, // B7 7
{&DDRD, &PIND, &PORTD, 0}, // D0 8
{&DDRD, &PIND, &PORTD, 1}, // D1 9
{&DDRD, &PIND, &PORTD, 2}, // D2 10
{&DDRD, &PIND, &PORTD, 3}, // D3 11
{&DDRD, &PIND, &PORTD, 4}, // D4 12
{&DDRD, &PIND, &PORTD, 5}, // D5 13
{&DDRD, &PIND, &PORTD, 6}, // D6 14
{&DDRD, &PIND, &PORTD, 7}, // D7 15
{&DDRC, &PINC, &PORTC, 0}, // C0 16
{&DDRC, &PINC, &PORTC, 1}, // C1 17
{&DDRC, &PINC, &PORTC, 2}, // C2 18
{&DDRC, &PINC, &PORTC, 3}, // C3 19
{&DDRC, &PINC, &PORTC, 4}, // C4 20
{&DDRC, &PINC, &PORTC, 5}, // C5 21
{&DDRC, &PINC, &PORTC, 6}, // C6 22
{&DDRC, &PINC, &PORTC, 7}, // C7 23
{&DDRA, &PINA, &PORTA, 7}, // A7 24
{&DDRA, &PINA, &PORTA, 6}, // A6 25
{&DDRA, &PINA, &PORTA, 5}, // A5 26
{&DDRA, &PINA, &PORTA, 4}, // A4 27
{&DDRA, &PINA, &PORTA, 3}, // A3 28
{&DDRA, &PINA, &PORTA, 2}, // A2 29
{&DDRA, &PINA, &PORTA, 1}, // A1 30
{&DDRA, &PINA, &PORTA, 0} // A0 31
};
//------------------------------------------------------------------------------
#elif defined(__AVR_ATmega32U4__)
// Teensy 2.0
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 6;
uint8_t const SCL_PIN = 5;
// SPI port
uint8_t const SS_PIN = 0;
uint8_t const MOSI_PIN = 2;
uint8_t const MISO_PIN = 3;
uint8_t const SCK_PIN = 1;
static const pin_map_t digitalPinMap[] = {
{&DDRB, &PINB, &PORTB, 0}, // B0 0
{&DDRB, &PINB, &PORTB, 1}, // B1 1
{&DDRB, &PINB, &PORTB, 2}, // B2 2
{&DDRB, &PINB, &PORTB, 3}, // B3 3
{&DDRB, &PINB, &PORTB, 7}, // B7 4
{&DDRD, &PIND, &PORTD, 0}, // D0 5
{&DDRD, &PIND, &PORTD, 1}, // D1 6
{&DDRD, &PIND, &PORTD, 2}, // D2 7
{&DDRD, &PIND, &PORTD, 3}, // D3 8
{&DDRC, &PINC, &PORTC, 6}, // C6 9
{&DDRC, &PINC, &PORTC, 7}, // C7 10
{&DDRD, &PIND, &PORTD, 6}, // D6 11
{&DDRD, &PIND, &PORTD, 7}, // D7 12
{&DDRB, &PINB, &PORTB, 4}, // B4 13
{&DDRB, &PINB, &PORTB, 5}, // B5 14
{&DDRB, &PINB, &PORTB, 6}, // B6 15
{&DDRF, &PINF, &PORTF, 7}, // F7 16
{&DDRF, &PINF, &PORTF, 6}, // F6 17
{&DDRF, &PINF, &PORTF, 5}, // F5 18
{&DDRF, &PINF, &PORTF, 4}, // F4 19
{&DDRF, &PINF, &PORTF, 1}, // F1 20
{&DDRF, &PINF, &PORTF, 0}, // F0 21
{&DDRD, &PIND, &PORTD, 4}, // D4 22
{&DDRD, &PIND, &PORTD, 5}, // D5 23
{&DDRE, &PINE, &PORTE, 6} // E6 24
};
//------------------------------------------------------------------------------
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
// Teensy++ 1.0 & 2.0
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 1;
uint8_t const SCL_PIN = 0;
// SPI port
uint8_t const SS_PIN = 20;
uint8_t const MOSI_PIN = 22;
uint8_t const MISO_PIN = 23;
uint8_t const SCK_PIN = 21;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRE, &PINE, &PORTE, 0}, // E0 8
{&DDRE, &PINE, &PORTE, 1}, // E1 9
{&DDRC, &PINC, &PORTC, 0}, // C0 10
{&DDRC, &PINC, &PORTC, 1}, // C1 11
{&DDRC, &PINC, &PORTC, 2}, // C2 12
{&DDRC, &PINC, &PORTC, 3}, // C3 13
{&DDRC, &PINC, &PORTC, 4}, // C4 14
{&DDRC, &PINC, &PORTC, 5}, // C5 15
{&DDRC, &PINC, &PORTC, 6}, // C6 16
{&DDRC, &PINC, &PORTC, 7}, // C7 17
{&DDRE, &PINE, &PORTE, 6}, // E6 18
{&DDRE, &PINE, &PORTE, 7}, // E7 19
{&DDRB, &PINB, &PORTB, 0}, // B0 20
{&DDRB, &PINB, &PORTB, 1}, // B1 21
{&DDRB, &PINB, &PORTB, 2}, // B2 22
{&DDRB, &PINB, &PORTB, 3}, // B3 23
{&DDRB, &PINB, &PORTB, 4}, // B4 24
{&DDRB, &PINB, &PORTB, 5}, // B5 25
{&DDRB, &PINB, &PORTB, 6}, // B6 26
{&DDRB, &PINB, &PORTB, 7}, // B7 27
{&DDRA, &PINA, &PORTA, 0}, // A0 28
{&DDRA, &PINA, &PORTA, 1}, // A1 29
{&DDRA, &PINA, &PORTA, 2}, // A2 30
{&DDRA, &PINA, &PORTA, 3}, // A3 31
{&DDRA, &PINA, &PORTA, 4}, // A4 32
{&DDRA, &PINA, &PORTA, 5}, // A5 33
{&DDRA, &PINA, &PORTA, 6}, // A6 34
{&DDRA, &PINA, &PORTA, 7}, // A7 35
{&DDRE, &PINE, &PORTE, 4}, // E4 36
{&DDRE, &PINE, &PORTE, 5}, // E5 37
{&DDRF, &PINF, &PORTF, 0}, // F0 38
{&DDRF, &PINF, &PORTF, 1}, // F1 39
{&DDRF, &PINF, &PORTF, 2}, // F2 40
{&DDRF, &PINF, &PORTF, 3}, // F3 41
{&DDRF, &PINF, &PORTF, 4}, // F4 42
{&DDRF, &PINF, &PORTF, 5}, // F5 43
{&DDRF, &PINF, &PORTF, 6}, // F6 44
{&DDRF, &PINF, &PORTF, 7} // F7 45
};
//------------------------------------------------------------------------------
#else // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// 168 and 328 Arduinos
// Two Wire (aka I2C) ports
uint8_t const SDA_PIN = 18;
uint8_t const SCL_PIN = 19;
// SPI port
uint8_t const SS_PIN = 10;
uint8_t const MOSI_PIN = 11;
uint8_t const MISO_PIN = 12;
uint8_t const SCK_PIN = 13;
static const pin_map_t digitalPinMap[] = {
{&DDRD, &PIND, &PORTD, 0}, // D0 0
{&DDRD, &PIND, &PORTD, 1}, // D1 1
{&DDRD, &PIND, &PORTD, 2}, // D2 2
{&DDRD, &PIND, &PORTD, 3}, // D3 3
{&DDRD, &PIND, &PORTD, 4}, // D4 4
{&DDRD, &PIND, &PORTD, 5}, // D5 5
{&DDRD, &PIND, &PORTD, 6}, // D6 6
{&DDRD, &PIND, &PORTD, 7}, // D7 7
{&DDRB, &PINB, &PORTB, 0}, // B0 8
{&DDRB, &PINB, &PORTB, 1}, // B1 9
{&DDRB, &PINB, &PORTB, 2}, // B2 10
{&DDRB, &PINB, &PORTB, 3}, // B3 11
{&DDRB, &PINB, &PORTB, 4}, // B4 12
{&DDRB, &PINB, &PORTB, 5}, // B5 13
{&DDRC, &PINC, &PORTC, 0}, // C0 14
{&DDRC, &PINC, &PORTC, 1}, // C1 15
{&DDRC, &PINC, &PORTC, 2}, // C2 16
{&DDRC, &PINC, &PORTC, 3}, // C3 17
{&DDRC, &PINC, &PORTC, 4}, // C4 18
{&DDRC, &PINC, &PORTC, 5} // C5 19
};
#endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
//------------------------------------------------------------------------------
static const uint8_t digitalPinCount = sizeof(digitalPinMap)/sizeof(pin_map_t);
uint8_t badPinNumber(void)
__attribute__((error("Pin number is too large or not a constant")));
static inline __attribute__((always_inline))
uint8_t getPinMode(uint8_t pin) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1;
} else {
return badPinNumber();
}
}
static inline __attribute__((always_inline))
void setPinMode(uint8_t pin, uint8_t mode) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
if (mode) {
*digitalPinMap[pin].ddr |= 1 << digitalPinMap[pin].bit;
} else {
*digitalPinMap[pin].ddr &= ~(1 << digitalPinMap[pin].bit);
}
} else {
badPinNumber();
}
}
static inline __attribute__((always_inline))
uint8_t fastDigitalRead(uint8_t pin) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1;
} else {
return badPinNumber();
}
}
static inline __attribute__((always_inline))
void fastDigitalWrite(uint8_t pin, uint8_t value) {
if (__builtin_constant_p(pin) && pin < digitalPinCount) {
if (value) {
*digitalPinMap[pin].port |= 1 << digitalPinMap[pin].bit;
} else {
*digitalPinMap[pin].port &= ~(1 << digitalPinMap[pin].bit);
}
} else {
badPinNumber();
}
}
#endif // Sd2PinMap_h

View file

@ -0,0 +1,547 @@
/* Arduino SdFat Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino SdFat Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino SdFat Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef SdFat_h
#define SdFat_h
/**
* \file
* SdFile and SdVolume classes
*/
#include <avr/pgmspace.h>
#include "Sd2Card.h"
#include "FatStructs.h"
#include "Print.h"
//------------------------------------------------------------------------------
/**
* Allow use of deprecated functions if non-zero
*/
#define ALLOW_DEPRECATED_FUNCTIONS 1
//------------------------------------------------------------------------------
// forward declaration since SdVolume is used in SdFile
class SdVolume;
//==============================================================================
// SdFile class
// flags for ls()
/** ls() flag to print modify date */
uint8_t const LS_DATE = 1;
/** ls() flag to print file size */
uint8_t const LS_SIZE = 2;
/** ls() flag for recursive list of subdirectories */
uint8_t const LS_R = 4;
// use the gnu style oflag in open()
/** open() oflag for reading */
uint8_t const O_READ = 0X01;
/** open() oflag - same as O_READ */
uint8_t const O_RDONLY = O_READ;
/** open() oflag for write */
uint8_t const O_WRITE = 0X02;
/** open() oflag - same as O_WRITE */
uint8_t const O_WRONLY = O_WRITE;
/** open() oflag for reading and writing */
uint8_t const O_RDWR = (O_READ | O_WRITE);
/** open() oflag mask for access modes */
uint8_t const O_ACCMODE = (O_READ | O_WRITE);
/** The file offset shall be set to the end of the file prior to each write. */
uint8_t const O_APPEND = 0X04;
/** synchronous writes - call sync() after each write */
uint8_t const O_SYNC = 0X08;
/** create the file if nonexistent */
uint8_t const O_CREAT = 0X10;
/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */
uint8_t const O_EXCL = 0X20;
/** truncate the file to zero length */
uint8_t const O_TRUNC = 0X40;
// flags for timestamp
/** set the file's last access date */
uint8_t const T_ACCESS = 1;
/** set the file's creation date and time */
uint8_t const T_CREATE = 2;
/** Set the file's write date and time */
uint8_t const T_WRITE = 4;
// values for type_
/** This SdFile has not been opened. */
uint8_t const FAT_FILE_TYPE_CLOSED = 0;
/** SdFile for a file */
uint8_t const FAT_FILE_TYPE_NORMAL = 1;
/** SdFile for a FAT16 root directory */
uint8_t const FAT_FILE_TYPE_ROOT16 = 2;
/** SdFile for a FAT32 root directory */
uint8_t const FAT_FILE_TYPE_ROOT32 = 3;
/** SdFile for a subdirectory */
uint8_t const FAT_FILE_TYPE_SUBDIR = 4;
/** Test value for directory type */
uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT16;
/** date field for FAT directory entry */
static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) {
return (year - 1980) << 9 | month << 5 | day;
}
/** year part of FAT directory date field */
static inline uint16_t FAT_YEAR(uint16_t fatDate) {
return 1980 + (fatDate >> 9);
}
/** month part of FAT directory date field */
static inline uint8_t FAT_MONTH(uint16_t fatDate) {
return (fatDate >> 5) & 0XF;
}
/** day part of FAT directory date field */
static inline uint8_t FAT_DAY(uint16_t fatDate) {
return fatDate & 0X1F;
}
/** time field for FAT directory entry */
static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) {
return hour << 11 | minute << 5 | second >> 1;
}
/** hour part of FAT directory time field */
static inline uint8_t FAT_HOUR(uint16_t fatTime) {
return fatTime >> 11;
}
/** minute part of FAT directory time field */
static inline uint8_t FAT_MINUTE(uint16_t fatTime) {
return(fatTime >> 5) & 0X3F;
}
/** second part of FAT directory time field */
static inline uint8_t FAT_SECOND(uint16_t fatTime) {
return 2*(fatTime & 0X1F);
}
/** Default date for file timestamps is 1 Jan 2000 */
uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1;
/** Default time for file timestamp is 1 am */
uint16_t const FAT_DEFAULT_TIME = (1 << 11);
//------------------------------------------------------------------------------
/**
* \class SdFile
* \brief Access FAT16 and FAT32 files on SD and SDHC cards.
*/
class SdFile : public Print {
public:
/** Create an instance of SdFile. */
SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {}
/**
* writeError is set to true if an error occurs during a write().
* Set writeError to false before calling print() and/or write() and check
* for true after calls to print() and/or write().
*/
bool writeError;
/**
* Cancel unbuffered reads for this file.
* See setUnbufferedRead()
*/
void clearUnbufferedRead(void) {
flags_ &= ~F_FILE_UNBUFFERED_READ;
}
uint8_t close(void);
uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
uint8_t createContiguous(SdFile* dirFile,
const char* fileName, uint32_t size);
/** \return The current cluster number for a file or directory. */
uint32_t curCluster(void) const {return curCluster_;}
/** \return The current position for a file or directory. */
uint32_t curPosition(void) const {return curPosition_;}
/**
* Set the date/time callback function
*
* \param[in] dateTime The user's call back function. The callback
* function is of the form:
*
* \code
* void dateTime(uint16_t* date, uint16_t* time) {
* uint16_t year;
* uint8_t month, day, hour, minute, second;
*
* // User gets date and time from GPS or real-time clock here
*
* // return date using FAT_DATE macro to format fields
* *date = FAT_DATE(year, month, day);
*
* // return time using FAT_TIME macro to format fields
* *time = FAT_TIME(hour, minute, second);
* }
* \endcode
*
* Sets the function that is called when a file is created or when
* a file's directory entry is modified by sync(). All timestamps,
* access, creation, and modify, are set when a file is created.
* sync() maintains the last access date and last modify date/time.
*
* See the timestamp() function.
*/
static void dateTimeCallback(
void (*dateTime)(uint16_t* date, uint16_t* time)) {
dateTime_ = dateTime;
}
/**
* Cancel the date/time callback function.
*/
static void dateTimeCallbackCancel(void) {
// use explicit zero since NULL is not defined for Sanguino
dateTime_ = 0;
}
/** \return Address of the block that contains this file's directory. */
uint32_t dirBlock(void) const {return dirBlock_;}
uint8_t dirEntry(dir_t* dir);
/** \return Index of this file's directory in the block dirBlock. */
uint8_t dirIndex(void) const {return dirIndex_;}
static void dirName(const dir_t& dir, char* name);
/** \return The total number of bytes in a file or directory. */
uint32_t fileSize(void) const {return fileSize_;}
/** \return The first cluster number for a file or directory. */
uint32_t firstCluster(void) const {return firstCluster_;}
/** \return True if this is a SdFile for a directory else false. */
uint8_t isDir(void) const {return type_ >= FAT_FILE_TYPE_MIN_DIR;}
/** \return True if this is a SdFile for a file else false. */
uint8_t isFile(void) const {return type_ == FAT_FILE_TYPE_NORMAL;}
/** \return True if this is a SdFile for an open file/directory else false. */
uint8_t isOpen(void) const {return type_ != FAT_FILE_TYPE_CLOSED;}
/** \return True if this is a SdFile for a subdirectory else false. */
uint8_t isSubDir(void) const {return type_ == FAT_FILE_TYPE_SUBDIR;}
/** \return True if this is a SdFile for the root directory. */
uint8_t isRoot(void) const {
return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32;
}
void ls(uint8_t flags = 0, uint8_t indent = 0);
uint8_t makeDir(SdFile* dir, const char* dirName);
uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag);
uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag);
uint8_t openRoot(SdVolume* vol);
static void printDirName(const dir_t& dir, uint8_t width);
static void printFatDate(uint16_t fatDate);
static void printFatTime(uint16_t fatTime);
static void printTwoDigits(uint8_t v);
/**
* Read the next byte from a file.
*
* \return For success read returns the next byte in the file as an int.
* If an error occurs or end of file is reached -1 is returned.
*/
int16_t read(void) {
uint8_t b;
return read(&b, 1) == 1 ? b : -1;
}
int16_t read(void* buf, uint16_t nbyte);
int8_t readDir(dir_t* dir);
static uint8_t remove(SdFile* dirFile, const char* fileName);
uint8_t remove(void);
/** Set the file's current position to zero. */
void rewind(void) {
curPosition_ = curCluster_ = 0;
}
uint8_t rmDir(void);
uint8_t rmRfStar(void);
/** Set the files position to current position + \a pos. See seekSet(). */
uint8_t seekCur(uint32_t pos) {
return seekSet(curPosition_ + pos);
}
/**
* Set the files current position to end of file. Useful to position
* a file for append. See seekSet().
*/
uint8_t seekEnd(void) {return seekSet(fileSize_);}
uint8_t seekSet(uint32_t pos);
/**
* Use unbuffered reads to access this file. Used with Wave
* Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP.
*
* Not recommended for normal applications.
*/
void setUnbufferedRead(void) {
if (isFile()) flags_ |= F_FILE_UNBUFFERED_READ;
}
uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t second);
uint8_t sync(void);
/** Type of this SdFile. You should use isFile() or isDir() instead of type()
* if possible.
*
* \return The file or directory type.
*/
uint8_t type(void) const {return type_;}
uint8_t truncate(uint32_t size);
/** \return Unbuffered read flag. */
uint8_t unbufferedRead(void) const {
return flags_ & F_FILE_UNBUFFERED_READ;
}
/** \return SdVolume that contains this file. */
SdVolume* volume(void) const {return vol_;}
void write(uint8_t b);
int16_t write(const void* buf, uint16_t nbyte);
void write(const char* str);
void write_P(PGM_P str);
void writeln_P(PGM_P str);
//------------------------------------------------------------------------------
#if ALLOW_DEPRECATED_FUNCTIONS
// Deprecated functions - suppress cpplint warnings with NOLINT comment
/** \deprecated Use:
* uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
*/
uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT
return contiguousRange(&bgnBlock, &endBlock);
}
/** \deprecated Use:
* uint8_t SdFile::createContiguous(SdFile* dirFile,
* const char* fileName, uint32_t size)
*/
uint8_t createContiguous(SdFile& dirFile, // NOLINT
const char* fileName, uint32_t size) {
return createContiguous(&dirFile, fileName, size);
}
/**
* \deprecated Use:
* static void SdFile::dateTimeCallback(
* void (*dateTime)(uint16_t* date, uint16_t* time));
*/
static void dateTimeCallback(
void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT
oldDateTime_ = dateTime;
dateTime_ = dateTime ? oldToNew : 0;
}
/** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */
uint8_t dirEntry(dir_t& dir) {return dirEntry(&dir);} // NOLINT
/** \deprecated Use:
* uint8_t SdFile::makeDir(SdFile* dir, const char* dirName);
*/
uint8_t makeDir(SdFile& dir, const char* dirName) { // NOLINT
return makeDir(&dir, dirName);
}
/** \deprecated Use:
* uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag);
*/
uint8_t open(SdFile& dirFile, // NOLINT
const char* fileName, uint8_t oflag) {
return open(&dirFile, fileName, oflag);
}
/** \deprecated Do not use in new apps */
uint8_t open(SdFile& dirFile, const char* fileName) { // NOLINT
return open(dirFile, fileName, O_RDWR);
}
/** \deprecated Use:
* uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag);
*/
uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT
return open(&dirFile, index, oflag);
}
/** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */
uint8_t openRoot(SdVolume& vol) {return openRoot(&vol);} // NOLINT
/** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */
int8_t readDir(dir_t& dir) {return readDir(&dir);} // NOLINT
/** \deprecated Use:
* static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName);
*/
static uint8_t remove(SdFile& dirFile, const char* fileName) { // NOLINT
return remove(&dirFile, fileName);
}
//------------------------------------------------------------------------------
// rest are private
private:
static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT
static void oldToNew(uint16_t* date, uint16_t* time) {
uint16_t d;
uint16_t t;
oldDateTime_(d, t);
*date = d;
*time = t;
}
#endif // ALLOW_DEPRECATED_FUNCTIONS
private:
// bits defined in flags_
// should be 0XF
static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC);
// available bits
static uint8_t const F_UNUSED = 0X30;
// use unbuffered SD read
static uint8_t const F_FILE_UNBUFFERED_READ = 0X40;
// sync of directory entry required
static uint8_t const F_FILE_DIR_DIRTY = 0X80;
// make sure F_OFLAG is ok
#if ((F_UNUSED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG)
#error flags_ bits conflict
#endif // flags_ bits
// private data
uint8_t flags_; // See above for definition of flags_ bits
uint8_t type_; // type of file see above for values
uint32_t curCluster_; // cluster for current file position
uint32_t curPosition_; // current file position in bytes from beginning
uint32_t dirBlock_; // SD block that contains directory entry for file
uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF
uint32_t fileSize_; // file size in bytes
uint32_t firstCluster_; // first cluster of file
SdVolume* vol_; // volume where file is located
// private functions
uint8_t addCluster(void);
uint8_t addDirCluster(void);
dir_t* cacheDirEntry(uint8_t action);
static void (*dateTime_)(uint16_t* date, uint16_t* time);
static uint8_t make83Name(const char* str, uint8_t* name);
uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
dir_t* readDirCache(void);
};
//==============================================================================
// SdVolume class
/**
* \brief Cache for an SD data block
*/
union cache_t {
/** Used to access cached file data blocks. */
uint8_t data[512];
/** Used to access cached FAT16 entries. */
uint16_t fat16[256];
/** Used to access cached FAT32 entries. */
uint32_t fat32[128];
/** Used to access cached directory entries. */
dir_t dir[16];
/** Used to access a cached MasterBoot Record. */
mbr_t mbr;
/** Used to access to a cached FAT boot sector. */
fbs_t fbs;
};
//------------------------------------------------------------------------------
/**
* \class SdVolume
* \brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
*/
class SdVolume {
public:
/** Create an instance of SdVolume */
SdVolume(void) :allocSearchStart_(2), fatType_(0) {}
/** Clear the cache and returns a pointer to the cache. Used by the WaveRP
* recorder to do raw write to the SD card. Not for normal apps.
*/
static uint8_t* cacheClear(void) {
cacheFlush();
cacheBlockNumber_ = 0XFFFFFFFF;
return cacheBuffer_.data;
}
/**
* Initialize a FAT volume. Try partition one first then try super
* floppy format.
*
* \param[in] dev The Sd2Card where the volume is located.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure. Reasons for
* failure include not finding a valid partition, not finding a valid
* FAT file system or an I/O error.
*/
uint8_t init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);}
uint8_t init(Sd2Card* dev, uint8_t part);
// inline functions that return volume info
/** \return The volume's cluster size in blocks. */
uint8_t blocksPerCluster(void) const {return blocksPerCluster_;}
/** \return The number of blocks in one FAT. */
uint32_t blocksPerFat(void) const {return blocksPerFat_;}
/** \return The total number of clusters in the volume. */
uint32_t clusterCount(void) const {return clusterCount_;}
/** \return The shift count required to multiply by blocksPerCluster. */
uint8_t clusterSizeShift(void) const {return clusterSizeShift_;}
/** \return The logical block number for the start of file data. */
uint32_t dataStartBlock(void) const {return dataStartBlock_;}
/** \return The number of FAT structures on the volume. */
uint8_t fatCount(void) const {return fatCount_;}
/** \return The logical block number for the start of the first FAT. */
uint32_t fatStartBlock(void) const {return fatStartBlock_;}
/** \return The FAT type of the volume. Values are 12, 16 or 32. */
uint8_t fatType(void) const {return fatType_;}
/** \return The number of entries in the root directory for FAT16 volumes. */
uint32_t rootDirEntryCount(void) const {return rootDirEntryCount_;}
/** \return The logical block number for the start of the root directory
on FAT16 volumes or the first cluster number on FAT32 volumes. */
uint32_t rootDirStart(void) const {return rootDirStart_;}
/** return a pointer to the Sd2Card object for this volume */
static Sd2Card* sdCard(void) {return sdCard_;}
//------------------------------------------------------------------------------
#if ALLOW_DEPRECATED_FUNCTIONS
// Deprecated functions - suppress cpplint warnings with NOLINT comment
/** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */
uint8_t init(Sd2Card& dev) {return init(&dev);} // NOLINT
/** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */
uint8_t init(Sd2Card& dev, uint8_t part) { // NOLINT
return init(&dev, part);
}
#endif // ALLOW_DEPRECATED_FUNCTIONS
//------------------------------------------------------------------------------
private:
// Allow SdFile access to SdVolume private data.
friend class SdFile;
// value for action argument in cacheRawBlock to indicate read from cache
static uint8_t const CACHE_FOR_READ = 0;
// value for action argument in cacheRawBlock to indicate cache dirty
static uint8_t const CACHE_FOR_WRITE = 1;
static cache_t cacheBuffer_; // 512 byte cache for device blocks
static uint32_t cacheBlockNumber_; // Logical number of block in the cache
static Sd2Card* sdCard_; // Sd2Card object for cache
static uint8_t cacheDirty_; // cacheFlush() will write block if true
static uint32_t cacheMirrorBlock_; // block number for mirror FAT
//
uint32_t allocSearchStart_; // start cluster for alloc search
uint8_t blocksPerCluster_; // cluster size in blocks
uint32_t blocksPerFat_; // FAT size in blocks
uint32_t clusterCount_; // clusters in one FAT
uint8_t clusterSizeShift_; // shift to convert cluster count to block count
uint32_t dataStartBlock_; // first data block number
uint8_t fatCount_; // number of FATs on volume
uint32_t fatStartBlock_; // start block for first FAT
uint8_t fatType_; // volume type (12, 16, OR 32)
uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir
uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32
//----------------------------------------------------------------------------
uint8_t allocContiguous(uint32_t count, uint32_t* curCluster);
uint8_t blockOfCluster(uint32_t position) const {
return (position >> 9) & (blocksPerCluster_ - 1);}
uint32_t clusterStartBlock(uint32_t cluster) const {
return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);}
uint32_t blockNumber(uint32_t cluster, uint32_t position) const {
return clusterStartBlock(cluster) + blockOfCluster(position);}
static uint8_t cacheFlush(void);
static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action);
static void cacheSetDirty(void) {cacheDirty_ |= CACHE_FOR_WRITE;}
static uint8_t cacheZeroBlock(uint32_t blockNumber);
uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const;
uint8_t fatGet(uint32_t cluster, uint32_t* value) const;
uint8_t fatPut(uint32_t cluster, uint32_t value);
uint8_t fatPutEOC(uint32_t cluster) {
return fatPut(cluster, 0x0FFFFFFF);
}
uint8_t freeChain(uint32_t cluster);
uint8_t isEOC(uint32_t cluster) const {
return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN);
}
uint8_t readBlock(uint32_t block, uint8_t* dst) {
return sdCard_->readBlock(block, dst);}
uint8_t readData(uint32_t block, uint16_t offset,
uint16_t count, uint8_t* dst) {
return sdCard_->readData(block, offset, count, dst);
}
uint8_t writeBlock(uint32_t block, const uint8_t* dst) {
return sdCard_->writeBlock(block, dst);
}
};
#endif // SdFat_h

View file

@ -0,0 +1,70 @@
/* Arduino SdFat Library
* Copyright (C) 2008 by William Greiman
*
* This file is part of the Arduino SdFat Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with the Arduino SdFat Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef SdFatUtil_h
#define SdFatUtil_h
/**
* \file
* Useful utility functions.
*/
#include <WProgram.h>
#include <avr/pgmspace.h>
/** Store and print a string in flash memory.*/
#define PgmPrint(x) SerialPrint_P(PSTR(x))
/** Store and print a string in flash memory followed by a CR/LF.*/
#define PgmPrintln(x) SerialPrintln_P(PSTR(x))
/** Defined so doxygen works for function definitions. */
#define NOINLINE __attribute__((noinline))
//------------------------------------------------------------------------------
/** Return the number of bytes currently free in RAM. */
static int FreeRam(void) {
extern int __bss_end;
extern int* __brkval;
int free_memory;
if (reinterpret_cast<int>(__brkval) == 0) {
// if no heap use from end of bss section
free_memory = reinterpret_cast<int>(&free_memory)
- reinterpret_cast<int>(&__bss_end);
} else {
// use from top of stack to heap
free_memory = reinterpret_cast<int>(&free_memory)
- reinterpret_cast<int>(__brkval);
}
return free_memory;
}
//------------------------------------------------------------------------------
/**
* %Print a string in flash memory to the serial port.
*
* \param[in] str Pointer to string stored in flash memory.
*/
static NOINLINE void SerialPrint_P(PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.print(c);
}
//------------------------------------------------------------------------------
/**
* %Print a string in flash memory followed by a CR/LF.
*
* \param[in] str Pointer to string stored in flash memory.
*/
static NOINLINE void SerialPrintln_P(PGM_P str) {
SerialPrint_P(str);
Serial.println();
}
#endif // #define SdFatUtil_h

View file

@ -0,0 +1,202 @@
/* Arduino SdFat Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino SdFat Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino SdFat Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
\mainpage Arduino SdFat Library
<CENTER>Copyright &copy; 2009 by William Greiman
</CENTER>
\section Intro Introduction
The Arduino SdFat Library is a minimal implementation of FAT16 and FAT32
file systems on SD flash memory cards. Standard SD and high capacity
SDHC cards are supported.
The SdFat only supports short 8.3 names.
The main classes in SdFat are Sd2Card, SdVolume, and SdFile.
The Sd2Card class supports access to standard SD cards and SDHC cards. Most
applications will only need to call the Sd2Card::init() member function.
The SdVolume class supports FAT16 and FAT32 partitions. Most applications
will only need to call the SdVolume::init() member function.
The SdFile class provides file access functions such as open(), read(),
remove(), write(), close() and sync(). This class supports access to the root
directory and subdirectories.
A number of example are provided in the SdFat/examples folder. These were
developed to test SdFat and illustrate its use.
SdFat was developed for high speed data recording. SdFat was used to implement
an audio record/play class, WaveRP, for the Adafruit Wave Shield. This
application uses special Sd2Card calls to write to contiguous files in raw mode.
These functions reduce write latency so that audio can be recorded with the
small amount of RAM in the Arduino.
\section SDcard SD\SDHC Cards
Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and
most consumer devices use the 4-bit parallel SD protocol. A card that
functions well on A PC or Mac may not work well on the Arduino.
Most cards have good SPI read performance but cards vary widely in SPI
write performance. Write performance is limited by how efficiently the
card manages internal erase/remapping operations. The Arduino cannot
optimize writes to reduce erase operations because of its limit RAM.
SanDisk cards generally have good write performance. They seem to have
more internal RAM buffering than other cards and therefore can limit
the number of flash erase operations that the Arduino forces due to its
limited RAM.
\section Hardware Hardware Configuration
SdFat was developed using an
<A HREF = "http://www.adafruit.com/"> Adafruit Industries</A>
<A HREF = "http://www.ladyada.net/make/waveshield/"> Wave Shield</A>.
The hardware interface to the SD card should not use a resistor based level
shifter. SdFat sets the SPI bus frequency to 8 MHz which results in signal
rise times that are too slow for the edge detectors in many newer SD card
controllers when resistor voltage dividers are used.
The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the
74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield
uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the
74LCX245.
If you are using a resistor based level shifter and are having problems try
setting the SPI bus frequency to 4 MHz. This can be done by using
card.init(SPI_HALF_SPEED) to initialize the SD card.
\section comment Bugs and Comments
If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net.
\section SdFatClass SdFat Usage
SdFat uses a slightly restricted form of short names.
Only printable ASCII characters are supported. No characters with code point
values greater than 127 are allowed. Space is not allowed even though space
was allowed in the API of early versions of DOS.
Short names are limited to 8 characters followed by an optional period (.)
and extension of up to 3 characters. The characters may be any combination
of letters and digits. The following special characters are also allowed:
$ % ' - _ @ ~ ` ! ( ) { } ^ # &
Short names are always converted to upper case and their original case
value is lost.
\note
The Arduino Print class uses character
at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink
function to control when data is written to the SD card.
\par
An application which writes to a file using \link Print::print() print()\endlink,
\link Print::println() println() \endlink
or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink
at the appropriate time to force data and directory information to be written
to the SD Card. Data and directory information are also written to the SD card
when \link SdFile::close() close() \endlink is called.
\par
Applications must use care calling \link SdFile::sync() sync() \endlink
since 2048 bytes of I/O is required to update file and
directory information. This includes writing the current data block, reading
the block that contains the directory entry for update, writing the directory
block back and reading back the current data block.
It is possible to open a file with two or more instances of SdFile. A file may
be corrupted if data is written to the file by more than one instance of SdFile.
\section HowTo How to format SD Cards as FAT Volumes
You should use a freshly formatted SD card for best performance. FAT
file systems become slower if many files have been created and deleted.
This is because the directory entry for a deleted file is marked as deleted,
but is not deleted. When a new file is created, these entries must be scanned
before creating the file, a flaw in the FAT design. Also files can become
fragmented which causes reads and writes to be slower.
Microsoft operating systems support removable media formatted with a
Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector
in block zero.
Microsoft operating systems expect MBR formatted removable media
to have only one partition. The first partition should be used.
Microsoft operating systems do not support partitioning SD flash cards.
If you erase an SD card with a program like KillDisk, Most versions of
Windows will format the card as a super floppy.
The best way to restore an SD card's format is to use SDFormatter
which can be downloaded from:
http://www.sdcard.org/consumers/formatter/
SDFormatter aligns flash erase boundaries with file
system structures which reduces write latency and file system overhead.
SDFormatter does not have an option for FAT type so it may format
small cards as FAT12.
After the MBR is restored by SDFormatter you may need to reformat small
cards that have been formatted FAT12 to force the volume type to be FAT16.
If you reformat the SD card with an OS utility, choose a cluster size that
will result in:
4084 < CountOfClusters && CountOfClusters < 65525
The volume will then be FAT16.
If you are formatting an SD card on OS X or Linux, be sure to use the first
partition. Format this partition with a cluster count in above range.
\section References References
Adafruit Industries:
http://www.adafruit.com/
http://www.ladyada.net/make/waveshield/
The Arduino site:
http://www.arduino.cc/
For more information about FAT file systems see:
http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
For information about using SD cards as SPI devices see:
http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
The ATmega328 datasheet:
http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf
*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,232 @@
/* Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino Sd2Card Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef SdInfo_h
#define SdInfo_h
#include <stdint.h>
// Based on the document:
//
// SD Specifications
// Part 1
// Physical Layer
// Simplified Specification
// Version 2.00
// September 25, 2006
//
// www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
//------------------------------------------------------------------------------
// SD card commands
/** GO_IDLE_STATE - init card in spi mode if CS low */
uint8_t const CMD0 = 0X00;
/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/
uint8_t const CMD8 = 0X08;
/** SEND_CSD - read the Card Specific Data (CSD register) */
uint8_t const CMD9 = 0X09;
/** SEND_CID - read the card identification information (CID register) */
uint8_t const CMD10 = 0X0A;
/** SEND_STATUS - read the card status register */
uint8_t const CMD13 = 0X0D;
/** READ_BLOCK - read a single data block from the card */
uint8_t const CMD17 = 0X11;
/** WRITE_BLOCK - write a single data block to the card */
uint8_t const CMD24 = 0X18;
/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */
uint8_t const CMD25 = 0X19;
/** ERASE_WR_BLK_START - sets the address of the first block to be erased */
uint8_t const CMD32 = 0X20;
/** ERASE_WR_BLK_END - sets the address of the last block of the continuous
range to be erased*/
uint8_t const CMD33 = 0X21;
/** ERASE - erase all previously selected blocks */
uint8_t const CMD38 = 0X26;
/** APP_CMD - escape for application specific command */
uint8_t const CMD55 = 0X37;
/** READ_OCR - read the OCR register of a card */
uint8_t const CMD58 = 0X3A;
/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be
pre-erased before writing */
uint8_t const ACMD23 = 0X17;
/** SD_SEND_OP_COMD - Sends host capacity support information and
activates the card's initialization process */
uint8_t const ACMD41 = 0X29;
//------------------------------------------------------------------------------
/** status for card in the ready state */
uint8_t const R1_READY_STATE = 0X00;
/** status for card in the idle state */
uint8_t const R1_IDLE_STATE = 0X01;
/** status bit for illegal command */
uint8_t const R1_ILLEGAL_COMMAND = 0X04;
/** start data token for read or write single block*/
uint8_t const DATA_START_BLOCK = 0XFE;
/** stop token for write multiple blocks*/
uint8_t const STOP_TRAN_TOKEN = 0XFD;
/** start data token for write multiple blocks*/
uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC;
/** mask for data response tokens after a write block operation */
uint8_t const DATA_RES_MASK = 0X1F;
/** write data accepted token */
uint8_t const DATA_RES_ACCEPTED = 0X05;
//------------------------------------------------------------------------------
typedef struct CID {
// byte 0
uint8_t mid; // Manufacturer ID
// byte 1-2
char oid[2]; // OEM/Application ID
// byte 3-7
char pnm[5]; // Product name
// byte 8
unsigned prv_m : 4; // Product revision n.m
unsigned prv_n : 4;
// byte 9-12
uint32_t psn; // Product serial number
// byte 13
unsigned mdt_year_high : 4; // Manufacturing date
unsigned reserved : 4;
// byte 14
unsigned mdt_month : 4;
unsigned mdt_year_low :4;
// byte 15
unsigned always1 : 1;
unsigned crc : 7;
}cid_t;
//------------------------------------------------------------------------------
// CSD for version 1.00 cards
typedef struct CSDV1 {
// byte 0
unsigned reserved1 : 6;
unsigned csd_ver : 2;
// byte 1
uint8_t taac;
// byte 2
uint8_t nsac;
// byte 3
uint8_t tran_speed;
// byte 4
uint8_t ccc_high;
// byte 5
unsigned read_bl_len : 4;
unsigned ccc_low : 4;
// byte 6
unsigned c_size_high : 2;
unsigned reserved2 : 2;
unsigned dsr_imp : 1;
unsigned read_blk_misalign :1;
unsigned write_blk_misalign : 1;
unsigned read_bl_partial : 1;
// byte 7
uint8_t c_size_mid;
// byte 8
unsigned vdd_r_curr_max : 3;
unsigned vdd_r_curr_min : 3;
unsigned c_size_low :2;
// byte 9
unsigned c_size_mult_high : 2;
unsigned vdd_w_cur_max : 3;
unsigned vdd_w_curr_min : 3;
// byte 10
unsigned sector_size_high : 6;
unsigned erase_blk_en : 1;
unsigned c_size_mult_low : 1;
// byte 11
unsigned wp_grp_size : 7;
unsigned sector_size_low : 1;
// byte 12
unsigned write_bl_len_high : 2;
unsigned r2w_factor : 3;
unsigned reserved3 : 2;
unsigned wp_grp_enable : 1;
// byte 13
unsigned reserved4 : 5;
unsigned write_partial : 1;
unsigned write_bl_len_low : 2;
// byte 14
unsigned reserved5: 2;
unsigned file_format : 2;
unsigned tmp_write_protect : 1;
unsigned perm_write_protect : 1;
unsigned copy : 1;
unsigned file_format_grp : 1;
// byte 15
unsigned always1 : 1;
unsigned crc : 7;
}csd1_t;
//------------------------------------------------------------------------------
// CSD for version 2.00 cards
typedef struct CSDV2 {
// byte 0
unsigned reserved1 : 6;
unsigned csd_ver : 2;
// byte 1
uint8_t taac;
// byte 2
uint8_t nsac;
// byte 3
uint8_t tran_speed;
// byte 4
uint8_t ccc_high;
// byte 5
unsigned read_bl_len : 4;
unsigned ccc_low : 4;
// byte 6
unsigned reserved2 : 4;
unsigned dsr_imp : 1;
unsigned read_blk_misalign :1;
unsigned write_blk_misalign : 1;
unsigned read_bl_partial : 1;
// byte 7
unsigned reserved3 : 2;
unsigned c_size_high : 6;
// byte 8
uint8_t c_size_mid;
// byte 9
uint8_t c_size_low;
// byte 10
unsigned sector_size_high : 6;
unsigned erase_blk_en : 1;
unsigned reserved4 : 1;
// byte 11
unsigned wp_grp_size : 7;
unsigned sector_size_low : 1;
// byte 12
unsigned write_bl_len_high : 2;
unsigned r2w_factor : 3;
unsigned reserved5 : 2;
unsigned wp_grp_enable : 1;
// byte 13
unsigned reserved6 : 5;
unsigned write_partial : 1;
unsigned write_bl_len_low : 2;
// byte 14
unsigned reserved7: 2;
unsigned file_format : 2;
unsigned tmp_write_protect : 1;
unsigned perm_write_protect : 1;
unsigned copy : 1;
unsigned file_format_grp : 1;
// byte 15
unsigned always1 : 1;
unsigned crc : 7;
}csd2_t;
//------------------------------------------------------------------------------
// union of old and new style CSD register
union csd_t {
csd1_t v1;
csd2_t v2;
};
#endif // SdInfo_h

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