Pre-merge upstream Arduino

This commit is contained in:
Cristian Maglie 2012-05-21 01:56:06 +02:00
commit 3786e337e0
30 changed files with 617 additions and 223 deletions

View File

@ -20,6 +20,7 @@ extern "C"{
#define INPUT 0x0 #define INPUT 0x0
#define OUTPUT 0x1 #define OUTPUT 0x1
#define INPUT_PULLUP 0x2
#define true 0x1 #define true 0x1
#define false 0x0 #define false 0x0

View File

@ -46,8 +46,8 @@
struct ring_buffer struct ring_buffer
{ {
unsigned char buffer[SERIAL_BUFFER_SIZE]; unsigned char buffer[SERIAL_BUFFER_SIZE];
volatile int head; volatile unsigned int head;
volatile int tail; volatile unsigned int tail;
}; };
#if defined(USBCON) #if defined(USBCON)

View File

@ -41,7 +41,7 @@ size_t Print::write(const uint8_t *buffer, size_t size)
size_t Print::print(const __FlashStringHelper *ifsh) size_t Print::print(const __FlashStringHelper *ifsh)
{ {
const prog_char *p = (const prog_char *)ifsh; const char PROGMEM *p = (const char PROGMEM *)ifsh;
size_t n = 0; size_t n = 0;
while (1) { while (1) {
unsigned char c = pgm_read_byte(p++); unsigned char c = pgm_read_byte(p++);

View File

@ -101,23 +101,25 @@ bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t
int c; int c;
if( *target == 0) if( *target == 0)
return true; // return true if target is a null string return true; // return true if target is a null string
while( (c = timedRead()) > 0){ while( (c = timedRead()) > 0){
if(c != target[index])
index = 0; // reset index if any char does not match
if( c == target[index]){ if( c == target[index]){
//////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1); //////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1);
if(++index >= targetLen){ // return true if all chars in the target match if(++index >= targetLen){ // return true if all chars in the target match
return true; return true;
} }
} }
else{
index = 0; // reset index if any char does not match
}
if(termLen > 0 && c == terminator[termIndex]){ if(termLen > 0 && c == terminator[termIndex]){
if(++termIndex >= termLen) if(++termIndex >= termLen)
return false; // return false if terminate string found before target string return false; // return false if terminate string found before target string
} }
else else
termIndex = 0; termIndex = 0;
} }
return false; return false;
} }

View File

@ -32,7 +32,7 @@
#include "wiring_private.h" #include "wiring_private.h"
volatile static voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS]; static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS];
// volatile static voidFuncPtr twiIntFunc; // volatile static voidFuncPtr twiIntFunc;
void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) { void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {
@ -121,8 +121,6 @@ void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {
#elif defined(MCUCR) && defined(ISC20) && defined(GIMSK) && defined(GIMSK) #elif defined(MCUCR) && defined(ISC20) && defined(GIMSK) && defined(GIMSK)
MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20); MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
GIMSK |= (1 << INT2); GIMSK |= (1 << INT2);
#else
#warning attachInterrupt may need some more work for this cpu (case 1)
#endif #endif
break; break;
#endif #endif

View File

@ -500,7 +500,7 @@ int String::lastIndexOf( char theChar ) const
int String::lastIndexOf(char ch, unsigned int fromIndex) const int String::lastIndexOf(char ch, unsigned int fromIndex) const
{ {
if (fromIndex >= len || fromIndex < 0) return -1; if (fromIndex >= len) return -1;
char tempchar = buffer[fromIndex + 1]; char tempchar = buffer[fromIndex + 1];
buffer[fromIndex + 1] = '\0'; buffer[fromIndex + 1] = '\0';
char* temp = strrchr( buffer, ch ); char* temp = strrchr( buffer, ch );
@ -516,7 +516,7 @@ int String::lastIndexOf(const String &s2) const
int String::lastIndexOf(const String &s2, unsigned int fromIndex) const int String::lastIndexOf(const String &s2, unsigned int fromIndex) const
{ {
if (s2.len == 0 || len == 0 || s2.len > len || fromIndex < 0) return -1; if (s2.len == 0 || len == 0 || s2.len > len) return -1;
if (fromIndex >= len) fromIndex = len - 1; if (fromIndex >= len) fromIndex = len - 1;
int found = -1; int found = -1;
for (char *p = buffer; p <= buffer + fromIndex; p++) { for (char *p = buffer; p <= buffer + fromIndex; p++) {

View File

@ -45,6 +45,8 @@ int analogRead(uint8_t pin)
if (pin >= 54) pin -= 54; // allow for channel or pin numbers if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__) #elif defined(__AVR_ATmega32U4__)
if (pin >= 18) pin -= 18; // allow for channel or pin numbers if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284__)
if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#else #else
if (pin >= 14) pin -= 14; // allow for channel or pin numbers if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif #endif

View File

@ -32,17 +32,25 @@ void pinMode(uint8_t pin, uint8_t mode)
{ {
uint8_t bit = digitalPinToBitMask(pin); uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin); uint8_t port = digitalPinToPort(pin);
volatile uint8_t *reg; volatile uint8_t *reg, *out;
if (port == NOT_A_PIN) return; if (port == NOT_A_PIN) return;
// JWS: can I let the optimizer do this? // JWS: can I let the optimizer do this?
reg = portModeRegister(port); reg = portModeRegister(port);
out = portOutputRegister(port);
if (mode == INPUT) { if (mode == INPUT) {
uint8_t oldSREG = SREG; uint8_t oldSREG = SREG;
cli(); cli();
*reg &= ~bit; *reg &= ~bit;
*out &= ~bit;
SREG = oldSREG;
} else if (mode == INPUT_PULLUP) {
uint8_t oldSREG = SREG;
cli();
*reg &= ~bit;
*out |= bit;
SREG = oldSREG; SREG = oldSREG;
} else { } else {
uint8_t oldSREG = SREG; uint8_t oldSREG = SREG;

View File

@ -11,13 +11,33 @@
int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout)
{ {
uint8_t dhcp_state = STATE_DHCP_START; _dhcpLeaseTime=0;
uint8_t messageType = 0; _dhcpT1=0;
_dhcpT2=0;
_lastCheck=0;
_timeout = timeout;
_responseTimeout = responseTimeout;
// zero out _dhcpMacAddr, _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp // zero out _dhcpMacAddr
memset(_dhcpMacAddr, 0, 26); memset(_dhcpMacAddr, 0, 6);
reset_DHCP_lease();
memcpy((void*)_dhcpMacAddr, (void*)mac, 6); memcpy((void*)_dhcpMacAddr, (void*)mac, 6);
_dhcp_state = STATE_DHCP_START;
return request_DHCP_lease();
}
void DhcpClass::reset_DHCP_lease(){
// zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp
memset(_dhcpLocalIp, 0, 20);
}
//return:0 on error, 1 if request is sent and response is received
int DhcpClass::request_DHCP_lease(){
uint8_t messageType = 0;
// Pick an initial transaction ID // Pick an initial transaction ID
_dhcpTransactionId = random(1UL, 2000UL); _dhcpTransactionId = random(1UL, 2000UL);
@ -35,48 +55,68 @@ int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long
unsigned long startTime = millis(); unsigned long startTime = millis();
while(dhcp_state != STATE_DHCP_LEASED) while(_dhcp_state != STATE_DHCP_LEASED)
{ {
if(dhcp_state == STATE_DHCP_START) if(_dhcp_state == STATE_DHCP_START)
{ {
_dhcpTransactionId++; _dhcpTransactionId++;
send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000));
dhcp_state = STATE_DHCP_DISCOVER; _dhcp_state = STATE_DHCP_DISCOVER;
} }
else if(dhcp_state == STATE_DHCP_DISCOVER) else if(_dhcp_state == STATE_DHCP_REREQUEST){
_dhcpTransactionId++;
send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000));
_dhcp_state = STATE_DHCP_REQUEST;
}
else if(_dhcp_state == STATE_DHCP_DISCOVER)
{ {
uint32_t respId; uint32_t respId;
messageType = parseDHCPResponse(responseTimeout, respId); messageType = parseDHCPResponse(_responseTimeout, respId);
if(messageType == DHCP_OFFER) if(messageType == DHCP_OFFER)
{ {
// We'll use the transaction ID that the offer came with, // We'll use the transaction ID that the offer came with,
// rather than the one we were up to // rather than the one we were up to
_dhcpTransactionId = respId; _dhcpTransactionId = respId;
send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000));
dhcp_state = STATE_DHCP_REQUEST; _dhcp_state = STATE_DHCP_REQUEST;
} }
} }
else if(dhcp_state == STATE_DHCP_REQUEST) else if(_dhcp_state == STATE_DHCP_REQUEST)
{ {
uint32_t respId; uint32_t respId;
messageType = parseDHCPResponse(responseTimeout, respId); messageType = parseDHCPResponse(_responseTimeout, respId);
if(messageType == DHCP_ACK) if(messageType == DHCP_ACK)
{ {
dhcp_state = STATE_DHCP_LEASED; _dhcp_state = STATE_DHCP_LEASED;
result = 1; result = 1;
//use default lease time if we didn't get it
if(_dhcpLeaseTime == 0){
_dhcpLeaseTime = DEFAULT_LEASE;
}
//calculate T1 & T2 if we didn't get it
if(_dhcpT1 == 0){
//T1 should be 50% of _dhcpLeaseTime
_dhcpT1 = _dhcpLeaseTime >> 1;
}
if(_dhcpT2 == 0){
//T2 should be 87.5% (7/8ths) of _dhcpLeaseTime
_dhcpT2 = _dhcpT1 << 1;
}
_renewInSec = _dhcpT1;
_rebindInSec = _dhcpT2;
} }
else if(messageType == DHCP_NAK) else if(messageType == DHCP_NAK)
dhcp_state = STATE_DHCP_START; _dhcp_state = STATE_DHCP_START;
} }
if(messageType == 255) if(messageType == 255)
{ {
messageType = 0; messageType = 0;
dhcp_state = STATE_DHCP_START; _dhcp_state = STATE_DHCP_START;
} }
if(result != 1 && ((millis() - startTime) > timeout)) if(result != 1 && ((millis() - startTime) > _timeout))
break; break;
} }
@ -303,7 +343,25 @@ uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& tr
} }
break; break;
case dhcpT1value :
opt_len = _dhcpUdpSocket.read();
_dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1));
_dhcpT1 = ntohl(_dhcpT1);
break;
case dhcpT2value :
opt_len = _dhcpUdpSocket.read();
_dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2));
_dhcpT2 = ntohl(_dhcpT2);
break;
case dhcpIPaddrLeaseTime : case dhcpIPaddrLeaseTime :
opt_len = _dhcpUdpSocket.read();
_dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime));
_dhcpLeaseTime = ntohl(_dhcpLeaseTime);
_renewInSec = _dhcpLeaseTime;
break;
default : default :
opt_len = _dhcpUdpSocket.read(); opt_len = _dhcpUdpSocket.read();
// Skip over the rest of this option // Skip over the rest of this option
@ -322,6 +380,68 @@ uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& tr
return type; return type;
} }
/*
returns:
0/DHCP_CHECK_NONE: nothing happened
1/DHCP_CHECK_RENEW_FAIL: renew failed
2/DHCP_CHECK_RENEW_OK: renew success
3/DHCP_CHECK_REBIND_FAIL: rebind fail
4/DHCP_CHECK_REBIND_OK: rebind success
*/
int DhcpClass::checkLease(){
//this uses a signed / unsigned trick to deal with millis overflow
unsigned long now = millis();
signed long snow = (long)now;
int rc=DHCP_CHECK_NONE;
if (_lastCheck != 0){
signed long factor;
//calc how many ms past the timeout we are
factor = snow - (long)_secTimeout;
//if on or passed the timeout, reduce the counters
if ( factor >= 0 ){
//next timeout should be now plus 1000 ms minus parts of second in factor
_secTimeout = snow + 1000 - factor % 1000;
//how many seconds late are we, minimum 1
factor = factor / 1000 +1;
//reduce the counters by that mouch
//if we can assume that the cycle time (factor) is fairly constant
//and if the remainder is less than cycle time * 2
//do it early instead of late
if(_renewInSec < factor*2 )
_renewInSec = 0;
else
_renewInSec -= factor;
if(_rebindInSec < factor*2 )
_rebindInSec = 0;
else
_rebindInSec -= factor;
}
//if we have a lease but should renew, do it
if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){
_dhcp_state = STATE_DHCP_REREQUEST;
rc = 1 + request_DHCP_lease();
}
//if we have a lease or is renewing but should bind, do it
if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){
//this should basically restart completely
_dhcp_state = STATE_DHCP_START;
reset_DHCP_lease();
rc = 3 + request_DHCP_lease();
}
}
else{
_secTimeout = snow + 1000;
}
_lastCheck = now;
return rc;
}
IPAddress DhcpClass::getLocalIp() IPAddress DhcpClass::getLocalIp()
{ {
return IPAddress(_dhcpLocalIp); return IPAddress(_dhcpLocalIp);

View File

@ -45,6 +45,13 @@
#define MAX_DHCP_OPT 16 #define MAX_DHCP_OPT 16
#define HOST_NAME "WIZnet" #define HOST_NAME "WIZnet"
#define DEFAULT_LEASE (900) //default lease time in seconds
#define DHCP_CHECK_NONE (0)
#define DHCP_CHECK_RENEW_FAIL (1)
#define DHCP_CHECK_RENEW_OK (2)
#define DHCP_CHECK_REBIND_FAIL (3)
#define DHCP_CHECK_REBIND_OK (4)
enum enum
{ {
@ -139,8 +146,19 @@ private:
uint8_t _dhcpGatewayIp[4]; uint8_t _dhcpGatewayIp[4];
uint8_t _dhcpDhcpServerIp[4]; uint8_t _dhcpDhcpServerIp[4];
uint8_t _dhcpDnsServerIp[4]; uint8_t _dhcpDnsServerIp[4];
uint32_t _dhcpLeaseTime;
uint32_t _dhcpT1, _dhcpT2;
signed long _renewInSec;
signed long _rebindInSec;
signed long _lastCheck;
unsigned long _timeout;
unsigned long _responseTimeout;
unsigned long _secTimeout;
uint8_t _dhcp_state;
EthernetUDP _dhcpUdpSocket; EthernetUDP _dhcpUdpSocket;
int request_DHCP_lease();
void reset_DHCP_lease();
void presend_DHCP(); void presend_DHCP();
void send_DHCP_MESSAGE(uint8_t, uint16_t); void send_DHCP_MESSAGE(uint8_t, uint16_t);
void printByte(char *, uint8_t); void printByte(char *, uint8_t);
@ -154,6 +172,7 @@ public:
IPAddress getDnsServerIp(); IPAddress getDnsServerIp();
int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
int checkLease();
}; };
#endif #endif

View File

@ -10,7 +10,8 @@ uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = {
int EthernetClass::begin(uint8_t *mac_address) int EthernetClass::begin(uint8_t *mac_address)
{ {
DhcpClass dhcp; _dhcp = new DhcpClass();
// Initialise the basic info // Initialise the basic info
W5100.init(); W5100.init();
@ -18,15 +19,15 @@ int EthernetClass::begin(uint8_t *mac_address)
W5100.setIPAddress(IPAddress(0,0,0,0).raw_address()); W5100.setIPAddress(IPAddress(0,0,0,0).raw_address());
// Now try to get our config info from a DHCP server // Now try to get our config info from a DHCP server
int ret = dhcp.beginWithDHCP(mac_address); int ret = _dhcp->beginWithDHCP(mac_address);
if(ret == 1) if(ret == 1)
{ {
// We've successfully found a DHCP server and got our configuration info, so set things // We've successfully found a DHCP server and got our configuration info, so set things
// accordingly // accordingly
W5100.setIPAddress(dhcp.getLocalIp().raw_address()); W5100.setIPAddress(_dhcp->getLocalIp().raw_address());
W5100.setGatewayIp(dhcp.getGatewayIp().raw_address()); W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address());
W5100.setSubnetMask(dhcp.getSubnetMask().raw_address()); W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address());
_dnsServerAddress = dhcp.getDnsServerIp(); _dnsServerAddress = _dhcp->getDnsServerIp();
} }
return ret; return ret;
@ -66,6 +67,31 @@ void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server
_dnsServerAddress = dns_server; _dnsServerAddress = dns_server;
} }
int EthernetClass::maintain(){
int rc = DHCP_CHECK_NONE;
if(_dhcp != NULL){
//we have a pointer to dhcp, use it
rc = _dhcp->checkLease();
switch ( rc ){
case DHCP_CHECK_NONE:
//nothing done
break;
case DHCP_CHECK_RENEW_OK:
case DHCP_CHECK_REBIND_OK:
//we might have got a new IP.
W5100.setIPAddress(_dhcp->getLocalIp().raw_address());
W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address());
W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address());
_dnsServerAddress = _dhcp->getDnsServerIp();
break;
default:
//this is actually a error, it will retry though
break;
}
}
return rc;
}
IPAddress EthernetClass::localIP() IPAddress EthernetClass::localIP()
{ {
IPAddress ret; IPAddress ret;

View File

@ -6,12 +6,14 @@
#include "IPAddress.h" #include "IPAddress.h"
#include "EthernetClient.h" #include "EthernetClient.h"
#include "EthernetServer.h" #include "EthernetServer.h"
#include "Dhcp.h"
#define MAX_SOCK_NUM 4 #define MAX_SOCK_NUM 4
class EthernetClass { class EthernetClass {
private: private:
IPAddress _dnsServerAddress; IPAddress _dnsServerAddress;
DhcpClass* _dhcp;
public: public:
static uint8_t _state[MAX_SOCK_NUM]; static uint8_t _state[MAX_SOCK_NUM];
static uint16_t _server_port[MAX_SOCK_NUM]; static uint16_t _server_port[MAX_SOCK_NUM];
@ -23,6 +25,7 @@ public:
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server);
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet);
int maintain();
IPAddress localIP(); IPAddress localIP();
IPAddress subnetMask(); IPAddress subnetMask();

View File

@ -41,7 +41,7 @@ int EthernetClient::connect(IPAddress ip, uint16_t port) {
for (int i = 0; i < MAX_SOCK_NUM; i++) { for (int i = 0; i < MAX_SOCK_NUM; i++) {
uint8_t s = W5100.readSnSR(i); uint8_t s = W5100.readSnSR(i);
if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) { if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT || s == SnSR::CLOSE_WAIT) {
_sock = i; _sock = i;
break; break;
} }

View File

@ -52,15 +52,16 @@ uint8_t EthernetUDP::begin(uint16_t port) {
return 0; return 0;
_port = port; _port = port;
_remaining = 0;
socket(_sock, SnMR::UDP, _port, 0); socket(_sock, SnMR::UDP, _port, 0);
return 1; return 1;
} }
/* Is data available in rx buffer? Returns 0 if no, number of available bytes if yes. /* return number of bytes available in the current packet,
* returned value includes 8 byte UDP header!*/ will return zero if parsePacket hasn't been called yet */
int EthernetUDP::available() { int EthernetUDP::available() {
return W5100.getRXReceivedSize(_sock); return _remaining;
} }
/* Release any resources being used by this EthernetUDP instance */ /* Release any resources being used by this EthernetUDP instance */
@ -116,7 +117,10 @@ size_t EthernetUDP::write(const uint8_t *buffer, size_t size)
int EthernetUDP::parsePacket() int EthernetUDP::parsePacket()
{ {
if (available() > 0) // discard any remaining bytes in the last packet
flush();
if (W5100.getRXReceivedSize(_sock) > 0)
{ {
//HACK - hand-parse the UDP packet using TCP recv method //HACK - hand-parse the UDP packet using TCP recv method
uint8_t tmpBuf[8]; uint8_t tmpBuf[8];
@ -128,8 +132,11 @@ int EthernetUDP::parsePacket()
_remoteIP = tmpBuf; _remoteIP = tmpBuf;
_remotePort = tmpBuf[4]; _remotePort = tmpBuf[4];
_remotePort = (_remotePort << 8) + tmpBuf[5]; _remotePort = (_remotePort << 8) + tmpBuf[5];
_remaining = tmpBuf[6];
_remaining = (_remaining << 8) + tmpBuf[7];
// When we get here, any remaining bytes are the data // When we get here, any remaining bytes are the data
ret = available(); ret = _remaining;
} }
return ret; return ret;
} }
@ -140,34 +147,58 @@ int EthernetUDP::parsePacket()
int EthernetUDP::read() int EthernetUDP::read()
{ {
uint8_t byte; uint8_t byte;
if (recv(_sock, &byte, 1) > 0)
if ((_remaining > 0) && (recv(_sock, &byte, 1) > 0))
{ {
// We read things without any problems // We read things without any problems
_remaining--;
return byte; return byte;
} }
// If we get here, there's no data available // If we get here, there's no data available
return -1; return -1;
} }
int EthernetUDP::read(unsigned char* buffer, size_t len) int EthernetUDP::read(unsigned char* buffer, size_t len)
{ {
/* In the readPacket that copes with truncating packets, the buffer was
filled with this code. Not sure why it loops round reading out a byte if (_remaining > 0)
at a time. {
int i;
for(i=0;i<(int)bufLen;i++) { int got;
recv(_sock,tmpBuf,1);
buf[i]=tmpBuf[0]; if (_remaining <= len)
{
// data should fit in the buffer
got = recv(_sock, buffer, _remaining);
}
else
{
// too much data for the buffer,
// grab as much as will fit
got = recv(_sock, buffer, len);
}
if (got > 0)
{
_remaining -= got;
return got;
}
} }
*/
return recv(_sock, buffer, len); // If we get here, there's no data available or recv failed
return -1;
} }
int EthernetUDP::peek() int EthernetUDP::peek()
{ {
uint8_t b; uint8_t b;
// Unlike recv, peek doesn't check to see if there's any data available, so we must // Unlike recv, peek doesn't check to see if there's any data available, so we must.
if (!available()) // If the user hasn't called parsePacket yet then return nothing otherwise they
// may get the UDP header
if (!_remaining)
return -1; return -1;
::peek(_sock, &b); ::peek(_sock, &b);
return b; return b;
@ -175,7 +206,11 @@ int EthernetUDP::peek()
void EthernetUDP::flush() void EthernetUDP::flush()
{ {
while (available()) // could this fail (loop endlessly) if _remaining > 0 and recv in read fails?
// should only occur if recv fails after telling us the data is there, lets
// hope the w5100 always behaves :)
while (_remaining)
{ {
read(); read();
} }

View File

@ -48,6 +48,7 @@ private:
IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed
uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed
uint16_t _offset; // offset into the packet being sent uint16_t _offset; // offset into the packet being sent
uint16_t _remaining; // remaining bytes of incoming packet yet to be processed
public: public:
EthernetUDP(); // Constructor EthernetUDP(); // Constructor

View File

@ -12,7 +12,7 @@
created 18 Dec 2009 created 18 Dec 2009
by David A. Mellis by David A. Mellis
modified 10 August 2010 modified 12 March 2012
by Tom Igoe by Tom Igoe
*/ */
@ -23,14 +23,16 @@
// Enter a MAC address and IP address for your controller below. // Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network. // The IP address will be dependent on your local network.
// gateway and subnet are optional: // gateway and subnet are optional:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1, 177); IPAddress ip(192,168,1, 177);
IPAddress gateway(192,168,1, 1); IPAddress gateway(192,168,1, 1);
IPAddress subnet(255, 255, 0, 0); IPAddress subnet(255, 255, 0, 0);
// telnet defaults to port 23 // telnet defaults to port 23
EthernetServer server(23); EthernetServer server(23);
boolean gotAMessage = false; // whether or not you got a message from the client yet boolean alreadyConnected = false; // whether or not the client was connected previously
void setup() { void setup() {
// initialize the ethernet device // initialize the ethernet device
@ -39,6 +41,8 @@ void setup() {
server.begin(); server.begin();
// open the serial port // open the serial port
Serial.begin(9600); Serial.begin(9600);
Serial.print("Chat server address:");
Serial.println(Ethernet.localIP());
} }
void loop() { void loop() {
@ -47,17 +51,24 @@ void loop() {
// when the client sends the first byte, say hello: // when the client sends the first byte, say hello:
if (client) { if (client) {
if (!gotAMessage) { if (!alreadyConnected) {
// clead out the input buffer:
client.flush();
Serial.println("We have a new client"); Serial.println("We have a new client");
client.println("Hello, client!"); client.println("Hello, client!");
gotAMessage = true; alreadyConnected = true;
} }
// read the bytes incoming from the client: if (client.available() > 0) {
char thisChar = client.read(); // read the bytes incoming from the client:
// echo the bytes back to the client: char thisChar = client.read();
server.write(thisChar); // echo the bytes back to the client:
// echo the bytes to the server as well: server.write(thisChar);
Serial.print(thisChar); // echo the bytes to the server as well:
Serial.write(thisChar);
}
} }
} }

View File

@ -6,15 +6,20 @@
the Adafruit Ethernet shield, either one will work, as long as it's got the Adafruit Ethernet shield, either one will work, as long as it's got
a Wiznet Ethernet module on board. a Wiznet Ethernet module on board.
This example has been updated to use version 2.0 of the Pachube.com API.
To make it work, create a feed with a datastream, and give it the ID
sensor1. Or change the code below to match your feed.
Circuit: Circuit:
* Analog sensor attached to analog in 0 * Analog sensor attached to analog in 0
* Ethernet shield attached to pins 10, 11, 12, 13 * Ethernet shield attached to pins 10, 11, 12, 13
created 15 March 2010 created 15 March 2010
updated 26 Oct 2011 updated 16 Mar 2012
by Tom Igoe by Tom Igoe with input from Usman Haque and Joe Saavedra
http://www.tigoe.net/pcomp/code/category/arduinowiring/873 http://arduino.cc/en/Tutorial/PachubeClient
This code is in the public domain. This code is in the public domain.
*/ */
@ -22,6 +27,10 @@
#include <SPI.h> #include <SPI.h>
#include <Ethernet.h> #include <Ethernet.h>
#define APIKEY "YOUR API KEY GOES HERE" // replace your pachube api key here
#define FEEDID 00000 // replace your feed ID
#define USERAGENT "My Project" // user agent is the project name
// assign a MAC address for the ethernet controller. // assign a MAC address for the ethernet controller.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield // Newer Ethernet shields have a MAC address printed on a sticker on the shield
// fill in your address here: // fill in your address here:
@ -34,26 +43,22 @@ IPAddress ip(10,0,1,20);
// initialize the library instance: // initialize the library instance:
EthernetClient client; EthernetClient client;
long lastConnectionTime = 0; // last time you connected to the server, in milliseconds // if you don't want to use DNS (and reduce your sketch size)
boolean lastConnected = false; // state of the connection last time through the main loop // use the numeric IP instead of the name for the server:
const int postingInterval = 10000; //delay between updates to Pachube.com IPAddress server(216,52,233,122); // numeric IP for api.pachube.com
//char server[] = "api.pachube.com"; // name address for pachube API
unsigned 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 unsigned long postingInterval = 10*1000; //delay between updates to Pachube.com
void setup() { void setup() {
// start serial port: // start serial port:
Serial.begin(9600); Serial.begin(9600);
// start the Ethernet connection: // start the Ethernet connection:
if (Ethernet.begin(mac) == 0) { if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP"); Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore: // DHCP failed, so use a fixed IP address:
for(;;)
;
}
// give the ethernet module time to boot up:
delay(1000);
// start the Ethernet connection:
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// Configure manually:
Ethernet.begin(mac, ip); Ethernet.begin(mac, ip);
} }
} }
@ -91,34 +96,43 @@ void loop() {
// this method makes a HTTP connection to the server: // this method makes a HTTP connection to the server:
void sendData(int thisData) { void sendData(int thisData) {
// if there's a successful connection: // if there's a successful connection:
if (client.connect("www.pachube.com", 80)) { if (client.connect(server, 80)) {
Serial.println("connecting..."); Serial.println("connecting...");
// send the HTTP PUT request. // send the HTTP PUT request:
// fill in your feed address here: client.print("PUT /v2/feeds/");
client.print("PUT /api/YOUR_FEED_HERE.csv HTTP/1.1\n"); client.print(FEEDID);
client.print("Host: www.pachube.com\n"); client.println(".csv HTTP/1.1");
// fill in your Pachube API key here: client.println("Host: api.pachube.com");
client.print("X-PachubeApiKey: YOUR_KEY_HERE\n"); client.print("X-PachubeApiKey: ");
client.println(APIKEY);
client.print("User-Agent: ");
client.println(USERAGENT);
client.print("Content-Length: "); client.print("Content-Length: ");
// calculate the length of the sensor reading in bytes: // calculate the length of the sensor reading in bytes:
int thisLength = getLength(thisData); // 8 bytes for "sensor1," + number of digits of the data:
client.println(thisLength, DEC); int thisLength = 8 + getLength(thisData);
client.println(thisLength);
// last pieces of the HTTP PUT request: // last pieces of the HTTP PUT request:
client.print("Content-Type: text/csv\n"); client.println("Content-Type: text/csv");
client.println("Connection: close\n"); client.println("Connection: close");
client.println();
// here's the actual content of the PUT request: // here's the actual content of the PUT request:
client.println(thisData, DEC); client.print("sensor1,");
client.println(thisData);
// note the time that the connection was made:
lastConnectionTime = millis();
} }
else { else {
// if you couldn't make a connection: // if you couldn't make a connection:
Serial.println("connection failed"); Serial.println("connection failed");
Serial.println();
Serial.println("disconnecting.");
client.stop();
} }
// note the time that the connection was made or attempted:
lastConnectionTime = millis();
} }

View File

@ -6,6 +6,10 @@
the Adafruit Ethernet shield, either one will work, as long as it's got the Adafruit Ethernet shield, either one will work, as long as it's got
a Wiznet Ethernet module on board. a Wiznet Ethernet module on board.
This example has been updated to use version 2.0 of the Pachube.com API.
To make it work, create a feed with two datastreams, and give them the IDs
sensor1 and sensor2. Or change the code below to match your feed.
This example uses the String library, which is part of the Arduino core from This example uses the String library, which is part of the Arduino core from
version 0019. version 0019.
@ -14,9 +18,10 @@
* Ethernet shield attached to pins 10, 11, 12, 13 * Ethernet shield attached to pins 10, 11, 12, 13
created 15 March 2010 created 15 March 2010
updated 26 Oct 2011 updated 16 Mar 2012
by Tom Igoe by Tom Igoe with input from Usman Haque and Joe Saavedra
http://arduino.cc/en/Tutorial/PachubeClientString
This code is in the public domain. This code is in the public domain.
*/ */
@ -24,9 +29,14 @@
#include <SPI.h> #include <SPI.h>
#include <Ethernet.h> #include <Ethernet.h>
#define APIKEY "YOUR API KEY GOES HERE" // replace your pachube api key here
#define FEEDID 00000 // replace your feed ID
#define USERAGENT "My Project" // user agent is the project name
// assign a MAC address for the ethernet controller. // assign a MAC address for the ethernet controller.
// fill in your address here: // fill in your address here:
byte mac[] = { byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// fill in an available IP address on your network here, // fill in an available IP address on your network here,
// for manual configuration: // for manual configuration:
@ -35,9 +45,14 @@ IPAddress ip(10,0,1,20);
// initialize the library instance: // initialize the library instance:
EthernetClient client; EthernetClient client;
long lastConnectionTime = 0; // last time you connected to the server, in milliseconds // if you don't want to use DNS (and reduce your sketch size)
boolean lastConnected = false; // state of the connection last time through the main loop // use the numeric IP instead of the name for the server:
const int postingInterval = 10000; //delay between updates to Pachube.com //IPAddress server(216,52,233,122); // numeric IP for api.pachube.com
char server[] = "api.pachube.com"; // name address for pachube API
unsigned 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 unsigned long postingInterval = 10*1000; //delay between updates to Pachube.com
void setup() { void setup() {
// start serial port: // start serial port:
@ -47,7 +62,7 @@ void setup() {
// start the Ethernet connection: // start the Ethernet connection:
if (Ethernet.begin(mac) == 0) { if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP"); Serial.println("Failed to configure Ethernet using DHCP");
// Configure manually: // DHCP failed, so use a fixed IP address:
Ethernet.begin(mac, ip); Ethernet.begin(mac, ip);
} }
} }
@ -56,13 +71,15 @@ void loop() {
// read the analog sensor: // read the analog sensor:
int sensorReading = analogRead(A0); int sensorReading = analogRead(A0);
// convert the data to a String to send it: // convert the data to a String to send it:
String dataString = String(sensorReading);
String dataString = "sensor1,";
dataString += sensorReading;
// you can append multiple readings to this String if your // you can append multiple readings to this String if your
// pachube feed is set up to handle multiple values: // pachube feed is set up to handle multiple values:
int otherSensorReading = analogRead(A1); int otherSensorReading = analogRead(A1);
dataString += ","; dataString += "\nsensor2,";
dataString += String(otherSensorReading); dataString += otherSensorReading;
// if there's incoming data from the net connection. // if there's incoming data from the net connection.
// send it out the serial port. This is for debugging // send it out the serial port. This is for debugging
@ -93,29 +110,36 @@ void loop() {
// this method makes a HTTP connection to the server: // this method makes a HTTP connection to the server:
void sendData(String thisData) { void sendData(String thisData) {
// if there's a successful connection: // if there's a successful connection:
if (client.connect("www.pachube.com", 80)) { if (client.connect(server, 80)) {
Serial.println("connecting..."); Serial.println("connecting...");
// send the HTTP PUT request. // send the HTTP PUT request:
// fill in your feed address here: client.print("PUT /v2/feeds/");
client.print("PUT /api/YOUR_FEED_HERE.csv HTTP/1.1\n"); client.print(FEEDID);
client.print("Host: www.pachube.com\n"); client.println(".csv HTTP/1.1");
// fill in your Pachube API key here: client.println("Host: api.pachube.com");
client.print("X-PachubeApiKey: YOUR_KEY_HERE\n"); client.print("X-PachubeApiKey: ");
client.println(APIKEY);
client.print("User-Agent: ");
client.println(USERAGENT);
client.print("Content-Length: "); client.print("Content-Length: ");
client.println(thisData.length(), DEC); client.println(thisData.length());
// last pieces of the HTTP PUT request: // last pieces of the HTTP PUT request:
client.print("Content-Type: text/csv\n"); client.println("Content-Type: text/csv");
client.println("Connection: close\n"); client.println("Connection: close");
client.println();
// here's the actual content of the PUT request: // here's the actual content of the PUT request:
client.println(thisData); client.println(thisData);
// note the time that the connection was made:
lastConnectionTime = millis();
} }
else { else {
// if you couldn't make a connection: // if you couldn't make a connection:
Serial.println("connection failed"); Serial.println("connection failed");
Serial.println();
Serial.println("disconnecting.");
client.stop();
} }
// note the time that the connection was made or attempted:
lastConnectionTime = millis();
} }

View File

@ -14,7 +14,7 @@
version 0019. version 0019.
Circuit: Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13 * Ethernet shield attached to pins 10, 11, 12, 13
created 21 May 2011 created 21 May 2011
by Tom Igoe by Tom Igoe
@ -35,12 +35,12 @@ IPAddress ip(192,168,1,20);
// initialize the library instance: // initialize the library instance:
EthernetClient client; EthernetClient client;
const int requestInterval = 60000; // delay between requests const unsigned long requestInterval = 60000; // delay between requests
char serverName[] = "api.twitter.com"; // twitter URL char serverName[] = "api.twitter.com"; // twitter URL
boolean requested; // whether you've made a request since connecting boolean requested; // whether you've made a request since connecting
long lastAttemptTime = 0; // last time you connected to the server, in milliseconds unsigned long lastAttemptTime = 0; // last time you connected to the server, in milliseconds
String currentLine = ""; // string to hold the text from server String currentLine = ""; // string to hold the text from server
String tweet = ""; // string to hold the tweet String tweet = ""; // string to hold the tweet
@ -51,13 +51,17 @@ void setup() {
currentLine.reserve(256); currentLine.reserve(256);
tweet.reserve(150); tweet.reserve(150);
// initialize serial: // initialize serial:
Serial.begin(9600); Serial.begin(9600);
// attempt a DHCP connection: // attempt a DHCP connection:
Serial.println("Attempting to get an IP address using DHCP:");
if (!Ethernet.begin(mac)) { if (!Ethernet.begin(mac)) {
// if DHCP fails, start with a hard-coded address: // if DHCP fails, start with a hard-coded address:
Serial.println("failed to get an IP address using DHCP, trying manually");
Ethernet.begin(mac, ip); Ethernet.begin(mac, ip);
} }
Serial.print("My address:");
Serial.println(Ethernet.localIP());
// connect to Twitter: // connect to Twitter:
connectToServer(); connectToServer();
} }
@ -114,7 +118,7 @@ void connectToServer() {
Serial.println("connecting to server..."); Serial.println("connecting to server...");
if (client.connect(serverName, 80)) { if (client.connect(serverName, 80)) {
Serial.println("making HTTP request..."); Serial.println("making HTTP request...");
// make HTTP GET request to twitter: // make HTTP GET request to twitter:
client.println("GET /1/statuses/user_timeline.xml?screen_name=arduino&count=1 HTTP/1.1"); client.println("GET /1/statuses/user_timeline.xml?screen_name=arduino&count=1 HTTP/1.1");
client.println("HOST: api.twitter.com"); client.println("HOST: api.twitter.com");
client.println(); client.println();
@ -122,3 +126,4 @@ void connectToServer() {
// note the time of this connect attempt: // note the time of this connect attempt:
lastAttemptTime = millis(); lastAttemptTime = millis();
} }

View File

@ -10,7 +10,7 @@
created 18 Dec 2009 created 18 Dec 2009
by David A. Mellis by David A. Mellis
modified 4 Sep 2010 modified 20 Mar 2012
by Tom Igoe by Tom Igoe
*/ */
@ -20,7 +20,8 @@
// Enter a MAC address and IP address for your controller below. // Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network: // The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1, 177); IPAddress ip(192,168,1, 177);
// Initialize the Ethernet server library // Initialize the Ethernet server library
@ -28,23 +29,27 @@ IPAddress ip(192,168,1, 177);
// (port 80 is default for HTTP): // (port 80 is default for HTTP):
EthernetServer server(80); EthernetServer server(80);
void setup() void setup() {
{ Serial.begin(9600);
// start the Ethernet connection and the server: // start the Ethernet connection and the server:
Ethernet.begin(mac, ip); Ethernet.begin(mac, ip);
server.begin(); server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
} }
void loop()
{ void loop() {
// listen for incoming clients // listen for incoming clients
EthernetClient client = server.available(); EthernetClient client = server.available();
if (client) { if (client) {
Serial.println("new client");
// an http request ends with a blank line // an http request ends with a blank line
boolean currentLineIsBlank = true; boolean currentLineIsBlank = true;
while (client.connected()) { while (client.connected()) {
if (client.available()) { if (client.available()) {
char c = client.read(); char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline // if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended, // character) and the line is blank, the http request has ended,
// so you can send a reply // so you can send a reply
@ -52,16 +57,22 @@ void loop()
// send a standard http response header // send a standard http response header
client.println("HTTP/1.1 200 OK"); client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html"); client.println("Content-Type: text/html");
client.println("Connnection: close");
client.println(); client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
// add a meta refresh tag, so the browser pulls again every 5 seconds:
client.println("<meta http-equiv=\"refresh\" content=\"5\">");
// output the value of each analog input pin // output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) { for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
int sensorReading = analogRead(analogChannel);
client.print("analog input "); client.print("analog input ");
client.print(analogChannel); client.print(analogChannel);
client.print(" is "); client.print(" is ");
client.print(analogRead(analogChannel)); client.print(sensorReading);
client.println("<br />"); client.println("<br />");
} }
client.println("</html>");
break; break;
} }
if (c == '\n') { if (c == '\n') {
@ -78,5 +89,7 @@ void loop()
delay(1); delay(1);
// close the connection: // close the connection:
client.stop(); client.stop();
Serial.println("client disonnected");
} }
} }

View File

@ -56,7 +56,7 @@ static UNUSEDOK int FreeRam(void) {
* \param[in] str Pointer to string stored in flash memory. * \param[in] str Pointer to string stored in flash memory.
*/ */
static NOINLINE void SerialPrint_P(PGM_P str) { static NOINLINE void SerialPrint_P(PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.print(c); for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.write(c);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**

View File

@ -89,7 +89,7 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t
} }
else { else {
// finished all channels so wait for the refresh period to expire before starting over // finished all channels so wait for the refresh period to expire before starting over
if( (unsigned)*TCNTn < (usToTicks(REFRESH_INTERVAL) + 4) ) // allow a few ticks to ensure the next OCR1A not missed if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) ) // allow a few ticks to ensure the next OCR1A not missed
*OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL); *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
else else
*OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed *OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed
@ -298,7 +298,7 @@ void Servo::writeMicroseconds(int value)
{ {
// calculate and store the values for the given channel // calculate and store the values for the given channel
byte channel = this->servoIndex; byte channel = this->servoIndex;
if( (channel >= 0) && (channel < MAX_SERVOS) ) // ensure channel is valid if( (channel < MAX_SERVOS) ) // ensure channel is valid
{ {
if( value < SERVO_MIN() ) // ensure pulse width is valid if( value < SERVO_MIN() ) // ensure pulse width is valid
value = SERVO_MIN(); value = SERVO_MIN();

View File

@ -1,6 +1,23 @@
/*
Software serial multple serial test
Receives from the hardware serial, sends to software serial.
Receives from software serial, sends to hardware serial.
The circuit:
* RX is digital pin 2 (connect to TX of other device)
* TX is digital pin 3 (connect to RX of other device)
created back in the mists of time
by Tom Igoe
based on Mikal Hart's example
This example code is in the public domain.
*/
#include <SoftwareSerial.h> #include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); SoftwareSerial mySerial(2, 3); // RX, TX
void setup() void setup()
{ {

View File

@ -15,6 +15,8 @@
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
*/ */
extern "C" { extern "C" {
@ -73,14 +75,14 @@ void TwoWire::begin(int address)
begin((uint8_t)address); begin((uint8_t)address);
} }
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
{ {
// clamp to buffer length // clamp to buffer length
if(quantity > BUFFER_LENGTH){ if(quantity > BUFFER_LENGTH){
quantity = BUFFER_LENGTH; quantity = BUFFER_LENGTH;
} }
// perform blocking read into buffer // perform blocking read into buffer
uint8_t read = twi_readFrom(address, rxBuffer, quantity); uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop);
// set rx buffer iterator vars // set rx buffer iterator vars
rxBufferIndex = 0; rxBufferIndex = 0;
rxBufferLength = read; rxBufferLength = read;
@ -88,9 +90,19 @@ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
return read; return read;
} }
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
{
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}
uint8_t TwoWire::requestFrom(int address, int quantity) uint8_t TwoWire::requestFrom(int address, int quantity)
{ {
return requestFrom((uint8_t)address, (uint8_t)quantity); return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}
uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop)
{
return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop);
} }
void TwoWire::beginTransmission(uint8_t address) void TwoWire::beginTransmission(uint8_t address)
@ -109,10 +121,23 @@ void TwoWire::beginTransmission(int address)
beginTransmission((uint8_t)address); beginTransmission((uint8_t)address);
} }
uint8_t TwoWire::endTransmission(void) //
// Originally, 'endTransmission' was an f(void) function.
// It has been modified to take one parameter indicating
// whether or not a STOP should be performed on the bus.
// Calling endTransmission(false) allows a sketch to
// perform a repeated start.
//
// WARNING: Nothing in the library keeps track of whether
// the bus tenure has been properly ended with a STOP. It
// is very possible to leave the bus in a hung state if
// no call to endTransmission(true) is made. Some I2C
// devices will behave oddly if they do not see a STOP.
//
uint8_t TwoWire::endTransmission(uint8_t sendStop)
{ {
// transmit buffer (blocking) // transmit buffer (blocking)
int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1); int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop);
// reset tx buffer iterator vars // reset tx buffer iterator vars
txBufferIndex = 0; txBufferIndex = 0;
txBufferLength = 0; txBufferLength = 0;
@ -121,6 +146,14 @@ uint8_t TwoWire::endTransmission(void)
return ret; return ret;
} }
// This provides backwards compatibility with the original
// definition, and expected behaviour, of endTransmission
//
uint8_t TwoWire::endTransmission(void)
{
return endTransmission(true);
}
// must be called in: // must be called in:
// slave tx event callback // slave tx event callback
// or after beginTransmission(address) // or after beginTransmission(address)

View File

@ -15,6 +15,8 @@
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
*/ */
#ifndef TwoWire_h #ifndef TwoWire_h
@ -50,8 +52,11 @@ class TwoWire : public Stream
void beginTransmission(uint8_t); void beginTransmission(uint8_t);
void beginTransmission(int); void beginTransmission(int);
uint8_t endTransmission(void); uint8_t endTransmission(void);
uint8_t endTransmission(uint8_t);
uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t);
uint8_t requestFrom(uint8_t, uint8_t, uint8_t);
uint8_t requestFrom(int, int); uint8_t requestFrom(int, int);
uint8_t requestFrom(int, int, int);
virtual size_t write(uint8_t); virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *, size_t); virtual size_t write(const uint8_t *, size_t);
virtual int available(void); virtual int available(void);

View File

@ -15,6 +15,8 @@
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
*/ */
#include <math.h> #include <math.h>
@ -37,14 +39,16 @@
#include "twi.h" #include "twi.h"
static volatile uint8_t twi_state; static volatile uint8_t twi_state;
static uint8_t twi_slarw; static volatile uint8_t twi_slarw;
static volatile uint8_t twi_sendStop; // should the transaction end with a stop
static volatile uint8_t twi_inRepStart; // in the middle of a repeated start
static void (*twi_onSlaveTransmit)(void); static void (*twi_onSlaveTransmit)(void);
static void (*twi_onSlaveReceive)(uint8_t*, int); static void (*twi_onSlaveReceive)(uint8_t*, int);
static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_masterBufferIndex; static volatile uint8_t twi_masterBufferIndex;
static uint8_t twi_masterBufferLength; static volatile uint8_t twi_masterBufferLength;
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_txBufferIndex; static volatile uint8_t twi_txBufferIndex;
@ -65,6 +69,8 @@ void twi_init(void)
{ {
// initialize state // initialize state
twi_state = TWI_READY; twi_state = TWI_READY;
twi_sendStop = true; // default value
twi_inRepStart = false;
// activate internal pullups for twi. // activate internal pullups for twi.
digitalWrite(SDA, 1); digitalWrite(SDA, 1);
@ -103,9 +109,10 @@ void twi_setAddress(uint8_t address)
* Input address: 7bit i2c device address * Input address: 7bit i2c device address
* data: pointer to byte array * data: pointer to byte array
* length: number of bytes to read into array * length: number of bytes to read into array
* sendStop: Boolean indicating whether to send a stop at the end
* Output number of bytes read * Output number of bytes read
*/ */
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length) uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
{ {
uint8_t i; uint8_t i;
@ -119,6 +126,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
continue; continue;
} }
twi_state = TWI_MRX; twi_state = TWI_MRX;
twi_sendStop = sendStop;
// reset error state (0xFF.. no error occured) // reset error state (0xFF.. no error occured)
twi_error = 0xFF; twi_error = 0xFF;
@ -135,8 +143,20 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
twi_slarw = TW_READ; twi_slarw = TW_READ;
twi_slarw |= address << 1; twi_slarw |= address << 1;
// send start condition if (true == twi_inRepStart) {
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); // if we're in the repeated start state, then we've already sent the start,
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent outselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
TWDR = twi_slarw;
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
}
else
// send start condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
// wait for read operation to complete // wait for read operation to complete
while(TWI_MRX == twi_state){ while(TWI_MRX == twi_state){
@ -162,13 +182,14 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
* data: pointer to byte array * data: pointer to byte array
* length: number of bytes in array * length: number of bytes in array
* wait: boolean indicating to wait for write or not * wait: boolean indicating to wait for write or not
* sendStop: boolean indicating whether or not to send a stop at the end
* Output 0 .. success * Output 0 .. success
* 1 .. length to long for buffer * 1 .. length to long for buffer
* 2 .. address send, NACK received * 2 .. address send, NACK received
* 3 .. data send, NACK received * 3 .. data send, NACK received
* 4 .. other twi error (lost bus arbitration, bus error, ..) * 4 .. other twi error (lost bus arbitration, bus error, ..)
*/ */
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait) uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)
{ {
uint8_t i; uint8_t i;
@ -182,6 +203,7 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
continue; continue;
} }
twi_state = TWI_MTX; twi_state = TWI_MTX;
twi_sendStop = sendStop;
// reset error state (0xFF.. no error occured) // reset error state (0xFF.. no error occured)
twi_error = 0xFF; twi_error = 0xFF;
@ -198,8 +220,23 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
twi_slarw = TW_WRITE; twi_slarw = TW_WRITE;
twi_slarw |= address << 1; twi_slarw |= address << 1;
// send start condition // if we're in a repeated start, then we've already sent the START
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); // in the ISR. Don't do it again.
//
if (true == twi_inRepStart) {
// if we're in the repeated start state, then we've already sent the start,
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent outselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
TWDR = twi_slarw;
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
}
else
// send start condition
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs
// wait for write operation to complete // wait for write operation to complete
while(wait && (TWI_MTX == twi_state)){ while(wait && (TWI_MTX == twi_state)){
@ -343,7 +380,16 @@ SIGNAL(TWI_vect)
TWDR = twi_masterBuffer[twi_masterBufferIndex++]; TWDR = twi_masterBuffer[twi_masterBufferIndex++];
twi_reply(1); twi_reply(1);
}else{ }else{
twi_stop(); if (twi_sendStop)
twi_stop();
else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
} }
break; break;
case TW_MT_SLA_NACK: // address sent, nack received case TW_MT_SLA_NACK: // address sent, nack received
@ -374,6 +420,17 @@ SIGNAL(TWI_vect)
case TW_MR_DATA_NACK: // data received, nack sent case TW_MR_DATA_NACK: // data received, nack sent
// put final byte into buffer // put final byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR; twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
if (twi_sendStop)
twi_stop();
else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
break;
case TW_MR_SLA_NACK: // address sent, nack received case TW_MR_SLA_NACK: // address sent, nack received
twi_stop(); twi_stop();
break; break;

View File

@ -40,8 +40,8 @@
void twi_init(void); void twi_init(void);
void twi_setAddress(uint8_t); void twi_setAddress(uint8_t);
uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t); uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t);
uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t); uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t);
uint8_t twi_transmit(const uint8_t*, uint8_t); uint8_t twi_transmit(const uint8_t*, uint8_t);
void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) ); void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) );
void twi_attachSlaveTxEvent( void (*)(void) ); void twi_attachSlaveTxEvent( void (*)(void) );

View File

@ -35,29 +35,29 @@
#define RXLED0 PORTB |= (1<<0) #define RXLED0 PORTB |= (1<<0)
#define RXLED1 PORTB &= ~(1<<0) #define RXLED1 PORTB &= ~(1<<0)
const static uint8_t SDA = 2; static const uint8_t SDA = 2;
const static uint8_t SCL = 3; static const uint8_t SCL = 3;
// Map SPI port to 'new' pins D14..D17 // Map SPI port to 'new' pins D14..D17
const static uint8_t SS = 17; static const uint8_t SS = 17;
const static uint8_t MOSI = 16; static const uint8_t MOSI = 16;
const static uint8_t MISO = 14; static const uint8_t MISO = 14;
const static uint8_t SCK = 15; static const uint8_t SCK = 15;
// Mapping of analog pins as digital I/O // Mapping of analog pins as digital I/O
// A6-A11 share with digital pins // A6-A11 share with digital pins
const static uint8_t A0 = 18; static const uint8_t A0 = 18;
const static uint8_t A1 = 19; static const uint8_t A1 = 19;
const static uint8_t A2 = 20; static const uint8_t A2 = 20;
const static uint8_t A3 = 21; static const uint8_t A3 = 21;
const static uint8_t A4 = 22; static const uint8_t A4 = 22;
const static uint8_t A5 = 23; static const uint8_t A5 = 23;
const static uint8_t A6 = 24; // D4 static const uint8_t A6 = 24; // D4
const static uint8_t A7 = 25; // D6 static const uint8_t A7 = 25; // D6
const static uint8_t A8 = 26; // D8 static const uint8_t A8 = 26; // D8
const static uint8_t A9 = 27; // D9 static const uint8_t A9 = 27; // D9
const static uint8_t A10 = 28; // D10 static const uint8_t A10 = 28; // D10
const static uint8_t A11 = 29; // D12 static const uint8_t A11 = 29; // D12
// __AVR_ATmega32U4__ has an unusual mapping of pins to channels // __AVR_ATmega32U4__ has an unusual mapping of pins to channels
extern const uint8_t PROGMEM analog_pin_to_channel_PGM[]; extern const uint8_t PROGMEM analog_pin_to_channel_PGM[];

View File

@ -32,31 +32,31 @@
#define analogInputToDigitalPin(p) ((p < 16) ? (p) + 54 : -1) #define analogInputToDigitalPin(p) ((p < 16) ? (p) + 54 : -1)
#define digitalPinHasPWM(p) (((p) >= 2 && (p) <= 13) || ((p) >= 44 && (p)<= 46)) #define digitalPinHasPWM(p) (((p) >= 2 && (p) <= 13) || ((p) >= 44 && (p)<= 46))
const static uint8_t SS = 53; static const uint8_t SS = 53;
const static uint8_t MOSI = 51; static const uint8_t MOSI = 51;
const static uint8_t MISO = 50; static const uint8_t MISO = 50;
const static uint8_t SCK = 52; static const uint8_t SCK = 52;
const static uint8_t SDA = 20; static const uint8_t SDA = 20;
const static uint8_t SCL = 21; static const uint8_t SCL = 21;
const static uint8_t LED_BUILTIN = 13; static const uint8_t LED_BUILTIN = 13;
const static uint8_t A0 = 54; static const uint8_t A0 = 54;
const static uint8_t A1 = 55; static const uint8_t A1 = 55;
const static uint8_t A2 = 56; static const uint8_t A2 = 56;
const static uint8_t A3 = 57; static const uint8_t A3 = 57;
const static uint8_t A4 = 58; static const uint8_t A4 = 58;
const static uint8_t A5 = 59; static const uint8_t A5 = 59;
const static uint8_t A6 = 60; static const uint8_t A6 = 60;
const static uint8_t A7 = 61; static const uint8_t A7 = 61;
const static uint8_t A8 = 62; static const uint8_t A8 = 62;
const static uint8_t A9 = 63; static const uint8_t A9 = 63;
const static uint8_t A10 = 64; static const uint8_t A10 = 64;
const static uint8_t A11 = 65; static const uint8_t A11 = 65;
const static uint8_t A12 = 66; static const uint8_t A12 = 66;
const static uint8_t A13 = 67; static const uint8_t A13 = 67;
const static uint8_t A14 = 68; static const uint8_t A14 = 68;
const static uint8_t A15 = 69; static const uint8_t A15 = 69;
// A majority of the pins are NOT PCINTs, SO BE WARNED (i.e. you cannot use them as receive pins) // A majority of the pins are NOT PCINTs, SO BE WARNED (i.e. you cannot use them as receive pins)
// Only pins available for RECEIVE (TRANSMIT can be on any pin): // Only pins available for RECEIVE (TRANSMIT can be on any pin):

View File

@ -37,23 +37,23 @@
#define digitalPinHasPWM(p) ((p) == 3 || (p) == 5 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 11) #define digitalPinHasPWM(p) ((p) == 3 || (p) == 5 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 11)
#endif #endif
const static uint8_t SS = 10; static const uint8_t SS = 10;
const static uint8_t MOSI = 11; static const uint8_t MOSI = 11;
const static uint8_t MISO = 12; static const uint8_t MISO = 12;
const static uint8_t SCK = 13; static const uint8_t SCK = 13;
const static uint8_t SDA = 18; static const uint8_t SDA = 18;
const static uint8_t SCL = 19; static const uint8_t SCL = 19;
const static uint8_t LED_BUILTIN = 13; static const uint8_t LED_BUILTIN = 13;
const static uint8_t A0 = 14; static const uint8_t A0 = 14;
const static uint8_t A1 = 15; static const uint8_t A1 = 15;
const static uint8_t A2 = 16; static const uint8_t A2 = 16;
const static uint8_t A3 = 17; static const uint8_t A3 = 17;
const static uint8_t A4 = 18; static const uint8_t A4 = 18;
const static uint8_t A5 = 19; static const uint8_t A5 = 19;
const static uint8_t A6 = 20; static const uint8_t A6 = 20;
const static uint8_t A7 = 21; static const uint8_t A7 = 21;
#define digitalPinToPCICR(p) (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)0)) #define digitalPinToPCICR(p) (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)0))
#define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1)) #define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1))