diff --git a/.arduino-ci.yml b/.arduino-ci.yml new file mode 100644 index 00000000..5fca9d0e --- /dev/null +++ b/.arduino-ci.yml @@ -0,0 +1,7 @@ + +unittest: + platforms: + - mega2560 +compile: + platforms: + - mega2560 diff --git a/.github/workflows/arduino_ci.yml b/.github/workflows/arduino_ci.yml new file mode 100644 index 00000000..5a2638cc --- /dev/null +++ b/.github/workflows/arduino_ci.yml @@ -0,0 +1,16 @@ +--- +name: Arduino CI + +on: [push, pull_request] + +jobs: + arduino_ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - name: Automated tests + run: scripts/testAndBuild.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8dffcb70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +/.bundle/ +/.yardoc +Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +vendor +*.gem + +# rspec failure tracking +.rspec_status + +# C++ stuff +*.bin +*.bin.dSYM +.DS_Store +.vscode +*.so* diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..14f68727 --- /dev/null +++ b/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org' +gem 'arduino_ci', git: 'https://github.com/jgfoster/arduino_ci', branch: 'main' diff --git a/README.adoc b/README.adoc index ed937b1b..0ff6792e 100644 --- a/README.adoc +++ b/README.adoc @@ -7,6 +7,8 @@ image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/ image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml/badge.svg["Compile Examples status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml"] image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml/badge.svg["Spell Check status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml"] +![Linux Build Status](https://github.com/Arduino-CI/Ethernet/workflows/ubuntu/badge.svg) ![macOS Build Status](https://github.com/Arduino-CI/Ethernet/workflows/macos/badge.svg) ![Windows Build Status](https://github.com/Arduino-CI/Ethernet/workflows/windows/badge.svg) + With the Arduino Ethernet Shield, this library allows an Arduino board to connect to the internet. For more information about this library please visit us at diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 00000000..f87e6c04 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,4 @@ +#! /bin/sh +bundle config --local path vendor/bundle +bundle install +bundle exec arduino_ci.rb --skip-examples-compilation diff --git a/scripts/testAndBuild.sh b/scripts/testAndBuild.sh new file mode 100755 index 00000000..f7c20abe --- /dev/null +++ b/scripts/testAndBuild.sh @@ -0,0 +1,4 @@ +#! /bin/sh +bundle config --local path vendor/bundle +bundle install +bundle exec arduino_ci.rb diff --git a/src/Ethernet.cpp b/src/Ethernet.cpp index 9257090c..a86926bc 100644 --- a/src/Ethernet.cpp +++ b/src/Ethernet.cpp @@ -23,10 +23,10 @@ #include "utility/w5100.h" #include "Dhcp.h" -IPAddress EthernetClass::_dnsServerAddress; -DhcpClass* EthernetClass::_dhcp = NULL; +IPAddress Ethernet_Base::_dnsServerAddress; +DhcpClass* Ethernet_Base::_dhcp = NULL; -int EthernetClass::begin(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) +int Ethernet_Base::begin(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) { static DhcpClass s_dhcp; _dhcp = &s_dhcp; @@ -49,12 +49,12 @@ int EthernetClass::begin(uint8_t *mac, unsigned long timeout, unsigned long resp W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); SPI.endTransaction(); _dnsServerAddress = _dhcp->getDnsServerIp(); - socketPortRand(micros()); + EthernetClass::socketPortRand(micros()); } return ret; } -void EthernetClass::begin(uint8_t *mac, IPAddress ip) +void Ethernet_Base::begin(uint8_t *mac, IPAddress ip) { // Assume the DNS server will be the machine on the same network as the local IP // but with last octet being '1' @@ -63,7 +63,7 @@ void EthernetClass::begin(uint8_t *mac, IPAddress ip) begin(mac, ip, dns); } -void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns) +void Ethernet_Base::begin(uint8_t *mac, IPAddress ip, IPAddress dns) { // Assume the gateway will be the machine on the same network as the local IP // but with last octet being '1' @@ -72,13 +72,13 @@ void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns) begin(mac, ip, dns, gateway); } -void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway) +void Ethernet_Base::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway) { IPAddress subnet(255, 255, 255, 0); begin(mac, ip, dns, gateway, subnet); } -void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) +void Ethernet_Base::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) { if (W5100.init() == 0) return; SPI.beginTransaction(SPI_ETHERNET_SETTINGS); @@ -92,20 +92,20 @@ void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress g W5100.setGatewayIp(gateway._address.bytes); W5100.setSubnetMask(subnet._address.bytes); #else - W5100.setIPAddress(ip._address); - W5100.setGatewayIp(gateway._address); - W5100.setSubnetMask(subnet._address); + W5100.setIPAddress(ip.raw_address()); + W5100.setGatewayIp(gateway.raw_address()); + W5100.setSubnetMask(subnet.raw_address()); #endif SPI.endTransaction(); _dnsServerAddress = dns; } -void EthernetClass::init(uint8_t sspin) +void Ethernet_Base::init(uint8_t sspin) { W5100.setSS(sspin); } -EthernetLinkStatus EthernetClass::linkStatus() +EthernetLinkStatus Ethernet_Base::linkStatus() { switch (W5100.getLinkStatus()) { case UNKNOWN: return Unknown; @@ -115,7 +115,7 @@ EthernetLinkStatus EthernetClass::linkStatus() } } -EthernetHardwareStatus EthernetClass::hardwareStatus() +EthernetHardwareStatus Ethernet_Base::hardwareStatus() { switch (W5100.getChip()) { case 51: return EthernetW5100; @@ -125,7 +125,7 @@ EthernetHardwareStatus EthernetClass::hardwareStatus() } } -int EthernetClass::maintain() +int Ethernet_Base::maintain() { int rc = DHCP_CHECK_NONE; if (_dhcp != NULL) { @@ -154,14 +154,14 @@ int EthernetClass::maintain() } -void EthernetClass::MACAddress(uint8_t *mac_address) +void Ethernet_Base::MACAddress(uint8_t *mac_address) { SPI.beginTransaction(SPI_ETHERNET_SETTINGS); W5100.getMACAddress(mac_address); SPI.endTransaction(); } -IPAddress EthernetClass::localIP() +IPAddress Ethernet_Base::localIP() { IPAddress ret; SPI.beginTransaction(SPI_ETHERNET_SETTINGS); @@ -170,7 +170,7 @@ IPAddress EthernetClass::localIP() return ret; } -IPAddress EthernetClass::subnetMask() +IPAddress Ethernet_Base::subnetMask() { IPAddress ret; SPI.beginTransaction(SPI_ETHERNET_SETTINGS); @@ -179,7 +179,7 @@ IPAddress EthernetClass::subnetMask() return ret; } -IPAddress EthernetClass::gatewayIP() +IPAddress Ethernet_Base::gatewayIP() { IPAddress ret; SPI.beginTransaction(SPI_ETHERNET_SETTINGS); @@ -188,14 +188,14 @@ IPAddress EthernetClass::gatewayIP() return ret; } -void EthernetClass::setMACAddress(const uint8_t *mac_address) +void Ethernet_Base::setMACAddress(const uint8_t *mac_address) { SPI.beginTransaction(SPI_ETHERNET_SETTINGS); W5100.setMACAddress(mac_address); SPI.endTransaction(); } -void EthernetClass::setLocalIP(const IPAddress local_ip) +void Ethernet_Base::setLocalIP(const IPAddress local_ip) { SPI.beginTransaction(SPI_ETHERNET_SETTINGS); IPAddress ip = local_ip; @@ -203,7 +203,7 @@ void EthernetClass::setLocalIP(const IPAddress local_ip) SPI.endTransaction(); } -void EthernetClass::setSubnetMask(const IPAddress subnet) +void Ethernet_Base::setSubnetMask(const IPAddress subnet) { SPI.beginTransaction(SPI_ETHERNET_SETTINGS); IPAddress ip = subnet; @@ -211,7 +211,7 @@ void EthernetClass::setSubnetMask(const IPAddress subnet) SPI.endTransaction(); } -void EthernetClass::setGatewayIP(const IPAddress gateway) +void Ethernet_Base::setGatewayIP(const IPAddress gateway) { SPI.beginTransaction(SPI_ETHERNET_SETTINGS); IPAddress ip = gateway; @@ -219,7 +219,7 @@ void EthernetClass::setGatewayIP(const IPAddress gateway) SPI.endTransaction(); } -void EthernetClass::setRetransmissionTimeout(uint16_t milliseconds) +void Ethernet_Base::setRetransmissionTimeout(uint16_t milliseconds) { if (milliseconds > 6553) milliseconds = 6553; SPI.beginTransaction(SPI_ETHERNET_SETTINGS); @@ -227,7 +227,7 @@ void EthernetClass::setRetransmissionTimeout(uint16_t milliseconds) SPI.endTransaction(); } -void EthernetClass::setRetransmissionCount(uint8_t num) +void Ethernet_Base::setRetransmissionCount(uint8_t num) { SPI.beginTransaction(SPI_ETHERNET_SETTINGS); W5100.setRetransmissionCount(num); @@ -235,12 +235,6 @@ void EthernetClass::setRetransmissionCount(uint8_t num) } - - - - - - - - -EthernetClass Ethernet; +#ifndef MOCK_PINS_COUNT +Ethernet_Base Ethernet; +#endif diff --git a/src/Ethernet.h b/src/Ethernet.h index 0045de88..56aff20b 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -1,21 +1,22 @@ /* Copyright 2018 Paul Stoffregen * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to the following - * conditions: + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #ifndef ethernet_h_ @@ -28,7 +29,6 @@ // these "friend" classes are now defined in the same header file. socket.h // was removed to avoid possible conflict with the C library header files. - // Configure the maximum number of sockets to support. W5100 chips can have // up to 4 sockets. W5200 & W5500 can have up to 8 sockets. Several bytes // of RAM are used for each socket. Reducing the maximum can save RAM, but @@ -47,23 +47,31 @@ // does not always seem to work in practice (maybe WIZnet bugs?) //#define ETHERNET_LARGE_BUFFERS - #include + +////////// TESTING ////////// +#ifdef MOCK_PINS_COUNT +#define Ethernet_CI EthernetClass +#define EthernetClient_CI EthernetClient +#define EthernetServer_CI EthernetServer +#else +#define Ethernet_Base EthernetClass +#define EthernetClient_Base EthernetClient +#define EthernetServer_Base EthernetServer +#endif +////////// TESTING ////////// + #include "Client.h" #include "Server.h" #include "Udp.h" -enum EthernetLinkStatus { - Unknown, - LinkON, - LinkOFF -}; +enum EthernetLinkStatus { Unknown, LinkON, LinkOFF }; enum EthernetHardwareStatus { - EthernetNoHardware, - EthernetW5100, - EthernetW5200, - EthernetW5500 + EthernetNoHardware, + EthernetW5100, + EthernetW5200, + EthernetW5500 }; class EthernetUDP; @@ -71,151 +79,167 @@ class EthernetClient; class EthernetServer; class DhcpClass; -class EthernetClass { +class Ethernet_Base { private: - static IPAddress _dnsServerAddress; - static DhcpClass* _dhcp; + static IPAddress _dnsServerAddress; + static DhcpClass *_dhcp; + public: - // Initialise the Ethernet shield to use the provided MAC address and - // gain the rest of the configuration through DHCP. - // Returns 0 if the DHCP configuration failed, and 1 if it succeeded - static int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); - static int maintain(); - static EthernetLinkStatus linkStatus(); - static EthernetHardwareStatus hardwareStatus(); - - // Manual configuration - static void begin(uint8_t *mac, IPAddress ip); - static void begin(uint8_t *mac, IPAddress ip, IPAddress dns); - static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway); - static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); - static void init(uint8_t sspin = 10); - - static void MACAddress(uint8_t *mac_address); - static IPAddress localIP(); - static IPAddress subnetMask(); - static IPAddress gatewayIP(); - static IPAddress dnsServerIP() { return _dnsServerAddress; } - - void setMACAddress(const uint8_t *mac_address); - void setLocalIP(const IPAddress local_ip); - void setSubnetMask(const IPAddress subnet); - void setGatewayIP(const IPAddress gateway); - void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; } - void setRetransmissionTimeout(uint16_t milliseconds); - void setRetransmissionCount(uint8_t num); - - friend class EthernetClient; - friend class EthernetServer; - friend class EthernetUDP; + // Initialise the Ethernet shield to use the provided MAC address and + // gain the rest of the configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + static int begin(uint8_t *mac, unsigned long timeout = 60000, + unsigned long responseTimeout = 4000); + static int maintain(); + static EthernetLinkStatus linkStatus(); + static EthernetHardwareStatus hardwareStatus(); + + // Manual configuration + static void begin(uint8_t *mac, IPAddress ip); + static void begin(uint8_t *mac, IPAddress ip, IPAddress dns); + static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, + IPAddress gateway); + static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, + IPAddress gateway, IPAddress subnet); + static void init(uint8_t sspin = 10); + + static void MACAddress(uint8_t *mac_address); + static IPAddress localIP(); + static IPAddress subnetMask(); + static IPAddress gatewayIP(); + static IPAddress dnsServerIP() { return _dnsServerAddress; } + + void setMACAddress(const uint8_t *mac_address); + void setLocalIP(const IPAddress local_ip); + void setSubnetMask(const IPAddress subnet); + void setGatewayIP(const IPAddress gateway); + void setDnsServerIP(const IPAddress dns_server) { + _dnsServerAddress = dns_server; + } + void setRetransmissionTimeout(uint16_t milliseconds); + void setRetransmissionCount(uint8_t num); + + friend class EthernetServer_Base; + friend class EthernetClient_Base; + friend class EthernetUDP; + private: - // Opens a socket(TCP or UDP or IP_RAW mode) - static uint8_t socketBegin(uint8_t protocol, uint16_t port); - static uint8_t socketBeginMulticast(uint8_t protocol, IPAddress ip,uint16_t port); - static uint8_t socketStatus(uint8_t s); - // Close socket - static void socketClose(uint8_t s); - // Establish TCP connection (Active connection) - static void socketConnect(uint8_t s, uint8_t * addr, uint16_t port); - // disconnect the connection - static void socketDisconnect(uint8_t s); - // Establish TCP connection (Passive connection) - static uint8_t socketListen(uint8_t s); - // Send data (TCP) - static uint16_t socketSend(uint8_t s, const uint8_t * buf, uint16_t len); - static uint16_t socketSendAvailable(uint8_t s); - // Receive data (TCP) - static int socketRecv(uint8_t s, uint8_t * buf, int16_t len); - static uint16_t socketRecvAvailable(uint8_t s); - static uint8_t socketPeek(uint8_t s); - // sets up a UDP datagram, the data for which will be provided by one - // or more calls to bufferData and then finally sent with sendUDP. - // return true if the datagram was successfully set up, or false if there was an error - static bool socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port); - // copy up to len bytes of data from buf into a UDP datagram to be - // sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls. - // return Number of bytes successfully buffered - static uint16_t socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len); - // Send a UDP datagram built up from a sequence of startUDP followed by one or more - // calls to bufferData. - // return true if the datagram was successfully sent, or false if there was an error - static bool socketSendUDP(uint8_t s); - // Initialize the "random" source port number - static void socketPortRand(uint16_t n); + // Opens a socket(TCP or UDP or IP_RAW mode) + static uint8_t socketBegin(uint8_t protocol, uint16_t port); + static uint8_t socketBeginMulticast(uint8_t protocol, IPAddress ip, + uint16_t port); + static uint8_t socketStatus(uint8_t s); + // Close socket + static void socketClose(uint8_t s); + // Establish TCP connection (Active connection) + static void socketConnect(uint8_t s, uint8_t *addr, uint16_t port); + // disconnect the connection + static void socketDisconnect(uint8_t s); + // Establish TCP connection (Passive connection) + static uint8_t socketListen(uint8_t s); + // Send data (TCP) + static uint16_t socketSend(uint8_t s, const uint8_t *buf, uint16_t len); + static uint16_t socketSendAvailable(uint8_t s); + // Receive data (TCP) + static int socketRecv(uint8_t s, uint8_t *buf, int16_t len); + static uint16_t socketRecvAvailable(uint8_t s); + static uint8_t socketPeek(uint8_t s); + // sets up a UDP datagram, the data for which will be provided by one + // or more calls to bufferData and then finally sent with sendUDP. + // return true if the datagram was successfully set up, or false if there was + // an error + static bool socketStartUDP(uint8_t s, uint8_t *addr, uint16_t port); + // copy up to len bytes of data from buf into a UDP datagram to be + // sent later by sendUDP. Allows datagrams to be built up from a series of + // bufferData calls. return Number of bytes successfully buffered + static uint16_t socketBufferData(uint8_t s, uint16_t offset, + const uint8_t *buf, uint16_t len); + // Send a UDP datagram built up from a sequence of startUDP followed by one or + // more calls to bufferData. return true if the datagram was successfully + // sent, or false if there was an error + static bool socketSendUDP(uint8_t s); + // Initialize the "random" source port number + static void socketPortRand(uint16_t n); }; -extern EthernetClass Ethernet; - - #define UDP_TX_PACKET_MAX_SIZE 24 class EthernetUDP : public UDP { private: - uint16_t _port; // local port to listen on - 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 _port; // local port to listen on + 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 protected: - uint8_t sockindex; - uint16_t _remaining; // remaining bytes of incoming packet yet to be processed + uint8_t sockindex; + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed public: - EthernetUDP() : sockindex(MAX_SOCK_NUM) {} // Constructor - virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use - virtual uint8_t beginMulticast(IPAddress, uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use - virtual void stop(); // Finish with the UDP socket - - // Sending UDP packets - - // Start building up a packet to send to the remote host specific in ip and port - // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port - virtual int beginPacket(IPAddress ip, uint16_t port); - // Start building up a packet to send to the remote host specific in host and port - // Returns 1 if successful, 0 if there was a problem resolving the hostname or port - virtual int beginPacket(const char *host, uint16_t port); - // Finish off this packet and send it - // Returns 1 if the packet was sent successfully, 0 if there was an error - virtual int endPacket(); - // Write a single byte into the packet - virtual size_t write(uint8_t); - // Write size bytes from buffer into the packet - virtual size_t write(const uint8_t *buffer, size_t size); - - using Print::write; - - // Start processing the next available incoming packet - // Returns the size of the packet in bytes, or 0 if no packets are available - virtual int parsePacket(); - // Number of bytes remaining in the current packet - virtual int available(); - // Read a single byte from the current packet - virtual int read(); - // Read up to len bytes from the current packet and place them into buffer - // Returns the number of bytes read, or 0 if none are available - virtual int read(unsigned char* buffer, size_t len); - // Read up to len characters from the current packet and place them into buffer - // Returns the number of characters read, or 0 if none are available - virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); }; - // Return the next byte from the current packet without moving on to the next byte - virtual int peek(); - virtual void flush(); // Finish reading the current packet - - // Return the IP address of the host who sent the current incoming packet - virtual IPAddress remoteIP() { return _remoteIP; }; - // Return the port of the host who sent the current incoming packet - virtual uint16_t remotePort() { return _remotePort; }; - virtual uint16_t localPort() { return _port; } + EthernetUDP() : sockindex(MAX_SOCK_NUM) {} // Constructor + virtual uint8_t begin( + uint16_t); // initialize, start listening on specified port. Returns 1 if + // successful, 0 if there are no sockets available to use + virtual uint8_t beginMulticast( + IPAddress, + uint16_t); // initialize, start listening on specified port. Returns 1 if + // successful, 0 if there are no sockets available to use + virtual void stop(); // Finish with the UDP socket + + // Sending UDP packets + + // Start building up a packet to send to the remote host specific in ip and + // port Returns 1 if successful, 0 if there was a problem with the supplied IP + // address or port + virtual int beginPacket(IPAddress ip, uint16_t port); + // Start building up a packet to send to the remote host specific in host and + // port Returns 1 if successful, 0 if there was a problem resolving the + // hostname or port + virtual int beginPacket(const char *host, uint16_t port); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket(); + // Write a single byte into the packet + virtual size_t write(uint8_t); + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size); + + using Print::write; + + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + virtual int parsePacket(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char *buffer, size_t len); + // Read up to len characters from the current packet and place them into + // buffer Returns the number of characters read, or 0 if none are available + virtual int read(char *buffer, size_t len) { + return read((unsigned char *)buffer, len); + }; + // Return the next byte from the current packet without moving on to the next + // byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress remoteIP() { return _remoteIP; }; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() { return _remotePort; }; + virtual uint16_t localPort() { return _port; } }; - - - -class EthernetClient : public Client { +class EthernetClient_Base : public Client { public: - EthernetClient() : _sockindex(MAX_SOCK_NUM), _timeout(1000) { } - EthernetClient(uint8_t s) : _sockindex(s), _timeout(1000) { } - virtual ~EthernetClient() {}; + EthernetClient_Base() : _sockindex(MAX_SOCK_NUM), _timeout(1000) { } + EthernetClient_Base(uint8_t s) : _sockindex(s), _timeout(1000) { } + virtual ~EthernetClient_Base() {}; uint8_t status(); virtual int connect(IPAddress ip, uint16_t port); @@ -233,8 +257,8 @@ class EthernetClient : public Client { virtual operator bool() { return _sockindex < MAX_SOCK_NUM; } virtual bool operator==(const bool value) { return bool() == value; } virtual bool operator!=(const bool value) { return bool() != value; } - virtual bool operator==(const EthernetClient&); - virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); } + virtual bool operator==(const EthernetClient_Base&); + virtual bool operator!=(const EthernetClient_Base& rhs) { return !this->operator==(rhs); } uint8_t getSocketNumber() const { return _sockindex; } virtual uint16_t localPort(); virtual IPAddress remoteIP(); @@ -250,74 +274,78 @@ class EthernetClient : public Client { uint16_t _timeout; }; - -class EthernetServer : public Server { +class EthernetServer_Base : public Server { private: - uint16_t _port; -public: - EthernetServer(uint16_t port) : _port(port) { } - EthernetClient available(); - EthernetClient accept(); - virtual void begin(); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - virtual operator bool(); - using Print::write; - //void statusreport(); + uint16_t _port; - // TODO: make private when socket allocation moves to EthernetClass - static uint16_t server_port[MAX_SOCK_NUM]; +public: + EthernetServer_Base(uint16_t port) : _port(port) {} + EthernetClient_Base available(); + EthernetClient_Base accept(); + virtual void begin(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual operator bool(); + using Print::write; + // void statusreport(); + + // TODO: make private when socket allocation moves to EthernetClass + static uint16_t server_port[MAX_SOCK_NUM]; }; - class DhcpClass { private: - uint32_t _dhcpInitialTransactionId; - uint32_t _dhcpTransactionId; - uint8_t _dhcpMacAddr[6]; + uint32_t _dhcpInitialTransactionId; + uint32_t _dhcpTransactionId; + uint8_t _dhcpMacAddr[6]; #ifdef __arm__ - uint8_t _dhcpLocalIp[4] __attribute__((aligned(4))); - uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4))); - uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4))); - uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4))); - uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4))); + uint8_t _dhcpLocalIp[4] __attribute__((aligned(4))); + uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4))); + uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4))); + uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4))); + uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4))); #else - uint8_t _dhcpLocalIp[4]; - uint8_t _dhcpSubnetMask[4]; - uint8_t _dhcpGatewayIp[4]; - uint8_t _dhcpDhcpServerIp[4]; - uint8_t _dhcpDnsServerIp[4]; + uint8_t _dhcpLocalIp[4]; + uint8_t _dhcpSubnetMask[4]; + uint8_t _dhcpGatewayIp[4]; + uint8_t _dhcpDhcpServerIp[4]; + uint8_t _dhcpDnsServerIp[4]; #endif - uint32_t _dhcpLeaseTime; - uint32_t _dhcpT1, _dhcpT2; - uint32_t _renewInSec; - uint32_t _rebindInSec; - unsigned long _timeout; - unsigned long _responseTimeout; - unsigned long _lastCheckLeaseMillis; - 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); - - uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); + uint32_t _dhcpLeaseTime; + uint32_t _dhcpT1, _dhcpT2; + uint32_t _renewInSec; + uint32_t _rebindInSec; + unsigned long _timeout; + unsigned long _responseTimeout; + unsigned long _lastCheckLeaseMillis; + 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); + + uint8_t parseDHCPResponse(unsigned long responseTimeout, + uint32_t &transactionId); + public: - IPAddress getLocalIp(); - IPAddress getSubnetMask(); - IPAddress getGatewayIp(); - IPAddress getDhcpServerIp(); - IPAddress getDnsServerIp(); - - int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); - int checkLease(); + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, + unsigned long responseTimeout = 4000); + int checkLease(); }; +#ifdef MOCK_PINS_COUNT +#include "Ethernet_CI.h" +#endif - - +extern EthernetClass Ethernet; #endif diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index 5a20c748..905ce2ea 100644 --- a/src/EthernetClient.cpp +++ b/src/EthernetClient.cpp @@ -23,7 +23,7 @@ #include "Dns.h" #include "utility/w5100.h" -int EthernetClient::connect(const char * host, uint16_t port) +int EthernetClient_Base::connect(const char * host, uint16_t port) { DNSClient dns; // Look up the host first IPAddress remote_addr; @@ -39,7 +39,7 @@ int EthernetClient::connect(const char * host, uint16_t port) return connect(remote_addr, port); } -int EthernetClient::connect(IPAddress ip, uint16_t port) +int EthernetClient_Base::connect(IPAddress ip, uint16_t port) { if (_sockindex < MAX_SOCK_NUM) { if (Ethernet.socketStatus(_sockindex) != SnSR::CLOSED) { @@ -47,7 +47,7 @@ int EthernetClient::connect(IPAddress ip, uint16_t port) } _sockindex = MAX_SOCK_NUM; } -#if defined(ESP8266) || defined(ESP32) +#if defined(ESP8266) || defined(ESP32) || defined(MOCK_PINS_COUNT) if (ip == IPAddress((uint32_t)0) || ip == IPAddress(0xFFFFFFFFul)) return 0; #else if (ip == IPAddress(0ul) || ip == IPAddress(0xFFFFFFFFul)) return 0; @@ -69,18 +69,18 @@ int EthernetClient::connect(IPAddress ip, uint16_t port) return 0; } -int EthernetClient::availableForWrite(void) +int EthernetClient_Base::availableForWrite(void) { if (_sockindex >= MAX_SOCK_NUM) return 0; return Ethernet.socketSendAvailable(_sockindex); } -size_t EthernetClient::write(uint8_t b) +size_t EthernetClient_Base::write(uint8_t b) { return write(&b, 1); } -size_t EthernetClient::write(const uint8_t *buf, size_t size) +size_t EthernetClient_Base::write(const uint8_t *buf, size_t size) { if (_sockindex >= MAX_SOCK_NUM) return 0; if (Ethernet.socketSend(_sockindex, buf, size)) return size; @@ -88,7 +88,7 @@ size_t EthernetClient::write(const uint8_t *buf, size_t size) return 0; } -int EthernetClient::available() +int EthernetClient_Base::available() { if (_sockindex >= MAX_SOCK_NUM) return 0; return Ethernet.socketRecvAvailable(_sockindex); @@ -100,27 +100,27 @@ int EthernetClient::available() // command to cause the WIZnet chip to resend the ACK packet. } -int EthernetClient::read(uint8_t *buf, size_t size) +int EthernetClient_Base::read(uint8_t *buf, size_t size) { if (_sockindex >= MAX_SOCK_NUM) return 0; return Ethernet.socketRecv(_sockindex, buf, size); } -int EthernetClient::peek() +int EthernetClient_Base::peek() { if (_sockindex >= MAX_SOCK_NUM) return -1; if (!available()) return -1; return Ethernet.socketPeek(_sockindex); } -int EthernetClient::read() +int EthernetClient_Base::read() { uint8_t b; if (Ethernet.socketRecv(_sockindex, &b, 1) > 0) return b; return -1; } -void EthernetClient::flush() +void EthernetClient_Base::flush() { while (_sockindex < MAX_SOCK_NUM) { uint8_t stat = Ethernet.socketStatus(_sockindex); @@ -129,7 +129,7 @@ void EthernetClient::flush() } } -void EthernetClient::stop() +void EthernetClient_Base::stop() { if (_sockindex >= MAX_SOCK_NUM) return; @@ -151,7 +151,7 @@ void EthernetClient::stop() _sockindex = MAX_SOCK_NUM; } -uint8_t EthernetClient::connected() +uint8_t EthernetClient_Base::connected() { if (_sockindex >= MAX_SOCK_NUM) return 0; @@ -160,7 +160,7 @@ uint8_t EthernetClient::connected() (s == SnSR::CLOSE_WAIT && !available())); } -uint8_t EthernetClient::status() +uint8_t EthernetClient_Base::status() { if (_sockindex >= MAX_SOCK_NUM) return SnSR::CLOSED; return Ethernet.socketStatus(_sockindex); @@ -168,7 +168,7 @@ uint8_t EthernetClient::status() // the next function allows us to use the client returned by // EthernetServer::available() as the condition in an if-statement. -bool EthernetClient::operator==(const EthernetClient& rhs) +bool EthernetClient_Base::operator==(const EthernetClient_Base& rhs) { if (_sockindex != rhs._sockindex) return false; if (_sockindex >= MAX_SOCK_NUM) return false; @@ -178,7 +178,7 @@ bool EthernetClient::operator==(const EthernetClient& rhs) // https://github.com/per1234/EthernetMod // from: https://github.com/ntruchsess/Arduino-1/commit/937bce1a0bb2567f6d03b15df79525569377dabd -uint16_t EthernetClient::localPort() +uint16_t EthernetClient_Base::localPort() { if (_sockindex >= MAX_SOCK_NUM) return 0; uint16_t port; @@ -190,7 +190,7 @@ uint16_t EthernetClient::localPort() // https://github.com/per1234/EthernetMod // returns the remote IP address: https://forum.arduino.cc/index.php?topic=82416.0 -IPAddress EthernetClient::remoteIP() +IPAddress EthernetClient_Base::remoteIP() { if (_sockindex >= MAX_SOCK_NUM) return IPAddress((uint32_t)0); uint8_t remoteIParray[4]; @@ -202,7 +202,7 @@ IPAddress EthernetClient::remoteIP() // https://github.com/per1234/EthernetMod // from: https://github.com/ntruchsess/Arduino-1/commit/ca37de4ba4ecbdb941f14ac1fe7dd40f3008af75 -uint16_t EthernetClient::remotePort() +uint16_t EthernetClient_Base::remotePort() { if (_sockindex >= MAX_SOCK_NUM) return 0; uint16_t port; diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp new file mode 100644 index 00000000..b1a6c6a1 --- /dev/null +++ b/src/EthernetClient_CI.cpp @@ -0,0 +1,184 @@ +#include "Ethernet.h" +#include + +#ifdef MOCK_PINS_COUNT +#include "Dns.h" +#include "utility/w5100.h" + +std::vector EthernetClient_CI::mockServers; +socket_t EthernetClient_CI::_sockets[MAX_SOCK_NUM]; + +EthernetClient_CI::EthernetClient_CI() + : sockindex(MAX_SOCK_NUM), _timeout(1000) {} + +EthernetClient_CI::EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000) { + if (s == MAX_SOCK_NUM) { + for (int i = 0; i < MAX_SOCK_NUM; ++i) { + if (_sockets[i].status == SnSR::CLOSED) { + sockindex = i; + setStatus(SnSR::ESTABLISHED); + break; + } + } + } +} + +size_t EthernetClient_CI::write(uint8_t b) { return write(&b, 1); } + +size_t EthernetClient_CI::write(const uint8_t *buf, size_t size) { + if (sockindex < MAX_SOCK_NUM) { + for (int i = 0; i < size; ++i) { + _sockets[sockindex].writeBuffer.push_back(buf[i]); + } + return size; + } + return 0; +} + +int EthernetClient_CI::read(uint8_t *buf, size_t bufSize) { + int available = + sockindex < MAX_SOCK_NUM ? _sockets[sockindex].readBuffer.size() : 0; + int size = max(min(bufSize, available), 0); + + for (int i = 0; i < size; ++i) { + buf[i] = _sockets[sockindex].readBuffer.front(); + _sockets[sockindex].readBuffer.pop_front(); + } + return size; +} + +int EthernetClient_CI::read() { + if (sockindex >= MAX_SOCK_NUM || + _sockets[sockindex].status != SnSR::ESTABLISHED) { + return -1; + } + if (!_sockets[sockindex].readBuffer.empty()) { + char x = _sockets[sockindex].readBuffer.front(); + _sockets[sockindex].readBuffer.pop_front(); + return x; + } + return -1; +} + +int EthernetClient_CI::peek() { + if (sockindex >= MAX_SOCK_NUM || + _sockets[sockindex].status != SnSR::ESTABLISHED) { + return -1; + } + if (!_sockets[sockindex].readBuffer.empty()) { + char x = _sockets[sockindex].readBuffer.front(); + return x; + } + return -1; +} + +void EthernetClient_CI::stop() { + if (sockindex < MAX_SOCK_NUM) { + _sockets[sockindex].readBuffer.clear(); + _sockets[sockindex].status = SnSR::CLOSED; + sockindex = MAX_SOCK_NUM; + } + _localPort = 0; + peer.ip = (uint32_t)0; + peer.port = 0; + setStatus(SnSR::CLOSED); +} + +int EthernetClient_CI::connect(const char *hostname, IPAddress ip, + uint16_t port) { + if (sockindex < MAX_SOCK_NUM) { + if (_sockets[sockindex].status != SnSR::CLOSED) { + return INVALID_SERVER; // we are already connected! + } + } else { + for (int i = 0; i < MAX_SOCK_NUM; ++i) { + if (_sockets[i].status == SnSR::CLOSED) { + sockindex = i; + break; + } + } + } + if (sockindex >= MAX_SOCK_NUM) { + return INVALID_SERVER; // unable to obtain a socket! + } + // Iterate though vector of mock servers + for (int i = 0; i < mockServers.size(); ++i) { + // If we find server with hostname or ip and port + bool flag = hostname ? strncmp(mockServers.at(i).hostname, hostname, + HOSTNAME_SIZE) == 0 + : false; + if ((flag || mockServers.at(i).ip == ip) && + (mockServers.at(i).port == port)) { + peer = mockServers.at(i); + setStatus(SnSR::ESTABLISHED); + _sockets[sockindex].status = SnSR::ESTABLISHED; + _localPort = 0xC000 + sockindex; + if (peer.data) { + pushToReadBuffer((const char *)peer.data); + } + return SUCCESS; + } + } + return INVALID_SERVER; +} + +int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { + return connect(nullptr, ip, port); +} + +int EthernetClient_CI::connect(const char *hostname, uint16_t port) { + return connect(hostname, (uint32_t)0, port); +} + +void EthernetClient_CI::setStatus(uint8_t status) { + if (sockindex < MAX_SOCK_NUM) { + _sockets[sockindex].status = status; + } +} + +void EthernetClient_CI::startMockServer(const char *hostname, IPAddress ip, + uint16_t port, const uint8_t *data) { + mockServer server; + if (hostname) { + strncpy(server.hostname, hostname, HOSTNAME_SIZE); + } else { + server.hostname[0] = '\0'; + } + server.ip = ip; + server.port = port; + server.data = data; + mockServers.push_back(server); +} + +void EthernetClient_CI::stopMockServer(const char *hostname, IPAddress ip, + uint16_t port) { + for (int i = (mockServers.size() - 1); i >= 0; --i) { + bool flag = hostname ? strncmp(mockServers.at(i).hostname, hostname, + HOSTNAME_SIZE) == 0 + : false; + if ((flag | mockServers.at(i).ip == ip) && + (mockServers.at(i).port == port)) { + mockServers.erase(mockServers.begin() + i); + } + } +} + +void EthernetClient_CI::pushToReadBuffer(uint8_t value) { + if (sockindex < MAX_SOCK_NUM) { + _sockets[sockindex].readBuffer.push_back(value); + } +} + +void EthernetClient_CI::pushToReadBuffer(const char *value) { + if (sockindex < MAX_SOCK_NUM) { + for (int i = 0; value[i]; ++i) { + _sockets[sockindex].readBuffer.push_back(value[i]); + } + } +} + +std::deque *EthernetClient_CI::writeBuffer() { + return sockindex < MAX_SOCK_NUM ? &_sockets[sockindex].writeBuffer : nullptr; +} + +#endif diff --git a/src/EthernetServer.cpp b/src/EthernetServer.cpp index ddebd154..3ff63a22 100644 --- a/src/EthernetServer.cpp +++ b/src/EthernetServer.cpp @@ -22,10 +22,10 @@ #include "Ethernet.h" #include "utility/w5100.h" -uint16_t EthernetServer::server_port[MAX_SOCK_NUM]; +uint16_t EthernetServer_Base::server_port[MAX_SOCK_NUM]; -void EthernetServer::begin() +void EthernetServer_Base::begin() { uint8_t sockindex = Ethernet.socketBegin(SnMR::TCP, _port); if (sockindex < MAX_SOCK_NUM) { @@ -37,14 +37,14 @@ void EthernetServer::begin() } } -EthernetClient EthernetServer::available() +EthernetClient_Base EthernetServer_Base::available() { bool listening = false; uint8_t sockindex = MAX_SOCK_NUM; uint8_t chip, maxindex=MAX_SOCK_NUM; chip = W5100.getChip(); - if (!chip) return EthernetClient(MAX_SOCK_NUM); + if (!chip) return EthernetClient_Base(MAX_SOCK_NUM); #if MAX_SOCK_NUM > 4 if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets #endif @@ -69,17 +69,17 @@ EthernetClient EthernetServer::available() } } if (!listening) begin(); - return EthernetClient(sockindex); + return EthernetClient_Base(sockindex); } -EthernetClient EthernetServer::accept() +EthernetClient_Base EthernetServer_Base::accept() { bool listening = false; uint8_t sockindex = MAX_SOCK_NUM; uint8_t chip, maxindex=MAX_SOCK_NUM; chip = W5100.getChip(); - if (!chip) return EthernetClient(MAX_SOCK_NUM); + if (!chip) return EthernetClient_Base(MAX_SOCK_NUM); #if MAX_SOCK_NUM > 4 if (chip == 51) maxindex = 4; // W5100 chip never supports more than 4 sockets #endif @@ -101,10 +101,10 @@ EthernetClient EthernetServer::accept() } } if (!listening) begin(); - return EthernetClient(sockindex); + return EthernetClient_Base(sockindex); } -EthernetServer::operator bool() +EthernetServer_Base::operator bool() { uint8_t maxindex=MAX_SOCK_NUM; #if MAX_SOCK_NUM > 4 @@ -121,9 +121,9 @@ EthernetServer::operator bool() } #if 0 -void EthernetServer::statusreport() +void EthernetServer_Base::statusreport() { - Serial.printf("EthernetServer, port=%d\n", _port); + Serial.printf("EthernetServer_Base, port=%d\n", _port); for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { uint16_t port = server_port[i]; uint8_t stat = Ethernet.socketStatus(i); @@ -153,12 +153,12 @@ void EthernetServer::statusreport() } #endif -size_t EthernetServer::write(uint8_t b) +size_t EthernetServer_Base::write(uint8_t b) { return write(&b, 1); } -size_t EthernetServer::write(const uint8_t *buffer, size_t size) +size_t EthernetServer_Base::write(const uint8_t *buffer, size_t size) { uint8_t chip, maxindex=MAX_SOCK_NUM; diff --git a/src/EthernetServer_CI.cpp b/src/EthernetServer_CI.cpp new file mode 100644 index 00000000..cbb51f53 --- /dev/null +++ b/src/EthernetServer_CI.cpp @@ -0,0 +1,66 @@ +#include +#include "Ethernet.h" + +#ifdef MOCK_PINS_COUNT +#include "utility/w5100.h" + +std::set EthernetServer_CI::servers; + +EthernetServer_CI::EthernetServer_CI(uint16_t port) : EthernetServer_Base(port) { + _port = port; + servers.emplace(this); +} + +EthernetServer_CI::~EthernetServer_CI() { + servers.erase(this); +} + +void EthernetServer_CI::begin() +{ + _didCallBegin = true; +} + +EthernetClient_CI EthernetServer_CI::available() +{ + return accept(); +} + +EthernetClient_CI EthernetServer_CI::accept() +{ + if (hasClientCalling) { + client = EthernetClient_CI(MAX_SOCK_NUM); // signal to select your own sockindex and "connect" + hasClientCalling = false; + } else { + client = EthernetClient_CI(); + } + return client; +} + +size_t EthernetServer_CI::write(const uint8_t *buffer, size_t size) +{ + EthernetClient_CI client = EthernetClient_CI(getSocketNumber()); + if (!client) { // test if client is connected + return 0; + } + return client.write(buffer, size); +} + +uint8_t EthernetServer_CI::getSocketNumber() const { + for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { + if (server_port[i] == _port) { + return i; + } + } + return MAX_SOCK_NUM; +} + +EthernetServer_CI* EthernetServer_CI::getServerForPort(uint16_t port) { + for (auto each : servers) { + if (each->getPort() == port) { + return each; + } + } + return nullptr; +} + +#endif diff --git a/src/Ethernet_CI.cpp b/src/Ethernet_CI.cpp new file mode 100644 index 00000000..be785256 --- /dev/null +++ b/src/Ethernet_CI.cpp @@ -0,0 +1,79 @@ +#include "Ethernet.h" +#include + +#ifdef MOCK_PINS_COUNT + +IPAddress Ethernet_CI::_mockDHCP = (uint32_t)0; + +int Ethernet_CI::begin(uint8_t *mac, unsigned long timeout, + unsigned long responseTimeout) { + if (_mockDHCP) { + begin(mac, _mockDHCP); + return 1; + } + return Ethernet_Base::begin(mac, timeout, responseTimeout); +} + +int Ethernet_CI::maintain() { return Ethernet_Base::maintain(); } +EthernetLinkStatus Ethernet_CI::linkStatus() { + return Ethernet_Base::linkStatus(); +} +EthernetHardwareStatus Ethernet_CI::hardwareStatus() { + return Ethernet_Base::hardwareStatus(); +} + +// Manual configuration +void Ethernet_CI::begin(uint8_t *mac, IPAddress ip) { + return Ethernet_Base::begin(mac, ip); +} +void Ethernet_CI::begin(uint8_t *mac, IPAddress ip, IPAddress dns) { + return Ethernet_Base::begin(mac, ip, dns); +} +void Ethernet_CI::begin(uint8_t *mac, IPAddress ip, IPAddress dns, + IPAddress gateway) { + return Ethernet_Base::begin(mac, ip, dns, gateway); +} +void Ethernet_CI::begin(uint8_t *mac, IPAddress ip, IPAddress dns, + IPAddress gateway, IPAddress subnet) { + return Ethernet_Base::begin(mac, ip, dns, gateway, subnet); +} +void Ethernet_CI::init(uint8_t sspin) { return Ethernet_Base::init(sspin); } + +void Ethernet_CI::MACAddress(uint8_t *mac_address) { + return Ethernet_Base::MACAddress(mac_address); +} +IPAddress Ethernet_CI::localIP() { + return _mockDHCP ? _mockDHCP : Ethernet_Base::localIP(); +} +IPAddress Ethernet_CI::subnetMask() { return Ethernet_Base::subnetMask(); } +IPAddress Ethernet_CI::gatewayIP() { return Ethernet_Base::gatewayIP(); } +IPAddress Ethernet_CI::dnsServerIP() { return Ethernet_Base::dnsServerIP(); } + +void Ethernet_CI::setMACAddress(const uint8_t *mac_address) { + Ethernet_Base::setMACAddress(mac_address); +} +void Ethernet_CI::setLocalIP(const IPAddress local_ip) { + Ethernet_Base::setLocalIP(local_ip); +} +void Ethernet_CI::setSubnetMask(const IPAddress subnet) { + Ethernet_Base::setSubnetMask(subnet); +} +void Ethernet_CI::setGatewayIP(const IPAddress gateway) { + Ethernet_Base::setGatewayIP(gateway); +} +void Ethernet_CI::setDnsServerIP(const IPAddress dns_server) { + Ethernet_Base::setDnsServerIP(dns_server); +} +void Ethernet_CI::setRetransmissionTimeout(uint16_t milliseconds) { + Ethernet_Base::setRetransmissionTimeout(milliseconds); +} +void Ethernet_CI::setRetransmissionCount(uint8_t num) { + Ethernet_Base::setRetransmissionCount(num); +} + +// testing support +void Ethernet_CI::mockDHCP(IPAddress ip) { _mockDHCP = ip; } + +Ethernet_CI Ethernet; + +#endif // MOCK_PINS_COUNT diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h new file mode 100644 index 00000000..610236b6 --- /dev/null +++ b/src/Ethernet_CI.h @@ -0,0 +1,186 @@ +#pragma once +#ifdef MOCK_PINS_COUNT + +#include "Client.h" +#include "Server.h" +#include "Udp.h" +#include +#include +#include +#include +#include + +// Possible return codes from ProcessResponse +// Originally defined in dns.cpp (Why not in header file?) +#define SUCCESS 1 +#define TIMED_OUT -1 +#define INVALID_SERVER -2 +#define TRUNCATED -3 +#define INVALID_RESPONSE -4 + +#define HOSTNAME_SIZE 64 +struct mockServer { + char hostname[HOSTNAME_SIZE]; + IPAddress ip; + uint16_t port; + const uint8_t *data = nullptr; +}; +struct socket_t { + uint8_t status = SnSR::CLOSED; + std::deque readBuffer; + std::deque writeBuffer; +}; + +class Ethernet_CI : public Ethernet_Base { +public: + // Initialise the Ethernet shield to use the provided MAC address and + // gain the rest of the configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + static int begin(uint8_t *mac, unsigned long timeout = 60000, + unsigned long responseTimeout = 4000); + static int maintain(); + static EthernetLinkStatus linkStatus(); + static EthernetHardwareStatus hardwareStatus(); + + // Manual configuration + static void begin(uint8_t *mac, IPAddress ip); + static void begin(uint8_t *mac, IPAddress ip, IPAddress dns); + static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, + IPAddress gateway); + static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, + IPAddress gateway, IPAddress subnet); + static void init(uint8_t sspin = 10); + + static void MACAddress(uint8_t *mac_address); + static IPAddress localIP(); + static IPAddress subnetMask(); + static IPAddress gatewayIP(); + static IPAddress dnsServerIP(); + + void setMACAddress(const uint8_t *mac_address); + void setLocalIP(const IPAddress local_ip); + void setSubnetMask(const IPAddress subnet); + void setGatewayIP(const IPAddress gateway); + void setDnsServerIP(const IPAddress dns_server); + void setRetransmissionTimeout(uint16_t milliseconds); + void setRetransmissionCount(uint8_t num); + + static void socketPortRand(uint16_t n); + + // testing + void mockDHCP(IPAddress ip); + +private: + static IPAddress _mockDHCP; + static uint8_t socketBegin(uint8_t protocol, uint16_t port); + static uint8_t socketBeginMulticast(uint8_t protocol, IPAddress ip, + uint16_t port); + static uint8_t socketStatus(uint8_t s); + static void socketClose(uint8_t s); + static void socketConnect(uint8_t s, uint8_t *addr, uint16_t port); + static void socketDisconnect(uint8_t s); + static uint8_t socketListen(uint8_t s); + static uint16_t socketSend(uint8_t s, const uint8_t *buf, uint16_t len); + static uint16_t socketSendAvailable(uint8_t s); + static int socketRecv(uint8_t s, uint8_t *buf, int16_t len); + static uint16_t socketRecvAvailable(uint8_t s); + static uint8_t socketPeek(uint8_t s); + static bool socketStartUDP(uint8_t s, uint8_t *addr, uint16_t port); + static uint16_t socketBufferData(uint8_t s, uint16_t offset, + const uint8_t *buf, uint16_t len); + static bool socketSendUDP(uint8_t s); + + friend class EthernetClient_Base; + friend class EthernetServer_Base; + friend class EthernetUDP; +}; + +class EthernetClient_CI : public EthernetClient_Base { +public: + EthernetClient_CI(); + EthernetClient_CI(uint8_t s); + + uint8_t status() { + return sockindex < MAX_SOCK_NUM ? _sockets[sockindex].status : SnSR::CLOSED; + }; + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual int availableForWrite(void) { return 1024 * 1024; } // returns 1mb + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int available() { + return sockindex < MAX_SOCK_NUM ? _sockets[sockindex].readBuffer.size() : 0; + }; + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + virtual void flush() {} + virtual void stop(); + virtual uint8_t connected() { return status() == SnSR::ESTABLISHED; } + virtual operator bool() { return connected(); } + virtual bool operator==(const bool value) { return bool() == value; } + virtual bool operator!=(const bool value) { return bool() != value; } + virtual bool operator==(const EthernetClient &rhs) const { + return sockindex == rhs.getSockindex(); + } + virtual bool operator!=(const EthernetClient &rhs) { + return !this->operator==(rhs); + } + virtual uint16_t localPort() const { return _localPort; }; + virtual IPAddress remoteIP() const { return peer.ip; } + virtual uint16_t remotePort() const { return peer.port; } + + friend class EthernetServer; + + using Print::write; + + // Testing Support + virtual int connect(const char *host, IPAddress ip, uint16_t port); + static void startMockServer(const char *host, IPAddress ip, uint16_t port, + const uint8_t *data = nullptr); + static void stopMockServer(const char *host, IPAddress ip, uint16_t port); + static void clearMockServers() { mockServers.clear(); } + virtual mockServer serverPeer() { return peer; } + void pushToReadBuffer(uint8_t value); + void pushToReadBuffer(const char *value); + std::deque *writeBuffer(); + void setStatus(uint8_t status); + uint8_t getSockindex() const { return sockindex; } + +private: + uint8_t sockindex; // MAX_SOCK_NUM means client not in use + uint16_t _timeout; + uint16_t _localPort = 0; + + static std::vector mockServers; + static socket_t _sockets[MAX_SOCK_NUM]; + mockServer peer; +}; + +class EthernetServer_CI : public EthernetServer_Base { +private: + uint16_t _port; + bool _didCallBegin = false; + uint8_t getSocketNumber() const; + static std::set servers; + bool hasClientCalling = false; + EthernetClient_CI client; + +public: + EthernetServer_CI(uint16_t port); + ~EthernetServer_CI(); + EthernetClient_CI available(); + EthernetClient_CI accept(); + virtual void begin(); + virtual size_t write(const uint8_t *buf, size_t size); + using Print::write; + + // Testing Functions + bool didCallBegin() { return _didCallBegin; } + uint16_t getPort() { return _port; } + static EthernetServer_CI *getServerForPort(uint16_t port); + void setHasClientCalling(bool flag) { hasClientCalling = flag; } + EthernetClient_CI getClient() { return client; } +}; + +#endif diff --git a/src/socket.cpp b/src/socket.cpp index 7dc83feb..65a11e1e 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -22,11 +22,13 @@ #include "Ethernet.h" #include "utility/w5100.h" -#if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32) +#if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32) && !defined(MOCK_PINS_COUNT) extern void yield(void); #else +#ifndef yield #define yield() #endif +#endif // TODO: randomize this when not using DHCP, but how? static uint16_t local_port = 49152; // 49152 to 65535 @@ -102,7 +104,7 @@ uint8_t EthernetClass::socketBegin(uint8_t protocol, uint16_t port) W5100.execCmdSn(s, Sock_CLOSE); makesocket: //Serial.printf("W5000socket %d\n", s); - EthernetServer::server_port[s] = 0; + EthernetServer_Base::server_port[s] = 0; delayMicroseconds(250); // TODO: is this needed?? W5100.writeSnMR(s, protocol); W5100.writeSnIR(s, 0xFF); @@ -166,7 +168,7 @@ uint8_t EthernetClass::socketBeginMulticast(uint8_t protocol, IPAddress ip, uint W5100.execCmdSn(s, Sock_CLOSE); makesocket: //Serial.printf("W5000socket %d\n", s); - EthernetServer::server_port[s] = 0; + EthernetServer_Base::server_port[s] = 0; delayMicroseconds(250); // TODO: is this needed?? W5100.writeSnMR(s, protocol); W5100.writeSnIR(s, 0xFF); diff --git a/test/test.cpp b/test/test.cpp new file mode 100644 index 00000000..15bcd204 --- /dev/null +++ b/test/test.cpp @@ -0,0 +1,217 @@ +/* +bundle config --local path vendor/bundle +bundle install +bundle exec arduino_ci_remote.rb --skip-compilation +*/ +#include +#include +#include + +#include "Arduino.h" +#include "ArduinoUnitTests.h" +#include "Ethernet.h" +#include "ci/ObservableDataStream.h" +#include "utility/w5100.h" + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +byte ip[] = {192, 168, 1, 2}; +byte serverIP[] = {192, 168, 1, 1}; +const char *serverName = "www.google.com"; + +unittest_teardown() { + EthernetClient::stopMockServer(nullptr, serverIP, 80); + EthernetClient::stopMockServer(serverName, (uint32_t) 0, 80); + EthernetClient::clearMockServers(); +} + +unittest(Ethernet_begin_pins) { + // Setup + GodmodeState *state = GODMODE(); + state->reset(); + uint8_t mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; + Ethernet.begin(mac); + + // Tests + int expected[] = {0, 0, -128, 1, -128, 0, 0, 0, 1, 0, 0, 0, -128, + 1, 8, 0, 0, 0, 1, 0, 0, 0, 4, -128, 0, 0, + 0, 0, 0, 0, 4, 8, 0, 0, 0, 0, -16, 0, 0, + -128, 15, 0, 0, 0, -16, 0, 0, 16, 15, 0, 0, 0}; + assertEqual(52, state->spi.dataOut.size()); + bool passed = true; + for (int i = 0; i < 52; i++) { + if (expected[i] != (int)state->spi.dataOut.at(i)) { + passed = false; + break; + } + } + assertTrue(passed); +} + +unittest(Ethernet_begin_DHCP) { + // Setup + GodmodeState *state = GODMODE(); + state->reset(); + uint8_t mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; + int flag = Ethernet.begin(mac); + assertEqual(0, flag); // DHCP failure + assertEqual(0, Ethernet.localIP()); + + Ethernet.mockDHCP(IPAddress(192, 168, 1, 42)); + flag = Ethernet.begin(mac); + assertEqual(1, flag); // DHCP success + assertEqual(IPAddress(192, 168, 1, 42), Ethernet.localIP()); +} + +unittest(Server_Constructor) { + // Testing port + EthernetServer ethernet_server(72); + assertEqual(72, ethernet_server.getPort()); +} + +unittest(Server_Begin) { + + EthernetServer server(72); + assertFalse(server.didCallBegin()); + + server.begin(); + assertTrue(server.didCallBegin()); +} + +unittest(Server_Write) {} + +unittest(Server_Available) {} + +unittest(Client_Connect_IP) { + Ethernet.begin(mac, ip); + EthernetClient client; + int result = client.connect(serverIP, 80); + assertEqual(INVALID_SERVER, result); + client.stop(); + + EthernetClient::startMockServer(nullptr, serverIP, 80); + result = client.connect(serverIP, 80); + assertEqual(SUCCESS, result); + client.stop(); +} + +unittest(Client_Connect_Name) { + Ethernet.begin(mac, ip); + EthernetClient client; + int result = client.connect(serverName, 80); + assertEqual(INVALID_SERVER, result); + client.stop(); + + EthernetClient::startMockServer(serverName, (uint32_t) 0, 80); + result = client.connect(serverName, 80); + assertEqual(SUCCESS, result); + client.stop(); +} + +unittest(Client_Write) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(nullptr, serverIP, 80); + assertEqual(MAX_SOCK_NUM, client.getSockindex()); + int result = client.connect(serverIP, 80); + assertEqual(1, result); + assertEqual(0, client.getSockindex()); + assertNotNull(client.writeBuffer()); + std::deque buffer = *(client.writeBuffer()); + assertEqual(0, buffer.size()); + assertEqual(0, client.writeBuffer()->size()); + client.write((const uint8_t *)"Hello", 5); + assertEqual(5, client.writeBuffer()->size()); + assertEqual('H', client.writeBuffer()->at(0)); + client.stop(); +} + +unittest(Client_Read_data_given_after_connection) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(nullptr, serverIP, 80); + int result = client.connect(serverIP, 80); + assertEqual(SUCCESS, result); + assertEqual(-1, client.read()); + client.pushToReadBuffer('A'); + assertEqual(1, client.available()); + assertEqual('A', client.read()); + assertEqual(0, client.available()); + client.stop(); +} + +unittest(Client_Read_data_given_before_connection) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(nullptr, serverIP, 80, (const uint8_t *) "ABC"); + int result = client.connect(serverIP, 80); + assertEqual(SUCCESS, result); + assertEqual(3, client.available()); + assertEqual('A', client.read()); + assertEqual(2, client.available()); + client.stop(); +} + +unittest(Client_Stop) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(nullptr, serverIP, 80); + int result = client.connect(serverIP, 80); + client.stop(); + + assertEqual(-1, client.read()); + assertEqual(0, client.localPort()); + assertEqual(0, client.serverPeer().ip); + assertEqual(0, client.serverPeer().port); +} + +unittest(Client_AvailableForWrite) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(nullptr, serverIP, 80); + int result = client.connect(serverIP, 80); + assertNotEqual(1024 * 1023, client.availableForWrite()); + assertEqual(1024 * 1024, client.availableForWrite()); +} + +unittest(Client_Peek) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(nullptr, serverIP, 80); + int result = client.connect(serverIP, 80); + assertEqual(SUCCESS, result); + + assertEqual(-1, client.peek()); + + client.pushToReadBuffer('A'); + assertEqual('A', client.peek()); +} + +unittest(Client_Connected) { + Ethernet.begin(mac, ip); + EthernetClient client; + assertFalse(client.connected()); + + EthernetClient::startMockServer(nullptr, serverIP, 80); + int result = client.connect(serverIP, 80); + assertTrue(client.connected()); +} + +unittest(Client_Server) { + EthernetServer ethernet_server(80); + EthernetClient_CI client1, client2; + client1 = ethernet_server.accept(); + assertFalse(client1); + + EthernetServer* pServer = EthernetServer::getServerForPort(80); + assertNotNull(pServer); + assertEqual(ðernet_server, pServer); + pServer->setHasClientCalling(true); + client1 = ethernet_server.accept(); + assertTrue(client1); + client2 = ethernet_server.getClient(); + assertTrue(client1 == (const EthernetClient) client2); + client1 = ethernet_server.accept(); + assertFalse(client1); +} + +unittest_main()