diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index bfec943..830c995 100755 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -20,6 +20,7 @@ extern "C"{ #define INPUT 0x0 #define OUTPUT 0x1 +#define INPUT_PULLUP 0x2 #define true 0x1 #define false 0x0 diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index 1b1fa71..9985b78 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -46,8 +46,8 @@ struct ring_buffer { unsigned char buffer[SERIAL_BUFFER_SIZE]; - volatile int head; - volatile int tail; + volatile unsigned int head; + volatile unsigned int tail; }; #if defined(USBCON) diff --git a/cores/arduino/Print.cpp b/cores/arduino/Print.cpp index ff9b154..e541a6c 100755 --- a/cores/arduino/Print.cpp +++ b/cores/arduino/Print.cpp @@ -41,7 +41,7 @@ size_t Print::write(const uint8_t *buffer, size_t size) 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; while (1) { unsigned char c = pgm_read_byte(p++); diff --git a/cores/arduino/Stream.cpp b/cores/arduino/Stream.cpp index 5fad8dd..3d5b905 100644 --- a/cores/arduino/Stream.cpp +++ b/cores/arduino/Stream.cpp @@ -99,25 +99,27 @@ bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t size_t index = 0; // maximum target string length is 64k bytes! size_t termIndex = 0; int c; - + 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){ + + if(c != target[index]) + index = 0; // reset index if any char does not match + 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 return true; } } - else{ - index = 0; // reset index if any char does not match - } + if(termLen > 0 && c == terminator[termIndex]){ - if(++termIndex >= termLen) - return false; // return false if terminate string found before target string + if(++termIndex >= termLen) + return false; // return false if terminate string found before target string } else - termIndex = 0; + termIndex = 0; } return false; } diff --git a/cores/arduino/WInterrupts.c b/cores/arduino/WInterrupts.c index 1449cfb..3b9fe08 100755 --- a/cores/arduino/WInterrupts.c +++ b/cores/arduino/WInterrupts.c @@ -32,7 +32,7 @@ #include "wiring_private.h" -volatile static voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS]; +static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS]; // volatile static voidFuncPtr twiIntFunc; 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) MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20); GIMSK |= (1 << INT2); - #else - #warning attachInterrupt may need some more work for this cpu (case 1) #endif break; #endif diff --git a/cores/arduino/WString.cpp b/cores/arduino/WString.cpp index 3e81331..c6839fc 100644 --- a/cores/arduino/WString.cpp +++ b/cores/arduino/WString.cpp @@ -500,7 +500,7 @@ int String::lastIndexOf( char theChar ) 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]; buffer[fromIndex + 1] = '\0'; 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 { - 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; int found = -1; for (char *p = buffer; p <= buffer + fromIndex; p++) { diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index 902b153..a8bc817 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -45,6 +45,8 @@ int analogRead(uint8_t pin) if (pin >= 54) pin -= 54; // allow for channel or pin numbers #elif defined(__AVR_ATmega32U4__) 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 if (pin >= 14) pin -= 14; // allow for channel or pin numbers #endif diff --git a/cores/arduino/wiring_digital.c b/cores/arduino/wiring_digital.c index 97ef134..584a28a 100755 --- a/cores/arduino/wiring_digital.c +++ b/cores/arduino/wiring_digital.c @@ -32,17 +32,25 @@ void pinMode(uint8_t pin, uint8_t mode) { uint8_t bit = digitalPinToBitMask(pin); uint8_t port = digitalPinToPort(pin); - volatile uint8_t *reg; + volatile uint8_t *reg, *out; if (port == NOT_A_PIN) return; // JWS: can I let the optimizer do this? reg = portModeRegister(port); + out = portOutputRegister(port); if (mode == INPUT) { uint8_t oldSREG = SREG; cli(); *reg &= ~bit; + *out &= ~bit; + SREG = oldSREG; + } else if (mode == INPUT_PULLUP) { + uint8_t oldSREG = SREG; + cli(); + *reg &= ~bit; + *out |= bit; SREG = oldSREG; } else { uint8_t oldSREG = SREG; diff --git a/libraries/Ethernet/Dhcp.cpp b/libraries/Ethernet/Dhcp.cpp index 62bbfeb..e4d27f7 100755 --- a/libraries/Ethernet/Dhcp.cpp +++ b/libraries/Ethernet/Dhcp.cpp @@ -11,13 +11,33 @@ int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) { - uint8_t dhcp_state = STATE_DHCP_START; - uint8_t messageType = 0; - - // zero out _dhcpMacAddr, _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp - memset(_dhcpMacAddr, 0, 26); + _dhcpLeaseTime=0; + _dhcpT1=0; + _dhcpT2=0; + _lastCheck=0; + _timeout = timeout; + _responseTimeout = responseTimeout; + + // zero out _dhcpMacAddr + memset(_dhcpMacAddr, 0, 6); + reset_DHCP_lease(); 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 _dhcpTransactionId = random(1UL, 2000UL); @@ -35,55 +55,75 @@ int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long 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++; 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; - messageType = parseDHCPResponse(responseTimeout, respId); + messageType = parseDHCPResponse(_responseTimeout, respId); if(messageType == DHCP_OFFER) { // We'll use the transaction ID that the offer came with, // rather than the one we were up to _dhcpTransactionId = respId; 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; - messageType = parseDHCPResponse(responseTimeout, respId); + messageType = parseDHCPResponse(_responseTimeout, respId); if(messageType == DHCP_ACK) { - dhcp_state = STATE_DHCP_LEASED; + _dhcp_state = STATE_DHCP_LEASED; 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) - dhcp_state = STATE_DHCP_START; + _dhcp_state = STATE_DHCP_START; } if(messageType == 255) { 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; } // We're done with the socket now _dhcpUdpSocket.stop(); _dhcpTransactionId++; - + return result; } @@ -302,8 +342,26 @@ uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& tr } } 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 : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); + _dhcpLeaseTime = ntohl(_dhcpLeaseTime); + _renewInSec = _dhcpLeaseTime; + break; + default : opt_len = _dhcpUdpSocket.read(); // Skip over the rest of this option @@ -322,6 +380,68 @@ uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& tr 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() { return IPAddress(_dhcpLocalIp); diff --git a/libraries/Ethernet/Dhcp.h b/libraries/Ethernet/Dhcp.h index 149876d..4a47936 100755 --- a/libraries/Ethernet/Dhcp.h +++ b/libraries/Ethernet/Dhcp.h @@ -45,6 +45,13 @@ #define MAX_DHCP_OPT 16 #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 { @@ -139,8 +146,19 @@ private: uint8_t _dhcpGatewayIp[4]; uint8_t _dhcpDhcpServerIp[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; + int request_DHCP_lease(); + void reset_DHCP_lease(); void presend_DHCP(); void send_DHCP_MESSAGE(uint8_t, uint16_t); void printByte(char *, uint8_t); @@ -154,6 +172,7 @@ public: IPAddress getDnsServerIp(); int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int checkLease(); }; #endif diff --git a/libraries/Ethernet/Ethernet.cpp b/libraries/Ethernet/Ethernet.cpp index c298f3d..5d28f71 100644 --- a/libraries/Ethernet/Ethernet.cpp +++ b/libraries/Ethernet/Ethernet.cpp @@ -10,7 +10,8 @@ uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { int EthernetClass::begin(uint8_t *mac_address) { - DhcpClass dhcp; + _dhcp = new DhcpClass(); + // Initialise the basic info W5100.init(); @@ -18,15 +19,15 @@ int EthernetClass::begin(uint8_t *mac_address) W5100.setIPAddress(IPAddress(0,0,0,0).raw_address()); // 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) { // We've successfully found a DHCP server and got our configuration info, so set things // accordingly - W5100.setIPAddress(dhcp.getLocalIp().raw_address()); - W5100.setGatewayIp(dhcp.getGatewayIp().raw_address()); - W5100.setSubnetMask(dhcp.getSubnetMask().raw_address()); - _dnsServerAddress = dhcp.getDnsServerIp(); + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); } return ret; @@ -66,6 +67,31 @@ void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress 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 ret; diff --git a/libraries/Ethernet/Ethernet.h b/libraries/Ethernet/Ethernet.h index c916dda..2a07ff3 100644 --- a/libraries/Ethernet/Ethernet.h +++ b/libraries/Ethernet/Ethernet.h @@ -6,12 +6,14 @@ #include "IPAddress.h" #include "EthernetClient.h" #include "EthernetServer.h" +#include "Dhcp.h" #define MAX_SOCK_NUM 4 class EthernetClass { private: IPAddress _dnsServerAddress; + DhcpClass* _dhcp; public: static uint8_t _state[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, IPAddress gateway); void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); + int maintain(); IPAddress localIP(); IPAddress subnetMask(); diff --git a/libraries/Ethernet/EthernetClient.cpp b/libraries/Ethernet/EthernetClient.cpp index a77a62b..9885efb 100644 --- a/libraries/Ethernet/EthernetClient.cpp +++ b/libraries/Ethernet/EthernetClient.cpp @@ -41,7 +41,7 @@ int EthernetClient::connect(IPAddress ip, uint16_t port) { for (int i = 0; i < MAX_SOCK_NUM; 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; break; } diff --git a/libraries/Ethernet/EthernetUdp.cpp b/libraries/Ethernet/EthernetUdp.cpp index 9c752fc..3760052 100644 --- a/libraries/Ethernet/EthernetUdp.cpp +++ b/libraries/Ethernet/EthernetUdp.cpp @@ -52,15 +52,16 @@ uint8_t EthernetUDP::begin(uint16_t port) { return 0; _port = port; + _remaining = 0; socket(_sock, SnMR::UDP, _port, 0); return 1; } -/* Is data available in rx buffer? Returns 0 if no, number of available bytes if yes. - * returned value includes 8 byte UDP header!*/ +/* return number of bytes available in the current packet, + will return zero if parsePacket hasn't been called yet */ int EthernetUDP::available() { - return W5100.getRXReceivedSize(_sock); + return _remaining; } /* Release any resources being used by this EthernetUDP instance */ @@ -116,11 +117,14 @@ size_t EthernetUDP::write(const uint8_t *buffer, size_t size) 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 uint8_t tmpBuf[8]; - int ret =0; + int ret =0; //read 8 header bytes and get IP and port from it ret = recv(_sock,tmpBuf,8); if (ret > 0) @@ -128,8 +132,11 @@ int EthernetUDP::parsePacket() _remoteIP = tmpBuf; _remotePort = tmpBuf[4]; _remotePort = (_remotePort << 8) + tmpBuf[5]; + _remaining = tmpBuf[6]; + _remaining = (_remaining << 8) + tmpBuf[7]; + // When we get here, any remaining bytes are the data - ret = available(); + ret = _remaining; } return ret; } @@ -140,34 +147,58 @@ int EthernetUDP::parsePacket() int EthernetUDP::read() { uint8_t byte; - if (recv(_sock, &byte, 1) > 0) + + if ((_remaining > 0) && (recv(_sock, &byte, 1) > 0)) { // We read things without any problems + _remaining--; return byte; } + // If we get here, there's no data available return -1; } 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 - at a time. - int i; - for(i=0;i<(int)bufLen;i++) { - recv(_sock,tmpBuf,1); - buf[i]=tmpBuf[0]; + + if (_remaining > 0) + { + + int got; + + 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() { uint8_t b; - // Unlike recv, peek doesn't check to see if there's any data available, so we must - if (!available()) + // Unlike recv, peek doesn't check to see if there's any data available, so we must. + // If the user hasn't called parsePacket yet then return nothing otherwise they + // may get the UDP header + if (!_remaining) return -1; ::peek(_sock, &b); return b; @@ -175,7 +206,11 @@ int EthernetUDP::peek() 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(); } diff --git a/libraries/Ethernet/EthernetUdp.h b/libraries/Ethernet/EthernetUdp.h index 9a2b653..8a6b7ab 100644 --- a/libraries/Ethernet/EthernetUdp.h +++ b/libraries/Ethernet/EthernetUdp.h @@ -48,6 +48,7 @@ private: 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 _offset; // offset into the packet being sent + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed public: EthernetUDP(); // Constructor diff --git a/libraries/Ethernet/examples/ChatServer/ChatServer.ino b/libraries/Ethernet/examples/ChatServer/ChatServer.ino index 9f819fd..de75257 100644 --- a/libraries/Ethernet/examples/ChatServer/ChatServer.ino +++ b/libraries/Ethernet/examples/ChatServer/ChatServer.ino @@ -12,7 +12,7 @@ created 18 Dec 2009 by David A. Mellis - modified 10 August 2010 + modified 12 March 2012 by Tom Igoe */ @@ -23,14 +23,16 @@ // 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 mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192,168,1, 177); IPAddress gateway(192,168,1, 1); IPAddress subnet(255, 255, 0, 0); + // telnet defaults to port 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() { // initialize the ethernet device @@ -39,25 +41,34 @@ void setup() { server.begin(); // open the serial port Serial.begin(9600); + Serial.print("Chat server address:"); + Serial.println(Ethernet.localIP()); } void loop() { // wait for a new client: EthernetClient client = server.available(); - + // when the client sends the first byte, say hello: if (client) { - if (!gotAMessage) { + if (!alreadyConnected) { + // clead out the input buffer: + client.flush(); Serial.println("We have a new client"); client.println("Hello, client!"); - gotAMessage = true; + alreadyConnected = true; + } + + if (client.available() > 0) { + // 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.write(thisChar); } - - // 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); } } + + + diff --git a/libraries/Ethernet/examples/PachubeClient/PachubeClient.ino b/libraries/Ethernet/examples/PachubeClient/PachubeClient.ino index a169443..4d4290d 100644 --- a/libraries/Ethernet/examples/PachubeClient/PachubeClient.ino +++ b/libraries/Ethernet/examples/PachubeClient/PachubeClient.ino @@ -6,15 +6,20 @@ the Adafruit Ethernet shield, either one will work, as long as it's got 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: * Analog sensor attached to analog in 0 * Ethernet shield attached to pins 10, 11, 12, 13 created 15 March 2010 - updated 26 Oct 2011 - by Tom Igoe + updated 16 Mar 2012 + 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. */ @@ -22,6 +27,10 @@ #include #include +#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. // Newer Ethernet shields have a MAC address printed on a sticker on the shield // fill in your address here: @@ -34,26 +43,22 @@ IPAddress ip(10,0,1,20); // initialize the library instance: EthernetClient client; -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 +// if you don't want to use DNS (and reduce your sketch size) +// use the numeric IP instead of the name for the server: +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() { // start serial port: Serial.begin(9600); - // start the Ethernet connection: + // start the Ethernet connection: if (Ethernet.begin(mac) == 0) { Serial.println("Failed to configure Ethernet using DHCP"); - // no point in carrying on, so do nothing forevermore: - 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: + // DHCP failed, so use a fixed IP address: Ethernet.begin(mac, ip); } } @@ -91,34 +96,43 @@ void loop() { // this method makes a HTTP connection to the server: void sendData(int thisData) { // if there's a successful connection: - if (client.connect("www.pachube.com", 80)) { + if (client.connect(server, 80)) { 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"); + // send the HTTP PUT request: + client.print("PUT /v2/feeds/"); + client.print(FEEDID); + client.println(".csv HTTP/1.1"); + client.println("Host: api.pachube.com"); + client.print("X-PachubeApiKey: "); + client.println(APIKEY); + client.print("User-Agent: "); + client.println(USERAGENT); client.print("Content-Length: "); // calculate the length of the sensor reading in bytes: - int thisLength = getLength(thisData); - client.println(thisLength, DEC); + // 8 bytes for "sensor1," + number of digits of the data: + int thisLength = 8 + getLength(thisData); + client.println(thisLength); // last pieces of the HTTP PUT request: - client.print("Content-Type: text/csv\n"); - client.println("Connection: close\n"); + client.println("Content-Type: text/csv"); + client.println("Connection: close"); + client.println(); // here's the actual content of the PUT request: - client.println(thisData, DEC); - - // note the time that the connection was made: - lastConnectionTime = millis(); + client.print("sensor1,"); + client.println(thisData); + } else { // if you couldn't make a connection: Serial.println("connection failed"); + Serial.println(); + Serial.println("disconnecting."); + client.stop(); } + // note the time that the connection was made or attempted: + lastConnectionTime = millis(); } diff --git a/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.ino b/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.ino index 4a03100..3535287 100644 --- a/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.ino +++ b/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.ino @@ -6,6 +6,10 @@ the Adafruit Ethernet shield, either one will work, as long as it's got 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 version 0019. @@ -14,9 +18,10 @@ * Ethernet shield attached to pins 10, 11, 12, 13 created 15 March 2010 - updated 26 Oct 2011 - by Tom Igoe + updated 16 Mar 2012 + by Tom Igoe with input from Usman Haque and Joe Saavedra + http://arduino.cc/en/Tutorial/PachubeClientString This code is in the public domain. */ @@ -24,9 +29,14 @@ #include #include + +#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. // fill in your address here: -byte mac[] = { + byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // fill in an available IP address on your network here, // for manual configuration: @@ -35,9 +45,14 @@ IPAddress ip(10,0,1,20); // initialize the library instance: EthernetClient client; -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 +// if you don't want to use DNS (and reduce your sketch size) +// use the numeric IP instead of the name for the server: +//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() { // start serial port: @@ -47,7 +62,7 @@ void setup() { // start the Ethernet connection: if (Ethernet.begin(mac) == 0) { Serial.println("Failed to configure Ethernet using DHCP"); - // Configure manually: + // DHCP failed, so use a fixed IP address: Ethernet.begin(mac, ip); } } @@ -56,13 +71,15 @@ void loop() { // read the analog sensor: int sensorReading = analogRead(A0); // 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 // pachube feed is set up to handle multiple values: int otherSensorReading = analogRead(A1); - dataString += ","; - dataString += String(otherSensorReading); + dataString += "\nsensor2,"; + dataString += otherSensorReading; // if there's incoming data from the net connection. // send it out the serial port. This is for debugging @@ -81,7 +98,7 @@ void loop() { } // if you're not connected, and ten seconds have passed since - // your last connection, then connect again and send data: + // your last connection, then connect again and send data: if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) { sendData(dataString); } @@ -93,29 +110,36 @@ void loop() { // this method makes a HTTP connection to the server: void sendData(String thisData) { // if there's a successful connection: - if (client.connect("www.pachube.com", 80)) { + if (client.connect(server, 80)) { 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"); + // send the HTTP PUT request: + client.print("PUT /v2/feeds/"); + client.print(FEEDID); + client.println(".csv HTTP/1.1"); + client.println("Host: api.pachube.com"); + client.print("X-PachubeApiKey: "); + client.println(APIKEY); + client.print("User-Agent: "); + client.println(USERAGENT); client.print("Content-Length: "); - client.println(thisData.length(), DEC); + client.println(thisData.length()); // last pieces of the HTTP PUT request: - client.print("Content-Type: text/csv\n"); - client.println("Connection: close\n"); + client.println("Content-Type: text/csv"); + client.println("Connection: close"); + client.println(); // 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"); + Serial.println(); + Serial.println("disconnecting."); + client.stop(); } + // note the time that the connection was made or attempted: + lastConnectionTime = millis(); } + diff --git a/libraries/Ethernet/examples/TwitterClient/TwitterClient.ino b/libraries/Ethernet/examples/TwitterClient/TwitterClient.ino index f113e17..a3b397d 100644 --- a/libraries/Ethernet/examples/TwitterClient/TwitterClient.ino +++ b/libraries/Ethernet/examples/TwitterClient/TwitterClient.ino @@ -14,7 +14,7 @@ version 0019. Circuit: - * Ethernet shield attached to pins 10, 11, 12, 13 + * Ethernet shield attached to pins 10, 11, 12, 13 created 21 May 2011 by Tom Igoe @@ -35,12 +35,12 @@ IPAddress ip(192,168,1,20); // initialize the library instance: EthernetClient client; -const int requestInterval = 60000; // delay between requests +const unsigned long requestInterval = 60000; // delay between requests char serverName[] = "api.twitter.com"; // twitter URL 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 tweet = ""; // string to hold the tweet @@ -51,13 +51,17 @@ void setup() { currentLine.reserve(256); tweet.reserve(150); -// initialize serial: + // initialize serial: Serial.begin(9600); // attempt a DHCP connection: + Serial.println("Attempting to get an IP address using DHCP:"); if (!Ethernet.begin(mac)) { // 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); } + Serial.print("My address:"); + Serial.println(Ethernet.localIP()); // connect to Twitter: connectToServer(); } @@ -114,7 +118,7 @@ void connectToServer() { Serial.println("connecting to server..."); if (client.connect(serverName, 80)) { 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("HOST: api.twitter.com"); client.println(); @@ -122,3 +126,4 @@ void connectToServer() { // note the time of this connect attempt: lastAttemptTime = millis(); } + diff --git a/libraries/Ethernet/examples/WebServer/WebServer.ino b/libraries/Ethernet/examples/WebServer/WebServer.ino index 6837f83..7cf2c53 100644 --- a/libraries/Ethernet/examples/WebServer/WebServer.ino +++ b/libraries/Ethernet/examples/WebServer/WebServer.ino @@ -10,7 +10,7 @@ created 18 Dec 2009 by David A. Mellis - modified 4 Sep 2010 + modified 20 Mar 2012 by Tom Igoe */ @@ -20,7 +20,8 @@ // 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 mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192,168,1, 177); // Initialize the Ethernet server library @@ -28,23 +29,27 @@ IPAddress ip(192,168,1, 177); // (port 80 is default for HTTP): EthernetServer server(80); -void setup() -{ +void setup() { + Serial.begin(9600); // start the Ethernet connection and the server: Ethernet.begin(mac, ip); server.begin(); + Serial.print("server is at "); + Serial.println(Ethernet.localIP()); } -void loop() -{ + +void loop() { // listen for incoming clients EthernetClient client = server.available(); if (client) { + Serial.println("new client"); // an http request ends with a blank line boolean currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); + Serial.write(c); // 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 @@ -52,16 +57,22 @@ void loop() // send a standard http response header client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); + client.println("Connnection: close"); client.println(); - + client.println(""); + client.println(""); + // add a meta refresh tag, so the browser pulls again every 5 seconds: + client.println(""); // output the value of each analog input pin for (int analogChannel = 0; analogChannel < 6; analogChannel++) { + int sensorReading = analogRead(analogChannel); client.print("analog input "); client.print(analogChannel); client.print(" is "); - client.print(analogRead(analogChannel)); - client.println("
"); + client.print(sensorReading); + client.println("
"); } + client.println(""); break; } if (c == '\n') { @@ -78,5 +89,7 @@ void loop() delay(1); // close the connection: client.stop(); + Serial.println("client disonnected"); } } + diff --git a/libraries/SD/utility/SdFatUtil.h b/libraries/SD/utility/SdFatUtil.h index 283fcb2..7d6b410 100644 --- a/libraries/SD/utility/SdFatUtil.h +++ b/libraries/SD/utility/SdFatUtil.h @@ -56,7 +56,7 @@ static UNUSEDOK int FreeRam(void) { * \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); + for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.write(c); } //------------------------------------------------------------------------------ /** diff --git a/libraries/Servo/Servo.cpp b/libraries/Servo/Servo.cpp index acac29a..a716433 100755 --- a/libraries/Servo/Servo.cpp +++ b/libraries/Servo/Servo.cpp @@ -89,7 +89,7 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t } else { // 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); else *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 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 value = SERVO_MIN(); diff --git a/libraries/SoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino b/libraries/SoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino index 1f535bd..615d2b3 100644 --- a/libraries/SoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino +++ b/libraries/SoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino @@ -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 mySerial(2, 3); +SoftwareSerial mySerial(2, 3); // RX, TX void setup() { diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index d83f478..4e7a17c 100755 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -15,6 +15,8 @@ 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 + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts */ extern "C" { @@ -73,14 +75,14 @@ void TwoWire::begin(int 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 if(quantity > BUFFER_LENGTH){ quantity = BUFFER_LENGTH; } // 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 rxBufferIndex = 0; rxBufferLength = read; @@ -88,9 +90,19 @@ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) 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) { - 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) @@ -109,10 +121,23 @@ void TwoWire::beginTransmission(int 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) - int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1); + int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop); // reset tx buffer iterator vars txBufferIndex = 0; txBufferLength = 0; @@ -121,6 +146,14 @@ uint8_t TwoWire::endTransmission(void) 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: // slave tx event callback // or after beginTransmission(address) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 9ea4afd..a93d0f5 100755 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -15,6 +15,8 @@ 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 + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts */ #ifndef TwoWire_h @@ -50,8 +52,11 @@ class TwoWire : public Stream void beginTransmission(uint8_t); void beginTransmission(int); 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); uint8_t requestFrom(int, int); + uint8_t requestFrom(int, int, int); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *, size_t); virtual int available(void); diff --git a/libraries/Wire/utility/twi.c b/libraries/Wire/utility/twi.c index d80114b..6b2db3c 100644 --- a/libraries/Wire/utility/twi.c +++ b/libraries/Wire/utility/twi.c @@ -15,6 +15,8 @@ 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 + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts */ #include @@ -37,14 +39,16 @@ #include "twi.h" 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_onSlaveReceive)(uint8_t*, int); static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; 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 volatile uint8_t twi_txBufferIndex; @@ -65,6 +69,8 @@ void twi_init(void) { // initialize state twi_state = TWI_READY; + twi_sendStop = true; // default value + twi_inRepStart = false; // activate internal pullups for twi. digitalWrite(SDA, 1); @@ -103,9 +109,10 @@ void twi_setAddress(uint8_t address) * Input address: 7bit i2c device address * data: pointer to byte 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 */ -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; @@ -119,6 +126,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length) continue; } twi_state = TWI_MRX; + twi_sendStop = sendStop; // reset error state (0xFF.. no error occured) 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 |= address << 1; - // send start condition - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); + 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(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); // wait for read operation to complete 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 * length: number of bytes in array * 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 * 1 .. length to long for buffer * 2 .. address send, NACK received * 3 .. data send, NACK received * 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; @@ -182,6 +203,7 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait continue; } twi_state = TWI_MTX; + twi_sendStop = sendStop; // reset error state (0xFF.. no error occured) 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 |= address << 1; - // send start condition - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); + // if we're in a repeated start, then we've already sent the START + // 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 while(wait && (TWI_MTX == twi_state)){ @@ -343,7 +380,16 @@ SIGNAL(TWI_vect) TWDR = twi_masterBuffer[twi_masterBufferIndex++]; twi_reply(1); }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; 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 // put final byte into buffer 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 twi_stop(); break; diff --git a/libraries/Wire/utility/twi.h b/libraries/Wire/utility/twi.h index 831b928..6526593 100755 --- a/libraries/Wire/utility/twi.h +++ b/libraries/Wire/utility/twi.h @@ -40,8 +40,8 @@ void twi_init(void); void twi_setAddress(uint8_t); - uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t); - uint8_t twi_writeTo(uint8_t, 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); uint8_t twi_transmit(const uint8_t*, uint8_t); void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) ); void twi_attachSlaveTxEvent( void (*)(void) ); diff --git a/variants/leonardo/pins_arduino.h b/variants/leonardo/pins_arduino.h index 15afb4e..5a15146 100644 --- a/variants/leonardo/pins_arduino.h +++ b/variants/leonardo/pins_arduino.h @@ -35,29 +35,29 @@ #define RXLED0 PORTB |= (1<<0) #define RXLED1 PORTB &= ~(1<<0) -const static uint8_t SDA = 2; -const static uint8_t SCL = 3; +static const uint8_t SDA = 2; +static const uint8_t SCL = 3; // Map SPI port to 'new' pins D14..D17 -const static uint8_t SS = 17; -const static uint8_t MOSI = 16; -const static uint8_t MISO = 14; -const static uint8_t SCK = 15; +static const uint8_t SS = 17; +static const uint8_t MOSI = 16; +static const uint8_t MISO = 14; +static const uint8_t SCK = 15; // Mapping of analog pins as digital I/O // A6-A11 share with digital pins -const static uint8_t A0 = 18; -const static uint8_t A1 = 19; -const static uint8_t A2 = 20; -const static uint8_t A3 = 21; -const static uint8_t A4 = 22; -const static uint8_t A5 = 23; -const static uint8_t A6 = 24; // D4 -const static uint8_t A7 = 25; // D6 -const static uint8_t A8 = 26; // D8 -const static uint8_t A9 = 27; // D9 -const static uint8_t A10 = 28; // D10 -const static uint8_t A11 = 29; // D12 +static const uint8_t A0 = 18; +static const uint8_t A1 = 19; +static const uint8_t A2 = 20; +static const uint8_t A3 = 21; +static const uint8_t A4 = 22; +static const uint8_t A5 = 23; +static const uint8_t A6 = 24; // D4 +static const uint8_t A7 = 25; // D6 +static const uint8_t A8 = 26; // D8 +static const uint8_t A9 = 27; // D9 +static const uint8_t A10 = 28; // D10 +static const uint8_t A11 = 29; // D12 // __AVR_ATmega32U4__ has an unusual mapping of pins to channels extern const uint8_t PROGMEM analog_pin_to_channel_PGM[]; diff --git a/variants/mega/pins_arduino.h b/variants/mega/pins_arduino.h index 57ec97f..5a9b4cb 100644 --- a/variants/mega/pins_arduino.h +++ b/variants/mega/pins_arduino.h @@ -32,31 +32,31 @@ #define analogInputToDigitalPin(p) ((p < 16) ? (p) + 54 : -1) #define digitalPinHasPWM(p) (((p) >= 2 && (p) <= 13) || ((p) >= 44 && (p)<= 46)) -const static uint8_t SS = 53; -const static uint8_t MOSI = 51; -const static uint8_t MISO = 50; -const static uint8_t SCK = 52; +static const uint8_t SS = 53; +static const uint8_t MOSI = 51; +static const uint8_t MISO = 50; +static const uint8_t SCK = 52; -const static uint8_t SDA = 20; -const static uint8_t SCL = 21; -const static uint8_t LED_BUILTIN = 13; +static const uint8_t SDA = 20; +static const uint8_t SCL = 21; +static const uint8_t LED_BUILTIN = 13; -const static uint8_t A0 = 54; -const static uint8_t A1 = 55; -const static uint8_t A2 = 56; -const static uint8_t A3 = 57; -const static uint8_t A4 = 58; -const static uint8_t A5 = 59; -const static uint8_t A6 = 60; -const static uint8_t A7 = 61; -const static uint8_t A8 = 62; -const static uint8_t A9 = 63; -const static uint8_t A10 = 64; -const static uint8_t A11 = 65; -const static uint8_t A12 = 66; -const static uint8_t A13 = 67; -const static uint8_t A14 = 68; -const static uint8_t A15 = 69; +static const uint8_t A0 = 54; +static const uint8_t A1 = 55; +static const uint8_t A2 = 56; +static const uint8_t A3 = 57; +static const uint8_t A4 = 58; +static const uint8_t A5 = 59; +static const uint8_t A6 = 60; +static const uint8_t A7 = 61; +static const uint8_t A8 = 62; +static const uint8_t A9 = 63; +static const uint8_t A10 = 64; +static const uint8_t A11 = 65; +static const uint8_t A12 = 66; +static const uint8_t A13 = 67; +static const uint8_t A14 = 68; +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) // Only pins available for RECEIVE (TRANSMIT can be on any pin): diff --git a/variants/standard/pins_arduino.h b/variants/standard/pins_arduino.h index 6e774d4..30b4266 100644 --- a/variants/standard/pins_arduino.h +++ b/variants/standard/pins_arduino.h @@ -37,23 +37,23 @@ #define digitalPinHasPWM(p) ((p) == 3 || (p) == 5 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 11) #endif -const static uint8_t SS = 10; -const static uint8_t MOSI = 11; -const static uint8_t MISO = 12; -const static uint8_t SCK = 13; +static const uint8_t SS = 10; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 12; +static const uint8_t SCK = 13; -const static uint8_t SDA = 18; -const static uint8_t SCL = 19; -const static uint8_t LED_BUILTIN = 13; +static const uint8_t SDA = 18; +static const uint8_t SCL = 19; +static const uint8_t LED_BUILTIN = 13; -const static uint8_t A0 = 14; -const static uint8_t A1 = 15; -const static uint8_t A2 = 16; -const static uint8_t A3 = 17; -const static uint8_t A4 = 18; -const static uint8_t A5 = 19; -const static uint8_t A6 = 20; -const static uint8_t A7 = 21; +static const uint8_t A0 = 14; +static const uint8_t A1 = 15; +static const uint8_t A2 = 16; +static const uint8_t A3 = 17; +static const uint8_t A4 = 18; +static const uint8_t A5 = 19; +static const uint8_t A6 = 20; +static const uint8_t A7 = 21; #define digitalPinToPCICR(p) (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)0)) #define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1)) @@ -215,4 +215,4 @@ const uint8_t PROGMEM digital_pin_to_timer_PGM[] = { #endif -#endif \ No newline at end of file +#endif