From 776a42965d77f15bf6a23a7d94ce6e2a8b263997 Mon Sep 17 00:00:00 2001 From: Kavikick <50475639+Kavikick@users.noreply.github.com> Date: Sun, 1 Nov 2020 23:23:58 -0700 Subject: [PATCH 01/21] Ethernet.begin() Low level test (#2) This basically shows that we can do low-level (pin-based) tests of the Ethernet library. Co-authored-by: Wesley Duerksen --- .arduino-ci.yml | 33 +++++++++++++++++++++++++++++++++ .gitignore | 18 ++++++++++++++++++ Gemfile | 2 ++ src/EthernetClient.cpp | 2 +- src/socket.cpp | 4 +++- test.sh | 3 +++ test/test.cpp | 35 +++++++++++++++++++++++++++++++++++ 7 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 .arduino-ci.yml create mode 100644 .gitignore create mode 100644 Gemfile create mode 100755 test.sh create mode 100644 test/test.cpp diff --git a/.arduino-ci.yml b/.arduino-ci.yml new file mode 100644 index 00000000..380d212b --- /dev/null +++ b/.arduino-ci.yml @@ -0,0 +1,33 @@ +platforms: + + uno: + board: arduino:avr:uno + package: arduino:avr + gcc: + features: + defines: + - __AVR_ATmega328P__ + - ARDUINO=10813 + - ARDUINO_CI + warnings: + flags: + due: + board: arduino:sam:arduino_due_x + package: arduino:sam + gcc: + features: + defines: + - __AVR_ATmega328__ + - ARDUINO=10813 + - ARDUINO_CI + warnings: + flags: + +unittest: + platforms: + - uno + - due +compile: + platforms: + - uno + - due diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0fff4dbd --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +/.bundle/ +/.yardoc +Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +vendor +*.gem + +# rspec failure tracking +.rspec_status + +# C++ stuff +*.bin +*.bin.dSYM +.DS_Store diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..fb422d12 --- /dev/null +++ b/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org' +gem 'arduino_ci' diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index e2406d7d..d6e758cb 100644 --- a/src/EthernetClient.cpp +++ b/src/EthernetClient.cpp @@ -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(ARDUINO_CI) if (ip == IPAddress((uint32_t)0) || ip == IPAddress(0xFFFFFFFFul)) return 0; #else if (ip == IPAddress(0ul) || ip == IPAddress(0xFFFFFFFFul)) return 0; diff --git a/src/socket.cpp b/src/socket.cpp index f059dc92..c473a1da 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(ARDUINO_CI) 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 diff --git a/test.sh b/test.sh new file mode 100755 index 00000000..3b0ccb81 --- /dev/null +++ b/test.sh @@ -0,0 +1,3 @@ +#! /bin/sh +bundle config --local path vendor/bundle +bundle exec arduino_ci_remote.rb --skip-compilation diff --git a/test/test.cpp b/test/test.cpp new file mode 100644 index 00000000..155a6e37 --- /dev/null +++ b/test/test.cpp @@ -0,0 +1,35 @@ +#include +#include +#include + +#include "Arduino.h" +#include "ArduinoUnitTests.h" +#include "Ethernet.h" +#include "ci/ObservableDataStream.h" +#include "utility/w5100.h" + +// Test the Ethernet.begin() behaves the same as it did in the simulators +unittest(test_EthernetBegin) { + // 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_main() From 114bed4502681ca24d480a69d7aece9919af318a Mon Sep 17 00:00:00 2001 From: James Foster Date: Sun, 15 Nov 2020 11:25:29 -0800 Subject: [PATCH 02/21] Update Gemfile to reference latest `arduino_ci` (#4) * Update Gemfile to reference latest `arduino_ci`. * Add GitHub Actions for CI. --- .github/macos.yaml | 19 +++++++++++++++++++ .github/ubuntu.yaml | 15 +++++++++++++++ .github/windows.yaml | 15 +++++++++++++++ Gemfile | 2 +- README.adoc | 3 +++ scripts/test.sh | 4 ++++ scripts/testAndBuild.sh | 4 ++++ test.sh | 3 --- 8 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 .github/macos.yaml create mode 100644 .github/ubuntu.yaml create mode 100644 .github/windows.yaml create mode 100755 scripts/test.sh create mode 100755 scripts/testAndBuild.sh delete mode 100755 test.sh diff --git a/.github/macos.yaml b/.github/macos.yaml new file mode 100644 index 00000000..5d3439a2 --- /dev/null +++ b/.github/macos.yaml @@ -0,0 +1,19 @@ +name: macos + +on: [push, pull_request] + +jobs: + runTest: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - run: | + # Install recent Arduino-IDE, Arduino-CI, and then run test + curl -fsSL https://downloads.arduino.cc/arduino-1.8.13-macosx.zip > arduino.zip + unzip arduino.zip > /dev/null + mv Arduino.app /Users/runner/ + bundle install + bundle exec arduino_ci.rb diff --git a/.github/ubuntu.yaml b/.github/ubuntu.yaml new file mode 100644 index 00000000..e21129f7 --- /dev/null +++ b/.github/ubuntu.yaml @@ -0,0 +1,15 @@ +name: ubuntu + +on: [push, pull_request] + +jobs: + runTest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - run: | + bundle install + bundle exec arduino_ci.rb diff --git a/.github/windows.yaml b/.github/windows.yaml new file mode 100644 index 00000000..dc109228 --- /dev/null +++ b/.github/windows.yaml @@ -0,0 +1,15 @@ +name: windows + +on: [push, pull_request] + +jobs: + runTest: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - run: | + bundle install + bundle exec arduino_ci.rb diff --git a/Gemfile b/Gemfile index fb422d12..80468b55 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.org' -gem 'arduino_ci' +gem 'arduino_ci', git: 'https://github.com/Arduino-CI/arduino_ci.git', branch: 'master' diff --git a/README.adoc b/README.adoc index 4dfea8da..f2609852 100644 --- a/README.adoc +++ b/README.adoc @@ -1,5 +1,8 @@ = Ethernet Library for Arduino = +![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/test.sh b/test.sh deleted file mode 100755 index 3b0ccb81..00000000 --- a/test.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -bundle config --local path vendor/bundle -bundle exec arduino_ci_remote.rb --skip-compilation From 97ebc3f6677f0a0826d304d857ec2024d31d1432 Mon Sep 17 00:00:00 2001 From: James Foster Date: Thu, 19 Nov 2020 16:56:14 -0800 Subject: [PATCH 03/21] Use `MOCK_PINS_COUNT` rather than `ARDUINO_CI` to simplify `.arduino_ci.yml` (#6) --- .arduino-ci.yml | 30 ++---------------------------- src/EthernetClient.cpp | 2 +- src/socket.cpp | 2 +- 3 files changed, 4 insertions(+), 30 deletions(-) diff --git a/.arduino-ci.yml b/.arduino-ci.yml index 380d212b..5fca9d0e 100644 --- a/.arduino-ci.yml +++ b/.arduino-ci.yml @@ -1,33 +1,7 @@ -platforms: - - uno: - board: arduino:avr:uno - package: arduino:avr - gcc: - features: - defines: - - __AVR_ATmega328P__ - - ARDUINO=10813 - - ARDUINO_CI - warnings: - flags: - due: - board: arduino:sam:arduino_due_x - package: arduino:sam - gcc: - features: - defines: - - __AVR_ATmega328__ - - ARDUINO=10813 - - ARDUINO_CI - warnings: - flags: unittest: platforms: - - uno - - due + - mega2560 compile: platforms: - - uno - - due + - mega2560 diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index d6e758cb..9c5b5be3 100644 --- a/src/EthernetClient.cpp +++ b/src/EthernetClient.cpp @@ -47,7 +47,7 @@ int EthernetClient::connect(IPAddress ip, uint16_t port) } sockindex = MAX_SOCK_NUM; } -#if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_CI) +#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; diff --git a/src/socket.cpp b/src/socket.cpp index c473a1da..665fd738 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -22,7 +22,7 @@ #include "Ethernet.h" #include "utility/w5100.h" -#if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32) && !defined(ARDUINO_CI) +#if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32) && !defined(MOCK_PINS_COUNT) extern void yield(void); #else #ifndef yield From d8d7ecdb1b95a313a77f89e1f0b8d83600769609 Mon Sep 17 00:00:00 2001 From: James Foster Date: Wed, 25 Nov 2020 13:49:08 -0800 Subject: [PATCH 04/21] Use published `arduino_ci` gem version 0.4.0. (#7) --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 80468b55..fb422d12 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.org' -gem 'arduino_ci', git: 'https://github.com/Arduino-CI/arduino_ci.git', branch: 'master' +gem 'arduino_ci' From dae1e0049676b1fbf04e22c8faff49cfea257310 Mon Sep 17 00:00:00 2001 From: atg7000 <38963069+atg7000@users.noreply.github.com> Date: Thu, 26 Nov 2020 21:38:45 -0800 Subject: [PATCH 05/21] Full Tests for EthernetServer and EthernetClient (#8) * WIP Trying to mock Ethernet package with CI * Adding high level tests for Ethernet client and server * Finished EthernetClient and EthernetServer Testing Co-authored-by: Wesley Duerksen Co-authored-by: Kavikick <50475639+Kavikick@users.noreply.github.com> --- src/Ethernet.h | 505 ++++++++++++++++++++------------------ src/EthernetClient.cpp | 34 +-- src/EthernetClient_CI.cpp | 99 ++++++++ src/EthernetServer.cpp | 26 +- src/EthernetServer_CI.cpp | 45 ++++ src/Ethernet_CI.h | 110 +++++++++ src/socket.cpp | 4 +- test/test.cpp | 125 +++++++++- 8 files changed, 676 insertions(+), 272 deletions(-) create mode 100644 src/EthernetClient_CI.cpp create mode 100644 src/EthernetServer_CI.cpp create mode 100644 src/Ethernet_CI.h diff --git a/src/Ethernet.h b/src/Ethernet.h index 376e6c59..0acc4e7f 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,29 @@ // does not always seem to work in practice (maybe Wiznet bugs?) //#define ETHERNET_LARGE_BUFFERS - #include + +////////// TESTING ////////// +#ifdef MOCK_PINS_COUNT +#define EthernetClient_CI EthernetClient +#define EthernetServer_CI EthernetServer +#else +#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; @@ -73,250 +79,273 @@ class DhcpClass; class EthernetClass { 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(); - - // Manaul 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(); + + // Manaul 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; + + #ifdef MOCK_PINS_COUNT + friend class EthernetServer_Base; + friend class EthernetClient_Base; + #endif + 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) { } - - uint8_t status(); - virtual int connect(IPAddress ip, uint16_t port); - virtual int connect(const char *host, uint16_t port); - virtual int availableForWrite(void); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - virtual int available(); - 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(); - 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); } - uint8_t getSocketNumber() const { return sockindex; } - virtual uint16_t localPort(); - virtual IPAddress remoteIP(); - virtual uint16_t remotePort(); - virtual void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; } - - friend class EthernetServer; - - using Print::write; + EthernetClient_Base() : sockindex(MAX_SOCK_NUM), _timeout(1000) {} + EthernetClient_Base(uint8_t s) : sockindex(s), _timeout(1000) {} + + uint8_t status(); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual int availableForWrite(void); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int available(); + 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(); + 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_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(); + virtual uint16_t remotePort(); + virtual void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; } + + friend class EthernetServer; + + using Print::write; private: - uint8_t sockindex; // MAX_SOCK_NUM means client not in use - uint16_t _timeout; + uint8_t sockindex; // MAX_SOCK_NUM means client not in use + uint16_t _timeout; }; - -class EthernetServer : public Server { +class EthernetServer_Base : public Server { private: - uint16_t _port; + 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(); - - // TODO: make private when socket allocation moves to EthernetClass - static uint16_t server_port[MAX_SOCK_NUM]; + 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(); }; - - - - #endif diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index 9c5b5be3..d6e1f7f9 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) { @@ -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: http://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..df29ec48 --- /dev/null +++ b/src/EthernetClient_CI.cpp @@ -0,0 +1,99 @@ +#include "Ethernet_CI.h" +#include + +#ifdef MOCK_PINS_COUNT +#include "Dns.h" +#include "utility/w5100.h" + +std::vector EthernetClient_CI::mockServers; +uint16_t EthernetClient_CI::nextPort = 49152; // 49152 to 65535 + +size_t EthernetClient_CI::write(uint8_t b) { return write(&b, 1); } + +size_t EthernetClient_CI::write(const uint8_t *buf, size_t size) { + for (int i = 0; i < size; ++i) { + _writeBuffer.push_back(buf[i]); + } + return size; +} + +int EthernetClient_CI::read(uint8_t *buf, size_t bufSize) { + int size = max(min(bufSize, _readBuffer.size()), 0); + + for (int i = 0; i < size; ++i) { + buf[i] = _readBuffer.front(); + _readBuffer.pop_front(); + } + return size; +} + +int EthernetClient_CI::read() { + if (!_readBuffer.empty()) { + char x = _readBuffer.front(); + _readBuffer.pop_front(); + return x; + } + return -1; +} + +int EthernetClient_CI::peek() { + if (!_readBuffer.empty()) { + char x = _readBuffer.front(); + return x; + } + return -1; +} + +void EthernetClient_CI::stop() { + + // Clear read and write buffers + _readBuffer.clear(); + _writeBuffer.clear(); + + // Set port and ip to zero + _localPort = 0; + + // Close peer connection + peer.ip = (uint32_t)0; + peer.port = 0; + + // Close the connection + _status = SnSR::CLOSED; +} + +int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { + + if (++nextPort < 49152) { + nextPort = 49152; + } + _localPort = nextPort; + // Iterate though vector of mock servers + for (int i = 0; i < mockServers.size(); ++i) { + // If we find server with ip and port + if ((mockServers.at(i).ip == ip) && (mockServers.at(i).port == port)) { + // Save ip and port in peer + peer.ip = ip; + peer.port = port; + _status = SnSR::ESTABLISHED; + return SUCCESS; + } + } + return INVALID_SERVER; +} + +void EthernetClient_CI::startMockServer(IPAddress ip, uint16_t port) { + mockServer server; + server.ip = ip; + server.port = port; + mockServers.push_back(server); +} + +void EthernetClient_CI::stopMockServer(IPAddress ip, uint16_t port) { + for (int i = (mockServers.size() - 1); i >= 0 ; --i) { + if (mockServers.at(i).ip == ip && mockServers.at(i).port == port) { + mockServers.erase(mockServers.begin() + i); + } + } +} + +#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..550b5a5d --- /dev/null +++ b/src/EthernetServer_CI.cpp @@ -0,0 +1,45 @@ +#include +#include "Ethernet_CI.h" + +#ifdef MOCK_PINS_COUNT +#include "utility/w5100.h" + +void EthernetServer_CI::begin() +{ + // EthernetServer_Base::begin(); + _didCallBegin = true; +} + +EthernetClient_CI EthernetServer_CI::available() +{ + EthernetClient_Base client = EthernetServer_Base::available(); + return EthernetClient_CI(client.getSocketNumber()); +} + +EthernetClient_CI EthernetServer_CI::accept() +{ + EthernetClient_Base client = EthernetServer_Base::accept(); + return EthernetClient_CI(client.getSocketNumber()); +} + +size_t EthernetServer_CI::write(const uint8_t *buffer, size_t size) +{ + EthernetClient_CI client = EthernetClient_CI(getSocketNumber()); + + // Is this necessary? + if (!client) { + 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; +} + +#endif diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h new file mode 100644 index 00000000..51a757ae --- /dev/null +++ b/src/Ethernet_CI.h @@ -0,0 +1,110 @@ +#pragma once +// Needed for workaround for problem in Arduino.CI +#include +#include +#ifdef MOCK_PINS_COUNT + +// 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 +// you are limited to fewer simultaneous connections. +#if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048) +#define MAX_SOCK_NUM 4 +#else +#define MAX_SOCK_NUM 8 +#endif + +#include "Client.h" +#include "Server.h" +#include "Udp.h" +#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 + +struct mockServer { + IPAddress ip; + uint16_t port; +}; + +class EthernetClient_CI : public EthernetClient_Base { +public: + EthernetClient_CI() : sockindex(MAX_SOCK_NUM), _timeout(1000) { } + EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000) { } + + uint8_t status() { return _status; } + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port) { return INVALID_SERVER; } + 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 _writeBuffer.size(); }; + 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) { return _localPort == rhs.localPort(); } + 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; } + virtual std::vector testServers() { return mockServers; } + virtual mockServer serverPeer() { return peer; } + + friend class EthernetServer; + + using Print::write; + + // Testing Support + static void startMockServer(IPAddress ip, uint16_t port); + static void stopMockServer(IPAddress ip, uint16_t port); + void pushToReadBuffer(uint8_t value) { _readBuffer.push_back(value); } + std::deque writeBuffer() {return _writeBuffer;} + + +private: + uint8_t sockindex; // MAX_SOCK_NUM means client not in use + uint16_t _timeout; + static std::vector mockServers; + static uint16_t nextPort; // 49152 to 65535 + mockServer peer; + uint16_t _localPort = 0; + uint8_t _status = SnSR::CLOSED; + std::deque _readBuffer, _writeBuffer; +}; + +class EthernetServer_CI : public EthernetServer_Base { +private: + uint16_t _port; + bool _didCallBegin = false; + uint8_t getSocketNumber() const; + +public: + EthernetServer_CI(uint16_t port) : EthernetServer_Base(port) { _port = port; } + 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; } +}; + +#endif \ No newline at end of file diff --git a/src/socket.cpp b/src/socket.cpp index 665fd738..bb249d5a 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -104,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); @@ -168,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 index 155a6e37..99606e40 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,15 +1,24 @@ +/* +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 "Ethernet_CI.h" #include "ci/ObservableDataStream.h" #include "utility/w5100.h" +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +byte ip[] = { 10, 0, 0, 177 }; +byte server[] = { 64, 233, 187, 99 }; // Google + // Test the Ethernet.begin() behaves the same as it did in the simulators -unittest(test_EthernetBegin) { +unittest(EthernetBegin) { // Setup GodmodeState *state = GODMODE(); state->reset(); @@ -32,4 +41,116 @@ unittest(test_EthernetBegin) { assertTrue(passed); } +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) { + + Ethernet.begin(mac, ip); + EthernetClient client; + int result = client.connect(server, 80); + assertEqual(INVALID_SERVER,result); + client.stop(); + + EthernetClient::startMockServer(server,80); + result = client.connect(server, 80); + assertEqual(SUCCESS,result); + client.stop(); + EthernetClient::stopMockServer(server,80); +} + +unittest(Client_Write) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(server,80); + int result = client.connect(server, 80); + assertEqual(0,client.writeBuffer().size()); + client.write((const uint8_t*)"Hello",5); + assertEqual(5,client.writeBuffer().size()); + assertEqual('H',client.writeBuffer().at(0)); + EthernetClient::stopMockServer(server,80); +} + +unittest(Client_Read) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(server,80); + int result = client.connect(server, 80); + assertEqual(SUCCESS,result); + assertEqual(-1,client.read()); + client.pushToReadBuffer('A'); + assertEqual('A',client.read()); + EthernetClient::stopMockServer(server,80); +} + +unittest(Client_Stop) { + Ethernet.begin(mac, ip); + EthernetClient client; + EthernetClient::startMockServer(server,80); + int result = client.connect(server, 80); + client.stop(); + + assertEqual(-1, client.read()); + assertEqual(0,client.writeBuffer().size()); + + assertEqual(0,client.localPort()); + + assertEqual(0,client.serverPeer().ip); + assertEqual(0,client.serverPeer().port); + EthernetClient::stopMockServer(server,80); +} + +unittest(Client_AvailableForWrite) { + Ethernet.begin(mac,ip); + EthernetClient client; + EthernetClient::startMockServer(server,80); + int result = client.connect(server, 80); + assertNotEqual(1024*1023,client.availableForWrite()); + assertEqual(1024*1024,client.availableForWrite()); +} + +unittest(Client_Peek) { + Ethernet.begin(mac,ip); + EthernetClient client; + EthernetClient::startMockServer(server,80); + int result = client.connect(server, 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(server,80); + int result = client.connect(server, 80); + assertTrue(client.connected()); +} + unittest_main() From 08ec74d8b1d325a4622331d2e9d4f383a27157ae Mon Sep 17 00:00:00 2001 From: James Foster Date: Mon, 30 Nov 2020 12:38:14 -0800 Subject: [PATCH 06/21] Use new `arduino_ci` GitHub Action. (#9) --- .github/macos.yaml | 19 ------------------- .github/ubuntu.yaml | 15 --------------- .github/windows.yaml | 15 --------------- .github/workflows/arduino_ci.yml | 12 ++++++++++++ 4 files changed, 12 insertions(+), 49 deletions(-) delete mode 100644 .github/macos.yaml delete mode 100644 .github/ubuntu.yaml delete mode 100644 .github/windows.yaml create mode 100644 .github/workflows/arduino_ci.yml diff --git a/.github/macos.yaml b/.github/macos.yaml deleted file mode 100644 index 5d3439a2..00000000 --- a/.github/macos.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: macos - -on: [push, pull_request] - -jobs: - runTest: - runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.6 - - run: | - # Install recent Arduino-IDE, Arduino-CI, and then run test - curl -fsSL https://downloads.arduino.cc/arduino-1.8.13-macosx.zip > arduino.zip - unzip arduino.zip > /dev/null - mv Arduino.app /Users/runner/ - bundle install - bundle exec arduino_ci.rb diff --git a/.github/ubuntu.yaml b/.github/ubuntu.yaml deleted file mode 100644 index e21129f7..00000000 --- a/.github/ubuntu.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: ubuntu - -on: [push, pull_request] - -jobs: - runTest: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.6 - - run: | - bundle install - bundle exec arduino_ci.rb diff --git a/.github/windows.yaml b/.github/windows.yaml deleted file mode 100644 index dc109228..00000000 --- a/.github/windows.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: windows - -on: [push, pull_request] - -jobs: - runTest: - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.6 - - run: | - bundle install - bundle exec arduino_ci.rb diff --git a/.github/workflows/arduino_ci.yml b/.github/workflows/arduino_ci.yml new file mode 100644 index 00000000..61e64e69 --- /dev/null +++ b/.github/workflows/arduino_ci.yml @@ -0,0 +1,12 @@ +--- +name: Arduino CI + +on: [push, pull_request] + +jobs: + arduino_ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: Arduino-CI/action@v0.1.0 From 11fb1adf5194e0ad3889dac5c5334f489e517dbf Mon Sep 17 00:00:00 2001 From: James Foster Date: Sun, 14 Feb 2021 23:47:58 -0800 Subject: [PATCH 07/21] Update Arduino-CI/action (#10) Co-authored-by: James Foster --- .github/workflows/arduino_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/arduino_ci.yml b/.github/workflows/arduino_ci.yml index 61e64e69..a7406654 100644 --- a/.github/workflows/arduino_ci.yml +++ b/.github/workflows/arduino_ci.yml @@ -9,4 +9,4 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: Arduino-CI/action@v0.1.0 + - uses: Arduino-CI/action@stable-1.x From 2446b0a6d3c8dac71421bd21cca28bfef9b88bf5 Mon Sep 17 00:00:00 2001 From: James Foster Date: Tue, 25 May 2021 10:53:23 -0700 Subject: [PATCH 08/21] Add ability to test connection to hostname (#11) - Fix problem with `available()` on read - Format using clang-format --- src/Ethernet.h | 4 +- src/EthernetClient_CI.cpp | 45 ++++++++++++++-- src/Ethernet_CI.h | 40 ++++++++------ test/test.cpp | 109 +++++++++++++++++++++----------------- 4 files changed, 127 insertions(+), 71 deletions(-) diff --git a/src/Ethernet.h b/src/Ethernet.h index 0acc4e7f..07833793 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -121,10 +121,10 @@ class EthernetClass { friend class EthernetServer; friend class EthernetUDP; - #ifdef MOCK_PINS_COUNT +#ifdef MOCK_PINS_COUNT friend class EthernetServer_Base; friend class EthernetClient_Base; - #endif +#endif private: // Opens a socket(TCP or UDP or IP_RAW mode) diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp index df29ec48..534e4ae4 100644 --- a/src/EthernetClient_CI.cpp +++ b/src/EthernetClient_CI.cpp @@ -62,7 +62,6 @@ void EthernetClient_CI::stop() { } int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { - if (++nextPort < 49152) { nextPort = 49152; } @@ -71,7 +70,8 @@ int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { for (int i = 0; i < mockServers.size(); ++i) { // If we find server with ip and port if ((mockServers.at(i).ip == ip) && (mockServers.at(i).port == port)) { - // Save ip and port in peer + // Save name, ip, and port in peer + peer.hostname[0] = '\0'; peer.ip = ip; peer.port = port; _status = SnSR::ESTABLISHED; @@ -81,19 +81,58 @@ int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { return INVALID_SERVER; } +int EthernetClient_CI::connect(const char *hostname, uint16_t port) { + if (++nextPort < 49152) { + nextPort = 49152; + } + _localPort = nextPort; + // Iterate though vector of mock servers + for (int i = 0; i < mockServers.size(); ++i) { + // If we find server with ip and port + if ((strncmp(mockServers.at(i).hostname, hostname, HOSTNAME_SIZE) == 0) && + (mockServers.at(i).port == port)) { + // Save name, ip, and port in peer + strncpy(peer.hostname, hostname, HOSTNAME_SIZE); + peer.ip = {0, 0, 0, 0}; + peer.port = port; + _status = SnSR::ESTABLISHED; + return SUCCESS; + } + } + return INVALID_SERVER; +} + void EthernetClient_CI::startMockServer(IPAddress ip, uint16_t port) { mockServer server; + server.hostname[0] = '\0'; server.ip = ip; server.port = port; mockServers.push_back(server); } +void EthernetClient_CI::startMockServer(const char *hostname, uint16_t port) { + mockServer server; + strncpy(server.hostname, hostname, HOSTNAME_SIZE); + server.ip = {0, 0, 0, 0}; + server.port = port; + mockServers.push_back(server); +} + void EthernetClient_CI::stopMockServer(IPAddress ip, uint16_t port) { - for (int i = (mockServers.size() - 1); i >= 0 ; --i) { + for (int i = (mockServers.size() - 1); i >= 0; --i) { if (mockServers.at(i).ip == ip && mockServers.at(i).port == port) { mockServers.erase(mockServers.begin() + i); } } } +void EthernetClient_CI::stopMockServer(const char *hostname, uint16_t port) { + for (int i = (mockServers.size() - 1); i >= 0; --i) { + if ((strncmp(mockServers.at(i).hostname, hostname, HOSTNAME_SIZE) == 0) && + (mockServers.at(i).port == port)) { + mockServers.erase(mockServers.begin() + i); + } + } +} + #endif diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index 51a757ae..cb03463a 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -1,7 +1,7 @@ #pragma once // Needed for workaround for problem in Arduino.CI -#include #include +#include #ifdef MOCK_PINS_COUNT // Configure the maximum number of sockets to support. W5100 chips can have @@ -17,36 +17,38 @@ #include "Client.h" #include "Server.h" #include "Udp.h" -#include #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 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; }; class EthernetClient_CI : public EthernetClient_Base { public: - EthernetClient_CI() : sockindex(MAX_SOCK_NUM), _timeout(1000) { } - EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000) { } + EthernetClient_CI() : sockindex(MAX_SOCK_NUM), _timeout(1000) {} + EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000) {} uint8_t status() { return _status; } virtual int connect(IPAddress ip, uint16_t port); - virtual int connect(const char *host, uint16_t port) { return INVALID_SERVER; } + 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 _writeBuffer.size(); }; + virtual int available() { return _readBuffer.size(); }; virtual int read(); virtual int read(uint8_t *buf, size_t size); virtual int peek(); @@ -56,9 +58,12 @@ class EthernetClient_CI : public EthernetClient_Base { 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) { return _localPort == rhs.localPort(); } - virtual bool operator!=(const EthernetClient& rhs) - { return !this->operator==(rhs); } + virtual bool operator==(const EthernetClient &rhs) { + return _localPort == rhs.localPort(); + } + virtual bool operator!=(const EthernetClient &rhs) { + return !this->operator==(rhs); + } virtual uint16_t localPort() const { return _localPort; }; virtual IPAddress remoteIP() const { return peer.ip; } @@ -72,10 +77,11 @@ class EthernetClient_CI : public EthernetClient_Base { // Testing Support static void startMockServer(IPAddress ip, uint16_t port); + static void startMockServer(const char *host, uint16_t port); static void stopMockServer(IPAddress ip, uint16_t port); + static void stopMockServer(const char *host, uint16_t port); void pushToReadBuffer(uint8_t value) { _readBuffer.push_back(value); } - std::deque writeBuffer() {return _writeBuffer;} - + std::deque writeBuffer() { return _writeBuffer; } private: uint8_t sockindex; // MAX_SOCK_NUM means client not in use @@ -107,4 +113,4 @@ class EthernetServer_CI : public EthernetServer_Base { uint16_t getPort() { return _port; } }; -#endif \ No newline at end of file +#endif diff --git a/test/test.cpp b/test/test.cpp index 99606e40..7894fb31 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -13,9 +13,15 @@ bundle exec arduino_ci_remote.rb --skip-compilation #include "ci/ObservableDataStream.h" #include "utility/w5100.h" -byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { 10, 0, 0, 177 }; -byte server[] = { 64, 233, 187, 99 }; // Google +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(serverIP, 80); + EthernetClient::stopMockServer(serverName, 80); +} // Test the Ethernet.begin() behaves the same as it did in the simulators unittest(EthernetBegin) { @@ -42,7 +48,6 @@ unittest(EthernetBegin) { } unittest(Server_Constructor) { - // Testing port EthernetServer ethernet_server(72); assertEqual(72, ethernet_server.getPort()); @@ -57,99 +62,105 @@ unittest(Server_Begin) { assertTrue(server.didCallBegin()); } -unittest(Server_Write) { +unittest(Server_Write) {} -} +unittest(Server_Available) {} -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(serverIP, 80); + result = client.connect(serverIP, 80); + assertEqual(SUCCESS, result); + client.stop(); } -unittest(Client_Connect) { - +unittest(Client_Connect_Name) { Ethernet.begin(mac, ip); EthernetClient client; - int result = client.connect(server, 80); - assertEqual(INVALID_SERVER,result); + int result = client.connect(serverName, 80); + assertEqual(INVALID_SERVER, result); client.stop(); - EthernetClient::startMockServer(server,80); - result = client.connect(server, 80); - assertEqual(SUCCESS,result); + EthernetClient::startMockServer(serverName, 80); + result = client.connect(serverName, 80); + assertEqual(SUCCESS, result); client.stop(); - EthernetClient::stopMockServer(server,80); } unittest(Client_Write) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(server,80); - int result = client.connect(server, 80); - assertEqual(0,client.writeBuffer().size()); - client.write((const uint8_t*)"Hello",5); - assertEqual(5,client.writeBuffer().size()); - assertEqual('H',client.writeBuffer().at(0)); - EthernetClient::stopMockServer(server,80); + EthernetClient::startMockServer(serverIP, 80); + int result = client.connect(serverIP, 80); + assertEqual(0, client.writeBuffer().size()); + client.write((const uint8_t *)"Hello", 5); + assertEqual(5, client.writeBuffer().size()); + assertEqual('H', client.writeBuffer().at(0)); } unittest(Client_Read) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(server,80); - int result = client.connect(server, 80); - assertEqual(SUCCESS,result); - assertEqual(-1,client.read()); + EthernetClient::startMockServer(serverIP, 80); + int result = client.connect(serverIP, 80); + assertEqual(SUCCESS, result); + assertEqual(-1, client.read()); client.pushToReadBuffer('A'); - assertEqual('A',client.read()); - EthernetClient::stopMockServer(server,80); + assertEqual(1, client.available()); + assertEqual('A', client.read()); + assertEqual(0, client.available()); } unittest(Client_Stop) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(server,80); - int result = client.connect(server, 80); + EthernetClient::startMockServer(serverIP, 80); + int result = client.connect(serverIP, 80); client.stop(); assertEqual(-1, client.read()); - assertEqual(0,client.writeBuffer().size()); + assertEqual(0, client.writeBuffer().size()); - assertEqual(0,client.localPort()); + assertEqual(0, client.localPort()); - assertEqual(0,client.serverPeer().ip); - assertEqual(0,client.serverPeer().port); - EthernetClient::stopMockServer(server,80); + assertEqual(0, client.serverPeer().ip); + assertEqual(0, client.serverPeer().port); } unittest(Client_AvailableForWrite) { - Ethernet.begin(mac,ip); + Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(server,80); - int result = client.connect(server, 80); - assertNotEqual(1024*1023,client.availableForWrite()); - assertEqual(1024*1024,client.availableForWrite()); + EthernetClient::startMockServer(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); + Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(server,80); - int result = client.connect(server, 80); - assertEqual(SUCCESS,result); + EthernetClient::startMockServer(serverIP, 80); + int result = client.connect(serverIP, 80); + assertEqual(SUCCESS, result); - assertEqual(-1,client.peek()); + assertEqual(-1, client.peek()); client.pushToReadBuffer('A'); - assertEqual('A',client.peek()); + assertEqual('A', client.peek()); } unittest(Client_Connected) { - Ethernet.begin(mac,ip); + Ethernet.begin(mac, ip); EthernetClient client; assertFalse(client.connected()); - EthernetClient::startMockServer(server,80); - int result = client.connect(server, 80); + EthernetClient::startMockServer(serverIP, 80); + int result = client.connect(serverIP, 80); assertTrue(client.connected()); } From f8db3d1918ebd05b4129f7403cc35415f1abb86a Mon Sep 17 00:00:00 2001 From: James Foster Date: Tue, 17 Aug 2021 18:57:19 -0700 Subject: [PATCH 09/21] Support mock DHCP for testing (#12) * Tests pass and we can simulate DHCP success and failure! --- .github/workflows/arduino_ci.yml | 6 ++- .gitignore | 1 + Gemfile | 2 +- src/Ethernet.cpp | 62 +++++++++++-------------- src/Ethernet.h | 21 +++++---- src/EthernetClient_CI.cpp | 2 +- src/EthernetServer_CI.cpp | 2 +- src/Ethernet_CI.cpp | 79 ++++++++++++++++++++++++++++++++ src/Ethernet_CI.h | 71 ++++++++++++++++++++++++++-- test/test.cpp | 20 ++++++-- 10 files changed, 212 insertions(+), 54 deletions(-) create mode 100644 src/Ethernet_CI.cpp diff --git a/.github/workflows/arduino_ci.yml b/.github/workflows/arduino_ci.yml index a7406654..bc91551f 100644 --- a/.github/workflows/arduino_ci.yml +++ b/.github/workflows/arduino_ci.yml @@ -9,4 +9,8 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: Arduino-CI/action@stable-1.x + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - name: Automated tests + run: scripts/testAndBuild.sh diff --git a/.gitignore b/.gitignore index 0fff4dbd..c43a6e6f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ vendor *.bin *.bin.dSYM .DS_Store +.vscode diff --git a/Gemfile b/Gemfile index fb422d12..14f68727 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,2 @@ source 'https://rubygems.org' -gem 'arduino_ci' +gem 'arduino_ci', git: 'https://github.com/jgfoster/arduino_ci', branch: 'main' 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 07833793..4c0ad775 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -51,9 +51,11 @@ ////////// 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 @@ -77,7 +79,7 @@ class EthernetClient; class EthernetServer; class DhcpClass; -class EthernetClass { +class Ethernet_Base { private: static IPAddress _dnsServerAddress; static DhcpClass *_dhcp; @@ -117,16 +119,11 @@ class EthernetClass { void setRetransmissionTimeout(uint16_t milliseconds); void setRetransmissionCount(uint8_t num); - friend class EthernetClient; - friend class EthernetServer; - friend class EthernetUDP; - -#ifdef MOCK_PINS_COUNT friend class EthernetServer_Base; friend class EthernetClient_Base; -#endif + friend class EthernetUDP; -private: +protected: // 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, @@ -165,8 +162,6 @@ class EthernetClass { static void socketPortRand(uint16_t n); }; -extern EthernetClass Ethernet; - #define UDP_TX_PACKET_MAX_SIZE 24 class EthernetUDP : public UDP { @@ -348,4 +343,10 @@ class DhcpClass { int checkLease(); }; +#ifdef MOCK_PINS_COUNT +#include "Ethernet_CI.h" +#endif + +extern EthernetClass Ethernet; + #endif diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp index 534e4ae4..75db4775 100644 --- a/src/EthernetClient_CI.cpp +++ b/src/EthernetClient_CI.cpp @@ -1,4 +1,4 @@ -#include "Ethernet_CI.h" +#include "Ethernet.h" #include #ifdef MOCK_PINS_COUNT diff --git a/src/EthernetServer_CI.cpp b/src/EthernetServer_CI.cpp index 550b5a5d..df272ad1 100644 --- a/src/EthernetServer_CI.cpp +++ b/src/EthernetServer_CI.cpp @@ -1,5 +1,5 @@ #include -#include "Ethernet_CI.h" +#include "Ethernet.h" #ifdef MOCK_PINS_COUNT #include "utility/w5100.h" diff --git a/src/Ethernet_CI.cpp b/src/Ethernet_CI.cpp new file mode 100644 index 00000000..b4e7c598 --- /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(); +} + +// Manaul 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 index cb03463a..3b0c649f 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -1,7 +1,4 @@ #pragma once -// Needed for workaround for problem in Arduino.CI -#include -#include #ifdef MOCK_PINS_COUNT // Configure the maximum number of sockets to support. W5100 chips can have @@ -37,6 +34,74 @@ struct mockServer { uint16_t port; }; +class Ethernet_CI : public Ethernet_Base { +private: + static IPAddress _mockDHCP; + +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(); + + // Manaul 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); + + // testing + void mockDHCP(IPAddress ip); + +protected: + 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); + +public: + static void socketPortRand(uint16_t n); + + friend class EthernetClient_Base; + friend class EthernetServer_Base; + friend class EthernetUDP; + +}; + class EthernetClient_CI : public EthernetClient_Base { public: EthernetClient_CI() : sockindex(MAX_SOCK_NUM), _timeout(1000) {} diff --git a/test/test.cpp b/test/test.cpp index 7894fb31..454df2da 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -9,7 +9,7 @@ bundle exec arduino_ci_remote.rb --skip-compilation #include "Arduino.h" #include "ArduinoUnitTests.h" -#include "Ethernet_CI.h" +#include "Ethernet.h" #include "ci/ObservableDataStream.h" #include "utility/w5100.h" @@ -23,8 +23,7 @@ unittest_teardown() { EthernetClient::stopMockServer(serverName, 80); } -// Test the Ethernet.begin() behaves the same as it did in the simulators -unittest(EthernetBegin) { +unittest(Ethernet_begin_pins) { // Setup GodmodeState *state = GODMODE(); state->reset(); @@ -47,6 +46,21 @@ unittest(EthernetBegin) { 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); From 47e29e5c3121ad0bbf2bc9e4e3d7777c5175a292 Mon Sep 17 00:00:00 2001 From: James Foster Date: Wed, 18 Aug 2021 09:03:26 -0700 Subject: [PATCH 10/21] Keep track of servers. --- .gitignore | 1 + src/EthernetServer_CI.cpp | 20 ++++++++++++++++++++ src/Ethernet_CI.h | 4 +++- test/test.cpp | 2 ++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c43a6e6f..8dffcb70 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ vendor *.bin.dSYM .DS_Store .vscode +*.so* diff --git a/src/EthernetServer_CI.cpp b/src/EthernetServer_CI.cpp index df272ad1..823338b1 100644 --- a/src/EthernetServer_CI.cpp +++ b/src/EthernetServer_CI.cpp @@ -4,6 +4,13 @@ #ifdef MOCK_PINS_COUNT #include "utility/w5100.h" +std::vector EthernetServer_CI::servers; + +EthernetServer_CI::EthernetServer_CI(uint16_t port) : EthernetServer_Base(port) { + _port = port; + servers.push_back(this); +} + void EthernetServer_CI::begin() { // EthernetServer_Base::begin(); @@ -42,4 +49,17 @@ uint8_t EthernetServer_CI::getSocketNumber() const { return MAX_SOCK_NUM; } +EthernetServer_CI* EthernetServer_CI::getServerForPort(uint16_t port) { + for (auto each : servers) { + if (each->getPort() == port) { + return each; + } + } + return nullptr; +} + +void EthernetServer_CI::setNextClient(EthernetClient_CI nextClient) { + this->nextClient = nextClient; +} + #endif diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index 3b0c649f..7f32a691 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -164,9 +164,10 @@ class EthernetServer_CI : public EthernetServer_Base { uint16_t _port; bool _didCallBegin = false; uint8_t getSocketNumber() const; + static std::vector servers; public: - EthernetServer_CI(uint16_t port) : EthernetServer_Base(port) { _port = port; } + EthernetServer_CI(uint16_t port); EthernetClient_CI available(); EthernetClient_CI accept(); virtual void begin(); @@ -176,6 +177,7 @@ class EthernetServer_CI : public EthernetServer_Base { // Testing Functions bool didCallBegin() { return _didCallBegin; } uint16_t getPort() { return _port; } + static EthernetServer_CI* getServerForPort(uint16_t port); }; #endif diff --git a/test/test.cpp b/test/test.cpp index 454df2da..a8f359e3 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -65,6 +65,8 @@ unittest(Server_Constructor) { // Testing port EthernetServer ethernet_server(72); assertEqual(72, ethernet_server.getPort()); + EthernetServer* pServer = EthernetServer::getServerForPort(72); + assertEqual(ðernet_server, pServer); } unittest(Server_Begin) { From 954ebaaa5ba0acb30cc3e572dfe4505efe76f7da Mon Sep 17 00:00:00 2001 From: James Foster Date: Wed, 18 Aug 2021 10:49:30 -0700 Subject: [PATCH 11/21] WIP: Able to control whether accept returns something connected. --- src/EthernetServer_CI.cpp | 28 ++++++++++++++++++---------- src/Ethernet_CI.h | 14 ++++++++++---- test/test.cpp | 15 +++++++++++++-- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/EthernetServer_CI.cpp b/src/EthernetServer_CI.cpp index 823338b1..ab69556e 100644 --- a/src/EthernetServer_CI.cpp +++ b/src/EthernetServer_CI.cpp @@ -4,11 +4,15 @@ #ifdef MOCK_PINS_COUNT #include "utility/w5100.h" -std::vector EthernetServer_CI::servers; +std::set EthernetServer_CI::servers; EthernetServer_CI::EthernetServer_CI(uint16_t port) : EthernetServer_Base(port) { _port = port; - servers.push_back(this); + servers.emplace(this); +} + +EthernetServer_CI::~EthernetServer_CI() { + servers.erase(this); } void EthernetServer_CI::begin() @@ -19,14 +23,22 @@ void EthernetServer_CI::begin() EthernetClient_CI EthernetServer_CI::available() { - EthernetClient_Base client = EthernetServer_Base::available(); - return EthernetClient_CI(client.getSocketNumber()); + if (hasClientCalling) { + EthernetClient_Base client = EthernetServer_Base::available(); + return EthernetClient_CI(client.getSocketNumber()); + } else { + return EthernetClient_CI(); + } } EthernetClient_CI EthernetServer_CI::accept() { - EthernetClient_Base client = EthernetServer_Base::accept(); - return EthernetClient_CI(client.getSocketNumber()); + if (hasClientCalling) { + EthernetClient_Base client = EthernetServer_Base::accept(); + return EthernetClient_CI(client.getSocketNumber()); + } else { + return EthernetClient_CI(); + } } size_t EthernetServer_CI::write(const uint8_t *buffer, size_t size) @@ -58,8 +70,4 @@ EthernetServer_CI* EthernetServer_CI::getServerForPort(uint16_t port) { return nullptr; } -void EthernetServer_CI::setNextClient(EthernetClient_CI nextClient) { - this->nextClient = nextClient; -} - #endif diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index 7f32a691..9adda58d 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -16,6 +16,7 @@ #include "Udp.h" #include #include +#include #include #include @@ -99,13 +100,12 @@ class Ethernet_CI : public Ethernet_Base { friend class EthernetClient_Base; friend class EthernetServer_Base; friend class EthernetUDP; - }; class EthernetClient_CI : public EthernetClient_Base { public: EthernetClient_CI() : sockindex(MAX_SOCK_NUM), _timeout(1000) {} - EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000) {} + EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000), _status(SnSR::ESTABLISHED) {} uint8_t status() { return _status; } virtual int connect(IPAddress ip, uint16_t port); @@ -147,6 +147,7 @@ class EthernetClient_CI : public EthernetClient_Base { static void stopMockServer(const char *host, uint16_t port); void pushToReadBuffer(uint8_t value) { _readBuffer.push_back(value); } std::deque writeBuffer() { return _writeBuffer; } + void setStatus(uint8_t status) { _status = status; } private: uint8_t sockindex; // MAX_SOCK_NUM means client not in use @@ -164,10 +165,13 @@ class EthernetServer_CI : public EthernetServer_Base { uint16_t _port; bool _didCallBegin = false; uint8_t getSocketNumber() const; - static std::vector servers; + static std::set servers; + bool hasClientCalling = false; + EthernetClient_CI *client = nullptr; public: EthernetServer_CI(uint16_t port); + ~EthernetServer_CI(); EthernetClient_CI available(); EthernetClient_CI accept(); virtual void begin(); @@ -177,7 +181,9 @@ class EthernetServer_CI : public EthernetServer_Base { // Testing Functions bool didCallBegin() { return _didCallBegin; } uint16_t getPort() { return _port; } - static EthernetServer_CI* getServerForPort(uint16_t port); + static EthernetServer_CI *getServerForPort(uint16_t port); + void setHasClientCalling(bool flag) { hasClientCalling = flag; } + EthernetClient_CI *getClient() { return client; } }; #endif diff --git a/test/test.cpp b/test/test.cpp index a8f359e3..118d2dfa 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -65,8 +65,6 @@ unittest(Server_Constructor) { // Testing port EthernetServer ethernet_server(72); assertEqual(72, ethernet_server.getPort()); - EthernetServer* pServer = EthernetServer::getServerForPort(72); - assertEqual(ðernet_server, pServer); } unittest(Server_Begin) { @@ -180,4 +178,17 @@ unittest(Client_Connected) { assertTrue(client.connected()); } +unittest(Client_Server) { + EthernetServer ethernet_server(80); + EthernetClient_CI client1 = ethernet_server.accept(); + assertFalse(client1); + + EthernetServer* pServer = EthernetServer::getServerForPort(80); + assertNotNull(pServer); + assertEqual(ðernet_server, pServer); + pServer->setHasClientCalling(true); + EthernetClient_CI client2 = ethernet_server.accept(); + assertTrue(client2); +} + unittest_main() From 566fe16566c80f01ad87724c0e26d52b5037d12d Mon Sep 17 00:00:00 2001 From: James Foster Date: Thu, 19 Aug 2021 16:10:47 -0700 Subject: [PATCH 12/21] Allow for copying of client and keeping read/write buffers available. --- src/Ethernet.h | 2 +- src/EthernetClient_CI.cpp | 95 ++++++++++++++++++++++++++++++--------- src/EthernetServer_CI.cpp | 20 +++------ src/Ethernet_CI.h | 58 +++++++++++------------- test/test.cpp | 19 +++++--- 5 files changed, 117 insertions(+), 77 deletions(-) diff --git a/src/Ethernet.h b/src/Ethernet.h index 4c0ad775..2a8e91c3 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -123,7 +123,7 @@ class Ethernet_Base { friend class EthernetClient_Base; friend class EthernetUDP; -protected: +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, diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp index 75db4775..79be0428 100644 --- a/src/EthernetClient_CI.cpp +++ b/src/EthernetClient_CI.cpp @@ -6,49 +6,74 @@ #include "utility/w5100.h" std::vector EthernetClient_CI::mockServers; -uint16_t EthernetClient_CI::nextPort = 49152; // 49152 to 65535 +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; + _status = 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) { - for (int i = 0; i < size; ++i) { - _writeBuffer.push_back(buf[i]); + if (sockindex < MAX_SOCK_NUM) { + for (int i = 0; i < size; ++i) { + _sockets[sockindex].writeBuffer.push_back(buf[i]); + } + return size; } - return size; + return 0; } int EthernetClient_CI::read(uint8_t *buf, size_t bufSize) { - int size = max(min(bufSize, _readBuffer.size()), 0); + int size = max(min(bufSize, sockindex < MAX_SOCK_NUM ? _sockets[sockindex].readBuffer.size() : 0), 0); for (int i = 0; i < size; ++i) { - buf[i] = _readBuffer.front(); - _readBuffer.pop_front(); + buf[i] = _sockets[sockindex].readBuffer.front(); + _sockets[sockindex].readBuffer.pop_front(); } return size; } int EthernetClient_CI::read() { - if (!_readBuffer.empty()) { - char x = _readBuffer.front(); - _readBuffer.pop_front(); + 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 (!_readBuffer.empty()) { - char x = _readBuffer.front(); + 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() { - - // Clear read and write buffers - _readBuffer.clear(); - _writeBuffer.clear(); + if (sockindex < MAX_SOCK_NUM) { + _sockets[sockindex].readBuffer.clear(); + _sockets[sockindex].writeBuffer.clear(); + _sockets[sockindex].status = SnSR::CLOSED; + sockindex = MAX_SOCK_NUM; + } // Set port and ip to zero _localPort = 0; @@ -62,10 +87,21 @@ void EthernetClient_CI::stop() { } int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { - if (++nextPort < 49152) { - nextPort = 49152; + 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! } - _localPort = nextPort; // Iterate though vector of mock servers for (int i = 0; i < mockServers.size(); ++i) { // If we find server with ip and port @@ -75,6 +111,8 @@ int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { peer.ip = ip; peer.port = port; _status = SnSR::ESTABLISHED; + _sockets[sockindex].status = SnSR::ESTABLISHED; + _localPort = 0xC000 + sockindex; return SUCCESS; } } @@ -82,10 +120,21 @@ int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { } int EthernetClient_CI::connect(const char *hostname, uint16_t port) { - if (++nextPort < 49152) { - nextPort = 49152; + 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! } - _localPort = nextPort; // Iterate though vector of mock servers for (int i = 0; i < mockServers.size(); ++i) { // If we find server with ip and port @@ -96,6 +145,8 @@ int EthernetClient_CI::connect(const char *hostname, uint16_t port) { peer.ip = {0, 0, 0, 0}; peer.port = port; _status = SnSR::ESTABLISHED; + _sockets[sockindex].status = SnSR::ESTABLISHED; + _localPort = 0xC000 + sockindex; return SUCCESS; } } diff --git a/src/EthernetServer_CI.cpp b/src/EthernetServer_CI.cpp index ab69556e..ef660ae8 100644 --- a/src/EthernetServer_CI.cpp +++ b/src/EthernetServer_CI.cpp @@ -17,39 +17,31 @@ EthernetServer_CI::~EthernetServer_CI() { void EthernetServer_CI::begin() { - // EthernetServer_Base::begin(); _didCallBegin = true; } EthernetClient_CI EthernetServer_CI::available() { - if (hasClientCalling) { - EthernetClient_Base client = EthernetServer_Base::available(); - return EthernetClient_CI(client.getSocketNumber()); - } else { - return EthernetClient_CI(); - } + return accept(); } EthernetClient_CI EthernetServer_CI::accept() { if (hasClientCalling) { - EthernetClient_Base client = EthernetServer_Base::accept(); - return EthernetClient_CI(client.getSocketNumber()); + client = EthernetClient_CI(MAX_SOCK_NUM); // signal to select your own sockindex and "connect" } else { - return EthernetClient_CI(); + client = EthernetClient_CI(); } + return client; } size_t EthernetServer_CI::write(const uint8_t *buffer, size_t size) { EthernetClient_CI client = EthernetClient_CI(getSocketNumber()); - - // Is this necessary? - if (!client) { + if (!client) { // test if client is connected return 0; } - return client.write(buffer,size); + return client.write(buffer, size); } uint8_t EthernetServer_CI::getSocketNumber() const { diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index 9adda58d..ea2447a9 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -1,16 +1,6 @@ #pragma once #ifdef MOCK_PINS_COUNT -// 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 -// you are limited to fewer simultaneous connections. -#if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048) -#define MAX_SOCK_NUM 4 -#else -#define MAX_SOCK_NUM 8 -#endif - #include "Client.h" #include "Server.h" #include "Udp.h" @@ -34,11 +24,13 @@ struct mockServer { IPAddress ip; uint16_t port; }; +struct socket_t { + uint8_t status = SnSR::CLOSED; + std::deque readBuffer; + std::deque writeBuffer; +}; class Ethernet_CI : public Ethernet_Base { -private: - static IPAddress _mockDHCP; - public: // Initialise the Ethernet shield to use the provided MAC address and // gain the rest of the configuration through DHCP. @@ -72,10 +64,13 @@ class Ethernet_CI : public Ethernet_Base { void setRetransmissionTimeout(uint16_t milliseconds); void setRetransmissionCount(uint8_t num); + static void socketPortRand(uint16_t n); + // testing void mockDHCP(IPAddress ip); -protected: +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); @@ -94,9 +89,6 @@ class Ethernet_CI : public Ethernet_Base { const uint8_t *buf, uint16_t len); static bool socketSendUDP(uint8_t s); -public: - static void socketPortRand(uint16_t n); - friend class EthernetClient_Base; friend class EthernetServer_Base; friend class EthernetUDP; @@ -104,8 +96,8 @@ class Ethernet_CI : public Ethernet_Base { class EthernetClient_CI : public EthernetClient_Base { public: - EthernetClient_CI() : sockindex(MAX_SOCK_NUM), _timeout(1000) {} - EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000), _status(SnSR::ESTABLISHED) {} + EthernetClient_CI(); + EthernetClient_CI(uint8_t s); uint8_t status() { return _status; } virtual int connect(IPAddress ip, uint16_t port); @@ -113,7 +105,7 @@ class EthernetClient_CI : public EthernetClient_Base { 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 _readBuffer.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(); @@ -123,18 +115,15 @@ class EthernetClient_CI : public EthernetClient_Base { 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) { - return _localPort == rhs.localPort(); + 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; } - virtual std::vector testServers() { return mockServers; } - virtual mockServer serverPeer() { return peer; } friend class EthernetServer; @@ -145,19 +134,22 @@ class EthernetClient_CI : public EthernetClient_Base { static void startMockServer(const char *host, uint16_t port); static void stopMockServer(IPAddress ip, uint16_t port); static void stopMockServer(const char *host, uint16_t port); - void pushToReadBuffer(uint8_t value) { _readBuffer.push_back(value); } - std::deque writeBuffer() { return _writeBuffer; } + virtual std::vector testServers() { return mockServers; } + virtual mockServer serverPeer() { return peer; } + void pushToReadBuffer(uint8_t value) { sockindex < MAX_SOCK_NUM ? _sockets[sockindex].readBuffer.push_back(value) : (void) 0; } + std::deque* writeBuffer() { return sockindex < MAX_SOCK_NUM ? &_sockets[sockindex].writeBuffer : nullptr; } void setStatus(uint8_t status) { _status = status; } + uint8_t getSockindex() const { return sockindex; } private: uint8_t sockindex; // MAX_SOCK_NUM means client not in use uint16_t _timeout; - static std::vector mockServers; - static uint16_t nextPort; // 49152 to 65535 - mockServer peer; uint16_t _localPort = 0; uint8_t _status = SnSR::CLOSED; - std::deque _readBuffer, _writeBuffer; + + static std::vector mockServers; + mockServer peer; + static socket_t _sockets[MAX_SOCK_NUM]; }; class EthernetServer_CI : public EthernetServer_Base { @@ -167,7 +159,7 @@ class EthernetServer_CI : public EthernetServer_Base { uint8_t getSocketNumber() const; static std::set servers; bool hasClientCalling = false; - EthernetClient_CI *client = nullptr; + EthernetClient_CI client; public: EthernetServer_CI(uint16_t port); @@ -183,7 +175,7 @@ class EthernetServer_CI : public EthernetServer_Base { uint16_t getPort() { return _port; } static EthernetServer_CI *getServerForPort(uint16_t port); void setHasClientCalling(bool flag) { hasClientCalling = flag; } - EthernetClient_CI *getClient() { return client; } + EthernetClient_CI getClient() { return client; } }; #endif diff --git a/test/test.cpp b/test/test.cpp index 118d2dfa..4a912075 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -111,10 +111,11 @@ unittest(Client_Write) { EthernetClient client; EthernetClient::startMockServer(serverIP, 80); int result = client.connect(serverIP, 80); - assertEqual(0, client.writeBuffer().size()); + assertEqual(0, client.writeBuffer()->size()); client.write((const uint8_t *)"Hello", 5); - assertEqual(5, client.writeBuffer().size()); - assertEqual('H', client.writeBuffer().at(0)); + assertEqual(5, client.writeBuffer()->size()); + assertEqual('H', client.writeBuffer()->at(0)); + client.stop(); } unittest(Client_Read) { @@ -128,6 +129,7 @@ unittest(Client_Read) { assertEqual(1, client.available()); assertEqual('A', client.read()); assertEqual(0, client.available()); + client.stop(); } unittest(Client_Stop) { @@ -138,7 +140,7 @@ unittest(Client_Stop) { client.stop(); assertEqual(-1, client.read()); - assertEqual(0, client.writeBuffer().size()); + assertNull(client.writeBuffer()); assertEqual(0, client.localPort()); @@ -180,15 +182,18 @@ unittest(Client_Connected) { unittest(Client_Server) { EthernetServer ethernet_server(80); - EthernetClient_CI client1 = ethernet_server.accept(); + EthernetClient_CI client1, client2; + client1 = ethernet_server.accept(); assertFalse(client1); EthernetServer* pServer = EthernetServer::getServerForPort(80); assertNotNull(pServer); assertEqual(ðernet_server, pServer); pServer->setHasClientCalling(true); - EthernetClient_CI client2 = ethernet_server.accept(); - assertTrue(client2); + client1 = ethernet_server.accept(); + assertTrue(client1); + client2 = ethernet_server.getClient(); + assertTrue(client1 == (const EthernetClient) client2); } unittest_main() From d18c60230ce6406489d908d884475e83093f6b63 Mon Sep 17 00:00:00 2001 From: James Foster Date: Thu, 19 Aug 2021 19:56:26 -0700 Subject: [PATCH 13/21] Don't clear writeBuffer on close so tests can go back and look at it. --- src/EthernetClient_CI.cpp | 38 +++++++++++++++++++++++--------------- src/Ethernet_CI.h | 4 ++-- test/test.cpp | 9 ++++++--- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp index 79be0428..a26e73ce 100644 --- a/src/EthernetClient_CI.cpp +++ b/src/EthernetClient_CI.cpp @@ -8,7 +8,8 @@ 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() + : sockindex(MAX_SOCK_NUM), _timeout(1000) {} EthernetClient_CI::EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000) { if (s == MAX_SOCK_NUM) { @@ -35,7 +36,9 @@ size_t EthernetClient_CI::write(const uint8_t *buf, size_t size) { } int EthernetClient_CI::read(uint8_t *buf, size_t bufSize) { - int size = max(min(bufSize, sockindex < MAX_SOCK_NUM ? _sockets[sockindex].readBuffer.size() : 0), 0); + 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(); @@ -45,7 +48,8 @@ int EthernetClient_CI::read(uint8_t *buf, size_t bufSize) { } int EthernetClient_CI::read() { - if (sockindex >= MAX_SOCK_NUM || _sockets[sockindex].status != SnSR::ESTABLISHED) { + if (sockindex >= MAX_SOCK_NUM || + _sockets[sockindex].status != SnSR::ESTABLISHED) { return -1; } if (!_sockets[sockindex].readBuffer.empty()) { @@ -57,7 +61,8 @@ int EthernetClient_CI::read() { } int EthernetClient_CI::peek() { - if (sockindex >= MAX_SOCK_NUM || _sockets[sockindex].status != SnSR::ESTABLISHED) { + if (sockindex >= MAX_SOCK_NUM || + _sockets[sockindex].status != SnSR::ESTABLISHED) { return -1; } if (!_sockets[sockindex].readBuffer.empty()) { @@ -70,26 +75,19 @@ int EthernetClient_CI::peek() { void EthernetClient_CI::stop() { if (sockindex < MAX_SOCK_NUM) { _sockets[sockindex].readBuffer.clear(); - _sockets[sockindex].writeBuffer.clear(); _sockets[sockindex].status = SnSR::CLOSED; sockindex = MAX_SOCK_NUM; } - - // Set port and ip to zero _localPort = 0; - - // Close peer connection peer.ip = (uint32_t)0; peer.port = 0; - - // Close the connection _status = SnSR::CLOSED; } int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { if (sockindex < MAX_SOCK_NUM) { if (_sockets[sockindex].status != SnSR::CLOSED) { - return INVALID_SERVER; // we are already connected! + return INVALID_SERVER; // we are already connected! } } else { for (int i = 0; i < MAX_SOCK_NUM; ++i) { @@ -100,7 +98,7 @@ int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { } } if (sockindex >= MAX_SOCK_NUM) { - return INVALID_SERVER; // unable to obtain a socket! + return INVALID_SERVER; // unable to obtain a socket! } // Iterate though vector of mock servers for (int i = 0; i < mockServers.size(); ++i) { @@ -122,7 +120,7 @@ int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { int EthernetClient_CI::connect(const char *hostname, uint16_t port) { if (sockindex < MAX_SOCK_NUM) { if (_sockets[sockindex].status != SnSR::CLOSED) { - return INVALID_SERVER; // we are already connected! + return INVALID_SERVER; // we are already connected! } } else { for (int i = 0; i < MAX_SOCK_NUM; ++i) { @@ -133,7 +131,7 @@ int EthernetClient_CI::connect(const char *hostname, uint16_t port) { } } if (sockindex >= MAX_SOCK_NUM) { - return INVALID_SERVER; // unable to obtain a socket! + return INVALID_SERVER; // unable to obtain a socket! } // Iterate though vector of mock servers for (int i = 0; i < mockServers.size(); ++i) { @@ -186,4 +184,14 @@ void EthernetClient_CI::stopMockServer(const char *hostname, uint16_t port) { } } +void EthernetClient_CI::pushToReadBuffer(uint8_t value) { + if (sockindex < MAX_SOCK_NUM) { + _sockets[sockindex].readBuffer.push_back(value); + } +} + +std::deque *EthernetClient_CI::writeBuffer() { + return sockindex < MAX_SOCK_NUM ? &_sockets[sockindex].writeBuffer : nullptr; +} + #endif diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index ea2447a9..121c13ea 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -136,8 +136,8 @@ class EthernetClient_CI : public EthernetClient_Base { static void stopMockServer(const char *host, uint16_t port); virtual std::vector testServers() { return mockServers; } virtual mockServer serverPeer() { return peer; } - void pushToReadBuffer(uint8_t value) { sockindex < MAX_SOCK_NUM ? _sockets[sockindex].readBuffer.push_back(value) : (void) 0; } - std::deque* writeBuffer() { return sockindex < MAX_SOCK_NUM ? &_sockets[sockindex].writeBuffer : nullptr; } + void pushToReadBuffer(uint8_t value); + std::deque* writeBuffer(); void setStatus(uint8_t status) { _status = status; } uint8_t getSockindex() const { return sockindex; } diff --git a/test/test.cpp b/test/test.cpp index 4a912075..a403c7a6 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -110,7 +110,13 @@ unittest(Client_Write) { Ethernet.begin(mac, ip); EthernetClient client; EthernetClient::startMockServer(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()); @@ -140,10 +146,7 @@ unittest(Client_Stop) { client.stop(); assertEqual(-1, client.read()); - assertNull(client.writeBuffer()); - assertEqual(0, client.localPort()); - assertEqual(0, client.serverPeer().ip); assertEqual(0, client.serverPeer().port); } From 80e4b8110f176abf3f315db0e12c5e0b55ead8d8 Mon Sep 17 00:00:00 2001 From: James Foster Date: Thu, 19 Aug 2021 21:48:30 -0700 Subject: [PATCH 14/21] Keep status with socket rather than with client. --- src/EthernetClient_CI.cpp | 14 ++++++++++---- src/Ethernet_CI.h | 9 ++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp index a26e73ce..b42607e6 100644 --- a/src/EthernetClient_CI.cpp +++ b/src/EthernetClient_CI.cpp @@ -16,7 +16,7 @@ EthernetClient_CI::EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000) { for (int i = 0; i < MAX_SOCK_NUM; ++i) { if (_sockets[i].status == SnSR::CLOSED) { sockindex = i; - _status = SnSR::ESTABLISHED; + setStatus(SnSR::ESTABLISHED); break; } } @@ -81,7 +81,7 @@ void EthernetClient_CI::stop() { _localPort = 0; peer.ip = (uint32_t)0; peer.port = 0; - _status = SnSR::CLOSED; + setStatus(SnSR::CLOSED); } int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { @@ -108,7 +108,7 @@ int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { peer.hostname[0] = '\0'; peer.ip = ip; peer.port = port; - _status = SnSR::ESTABLISHED; + setStatus(SnSR::ESTABLISHED); _sockets[sockindex].status = SnSR::ESTABLISHED; _localPort = 0xC000 + sockindex; return SUCCESS; @@ -142,7 +142,7 @@ int EthernetClient_CI::connect(const char *hostname, uint16_t port) { strncpy(peer.hostname, hostname, HOSTNAME_SIZE); peer.ip = {0, 0, 0, 0}; peer.port = port; - _status = SnSR::ESTABLISHED; + setStatus(SnSR::ESTABLISHED); _sockets[sockindex].status = SnSR::ESTABLISHED; _localPort = 0xC000 + sockindex; return SUCCESS; @@ -151,6 +151,12 @@ int EthernetClient_CI::connect(const char *hostname, uint16_t port) { return INVALID_SERVER; } +void EthernetClient_CI::setStatus(uint8_t status) { + if (sockindex < MAX_SOCK_NUM) { + _sockets[sockindex].status = status; + } +} + void EthernetClient_CI::startMockServer(IPAddress ip, uint16_t port) { mockServer server; server.hostname[0] = '\0'; diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index 121c13ea..3b5d4d0a 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -99,7 +99,7 @@ class EthernetClient_CI : public EthernetClient_Base { EthernetClient_CI(); EthernetClient_CI(uint8_t s); - uint8_t status() { return _status; } + 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 @@ -111,7 +111,7 @@ class EthernetClient_CI : public EthernetClient_Base { virtual int peek(); virtual void flush() {} virtual void stop(); - virtual uint8_t connected() { return _status == SnSR::ESTABLISHED; } + 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; } @@ -138,18 +138,17 @@ class EthernetClient_CI : public EthernetClient_Base { virtual mockServer serverPeer() { return peer; } void pushToReadBuffer(uint8_t value); std::deque* writeBuffer(); - void setStatus(uint8_t status) { _status = status; } + 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; - uint8_t _status = SnSR::CLOSED; static std::vector mockServers; - mockServer peer; static socket_t _sockets[MAX_SOCK_NUM]; + mockServer peer; }; class EthernetServer_CI : public EthernetServer_Base { From 1c99dbf5b312083494e8d05d54c40f442609d531 Mon Sep 17 00:00:00 2001 From: James Foster Date: Fri, 20 Aug 2021 09:20:37 -0700 Subject: [PATCH 15/21] Refactor to allow pushing data before connection is established. --- src/EthernetClient_CI.cpp | 92 +++++++++++++++------------------------ src/Ethernet_CI.h | 10 ++--- test/test.cpp | 35 ++++++++++----- 3 files changed, 63 insertions(+), 74 deletions(-) diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp index b42607e6..470f86ee 100644 --- a/src/EthernetClient_CI.cpp +++ b/src/EthernetClient_CI.cpp @@ -84,7 +84,8 @@ void EthernetClient_CI::stop() { setStatus(SnSR::CLOSED); } -int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { +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! @@ -102,53 +103,34 @@ int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { } // Iterate though vector of mock servers for (int i = 0; i < mockServers.size(); ++i) { - // If we find server with ip and port - if ((mockServers.at(i).ip == ip) && (mockServers.at(i).port == port)) { - // Save name, ip, and port in peer - peer.hostname[0] = '\0'; - peer.ip = ip; - peer.port = port; + // 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) { + int i; + for (i = 0; peer.data[i] != '\0'; ++i) { + pushToReadBuffer(peer.data[i]); + } + } 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) { - 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 ip and port - if ((strncmp(mockServers.at(i).hostname, hostname, HOSTNAME_SIZE) == 0) && - (mockServers.at(i).port == port)) { - // Save name, ip, and port in peer - strncpy(peer.hostname, hostname, HOSTNAME_SIZE); - peer.ip = {0, 0, 0, 0}; - peer.port = port; - setStatus(SnSR::ESTABLISHED); - _sockets[sockindex].status = SnSR::ESTABLISHED; - _localPort = 0xC000 + sockindex; - return SUCCESS; - } - } - return INVALID_SERVER; + return connect(hostname, (uint32_t)0, port); } void EthernetClient_CI::setStatus(uint8_t status) { @@ -157,33 +139,27 @@ void EthernetClient_CI::setStatus(uint8_t status) { } } -void EthernetClient_CI::startMockServer(IPAddress ip, uint16_t port) { +void EthernetClient_CI::startMockServer(const char *hostname, IPAddress ip, + uint16_t port, const uint8_t *data) { mockServer server; - server.hostname[0] = '\0'; + 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::startMockServer(const char *hostname, uint16_t port) { - mockServer server; - strncpy(server.hostname, hostname, HOSTNAME_SIZE); - server.ip = {0, 0, 0, 0}; - server.port = port; - mockServers.push_back(server); -} - -void EthernetClient_CI::stopMockServer(IPAddress ip, uint16_t port) { - for (int i = (mockServers.size() - 1); i >= 0; --i) { - if (mockServers.at(i).ip == ip && mockServers.at(i).port == port) { - mockServers.erase(mockServers.begin() + i); - } - } -} - -void EthernetClient_CI::stopMockServer(const char *hostname, uint16_t port) { +void EthernetClient_CI::stopMockServer(const char *hostname, IPAddress ip, + uint16_t port) { for (int i = (mockServers.size() - 1); i >= 0; --i) { - if ((strncmp(mockServers.at(i).hostname, hostname, HOSTNAME_SIZE) == 0) && + 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); } diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index 3b5d4d0a..73c01428 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -23,6 +23,7 @@ struct mockServer { char hostname[HOSTNAME_SIZE]; IPAddress ip; uint16_t port; + const uint8_t *data = nullptr; }; struct socket_t { uint8_t status = SnSR::CLOSED; @@ -130,11 +131,10 @@ class EthernetClient_CI : public EthernetClient_Base { using Print::write; // Testing Support - static void startMockServer(IPAddress ip, uint16_t port); - static void startMockServer(const char *host, uint16_t port); - static void stopMockServer(IPAddress ip, uint16_t port); - static void stopMockServer(const char *host, uint16_t port); - virtual std::vector testServers() { return mockServers; } + 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); std::deque* writeBuffer(); diff --git a/test/test.cpp b/test/test.cpp index a403c7a6..40fadb1c 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -19,8 +19,9 @@ byte serverIP[] = {192, 168, 1, 1}; const char *serverName = "www.google.com"; unittest_teardown() { - EthernetClient::stopMockServer(serverIP, 80); - EthernetClient::stopMockServer(serverName, 80); + EthernetClient::stopMockServer(nullptr, serverIP, 80); + EthernetClient::stopMockServer(serverName, (uint32_t) 0, 80); + EthernetClient::clearMockServers(); } unittest(Ethernet_begin_pins) { @@ -87,7 +88,7 @@ unittest(Client_Connect_IP) { assertEqual(INVALID_SERVER, result); client.stop(); - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); result = client.connect(serverIP, 80); assertEqual(SUCCESS, result); client.stop(); @@ -100,7 +101,7 @@ unittest(Client_Connect_Name) { assertEqual(INVALID_SERVER, result); client.stop(); - EthernetClient::startMockServer(serverName, 80); + EthernetClient::startMockServer(serverName, (uint32_t) 0, 80); result = client.connect(serverName, 80); assertEqual(SUCCESS, result); client.stop(); @@ -109,7 +110,7 @@ unittest(Client_Connect_Name) { unittest(Client_Write) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); assertEqual(MAX_SOCK_NUM, client.getSockindex()); int result = client.connect(serverIP, 80); assertEqual(1, result); @@ -124,10 +125,10 @@ unittest(Client_Write) { client.stop(); } -unittest(Client_Read) { +unittest(Client_Read_data_given_after_connection) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); assertEqual(SUCCESS, result); assertEqual(-1, client.read()); @@ -138,10 +139,22 @@ unittest(Client_Read) { 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(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); client.stop(); @@ -154,7 +167,7 @@ unittest(Client_Stop) { unittest(Client_AvailableForWrite) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); assertNotEqual(1024 * 1023, client.availableForWrite()); assertEqual(1024 * 1024, client.availableForWrite()); @@ -163,7 +176,7 @@ unittest(Client_AvailableForWrite) { unittest(Client_Peek) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); assertEqual(SUCCESS, result); @@ -178,7 +191,7 @@ unittest(Client_Connected) { EthernetClient client; assertFalse(client.connected()); - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); assertTrue(client.connected()); } From 1355a53de57af10352c58179e335ce609b49bc71 Mon Sep 17 00:00:00 2001 From: James Foster Date: Fri, 20 Aug 2021 16:37:22 -0700 Subject: [PATCH 16/21] formatting --- src/Ethernet_CI.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index 73c01428..9d866d1f 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -100,13 +100,17 @@ class EthernetClient_CI : public EthernetClient_Base { EthernetClient_CI(); EthernetClient_CI(uint8_t s); - uint8_t status() { return sockindex < MAX_SOCK_NUM ? _sockets[sockindex].status : SnSR::CLOSED; }; + 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 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(); @@ -132,12 +136,13 @@ class EthernetClient_CI : public EthernetClient_Base { // 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 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); - std::deque* writeBuffer(); + std::deque *writeBuffer(); void setStatus(uint8_t status); uint8_t getSockindex() const { return sockindex; } From c02a9a04f0828124670869202eb626545d011e17 Mon Sep 17 00:00:00 2001 From: James Foster Date: Fri, 20 Aug 2021 19:59:45 -0700 Subject: [PATCH 17/21] * Clear flag to simulate client calling. * Add `pushToReadBuffer()` that accepts a C string. --- src/EthernetClient_CI.cpp | 13 +++++++++---- src/EthernetServer_CI.cpp | 1 + src/Ethernet_CI.h | 1 + test/test.cpp | 2 ++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp index 470f86ee..b1a6c6a1 100644 --- a/src/EthernetClient_CI.cpp +++ b/src/EthernetClient_CI.cpp @@ -114,10 +114,7 @@ int EthernetClient_CI::connect(const char *hostname, IPAddress ip, _sockets[sockindex].status = SnSR::ESTABLISHED; _localPort = 0xC000 + sockindex; if (peer.data) { - int i; - for (i = 0; peer.data[i] != '\0'; ++i) { - pushToReadBuffer(peer.data[i]); - } + pushToReadBuffer((const char *)peer.data); } return SUCCESS; } @@ -172,6 +169,14 @@ void EthernetClient_CI::pushToReadBuffer(uint8_t 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; } diff --git a/src/EthernetServer_CI.cpp b/src/EthernetServer_CI.cpp index ef660ae8..cbb51f53 100644 --- a/src/EthernetServer_CI.cpp +++ b/src/EthernetServer_CI.cpp @@ -29,6 +29,7 @@ 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(); } diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index 9d866d1f..f9f6bb55 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -142,6 +142,7 @@ class EthernetClient_CI : public EthernetClient_Base { 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; } diff --git a/test/test.cpp b/test/test.cpp index 40fadb1c..15bcd204 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -210,6 +210,8 @@ unittest(Client_Server) { assertTrue(client1); client2 = ethernet_server.getClient(); assertTrue(client1 == (const EthernetClient) client2); + client1 = ethernet_server.accept(); + assertFalse(client1); } unittest_main() From cee7d5e1d3933f3e131eaf98ce9c709edf10d46b Mon Sep 17 00:00:00 2001 From: James Foster Date: Tue, 24 Aug 2021 21:03:32 -0700 Subject: [PATCH 18/21] Refactor testing stub to allow for server testing (#13) * Keep track of servers. * WIP: Able to control whether accept returns something connected. * Allow for copying of client and keeping read/write buffers available. * Don't clear writeBuffer on close so tests can go back and look at it. * Keep status with socket rather than with client. * Refactor to allow pushing data before connection is established. * formatting * * Clear flag to simulate client calling. * Add `pushToReadBuffer()` that accepts a C string. Co-authored-by: James Foster --- .gitignore | 1 + src/Ethernet.h | 2 +- src/EthernetClient_CI.cpp | 182 ++++++++++++++++++++++++-------------- src/EthernetServer_CI.cpp | 39 ++++++-- src/Ethernet_CI.h | 83 +++++++++-------- test/test.cpp | 70 +++++++++++---- 6 files changed, 243 insertions(+), 134 deletions(-) diff --git a/.gitignore b/.gitignore index c43a6e6f..8dffcb70 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ vendor *.bin.dSYM .DS_Store .vscode +*.so* diff --git a/src/Ethernet.h b/src/Ethernet.h index 4c0ad775..2a8e91c3 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -123,7 +123,7 @@ class Ethernet_Base { friend class EthernetClient_Base; friend class EthernetUDP; -protected: +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, diff --git a/src/EthernetClient_CI.cpp b/src/EthernetClient_CI.cpp index 75db4775..b1a6c6a1 100644 --- a/src/EthernetClient_CI.cpp +++ b/src/EthernetClient_CI.cpp @@ -6,133 +6,179 @@ #include "utility/w5100.h" std::vector EthernetClient_CI::mockServers; -uint16_t EthernetClient_CI::nextPort = 49152; // 49152 to 65535 +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) { - for (int i = 0; i < size; ++i) { - _writeBuffer.push_back(buf[i]); + if (sockindex < MAX_SOCK_NUM) { + for (int i = 0; i < size; ++i) { + _sockets[sockindex].writeBuffer.push_back(buf[i]); + } + return size; } - return size; + return 0; } int EthernetClient_CI::read(uint8_t *buf, size_t bufSize) { - int size = max(min(bufSize, _readBuffer.size()), 0); + 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] = _readBuffer.front(); - _readBuffer.pop_front(); + buf[i] = _sockets[sockindex].readBuffer.front(); + _sockets[sockindex].readBuffer.pop_front(); } return size; } int EthernetClient_CI::read() { - if (!_readBuffer.empty()) { - char x = _readBuffer.front(); - _readBuffer.pop_front(); + 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 (!_readBuffer.empty()) { - char x = _readBuffer.front(); + 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() { - - // Clear read and write buffers - _readBuffer.clear(); - _writeBuffer.clear(); - - // Set port and ip to zero + if (sockindex < MAX_SOCK_NUM) { + _sockets[sockindex].readBuffer.clear(); + _sockets[sockindex].status = SnSR::CLOSED; + sockindex = MAX_SOCK_NUM; + } _localPort = 0; - - // Close peer connection peer.ip = (uint32_t)0; peer.port = 0; - - // Close the connection - _status = SnSR::CLOSED; + setStatus(SnSR::CLOSED); } -int EthernetClient_CI::connect(IPAddress ip, uint16_t port) { - if (++nextPort < 49152) { - nextPort = 49152; - } - _localPort = nextPort; - // Iterate though vector of mock servers - for (int i = 0; i < mockServers.size(); ++i) { - // If we find server with ip and port - if ((mockServers.at(i).ip == ip) && (mockServers.at(i).port == port)) { - // Save name, ip, and port in peer - peer.hostname[0] = '\0'; - peer.ip = ip; - peer.port = port; - _status = SnSR::ESTABLISHED; - return SUCCESS; +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; + } } } - return INVALID_SERVER; -} - -int EthernetClient_CI::connect(const char *hostname, uint16_t port) { - if (++nextPort < 49152) { - nextPort = 49152; + if (sockindex >= MAX_SOCK_NUM) { + return INVALID_SERVER; // unable to obtain a socket! } - _localPort = nextPort; // Iterate though vector of mock servers for (int i = 0; i < mockServers.size(); ++i) { - // If we find server with ip and port - if ((strncmp(mockServers.at(i).hostname, hostname, HOSTNAME_SIZE) == 0) && + // 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)) { - // Save name, ip, and port in peer - strncpy(peer.hostname, hostname, HOSTNAME_SIZE); - peer.ip = {0, 0, 0, 0}; - peer.port = port; - _status = SnSR::ESTABLISHED; + 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; } -void EthernetClient_CI::startMockServer(IPAddress ip, uint16_t port) { - mockServer server; - server.hostname[0] = '\0'; - server.ip = ip; - server.port = port; - mockServers.push_back(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, uint16_t port) { +void EthernetClient_CI::startMockServer(const char *hostname, IPAddress ip, + uint16_t port, const uint8_t *data) { mockServer server; - strncpy(server.hostname, hostname, HOSTNAME_SIZE); - server.ip = {0, 0, 0, 0}; + 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(IPAddress ip, uint16_t port) { +void EthernetClient_CI::stopMockServer(const char *hostname, IPAddress ip, + uint16_t port) { for (int i = (mockServers.size() - 1); i >= 0; --i) { - if (mockServers.at(i).ip == ip && mockServers.at(i).port == 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)) { mockServers.erase(mockServers.begin() + i); } } } -void EthernetClient_CI::stopMockServer(const char *hostname, uint16_t port) { - for (int i = (mockServers.size() - 1); i >= 0; --i) { - if ((strncmp(mockServers.at(i).hostname, hostname, HOSTNAME_SIZE) == 0) && - (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_CI.cpp b/src/EthernetServer_CI.cpp index df272ad1..cbb51f53 100644 --- a/src/EthernetServer_CI.cpp +++ b/src/EthernetServer_CI.cpp @@ -4,33 +4,45 @@ #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() { - // EthernetServer_Base::begin(); _didCallBegin = true; } EthernetClient_CI EthernetServer_CI::available() { - EthernetClient_Base client = EthernetServer_Base::available(); - return EthernetClient_CI(client.getSocketNumber()); + return accept(); } EthernetClient_CI EthernetServer_CI::accept() { - EthernetClient_Base client = EthernetServer_Base::accept(); - return EthernetClient_CI(client.getSocketNumber()); + 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()); - - // Is this necessary? - if (!client) { + if (!client) { // test if client is connected return 0; } - return client.write(buffer,size); + return client.write(buffer, size); } uint8_t EthernetServer_CI::getSocketNumber() const { @@ -42,4 +54,13 @@ uint8_t EthernetServer_CI::getSocketNumber() const { 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.h b/src/Ethernet_CI.h index 3b0c649f..f9f6bb55 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -1,21 +1,12 @@ #pragma once #ifdef MOCK_PINS_COUNT -// 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 -// you are limited to fewer simultaneous connections. -#if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048) -#define MAX_SOCK_NUM 4 -#else -#define MAX_SOCK_NUM 8 -#endif - #include "Client.h" #include "Server.h" #include "Udp.h" #include #include +#include #include #include @@ -32,12 +23,15 @@ 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 { -private: - static IPAddress _mockDHCP; - public: // Initialise the Ethernet shield to use the provided MAC address and // gain the rest of the configuration through DHCP. @@ -71,10 +65,13 @@ class Ethernet_CI : public Ethernet_Base { void setRetransmissionTimeout(uint16_t milliseconds); void setRetransmissionCount(uint8_t num); + static void socketPortRand(uint16_t n); + // testing void mockDHCP(IPAddress ip); -protected: +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); @@ -93,70 +90,71 @@ class Ethernet_CI : public Ethernet_Base { const uint8_t *buf, uint16_t len); static bool socketSendUDP(uint8_t s); -public: - static void socketPortRand(uint16_t n); - friend class EthernetClient_Base; friend class EthernetServer_Base; friend class EthernetUDP; - }; class EthernetClient_CI : public EthernetClient_Base { public: - EthernetClient_CI() : sockindex(MAX_SOCK_NUM), _timeout(1000) {} - EthernetClient_CI(uint8_t s) : sockindex(s), _timeout(1000) {} + EthernetClient_CI(); + EthernetClient_CI(uint8_t s); - uint8_t status() { return _status; } + 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 _readBuffer.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 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) { - return _localPort == rhs.localPort(); + 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; } - virtual std::vector testServers() { return mockServers; } - virtual mockServer serverPeer() { return peer; } friend class EthernetServer; using Print::write; // Testing Support - static void startMockServer(IPAddress ip, uint16_t port); - static void startMockServer(const char *host, uint16_t port); - static void stopMockServer(IPAddress ip, uint16_t port); - static void stopMockServer(const char *host, uint16_t port); - void pushToReadBuffer(uint8_t value) { _readBuffer.push_back(value); } - std::deque writeBuffer() { return _writeBuffer; } + 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 uint16_t nextPort; // 49152 to 65535 + static socket_t _sockets[MAX_SOCK_NUM]; mockServer peer; - uint16_t _localPort = 0; - uint8_t _status = SnSR::CLOSED; - std::deque _readBuffer, _writeBuffer; }; class EthernetServer_CI : public EthernetServer_Base { @@ -164,9 +162,13 @@ class EthernetServer_CI : public EthernetServer_Base { 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_Base(port) { _port = port; } + EthernetServer_CI(uint16_t port); + ~EthernetServer_CI(); EthernetClient_CI available(); EthernetClient_CI accept(); virtual void begin(); @@ -176,6 +178,9 @@ class EthernetServer_CI : public EthernetServer_Base { // 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/test/test.cpp b/test/test.cpp index 454df2da..15bcd204 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -19,8 +19,9 @@ byte serverIP[] = {192, 168, 1, 1}; const char *serverName = "www.google.com"; unittest_teardown() { - EthernetClient::stopMockServer(serverIP, 80); - EthernetClient::stopMockServer(serverName, 80); + EthernetClient::stopMockServer(nullptr, serverIP, 80); + EthernetClient::stopMockServer(serverName, (uint32_t) 0, 80); + EthernetClient::clearMockServers(); } unittest(Ethernet_begin_pins) { @@ -87,7 +88,7 @@ unittest(Client_Connect_IP) { assertEqual(INVALID_SERVER, result); client.stop(); - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); result = client.connect(serverIP, 80); assertEqual(SUCCESS, result); client.stop(); @@ -100,7 +101,7 @@ unittest(Client_Connect_Name) { assertEqual(INVALID_SERVER, result); client.stop(); - EthernetClient::startMockServer(serverName, 80); + EthernetClient::startMockServer(serverName, (uint32_t) 0, 80); result = client.connect(serverName, 80); assertEqual(SUCCESS, result); client.stop(); @@ -109,18 +110,25 @@ unittest(Client_Connect_Name) { unittest(Client_Write) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); + assertEqual(MAX_SOCK_NUM, client.getSockindex()); int result = client.connect(serverIP, 80); - assertEqual(0, client.writeBuffer().size()); + 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)); + assertEqual(5, client.writeBuffer()->size()); + assertEqual('H', client.writeBuffer()->at(0)); + client.stop(); } -unittest(Client_Read) { +unittest(Client_Read_data_given_after_connection) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); assertEqual(SUCCESS, result); assertEqual(-1, client.read()); @@ -128,20 +136,30 @@ unittest(Client_Read) { 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(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); client.stop(); assertEqual(-1, client.read()); - assertEqual(0, client.writeBuffer().size()); - assertEqual(0, client.localPort()); - assertEqual(0, client.serverPeer().ip); assertEqual(0, client.serverPeer().port); } @@ -149,7 +167,7 @@ unittest(Client_Stop) { unittest(Client_AvailableForWrite) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); assertNotEqual(1024 * 1023, client.availableForWrite()); assertEqual(1024 * 1024, client.availableForWrite()); @@ -158,7 +176,7 @@ unittest(Client_AvailableForWrite) { unittest(Client_Peek) { Ethernet.begin(mac, ip); EthernetClient client; - EthernetClient::startMockServer(serverIP, 80); + EthernetClient::startMockServer(nullptr, serverIP, 80); int result = client.connect(serverIP, 80); assertEqual(SUCCESS, result); @@ -173,9 +191,27 @@ unittest(Client_Connected) { EthernetClient client; assertFalse(client.connected()); - EthernetClient::startMockServer(serverIP, 80); + 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() From 1f083fc2218f9a1ce457577bc785b5f8227cce75 Mon Sep 17 00:00:00 2001 From: James Foster Date: Sun, 3 Jul 2022 12:56:26 -0700 Subject: [PATCH 19/21] Merge latest from arduino-libraries (#14) * Keep track of servers. * WIP: Able to control whether accept returns something connected. * Allow for copying of client and keeping read/write buffers available. * Don't clear writeBuffer on close so tests can go back and look at it. * Keep status with socket rather than with client. * Refactor to allow pushing data before connection is established. * formatting * Clear flag to simulate client calling. * Add `pushToReadBuffer()` that accepts a C string. * Merge latest from arduino-libraries * Fix spelling and compile errors. --- .codespellrc | 7 + .github/dependabot.yml | 10 + .github/workflows/check-arduino.yml | 28 + .github/workflows/compile-examples.yml | 111 + .github/workflows/report-size-deltas.yml | 24 + .github/workflows/spell-check.yml | 22 + .github/workflows/sync-labels.yml | 138 + AUTHORS | 6 +- README.adoc | 11 +- docs/api.md | 2607 +++++++++++++++++ docs/arduino_mega_ethernet_pins.png | Bin 0 -> 19276 bytes docs/arduino_uno_ethernet_pins.png | Bin 0 -> 11418 bytes docs/readme.md | 16 + .../AdvancedChatServer/AdvancedChatServer.ino | 8 +- .../BarometricPressureWebServer.ino | 22 +- examples/ChatServer/ChatServer.ino | 13 +- .../DhcpAddressPrinter/DhcpAddressPrinter.ino | 11 +- examples/DhcpChatServer/DhcpChatServer.ino | 13 +- examples/LinkStatus/LinkStatus.ino | 15 +- examples/PagerServer/PagerServer.ino | 71 + examples/TelnetClient/TelnetClient.ino | 20 +- .../UDPSendReceiveString.ino | 15 +- examples/UdpNtpClient/UdpNtpClient.ino | 19 +- examples/WebClient/WebClient.ino | 13 +- .../WebClientRepeating/WebClientRepeating.ino | 26 +- examples/WebServer/WebServer.ino | 17 +- library.properties | 6 +- src/Dns.cpp | 5 +- src/Dns.h | 2 +- src/Ethernet.h | 73 +- src/EthernetClient.cpp | 102 +- src/EthernetUdp.cpp | 3 +- src/EthernetUdp.h | 2 +- src/Ethernet_CI.cpp | 2 +- src/Ethernet_CI.h | 2 +- src/socket.cpp | 1 - src/utility/w5100.cpp | 10 +- src/utility/w5100.h | 11 +- 38 files changed, 3239 insertions(+), 223 deletions(-) create mode 100644 .codespellrc create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/check-arduino.yml create mode 100644 .github/workflows/compile-examples.yml create mode 100644 .github/workflows/report-size-deltas.yml create mode 100644 .github/workflows/spell-check.yml create mode 100644 .github/workflows/sync-labels.yml create mode 100644 docs/api.md create mode 100644 docs/arduino_mega_ethernet_pins.png create mode 100644 docs/arduino_uno_ethernet_pins.png create mode 100644 docs/readme.md create mode 100644 examples/PagerServer/PagerServer.ino diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 00000000..a231f49a --- /dev/null +++ b/.codespellrc @@ -0,0 +1,7 @@ +# See: https://github.com/codespell-project/codespell#using-a-config-file +[codespell] +# In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: +ignore-words-list = nd, +check-filenames = +check-hidden = +skip = ./.git diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..03600dd7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +# See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file +version: 2 + +updates: + # Configure check for outdated GitHub Actions actions in workflows. + # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot + - package-ecosystem: github-actions + directory: / # Check the repository's workflows under /.github/workflows/ + schedule: + interval: daily diff --git a/.github/workflows/check-arduino.yml b/.github/workflows/check-arduino.yml new file mode 100644 index 00000000..3e0d26c9 --- /dev/null +++ b/.github/workflows/check-arduino.yml @@ -0,0 +1,28 @@ +name: Check Arduino + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + pull_request: + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Arduino Lint + uses: arduino/arduino-lint-action@v1 + with: + compliance: specification + library-manager: update + # Always use this setting for official repositories. Remove for 3rd party projects. + official: true + project-type: library diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml new file mode 100644 index 00000000..1e73ea3e --- /dev/null +++ b/.github/workflows/compile-examples.yml @@ -0,0 +1,111 @@ +name: Compile Examples + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/compile-examples.yml" + - "examples/**" + - "src/**" + pull_request: + paths: + - ".github/workflows/compile-examples.yml" + - "examples/**" + - "src/**" + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + build: + name: ${{ matrix.board.fqbn }} + runs-on: ubuntu-latest + + env: + SKETCHES_REPORTS_PATH: sketches-reports + + strategy: + fail-fast: false + + matrix: + board: + - fqbn: arduino:avr:nano + platforms: | + - name: arduino:avr + - fqbn: arduino:avr:mega + platforms: | + - name: arduino:avr + - fqbn: arduino:avr:leonardo + platforms: | + - name: arduino:avr + - fqbn: arduino:megaavr:uno2018 + platforms: | + - name: arduino:megaavr + - fqbn: arduino:megaavr:nona4809 + platforms: | + - name: arduino:megaavr + - fqbn: arduino:sam:arduino_due_x_dbg + platforms: | + - name: arduino:sam + - fqbn: arduino:samd:arduino_zero_edbg + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkr1000 + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrzero + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrwifi1010 + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrfox1200 + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrwan1300 + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrwan1310 + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrgsm1400 + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrnb1500 + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrvidor4000 + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:nano_33_iot + platforms: | + - name: arduino:samd + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Compile examples + uses: arduino/compile-sketches@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fqbn: ${{ matrix.board.fqbn }} + platforms: ${{ matrix.board.platforms }} + libraries: | + # Install the library from the local path. + - source-path: ./ + # Additional library dependencies can be listed here. + # See: https://github.com/arduino/compile-sketches#libraries + sketch-paths: | + - examples + enable-deltas-report: true + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + + - name: Save sketches report as workflow artifact + uses: actions/upload-artifact@v3 + with: + if-no-files-found: error + path: ${{ env.SKETCHES_REPORTS_PATH }} + name: ${{ env.SKETCHES_REPORTS_PATH }} diff --git a/.github/workflows/report-size-deltas.yml b/.github/workflows/report-size-deltas.yml new file mode 100644 index 00000000..652be5d9 --- /dev/null +++ b/.github/workflows/report-size-deltas.yml @@ -0,0 +1,24 @@ +name: Report Size Deltas + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/report-size-deltas.yml" + schedule: + # Run at the minimum interval allowed by GitHub Actions. + # Note: GitHub Actions periodically has outages which result in workflow failures. + # In this event, the workflows will start passing again once the service recovers. + - cron: "*/5 * * * *" + workflow_dispatch: + repository_dispatch: + +jobs: + report: + runs-on: ubuntu-latest + steps: + - name: Comment size deltas reports to PRs + uses: arduino/report-size-deltas@v1 + with: + # The name of the workflow artifact created by the sketch compilation workflow + sketches-reports-source: sketches-reports diff --git a/.github/workflows/spell-check.yml b/.github/workflows/spell-check.yml new file mode 100644 index 00000000..3f6b03fb --- /dev/null +++ b/.github/workflows/spell-check.yml @@ -0,0 +1,22 @@ +name: Spell Check + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + pull_request: + schedule: + # Run every Tuesday at 8 AM UTC to catch new misspelling detections resulting from dictionary updates. + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + spellcheck: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Spell check + uses: codespell-project/actions-codespell@master diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml new file mode 100644 index 00000000..986bda6f --- /dev/null +++ b/.github/workflows/sync-labels.yml @@ -0,0 +1,138 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md +name: Sync Labels + +# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/sync-labels.ya?ml" + - ".github/label-configuration-files/*.ya?ml" + pull_request: + paths: + - ".github/workflows/sync-labels.ya?ml" + - ".github/label-configuration-files/*.ya?ml" + schedule: + # Run daily at 8 AM UTC to sync with changes to shared label configurations. + - cron: "0 8 * * *" + workflow_dispatch: + repository_dispatch: + +env: + CONFIGURATIONS_FOLDER: .github/label-configuration-files + CONFIGURATIONS_ARTIFACT: label-configuration-files + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Download JSON schema for labels configuration file + id: download-schema + uses: carlosperate/download-file-action@v1 + with: + file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json + location: ${{ runner.temp }}/label-configuration-schema + + - name: Install JSON schema validator + run: | + sudo npm install \ + --global \ + ajv-cli \ + ajv-formats + + - name: Validate local labels configuration + run: | + # See: https://github.com/ajv-validator/ajv-cli#readme + ajv validate \ + --all-errors \ + -c ajv-formats \ + -s "${{ steps.download-schema.outputs.file-path }}" \ + -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" + + download: + needs: check + runs-on: ubuntu-latest + + strategy: + matrix: + filename: + # Filenames of the shared configurations to apply to the repository in addition to the local configuration. + # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels + - universal.yml + + steps: + - name: Download + uses: carlosperate/download-file-action@v1 + with: + file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} + + - name: Pass configuration files to next job via workflow artifact + uses: actions/upload-artifact@v3 + with: + path: | + *.yaml + *.yml + if-no-files-found: error + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + + sync: + needs: download + runs-on: ubuntu-latest + + steps: + - name: Set environment variables + run: | + # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable + echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" + + - name: Determine whether to dry run + id: dry-run + if: > + github.event_name == 'pull_request' || + ( + ( + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' + ) && + github.ref != format('refs/heads/{0}', github.event.repository.default_branch) + ) + run: | + # Use of this flag in the github-label-sync command will cause it to only check the validity of the + # configuration. + echo "::set-output name=flag::--dry-run" + + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Download configuration files artifact + uses: actions/download-artifact@v3 + with: + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + path: ${{ env.CONFIGURATIONS_FOLDER }} + + - name: Remove unneeded artifact + uses: geekyeggo/delete-artifact@v1 + with: + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + + - name: Merge label configuration files + run: | + # Merge all configuration files + shopt -s extglob + cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" + + - name: Install github-label-sync + run: sudo npm install --global github-label-sync + + - name: Sync labels + env: + GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # See: https://github.com/Financial-Times/github-label-sync + github-label-sync \ + --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ + ${{ steps.dry-run.outputs.flag }} \ + ${{ github.repository }} diff --git a/AUTHORS b/AUTHORS index 14097dff..1faeec41 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,9 +1,8 @@ - Alberto Panu https://github.com/bigjohnson Alasdair Allan https://github.com/aallan Alice Pintus https://github.com/00alis Adrian McEwen https://github.com/amcewen -Arduino LLC http://arduino.cc/ +Arduino LLC https://arduino.cc/ Arnie97 https://github.com/Arnie97 Arturo Guadalupi https://github.com/agdl Bjoern Hartmann https://people.eecs.berkeley.edu/~bjoern/ @@ -33,6 +32,5 @@ Richard Sim Scott Fitzgerald https://github.com/shfitz Thibaut Viard https://github.com/aethaniel Tom Igoe https://github.com/tigoe -WizNet http://www.wiznet.co.kr +WIZnet http://www.wiznet.co.kr Zach Eveland https://github.com/zeveland - diff --git a/README.adoc b/README.adoc index f2609852..ef47e492 100644 --- a/README.adoc +++ b/README.adoc @@ -1,4 +1,11 @@ -= Ethernet Library for Arduino = +:repository-owner: arduino-libraries +:repository-name: Ethernet + += {repository-name} Library for Arduino = + +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml/badge.svg["Check Arduino status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml"] +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) @@ -6,7 +13,7 @@ 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 -https://www.arduino.cc/en/Reference/Ethernet +https://www.arduino.cc/en/Reference/{repository-name} == License == diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 00000000..76265c82 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,2607 @@ +# Ethernet Library + +## Ethernet Class + +### `Ethernet.begin()` + +#### Description + +Initializes the Ethernet library and network settings. + +With version 1.0, the library supports DHCP. Using Ethernet.begin(mac) with the proper network setup, the Ethernet shield will automatically obtain an IP address. This increases the sketch size significantly. To make sure the DHCP lease is properly renewed when needed, be sure to call Ethernet.maintain() regularly. + + +#### Syntax + +``` +Ethernet.begin(mac); +Ethernet.begin(mac, ip); +Ethernet.begin(mac, ip, dns); +Ethernet.begin(mac, ip, dns, gateway); +Ethernet.begin(mac, ip, dns, gateway, subnet); +``` + +#### Parameters +- mac: the MAC (Media access control) address for the device (array of 6 bytes). this is the Ethernet hardware address of your shield. Newer Arduino Ethernet Shields include a sticker with the device's MAC address. For older shields, choose your own. +- ip: the IP address of the device (array of 4 bytes) +- dns: the IP address of the DNS server (array of 4 bytes). optional: defaults to the device IP address with the last octet set to 1 +- gateway: the IP address of the network gateway (array of 4 bytes). optional: defaults to the device IP address with the last octet set to 1 +- subnet: the subnet mask of the network (array of 4 bytes). optional: defaults to 255.255.255.0 + +#### Returns +- The DHCP version of this function, Ethernet.begin(mac), returns an int: 1 on a successful DHCP connection, 0 on failure. +- The other versions don't return anything. + +#### Example + +``` +#include +#include + +// the media access control (ethernet hardware) address for the shield: +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +//the IP address for the shield: +byte ip[] = { 10, 0, 0, 177 }; + +void setup() +{ + Ethernet.begin(mac, ip); +} + +void loop () {} +``` + +### `Ethernet.dnsServerIP()` + +#### Description + +Returns the DNS server IP address for the device. + + +#### Syntax + +``` +Ethernet.dnsServerIP() + +``` + +#### Parameters +none + +#### Returns +- the DNS server IP address for the device (IPAddress). + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + Ethernet.begin(mac, ip); + + Serial.print("The DNS server IP address is: "); + Serial.println(Ethernet.dnsServerIP()); +} + +void loop () {} +``` + +### `Ethernet.gatewayIP()` + +#### Description + +Returns the gateway IP address for the device. + + +#### Syntax + +``` +Ethernet.gatewayIP() + +``` + +#### Parameters +none + +#### Returns +- the gateway IP address for the device (IPAddress). + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + Ethernet.begin(mac, ip); + + Serial.print("The gateway IP address is: "); + Serial.println(Ethernet.gatewayIP()); +} + +void loop () {} +``` + +### `Ethernet.hardwareStatus()` + +#### Description + +Ethernet.hardwareStatus() tells you which WIZnet Ethernet controller chip was detected during Ethernet.begin(), if any. This can be used for troubleshooting. If no Ethernet controller was detected then there is likely a hardware problem. + + +#### Syntax + +``` +Ethernet.hardwareStatus() + +``` + +#### Parameters +none + +#### Returns +- which WIZnet Ethernet controller chip was detected during Ethernet.begin() (EthernetHardwareStatus): + +``` +EthernetNoHardware +EthernetW5100 +EthernetW5200 +EthernetW5500 +``` + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + Ethernet.begin(mac, ip); + + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet shield was not found."); + } + else if (Ethernet.hardwareStatus() == EthernetW5100) { + Serial.println("W5100 Ethernet controller detected."); + } + else if (Ethernet.hardwareStatus() == EthernetW5200) { + Serial.println("W5200 Ethernet controller detected."); + } + else if (Ethernet.hardwareStatus() == EthernetW5500) { + Serial.println("W5500 Ethernet controller detected."); + } +} + +void loop () {} +``` + +### `Ethernet.init()` + +#### Description + +Used to configure the CS (chip select) pin for the Ethernet controller chip. The Ethernet library has a default CS pin, which is usually correct, but with some non-standard Ethernet hardware you might need to use a different CS pin. + + +#### Syntax + +``` +Ethernet.init(sspin) + +``` + +#### Parameters +- sspin: the pin number to use for CS (byte) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Ethernet.init(53); // use pin 53 for Ethernet CS + Ethernet.begin(mac, ip); +} + +void loop () {} +``` + +### `Ethernet.linkStatus()` + +#### Description + +Tells you whether the link is active. LinkOFF could indicate the Ethernet cable is unplugged or defective. This feature is only available when using the W5200 and W5500 Ethernet controller chips. + + +#### Syntax + +``` +Ethernet.linkStatus() + +``` + +#### Parameters +none + +#### Returns +- the link status (EthernetLinkStatus): + +- Unknown + +- LinkON + +- LinkOFF + +#### Example + +``` +#include +#include + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } +} + +void loop () { + if (Ethernet.linkStatus() == Unknown) { + Serial.println("Link status unknown. Link status detection is only available with W5200 and W5500."); + } + else if (Ethernet.linkStatus() == LinkON) { + Serial.println("Link status: On"); + } + else if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Link status: Off"); + } +} +``` + +### `Ethernet.localIP()` + +#### Description + +Obtains the IP address of the Ethernet shield. Useful when the address is auto assigned through DHCP. + + +#### Syntax + +``` +Ethernet.localIP(); + +``` + +#### Parameters +none + +#### Returns +- the IP address + +#### Example + +``` +#include +#include + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { + 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 }; + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +EthernetClient client; + +void setup() { + // start the serial library: + Serial.begin(9600); + // 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(;;) + ; + } + // print your local IP address: + Serial.println(Ethernet.localIP()); + +} + +void loop() { + +} +``` + +### `Ethernet.MACAddress()` + +#### Description + +Fills the supplied buffer with the MAC address of the device. + + +#### Syntax + +``` +Ethernet.MACAddress(mac_address) + +``` + +#### Parameters +- mac_address: buffer to receive the MAC address (array of 6 bytes) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + Ethernet.begin(mac, ip); + + byte macBuffer[6]; // create a buffer to hold the MAC address + Ethernet.MACAddress(macBuffer); // fill the buffer + Serial.print("The MAC address is: "); + for (byte octet = 0; octet < 6; octet++) { + Serial.print(macBuffer[octet], HEX); + if (octet < 5) { + Serial.print('-'); + } + } +} + +void loop () {} + +``` + +### `Ethernet.maintain()` + +#### Description + +Allows for the renewal of DHCP leases. When assigned an IP address via DHCP, ethernet devices are given a lease on the address for an amount of time. With Ethernet.maintain(), it is possible to request a renewal from the DHCP server. Depending on the server's configuration, you may receive the same address, a new one, or none at all. + +You can call this function as often as you want, it will only re-request a DHCP lease when needed (returning 0 in all other cases). The easiest way is to just call it on every loop() invocation, but less often is also fine. Not calling this function (or calling it significantly less then once per second) will prevent the lease to be renewed when the DHCP protocol requires this, continuing to use the expired lease instead (which will not directly break connectivity, but if the DHCP server leases the same address to someone else, things will likely break). + +Ethernet.maintain() was added to Arduino 1.0.1. + + +#### Syntax + +``` +Ethernet.maintain(); + +``` + +#### Parameters +none + +#### Returns + +byte: + +- 0: nothing happened + +- 1: renew failed + +- 2: renew success + +- 3: rebind fail + +- 4: rebind success + +### `Ethernet.setDnsServerIP()` + +#### Description + +Set the IP address of the DNS server. Not for use with DHCP. + + +#### Syntax + +``` +Ethernet.setDnsServerIP(dns_server) + +``` + +#### Parameters +- dns_server: the IP address of the DNS server (IPAddress) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); +IPAddress myDns(192, 168, 1, 1); + +void setup() { + Ethernet.begin(mac, ip, myDns); + IPAddress newDns(192, 168, 1, 1); + Ethernet.setDnsServerIP(newDns); // change the DNS server IP address +} + +void loop () {} +``` + +### `Ethernet.setGatewayIP()` + +#### Description + +Set the IP address of the network gateway. Not for use with DHCP. + + +#### Syntax + +``` +Ethernet.setGatewayIP(gateway) + +``` + +#### Parameters +- gateway: the IP address of the network gateway (IPAddress) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); +IPAddress myDns(192, 168, 1, 1); +IPAddress gateway(192, 168, 1, 1); + +void setup() { + Ethernet.begin(mac, ip, myDns, gateway); + IPAddress newGateway(192, 168, 100, 1); + Ethernet.setGatewayIP(newGateway); // change the gateway IP address +} + +void loop () {} +``` + +### `Ethernet.setLocalIP()` + +#### Description + +Set the IP address of the device. Not for use with DHCP. + + +#### Syntax + +``` +Ethernet.setLocalIP(local_ip) + +``` + +#### Parameters +- local_ip: the IP address to use (IPAddress) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Ethernet.begin(mac, ip); + IPAddress newIp(10, 0, 0, 178); + Ethernet.setLocalIP(newIp); // change the IP address +} + +void loop () {} +``` + +### `Ethernet.setMACAddress()` + +#### Description + +Set the MAC address. Not for use with DHCP. + + +#### Syntax + +``` +Ethernet.setMACAddress(mac) + +``` + +#### Parameters +- mac: the MAC address to use (array of 6 bytes) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Ethernet.begin(mac, ip); + byte newMac[] = {0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02}; + Ethernet.setMACAddress(newMac); // change the MAC address +} + +void loop () {} +``` + +### `Ethernet.setRetransmissionCount()` + +#### Description + +Set the number of transmission attempts the Ethernet controller will make before giving up. The initial value is 8. 8 transmission attempts times the 200 ms default timeout equals a blocking delay of 1600 ms during a communications failure. You might prefer to set a lower number to make your program more responsive in the event something goes wrong with communications. Despite the name, this sets the total number of transmission attempts (not the number of retries after the first attempt fails) so the minimum value you would ever want to set is 1. + + +#### Syntax + +``` +Ethernet.setRetransmissionCount(number) + +``` + +#### Parameters +- number: number of transmission attempts the Ethernet controller should make before giving up (byte) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Ethernet.begin(mac, ip); + Ethernet.setRetransmissionCount(1); // configure the Ethernet controller to only attempt one transmission before giving up +} + +void loop () {} +``` + +### `Ethernet.setRetransmissionTimeout()` + +#### Description + +Set the Ethernet controller's timeout. The initial value is 200 ms. A 200 ms timeout times the default of 8 attempts equals a blocking delay of 1600 ms during a communications failure. You might prefer to set a shorter timeout to make your program more responsive in the event something goes wrong with communications. You will need to do some experimentation to determine an appropriate value for your specific application. + + +#### Syntax + +``` +Ethernet.setRetransmissionTimeout(milliseconds) + +``` + +#### Parameters +- milliseconds: the timeout duration (uint16_t) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Ethernet.begin(mac, ip); + Ethernet.setRetransmissionTimeout(50); // set the Ethernet controller's timeout to 50 ms +} + +void loop () {} +``` + +### `Ethernet.setSubnetMask()` + +#### Description + +Set the subnet mask of the network. Not for use with DHCP. + + +#### Syntax + +``` +Ethernet.setSubnetMask(subnet) + +``` + +#### Parameters +- subnet: the subnet mask of the network (IPAddress) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); +IPAddress myDns(192, 168, 1, 1); +IPAddress gateway(192, 168, 1, 1); +IPAddress subnet(255, 255, 0, 0); + +void setup() { + Ethernet.begin(mac, ip, myDns, gateway, subnet); + IPAddress newSubnet(255, 255, 255, 0); + Ethernet.setSubnetMask(newSubnet); // change the subnet mask +} + +void loop () {} +``` + +### `Ethernet.subnetMask()` + +#### Description + +Returns the subnet mask of the device. + + +#### Syntax + +``` +Ethernet.subnetMask() + +``` + +#### Parameters +none + +#### Returns +- the subnet mask of the device (IPAddress) + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +void setup() { + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + Ethernet.begin(mac, ip); + + Serial.print("The subnet mask is: "); + Serial.println(Ethernet.subnetMask()); +} + +void loop () {} +``` + +## IPAddress Class + +### `IPAddress()` + +#### Description + +Defines an IP address. It can be used to declare both local and remote addresses. + + +#### Syntax + +``` +IPAddress(address); + +``` + +#### Parameters +- address: a comma delimited list representing the address (4 bytes, ex. 192, 168, 1, 1) + +#### Returns +None + +#### Example + +``` +#include +#include + +// network configuration. dns server, gateway and subnet are optional. + + // the media access control (ethernet hardware) address for the shield: +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// the dns server ip +IPAddress dnServer(192, 168, 0, 1); +// the router's gateway address: +IPAddress gateway(192, 168, 0, 1); +// the subnet: +IPAddress subnet(255, 255, 255, 0); + +//the IP address is dependent on your network +IPAddress ip(192, 168, 0, 2); + +void setup() { + Serial.begin(9600); + + // initialize the ethernet device + Ethernet.begin(mac, ip, dnServer, gateway, subnet); + //print out the IP address + Serial.print("IP = "); + Serial.println(Ethernet.localIP()); +} + +void loop() { +} +``` + +## Server Class + +### `Server` + +#### Description +Server is the base class for all Ethernet server based calls. It is not called directly, but invoked whenever you use a function that relies on it. + +### `EthernetServer()` + +#### Description + +Create a server that listens for incoming connections on the specified port. + + +#### Syntax + +``` +Server(port); + +``` + +#### Parameters +- port: the port to listen on (int) + +#### Returns +None + +#### Example + +``` +#include +#include + +// network configuration. gateway and subnet are optional. + + // the media access control (ethernet hardware) address for the shield: +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +//the IP address for the shield: +byte ip[] = { 10, 0, 0, 177 }; +// the router's gateway address: +byte gateway[] = { 10, 0, 0, 1 }; +// the subnet: +byte subnet[] = { 255, 255, 0, 0 }; + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() +{ + // initialize the ethernet device + Ethernet.begin(mac, ip, gateway, subnet); + + // start listening for clients + server.begin(); +} + +void loop() +{ + // if an incoming client connects, there will be bytes available to read: + EthernetClient client = server.available(); + if (client == true) { + // read bytes from the incoming client and write them back + // to any clients connected to the server: + server.write(client.read()); + } +} +``` + +### `server.begin()` + +#### Description + +Tells the server to begin listening for incoming connections. + + +#### Syntax + +``` +server.begin() + +``` + +#### Parameters +None + +#### Returns +None + +#### Example + +``` +#include +#include + +// the media access control (ethernet hardware) address for the shield: +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +//the IP address for the shield: +byte ip[] = { 10, 0, 0, 177 }; +// the router's gateway address: +byte gateway[] = { 10, 0, 0, 1 }; +// the subnet: +byte subnet[] = { 255, 255, 0, 0 }; + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() +{ + // initialize the ethernet device + Ethernet.begin(mac, ip, gateway, subnet); + + // start listening for clients + server.begin(); +} + +void loop() +{ + // if an incoming client connects, there will be bytes available to read: + EthernetClient client = server.available(); + if (client == true) { + // read bytes from the incoming client and write them back + // to any clients connected to the server: + server.write(client.read()); + } +} +``` + +### `server.accept()` + +#### Description + +The traditional server.available() function would only tell you of a new client after it sent data, which makes some protocols like FTP impossible to properly implement. + +The intention is programs will use either available() or accept(), but not both. With available(), the client connection continues to be managed by EthernetServer. You don’t need to keep a client object, since calling available() will give you whatever client has sent data. Simple servers can be written with very little code using available(). + +With accept(), EthernetServer gives you the client only once, regardless of whether it has sent any data. You must keep track of the connected clients. This requires more code, but you gain more control. + + +#### Syntax + +``` +server.accept() + +``` + +#### Parameters +none + +#### Returns +- a Client object. If no client has data available for reading, this object will evaluate to false in an if-statement. (EthernetClient). + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(192, 168, 69, 104); + +// telnet defaults to port 23 +EthernetServer server(23); + +EthernetClient clients[8]; + +void setup() { + Ethernet.begin(mac, ip); + + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // start listening for clients + server.begin(); +} + +void loop() { + // check for any new client connecting, and say hello (before any incoming data) + EthernetClient newClient = server.accept(); + if (newClient) { + for (byte i = 0; i < 8; i++) { + if (!clients[i]) { + newClient.print("Hello, client number: "); + newClient.println(i); + // Once we "accept", the client is no longer tracked by EthernetServer + // so we must store it into our list of clients + clients[i] = newClient; + break; + } + } + } + + // check for incoming data from all clients + for (byte i = 0; i < 8; i++) { + while (clients[i] && clients[i].available() > 0) { + // read incoming data from the client + Serial.write(clients[i].read()); + } + } + + // stop any clients which disconnect + for (byte i = 0; i < 8; i++) { + if (clients[i] && !clients[i].connected()) { + clients[i].stop(); + } + } +} +``` + +### `server.available()` + +#### Description + +Gets a client that is connected to the server and has data available for reading. The connection persists when the returned client object goes out of scope; you can close it by calling client.stop(). + + +#### Syntax + +``` +server.available() + +``` + +#### Parameters +None + +#### Returns +- a Client object; if no Client has data available for reading, this object will evaluate to false in an if-statement (see the example below) + +#### Example + +``` +#include +#include + +// the media access control (ethernet hardware) address for the shield: +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +//the IP address for the shield: +byte ip[] = { 10, 0, 0, 177 }; +// the router's gateway address: +byte gateway[] = { 10, 0, 0, 1 }; +// the subnet: +byte subnet[] = { 255, 255, 0, 0 }; + + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() +{ + // initialize the ethernet device + Ethernet.begin(mac, ip, gateway, subnet); + + // start listening for clients + server.begin(); +} + +void loop() +{ + // if an incoming client connects, there will be bytes available to read: + EthernetClient client = server.available(); + if (client) { + // read bytes from the incoming client and write them back + // to any clients connected to the server: + server.write(client.read()); + } +} +``` + +### `if(server)` + +#### Description +Indicates whether the server is listening for new clients. You can use this to detect whether server.begin() was successful. It can also tell you when no more sockets are available to listen for more clients, because the maximum number have connected. + + +#### Syntax + +``` +if(server) + +``` + +#### Parameters +none + +#### Returns +- whether the server is listening for new clients (bool). + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // initialize the Ethernet device + Ethernet.begin(mac, ip); + + // start listening for clients + server.begin(); +} + +void loop() { + if (server) { + Serial.println("Server is listening"); + } + else { + Serial.println("Server is not listening"); + } +} +``` + +### `server.write()` + +#### Description + +Write data to all the clients connected to a server. This data is sent as a byte or series of bytes. + + +#### Syntax + +``` +server.write(val) +server.write(buf, len) + +``` + +#### Parameters +- val: a value to send as a single byte (byte or char) + +- buf: an array to send as a series of bytes (byte or char) + +- len: the length of the buffer + +#### Returns +- byte +- write() returns the number of bytes written. It is not necessary to read this. + +#### Example + +``` +#include +#include + +// network configuration. gateway and subnet are optional. + + // the media access control (ethernet hardware) address for the shield: +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +//the IP address for the shield: +byte ip[] = { 10, 0, 0, 177 }; +// the router's gateway address: +byte gateway[] = { 10, 0, 0, 1 }; +// the subnet: +byte subnet[] = { 255, 255, 0, 0 }; + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() +{ + // initialize the ethernet device + Ethernet.begin(mac, ip, gateway, subnet); + + // start listening for clients + server.begin(); +} + +void loop() +{ + // if an incoming client connects, there will be bytes available to read: + EthernetClient client = server.available(); + if (client == true) { + // read bytes from the incoming client and write them back + // to any clients connected to the server: + server.write(client.read()); + } +} +``` + +### `server.print()` + +#### Description + +Print data to all the clients connected to a server. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). + + +#### Syntax + +``` +server.print(data) +server.print(data, BASE) + +``` + +#### Parameters +- data: the data to print (char, byte, int, long, or string) + +- BASE (optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). + +#### Returns +- byte +- print() will return the number of bytes written, though reading that number is optional + + +### `server.println()` + +#### Description + +Print data, followed by a newline, to all the clients connected to a server. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). + + +#### Syntax + +``` +server.println() +server.println(data) +server.println(data, BASE) + +``` + +#### Parameters +- data (optional): the data to print (char, byte, int, long, or string) + +- BASE (optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). + +#### Returns +- byte + +- println() will return the number of bytes written, though reading that number is optional + +## Client Class + +### `Client` + +#### Description +Client is the base class for all Ethernet client based calls. It is not called directly, but invoked whenever you use a function that relies on it. + +### `EthernetClient()` + +#### Description + +Creates a client which can connect to a specified internet IP address and port (defined in the client.connect() function). + + +#### Syntax + +``` +EthernetClient() + +``` + +#### Parameters +None + +#### Example + +``` +#include +#include + + +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +byte ip[] = { 10, 0, 0, 177 }; +byte server[] = { 64, 233, 187, 99 }; // Google + +EthernetClient client; + +void setup() +{ + Ethernet.begin(mac, ip); + Serial.begin(9600); + + delay(1000); + + Serial.println("connecting..."); + + if (client.connect(server, 80)) { + Serial.println("connected"); + client.println("GET /search?q=arduino HTTP/1.0"); + client.println(); + } else { + Serial.println("connection failed"); + } +} + +void loop() +{ + if (client.available()) { + char c = client.read(); + Serial.print(c); + } + + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + for(;;) + ; + } +} +``` + +### `if (EthernetClient)` + +#### Description +Indicates if the specified Ethernet client is ready. + + +#### Syntax + +``` +if (client) + +``` + +#### Parameters +none + +#### Returns +- boolean : returns true if the specified client is available. + +#### Example + +``` +#include +#include + + +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +byte ip[] = { 10, 0, 0, 177 }; +byte server[] = { 64, 233, 187, 99 }; // Google + +EthernetClient client; + +void setup() +{ + Ethernet.begin(mac, ip); + Serial.begin(9600); + + delay(1000); + + Serial.println("connecting..."); + while(!client){ + ; // wait until there is a client connected to proceed + } + if (client.connect(server, 80)) { + Serial.println("connected"); + client.println("GET /search?q=arduino HTTP/1.0"); + client.println(); + } else { + Serial.println("connection failed"); + } +} + +void loop() +{ + if (client.available()) { + char c = client.read(); + Serial.print(c); + } + + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + for(;;) + ; + } +} + +``` + +### `client.connected()` + +#### Description + +Whether or not the client is connected. Note that a client is considered connected if the connection has been closed but there is still unread data. + + +#### Syntax + +``` +client.connected() + +``` + +#### Parameters +none + +#### Returns +- Returns true if the client is connected, false if not. + +#### Example + +``` +#include +#include + +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +byte ip[] = { 10, 0, 0, 177 }; +byte server[] = { 64, 233, 187, 99 }; // Google + +EthernetClient client; + +void setup() +{ + Ethernet.begin(mac, ip); + Serial.begin(9600); + client.connect(server, 80); + delay(1000); + + Serial.println("connecting..."); + + if (client.connected()) { + Serial.println("connected"); + client.println("GET /search?q=arduino HTTP/1.0"); + client.println(); + } else { + Serial.println("connection failed"); + } +} + +void loop() +{ + if (client.available()) { + char c = client.read(); + Serial.print(c); + } + + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + for(;;) + ; + } +} +``` + +### `client.connect()` + +#### Description + +Connects to a specified IP address and port. The return value indicates success or failure. Also supports DNS lookups when using a domain name. + + +#### Syntax + +``` +client.connect() +client.connect(ip, port) +client.connect(URL, port) + +``` + +#### Parameters +- ip: the IP address that the client will connect to (array of 4 bytes) + +- URL: the domain name the client will connect to (string, ex.:"arduino.cc") + +- port: the port that the client will connect to (int) + +#### Returns +- Returns an int (1,-1,-2,-3,-4) indicating connection status : + +- SUCCESS 1 +- TIMED_OUT -1 +- INVALID_SERVER -2 +- TRUNCATED -3 +- INVALID_RESPONSE -4 +#### Example + +``` +#include +#include + +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +byte ip[] = { 10, 0, 0, 177 }; +byte server[] = { 64, 233, 187, 99 }; // Google + +EthernetClient client; + +void setup() +{ + Ethernet.begin(mac, ip); + Serial.begin(9600); + + delay(1000); + + Serial.println("connecting..."); + + if (client.connect(server, 80)) { + Serial.println("connected"); + client.println("GET /search?q=arduino HTTP/1.0"); + client.println(); + } else { + Serial.println("connection failed"); + } +} + +void loop() +{ + if (client.available()) { + char c = client.read(); + Serial.print(c); + } + + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + for(;;) + ; + } +} +``` + +### `client.localPort()` + +#### Description + +Returns the local port number the client is connected to. + + +#### Syntax + +``` +client.localPort + +``` + +#### Parameters +none + +#### Returns +- the local port number the client is connected to (uint16_t). + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // initialize the Ethernet device + Ethernet.begin(mac, ip); + + // start listening for clients + server.begin(); +} + +void loop() { + // if an incoming client connects, there will be bytes available to read: + EthernetClient client = server.available(); + if (client) { + Serial.print("Client is connected on port: "); + Serial.println(client.localPort()); + client.stop(); + } +} +``` + +### `client.remoteIP()` + +#### Description + +Returns the IP address of the client. + + +#### Syntax + +``` +client.remoteIP() + +``` + +#### Parameters +none + +#### Returns +- the client's IP address (IPAddress). + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // initialize the Ethernet device + Ethernet.begin(mac, ip); + + // start listening for clients + server.begin(); +} + +void loop() { + // if an incoming client connects, there will be bytes available to read: + EthernetClient client = server.available(); + if (client) { + Serial.print("Remote IP address: "); + Serial.println(client.remoteIP()); + client.stop(); + } +} +``` + +### `client.remotePort()` + +#### Description + +Returns the port of the host that sent the current incoming packet. + + +#### Syntax + +``` +client.remotePort() + +``` + +#### Parameters +none + +#### Returns +- the port of the host that sent the current incoming packet (uint16_t). + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // initialize the Ethernet device + Ethernet.begin(mac, ip); + + // start listening for clients + server.begin(); +} + +void loop() { + // if an incoming client connects, there will be bytes available to read: + EthernetClient client = server.available(); + if (client) { + Serial.print("Remote port: "); + Serial.println(client.remotePort()); + client.stop(); + } +} +``` + +### `client.setConnectionTimeout()` + +#### Description + +Set the timeout for client.connect() and client.stop(). The initial value is 1000 ms. You might prefer to set a lower timeout value to make your program more responsive in the event something goes wrong. + + +#### Syntax + +``` +client.setConnectionTimeout(milliseconds) + +``` + +#### Parameters +- milliseconds: the timeout duration for client.connect() and client.stop() (uint16_t) + +#### Returns +Nothing + +#### Example + +``` +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress ip(10, 0, 0, 177); + +// telnet defaults to port 23 +EthernetServer server = EthernetServer(23); + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // initialize the Ethernet device + Ethernet.begin(mac, ip); + + // start listening for clients + server.begin(); +} + +void loop() { + // if an incoming client connects, there will be bytes available to read: + EthernetClient client = server.available(); + if (client) { + client.setConnectionTimeout(100); // set the timeout duration for client.connect() and client.stop() + } +} +``` + +### `client.write()` + +#### Description + +Write data to the server the client is connected to. This data is sent as a byte or series of bytes. + + +#### Syntax + +``` +client.write(val) +client.write(buf, len) + +``` + +#### Parameters +- val: a value to send as a single byte (byte or char) + +- buf: an array to send as a series of bytes (byte or char) + +- len: the length of the buffer + +#### Returns +byte: +- write() returns the number of bytes written. It is not necessary to read this value. + +### `print()` + +#### Description + +Print data to the server that a client is connected to. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). + + +#### Syntax + +``` +client.print(data) +client.print(data, BASE) + +``` + +#### Parameters +- data: the data to print (char, byte, int, long, or string) + +- BASE (optional): the base in which to print numbers: DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). +#### Returns +- byte: returns the number of bytes written, though reading that number is optional + +### `client.println()` + +#### Description + +Print data, followed by a carriage return and newline, to the server a client is connected to. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3'). + + +#### Syntax + +``` +client.println() +client.println(data) +client.print(data, BASE) + +``` + +#### Parameters +- data (optional): the data to print (char, byte, int, long, or string) + +- BASE (optional): the base in which to print numbers: DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). + +#### Returns +- byte: return the number of bytes written, though reading that number is optional + + +### `client.available()` + +#### Description + +Returns the number of bytes available for reading (that is, the amount of data that has been written to the client by the server it is connected to). + +available() inherits from the Stream utility class. + + +#### Syntax + +``` +client.available() + +``` + +#### Parameters +none + +#### Returns +- The number of bytes available. + +#### Example + +``` +#include +#include + +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +byte ip[] = { 10, 0, 0, 177 }; +byte server[] = { 64, 233, 187, 99 }; // Google + +EthernetClient client; + +void setup() +{ + Ethernet.begin(mac, ip); + Serial.begin(9600); + + delay(1000); + + Serial.println("connecting..."); + + if (client.connect(server, 80)) { + Serial.println("connected"); + client.println("GET /search?q=arduino HTTP/1.0"); + client.println(); + } else { + Serial.println("connection failed"); + } +} + +void loop() +{ + if (client.available()) { + char c = client.read(); + Serial.print(c); + } + + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + for(;;) + ; + } +} +``` + +### `client.read()` + +#### Description + +Read the next byte received from the server the client is connected to (after the last call to read()). + +read() inherits from the Stream utility class. + + +#### Syntax + +``` +client.read() + +``` + +#### Parameters +none + +#### Returns +- The next byte (or character), or -1 if none is available. + +### `client.flush()` +Waits until all outgoing characters in buffer have been sent. + +flush() inherits from the Stream utility class. + + +#### Syntax + +``` +client.flush() + +``` + +#### Parameters +none + +#### Returns +none + +### `client.stop()` + +#### Description + +Disconnect from the server. + + +#### Syntax + +``` +client.stop() + +``` + +#### Parameters +none + +#### Returns +none + +## EthernetUDP Class + +### `EthernetUDP.begin()` + +#### Description +Initializes the ethernet UDP library and network settings. + + +#### Syntax + +``` +EthernetUDP.begin(localPort); +``` + +#### Parameters +- localPort: the local port to listen on (int) + +#### Returns +- 1 if successful, 0 if there are no sockets available to use. + +#### Example + +``` + +#include + +#include + +#include + + + +// 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 }; + +IPAddress ip(192, 168, 1, 177); + + + +unsigned int localPort = 8888; // local port to listen on + + + +// An EthernetUDP instance to let us send and receive packets over UDP + +EthernetUDP Udp; + + + +void setup() { + + // start the Ethernet and UDP: + + Ethernet.begin(mac,ip); + + Udp.begin(localPort); + + + +} + + + +void loop() { +} + + +``` + +### `EthernetUDP.read()` + +#### Description +Reads UDP data from the specified buffer. If no arguments are given, it will return the next character in the buffer. + +This function can only be successfully called after UDP.parsePacket(). + + +#### Syntax + +``` +EthernetUDP.read(); +EthernetUDP.read(packetBuffer, MaxSize); +``` + +#### Parameters +- packetBuffer: buffer to hold incoming packets (char) +- MaxSize: maximum size of the buffer (int) + +#### Returns +- char : returns the characters in the buffer + +#### Example + +``` +#include +#include +#include + +// 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 }; +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, + +void setup() { + // start the Ethernet and UDP: + Ethernet.begin(mac,ip); + Udp.begin(localPort); + +} + +void loop() { + + int packetSize = Udp.parsePacket(); + if(packetSize) + { + Serial.print("Received packet of size "); + Serial.println(packetSize); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i =0; i < 4; i++) + { + Serial.print(remote[i], DEC); + if (i < 3) + { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); + + // read the packet into packetBuffer + Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE); + Serial.println("Contents:"); + Serial.println(packetBuffer); +} +} +``` + +### `EthernetUDP.write()` + +#### Description +Writes UDP data to the remote connection. Must be wrapped between beginPacket() and endPacket(). beginPacket() initializes the packet of data, it is not sent until endPacket() is called. + + +#### Syntax + +``` +EthernetUDP.write(message); +EthernetUDP.write(buffer, size); + +``` + +#### Parameters + +- message: the outgoing message (char) + +- buffer: an array to send as a series of bytes (byte or char) + +- size: the length of the buffer + +#### Returns +- byte : returns the number of characters sent. This does not have to be read + +#### Example + +``` + + + +#include + +#include + +#include + + + +// 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 }; + +IPAddress ip(192, 168, 1, 177); + + + +unsigned int localPort = 8888; // local port to listen on + + + +// An EthernetUDP instance to let us send and receive packets over UDP + +EthernetUDP Udp; + + + +void setup() { + + // start the Ethernet and UDP: + + Ethernet.begin(mac,ip); + + Udp.begin(localPort); + +} + + + +void loop() { + + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + + Udp.write("hello"); + + Udp.endPacket(); + +} + + +``` + +### `EthernetUDP.beginPacket()` + +#### Description +Starts a connection to write UDP data to the remote connection + + +#### Syntax + +``` +EthernetUDP.beginPacket(remoteIP, remotePort); +``` + +#### Parameters +- remoteIP: the IP address of the remote connection (4 bytes) +- remotePort: the port of the remote connection (int) +#### Returns +- Returns an int: 1 if successful, 0 if there was a problem resolving the hostname or port. + +#### Example + +``` +#include +#include +#include + +// 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 }; +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +void setup() { + // start the Ethernet and UDP: + Ethernet.begin(mac,ip); + Udp.begin(localPort); + +} + +void loop() { + + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + Udp.write("hello"); + Udp.endPacket(); + +} +``` + +### `EthernetUDP.endPacket()` + +#### Description +Called after writing UDP data to the remote connection. + + +#### Syntax + +``` +EthernetUDP.endPacket(); +``` + +#### Parameters +None + +#### Returns +- Returns an int: 1 if the packet was sent successfully, 0 if there was an error + +#### Example + +``` +#include +#include +#include + +// 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 }; +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +void setup() { + // start the Ethernet and UDP: + Ethernet.begin(mac,ip); + Udp.begin(localPort); + +} + +void loop() { + + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + Udp.write("hello"); + Udp.endPacket(); + +} +``` + +### `EthernetUDP.parsePacket()` + +#### Description +Checks for the presence of a UDP packet, and reports the size. parsePacket() must be called before reading the buffer with UDP.read(). + + +#### Syntax + +``` +EthernetUDP.parsePacket(); +``` + +#### Parameters +None + +#### Returns +- int: the size of a received UDP packet + +#### Example + +``` + +#include // needed for Arduino versions later than 0018 +#include +#include // UDP library from: bjoern@cs.stanford.edu 12/30/2008 + + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +void setup() { + // start the Ethernet and UDP: + Ethernet.begin(mac,ip); + Udp.begin(localPort); + + Serial.begin(9600); +} + +void loop() { + // if there's data available, read a packet + int packetSize = Udp.parsePacket(); + if(packetSize) + { + Serial.print("Received packet of size "); + Serial.println(packetSize); + } + delay(10); +} +``` + +### `EthernetUDP.available()` + +#### Description + +Get the number of bytes (characters) available for reading from the buffer. This is data that's already arrived. + +This function can only be successfully called after UDP.parsePacket(). + +available() inherits from the Stream utility class. + + +#### Syntax + +``` +EthernetUDP.available() + +``` + +#### Parameters +None + +#### Returns +- the number of bytes available to read + +#### Example + +``` +#include +#include +#include + +// 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 }; +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, + +void setup() { + // start the Ethernet and UDP: + Ethernet.begin(mac,ip); + Udp.begin(localPort); + +} + +void loop() { + + int packetSize = Udp.parsePacket(); + if(Udp.available()) + { + Serial.print("Received packet of size "); + Serial.println(packetSize); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i =0; i < 4; i++) + { + Serial.print(remote[i], DEC); + if (i < 3) + { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); + + // read the packet into packetBuffer + Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE); + Serial.println("Contents:"); + Serial.println(packetBuffer); + } +} +``` + +## UDP class + +### `UDP.stop()` + +#### Description + +Disconnect from the server. Release any resource being used during the UDP session. + + +#### Syntax + +``` +UDP.stop() + +``` + +#### Parameters +none + +#### Returns +none + +### `UDP.remoteIP()` + +#### Description +Gets the IP address of the remote connection. + +This function must be called after UDP.parsePacket(). + + +#### Syntax + +``` +UDP.remoteIP(); +``` + +#### Parameters +None + +#### Returns +- 4 bytes : the IP address of the remote connection + +#### Example + +``` + +#include +#include +#include + +// 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 }; +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +void setup() { + // start the Ethernet and UDP: + Ethernet.begin(mac,ip); + Udp.begin(localPort); +} + +void loop() { + + int packetSize = Udp.parsePacket(); + if(packetSize) + { + Serial.print("Received packet of size "); + Serial.println(packetSize); + Serial.print("From IP : "); + + IPAddress remote = Udp.remoteIP(); + //print out the remote connection's IP address + Serial.print(remote); + + Serial.print(" on port : "); + //print out the remote connection's port + Serial.println(Udp.remotePort()); + } + +} + + +``` + +### `UDP.remotePort()` + +#### Description +Gets the port of the remote UDP connection. + +This function must be called after UDP.parsePacket(). + + +#### Syntax + +``` +UDP.remotePort(); +``` + +#### Parameters +None + +#### Returns +- int : the port of the UDP connection to a remote host + +#### Example + +``` +#include +#include +#include + +// 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 }; +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, + +void setup() { + // start the Ethernet and UDP: + Ethernet.begin(mac,ip); + Udp.begin(localPort); + +} + +void loop() { + + int packetSize = Udp.parsePacket(); + if(packetSize) + { + Serial.print("Received packet of size "); + Serial.println(packetSize); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i =0; i < 4; i++) + { + Serial.print(remote[i], DEC); + if (i < 3) + { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); + + // read the packet into packetBuffer + Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE); + Serial.println("Contents:"); + Serial.println(packetBuffer); + } +} +``` \ No newline at end of file diff --git a/docs/arduino_mega_ethernet_pins.png b/docs/arduino_mega_ethernet_pins.png new file mode 100644 index 0000000000000000000000000000000000000000..e35edc2c8db2ab7ed4aa17b94854931f86ca525d GIT binary patch literal 19276 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 i&D^-vU=QsfK>j}?Eii=>Xe(9#0000E3^jRp zxIJC<$_oP?J$j_9tSlxbYHF;@&(Ful$LsE5YGtAKCeqW%PQ%JlJvQ97Ak*t!9|L_& z9c{I=)KsO0j)AS+$njZF(%ZYcI}tNWkEsn%R5JOyDi9=6N=iy!Uw?3LP(ngH7y{AK z(povWU@NOnU)mF{Znm zvZJFT1qH>kXU{S-Gp)m;nV6XT{QT6^)ULWgii(Pr<>jD^;)exQo@tqKa&mt!FIiJ^ z-@UOFsc8nqWq?xh#l^+t<5EaTNkM4^Ab$wr;Lx#WSW{C|Qc{wGgClBUR$E*95v=}Q zqSfHA)_^1? z&Fke$!Ng3z;1EzqOlMQf;pNqv#6*J3Vsmrzu|H_s)Phgjf5jUI#f^+J5%V9{HfU4x z=>tP$Wo3;FR6sGQXV*7=LlXuD2B5^OXP^2^O-=o0woD>p?QNcQy_3Vwt$?rp34=Is zI(x>=?}{g+^#LcO1vM6CszH9HvMnQ`qEA&-RcY*9oW_0{_9DpB3!htB8XFty=;#Ou z32JqHWrQ^e2=M2x?q?^v^VmDv+S=ONsHwD#D=8^?Lc^TGBJ`pY9#ymo3JOvc*ZcYU zK6=E4K%LcX9_44gREv3|=Z_=z5E5v~ zm|4V?lx;TngZWiZFxXyR{@z0>+uPgW5Ub)GpTsB!y!cE?7ccCT{Msy|n$jTdnhtLd z163cex|&K-jH{=s>DK9`i=$RSws%DE^U_@3_}EyGZxATE?7jt>H}am}_Z|?~#L)C! z{Qvm^nVu7Hfj~T*I_fHB!7GO)#Kl|a@LyCloLu?Ym2KATdu>5BIvlShehEByd|)2; zC+V>0iNWrU+u4)pY5Tk*GB6vw=+F(a_nOxmM=$Cmfz?mBlUNJeGGH;T{qbYZ(nWEf z@8@e(8KI|0)Yxw3Rit){%LMCG-j^2i)n|8!Y}e48yZO80&|8POBE3twJ0-lmTyuLI z1eUBa0RUow;eeya{-1kVpuZM*Wi7QRd zd^4^F>N7+9YH0ud!TW0|IJQBKM<0_XbTv;`)O|Y9J3_ zHMlq6Uz>Z^D(mu;ga47SZ-Rk+wXWC*zBj1xC(1@xn9tEudNQ#Q(qBww#hOo3Xa)gAN;0byZhNU}Lb%x2Qvgih=gd{>R+3%GfX9qRdU z)5gMRuoE$t7dvKD7RFOKN{f#<9zWfp!sew#bNQ~w^|8V7aWPakXVQKj{SX-%jY?RE z*snN0hfwX$ex z7R(Ozk&clN2;HK*E*m)GyCp~Nb0m$PD0i}>)7J)J-XZ8{ha19NW_Q9mE$AwmXF=ft zKv7M(0p$0D)2vpbDhvw~hv=9Yr9Dw-lW9$&hPji5AB<<7OiTGETJv5$W^%OJ{L9@uSPPH`qz7G_5K z$YLt(SYW#hnCVrO$kT_)!u06jRnf@PndZVBk>*)JeXQ`O&$+Q-?hkGdXNZi{nsh^c1o>EjP zAzGm|idBF?<~s@pAr^hRqz^mKXG-A^!~^w72g)vB8D!ASb%5>mmq-Zo5?w+W$SM3a)0J95aPrncl80LR}7bf(hkcF_ui%D zn|5uiaYdznRH98y=nr(V+0~`TL>-1>6PlOPKdq_Qv)v^@nN`x|gi#1@FvN_-;u(<< zDX1WtE*@1y3^#V%H#oI3{((S$|KlI8|7^m9^(*GNk6Uzb)D-MHr`)n>q9-|4Xr~z4 zE6QtC0;Erbqa&u*-#aavvjWci8?A9dft`U@f!)JQ<*wS_uB6$Y8pR6vqi-ZphQ!56 zy*TJFRjnUB6CW|VuT3+0e3rz81JWmCdy`8aeDf@ibUg_Jp>5T)mc(VGty}TSK;7m3 zb?MOV@n$kKIwV?Xzk2%eM+i&WS+b%cSZ$;UzF}u_KW1wRp_W*7?#&K9P6=lW{;D)#`MdiQ+a<0gt`rhum#`A*>*K@ zBFf~q3fVs)WTkiKq1G&uM!l=KqaDV7Qs9Ew=a0Y z02hmar>+1hyn=hc)(s<9UlRRba~Ox61{RPXxBxK4!25s_ucd=d-|%HOWum#2I3 zb>K=>m1z8}sgmyRU0!tLMUHxeB+(5W5blqlNjLRLoPXn!uEwpqF8yy|AZ=SJ`BgDP zNoTjYJkWk5UCu{I$FKF(zcIg2-fb!VTNjxW9eH=kyKU0t_*eh9V*Bl^V$|YeXR@&D z-9oYg;jQ;cAMI{8yM|PxtSIB7wO8fiUoCIPMDUfUQvbuX`z^3w;`+0Xlx$3w*LK3T zewB_(+FTrver`{}LKqaR^Vv8nc`5N0332W^`_8WBu*3?T+`6*(B!Unjef-LKKJ^qm zV5CrQX&-7D4bL!rBEGg3O8ccu`32nlW!4q`#fOdH#w&kb%3#XAI1C3d`fP zpa0n(Kcoil>QV3Hk5AMjl%Lu@g@Dk1CgYCi(Fek~3c%aL{1@ds{i^ntlO!=b)=VU= zF&PJ5UsrT}D`F#DWI^FXz)Velv+uf8*yLiQ>w162n5OQ}2T-4DQ3@;LBLoXQ zVN>12&Ff{C3QyGY#@@0!cPN3y=X(WvquRU#$)Po?lk>%j5I&n^gaqq3_~(>)NSwwI za`p&$bINJZYj4wV4ki7f;JWbgAke*7XL_Z|>Yo}{n3oL{QOLD%DA`de9|IYGGwOVHIopQwx*f0SQE=xqm+B+Zr51ukr z*qdTejpZ7}J|snRN|bEe zb#;sOz~0)LEjvv-^slt5onKf9PIZ6Tb|f2(tpBi0gxTOos_&bn?PjmWUmFSR8fEw} zkeOoS*t@@6j;ERa^XKDsm*3uSVgoxvLg)y)Whw3#Qyo&V*7#SoF)*taPY_c%8=~yX z8<{yuKKsxT<5(|76KvfcV03{DQ7?xQsi(KNn$|@(D9uz?~yI z`I@m?2Or{EC$r|gS$DkDP-oNc|Kv&Z@qM^&OYWnsN%FDbRa)nYb#B|2FVLpN#zqN? zo`aBB#WV=m9lq*u+IVad0~t}-)wxl==B<|a(T1@Rj&M0Y%HVuDeD==VJL2Gj32;Ee zsHPFz;l&RM7maU7lI%=XN*%73pCT zxa}!y%6|Tv2>R}JvkX-_-ZuGzzXTc3VQKSZIJ7M$^n4}ZAh3iB4T*P6hjF(~dXw87 z^G|Cgp69b&h`U`jtE*7AVZK=VhSRXVTE(U;#RNE&ye(*wQ7{0K`DE4L z&b#~n+##rbJ{_vX{X=WwAYVFJvM4vGm$-h5DIP#|Oq?t)2-X?Y7^Go?+G1~+&qfGP zpe>rZk%y;stl*ymYrnMJg4z)`e(Oa-Yy{B3(WeJ83XkR(++Ds=Ongy_5ceh`OcfN5 z@imkB8$+fxRu@vSna~gEo@$LhioZgl3pz4z6cGI(^vEQG^b$(w2uYD9Hb-guYMW$I zjw*u5yxPQP>uLX#*)e%*e8jX-=H)>LBn09A*gJ9lug6$UBx0Pq$TsIs=rba~a|vk^ zq|eH~J=M1@NmlkU)ZeUCg_YpuR1r*;zXLn9G(%T(7!z5RmfHIz>M?`Ub|omz!3?Kh zdv*nd8iZ9;DJ3}9+{)Th=Ph$l&dKWBp_?RvxF5&)`mc~bb@xb8`Qu%hZkGpjjV}(m zMA{}3SpQa2&3hzw;cz5GP!XPo*ORZQCo}Np_!xM{w)(e8eE3`_6FVb?XK02S+g3R3 znJOk8oi&fkUH&bOivA8Fst{hGN$YLy9p>d@<~5(dM87teP{g1Xxn~g3iIT7NE<}v9 zQ?q#P8H6$doYBS$-cFzH$D<6cPvcK>Np%^XHhmDPC|iJXu2(6B0=@wS4k6nE#ry!a z(X_gNl4~lnj-z^+1U6{P=q$|C+%2?dAzyU$0nRNxyo28@K2d~0 zxR$#2^;y;C`#eDV_Fqis^m*Hf_e1h2c8N#}pWFBZN!>3;nT1}M&f}`E;b2^a!%jz& zuJ`6KE1JGlF<3pRO!`gsT&8l$Wl6&tUv^%*GXpuAGe7oA_!!rQ5zT8QSb&UQ=ZJE~ z@_iqx&lw$`Uum%J=}E=11Yq`_h}u4dK)@lajQb8cLmPnA^An8F!FG2X9ir@z#Ja&yxbSJ3m;fqoEA#meJoTzD#N5Ze4*xgQu~6wo#-)P~Pdoo0PVk3+0g!41 zF965wxsAIF;TX*pk+N{?;o4&a%eNh7!;!n)Id=C8B|g&?B$jOZODY=eO&Mcj zPKBDlZlT~E2IG>+Get%8SedW@Scg+clEK%HJ)pP56cBCet6($xTu3IHQRdI}EP_)q z`nb*=qGa1Mx(GNcZPLE+3 zX6U?pD&;#zrJo)0?dOi;G+08Jc;3asqx>xI5F48o*k{Dp=e>XO9- zoP`y9f<$WeeB+JDH)shlqReNb@b}>}XCap@_<=GiT=-G-*sHdSy}&f7ILiK5f-p;Iqu+;x6fb_WG8l}KCc^8=eiZAVY} z`Pax^^roA7BAhqE3E~acQ=VkcL$P2*bJDizdM>1Pf7HfY2LuD9!IdrHH?8he4VBMk zZTHx;JM2GeUO*enA!Byc$&7u6WGKPSUh-w)zIRHtt}v7Ed)k8}>Cs|C_`hzR+CSPrAJUGH4)Lnl_k1uo ze$Xo@ay2nph9y7w$+|x3QO8zUvj{5~3S)~#J`1d$3?2IgL9eF_c;3RLy57lC z1^j`EJERC&#=wzzeL@rqZAMAoLlV1aiT>3VS~F$Fcc=)Kui1jqbjDNLf$Unh@*D%cWkU3;Im;qJ;xV8#@^Xm+2LN)8yYf}wE1Eff3jBxWI%f?#(7H8oujFD)-qu>oBiNWmFUlL}vj%dqU#h!!w&jyB!X zS;S-tzuH=#SFmx=yc28~Uk775M_Zs4r$z6-g#TRo zZmMsd{`@ylKMn`9?#?=%Q{;A}aRXu1p|(xf-PCyO!G&)FHYi%)Y@uuf*pvEiQ(%G2 zv&11WM|-x_kN$do-Q$9E0s^xqdBd&Rfu#Cy@fYImh{Ni^2{Pp}T3O-p#u+GGB!cHNLNnH*o3#;p}klth1ZnQRl2*J`fA)$pDeEtYnv_G0) za{mY|i|OLad?_Zn?mfw?4B?!e%QVDx?l*7e$0WS@2JJ4cFP*vatPe0XSpLrn$ayAB ztY`e%oW->`%R7h4Ou-0Ibd#J#YnEEP(l+_8`u%u1dr|LdLGbjiwa>yCR`r`pGxNL_|s&f8=_U`^c7W-`bpI!cW>_>xRT}G);Vl25eI#aHR0?uax6- zzAnkyTR|q3v6=Z>zA+OkNz1@)LGiRCKI1oaPOZA)9Fvm8lxq1w^Mq;;pjz_sq{Z{Q zOQ0-rHZ5iO>-5JdlC{tN#<7UgVimUaM8yYo0gG5!7yQloh%s%)x6*2XmDQ$kB`ny) zZ=abZ?BlLf{<=3&SC@m5-e)?1#6YYB+jsXdui+ECwcbPP>wEX*H=uYP=}LL`2QUzM_B+hb%BU+U|x;d2mV!33Kt@;fBfIeG{&@ zBM%SK_Z~P?pyr9zgri#T(MBPO0R*?OIOk}{wg9f}pZwbhet)UEFG5kJ!nxD=afmGr zcaqgV0S?3W=0S`dI5gy1U0-L8nMw`Q8V0ikLg8*zkmKrT^N(BH=Rjz zS4V>QM{Z|}@~yYeBjO;)ZXMzSfBM+OLS^87#AxUX>FHuJeKxzzK3r@!_rCe#Q01>3v)GZ7IhU zNbJ7)Rh&NO`um+EuN8JhWv#W#&XnmTh0o9l$A@aKJnBpF^nYi5yaw1?N(YI`t)Gs% zUUe-@gsCp#rD^reZBo)QW}mw0*Tq&4R5Sx(F>^bf>hq_pSrg#fXOi>b74XNf`9E#eSx9_VufnD!yrJAMuGIIZBt z+&U|@G+Zx*siMFPZqrJc#Qd?pSAbDIc5Bynuc{RH=U$$g+0{q1Ag{_1X(lRC+uB9? zrGuTeYH>D5tf>{v-_`rFAz7)QgSNADmGhia#f?>@h7={9VD2RD8s*YwNkVQBY29f528%WN%Y90h_`M%*1iylWWijxU+J zQOF_*u&H~Br7hZ9Vi-$U1m+7m$eIRTLMTk(bL(*B#Hudr2nIyOJHfSZfU@xQm)1T}3QT)MxG^{!X`k?4J9Y zr!flBBJP6vI9F_{-oKDmXN#g8Dm-oSyAl5Kl^kAHY8}!RVzCHR1g{5G2Vb!H2I0K->^j)f2g$`oKFfI%A5T zl>=zl*4)|qkJEuJb7N*@^Qsq;St)DPIV59|CDuHXHx1mMV*s=zB@G0r> zv?NXAUzJ-88;1cM>Tt1T<$P*W8Z)sL|BSC++I60$T!&6TrR)?zB|ZTW*cF`bHM@s< zDzpt9PiOwngYCt(Uedz`+IY#^b)2Z^m4bP796yA)i9}Oa9^bFRvg<-<6u-~S8GL&H z?8y<7%u7tCa!PNlt^8#C+i8IONUV2{EojDwDf5@m=8`iMX@kZ6fx~jF6M`I+l7d|n zcU(q}oXbL$u~Rps)F!d7-XwFK!3RE75X-15l-Eqv^~~ASQqlW+cWpKTnM;hwb54b= z)XAjkKWcX?>9yY3CaBB(C_CKuJcjD+y9()%4QK8>@B@;Qa2rj-W7*&8inZNDydO!5#36D$}b;aRsESv zAIC{e?ye25=Jaw0FAmt26r+t>ZWzNERLSv;f0jzW*~~g1Bd`ffS z&53g7k41acpS~e`UP;4Mtjiq`6p|Q{if7ki_MTpbNw{crZH_RtcX!C1JyH@MTtAVL zIVjB}Y*U9&GAux?N#TQypPK360$a*PYf}l937+xF>+e9a44ouuylq}V!Pp$D$Y#k-Pi%JJ3)}&=?o*YLpBS&GZnqvh6M6i$%kI=M$`L!z<0iOey^@)2b5iy zfuk8RoFj7`(-*HDKTt17Q|UOGd62SszpP1^5a{*Jsg@X3ySrrL65aXB%8|T>`t;Mt z@x$BkDYqs2Ke+WtRw2u_mvaL>4cDF+#PpJ=M1am~ujE*vm+6nYx)eXrOV(HW78jVA zM86sv9ZIgVvj_5Y@a0za`syyMNKviPjr9Isr)=@8X>j3tD_?nzhEy_^XAKspYt#`+ z6bV8b1*G}CmV()BcFMp9#$nsnlepoJqQ8w4ADbAy3gFx2-~V9t0ALSq)Nmhs;*M7E zjbD#``Xt(zcI|p-@QVU_4g9WBUU}U*b=(4bE45*3U?^|d*weuB+8PdVWEPJ8W_|+aD`QtQ4ZaMphjaUPN{cG-<&ZK9m8K;GaulBstpzq9 zpYH6l-0#Au(M`-qXFu9$m;A%wF!a?I48jE4w^PW8oCRiz$D6-lFItTmTwK3!KbJOD zq2tnaL}SP9ftmt8wcAG5D>H@J7QN+)Ha@w2uWbrBsjjKPFHyg0$pgkEr~i1I-24Xv zNrhMca$8zI3SHKR%o!U?bp71ht5u=1(sSpspsU5wwG56xutPixDCsp`R*M_V-s&<= znFNy9iOUQdO82&2q)PP;@DTN>lhG~sX_w&J(AWVc$WyR5PjmYFe^km4HT+33yWQZD zVXk)q3*`*nZxqOcC!0!-o{KMV%Yd`~k+xJNAk~SP4cIqCU*>#0GNI~tpkp!13ogta z;YxfpZg2m(3r5ZeR6Ti3Kja%#2O|7#{6u6&nZp#J+3)^N|;*46O6re zz)qMNuv9mZ?Kt!U?(iiwGn#Haw@!&gfhP~>>^JvLh{4tDZA<^Wd?RBz_2f;A-%Y8n z+KD9FQODLC+X?(mIIK_+j2tpww_E39;OhKNOOB?Z51MaZD^~0%a>u_x-3)T&U*|{u zR=c}R5x)G4x*$+Qqr$ec2f}>t5ms7~^6M{`;h}`7!9A>n$i1LI?t5q{ghusVY?7bD z@3tJeE+9%j6K{(K0*)c!(l|g}LdlyS9oDY_du)z_P}@zqfVrd|n>hh^YdIgZ-~0qT z7tTFZGWaHwe!(x-N4J?YCQ9|rw`1+~w*eyMfx~t^4B`&c@v7&rN0b3+wf3(e*+ZbU z^Q6;4i&Nxfan3!aqD#Wb_q*9}P(Pt%!`S@YW4l7ECm5MVvo3r!9&ig^uPY}kW}bVp zZQw|fI$1l)lPvzjRm;4+ne*q9Dt$-!=1-r%>?wn0Sqa_x#5a#HH)k~@eZL7)$I`P` zGg$Rlx_#cbpmu6uD5Z$f>j-k;Dk6n1XAvKi5yHe%jzy*DbwBY_wd2(8K6if9P4^t*t{6rmjbLS9&+cJWL=p)l^hV;CE0-?m4$SdFJ# zvBJFS(MU7`b^cJ~{?yH{GN}Aod@;w~oZxRmvg-)$s7o|rmb`aYxlXWlsyD{RiAqgL z`a-G%f5%iUL;R&L@4U>K2#YPA%L`yEv_SFgsIwv&8ej{RPm$!Z9k`Lu?q5v2>bpRP zHlD&FiA!!~AE0xx)=`f{*hs^9IsNB0j=kYU#nk}@vuC$=_95GH*kt)^!P^85OdtF1 zt}Jg)hMko&($%l>t8=-bcrl71qO3ptfsu{R1{%FiB5-gww(Al9^ zKxiokv@WdXZl_2XGhhsmYeXnHmXbi3PjO<;E9>9O3Gjkd-~%$ht#599;d33Aftb5X z$bHy#a51K_1IXb)`NwGb{hC&br11obxPyTZ;%uce_??L24O@iK-QzIE`MKE));Cd? zXNZa)=ZQU-Xzx@u9P8zVEhcyTZj6p&DKqpf#w1Uyn;Q@Qpqrh+*jWDI{br;v;>KHJQf0${)ezWx>j*iq(JkWQrWx9-tQK*)ZBUi)MDh% zGDNkUv*eoCr2E<489h*NlYnd{B^s7SY{unS$^>6; +#include +``` \ No newline at end of file diff --git a/examples/AdvancedChatServer/AdvancedChatServer.ino b/examples/AdvancedChatServer/AdvancedChatServer.ino index 3b431c88..c97a9589 100644 --- a/examples/AdvancedChatServer/AdvancedChatServer.ino +++ b/examples/AdvancedChatServer/AdvancedChatServer.ino @@ -5,7 +5,7 @@ to all connected clients but the client the message comes from. To use, telnet to your device's IP address and type. You can see the client's input in the serial monitor as well. - Using an Arduino Wiznet Ethernet shield. + Using an Arduino WIZnet Ethernet shield. Circuit: * Ethernet shield attached to pins 10, 11, 12, 13 @@ -42,11 +42,11 @@ EthernetClient clients[8]; void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // initialize the Ethernet device Ethernet.begin(mac, ip, myDns, gateway, subnet); diff --git a/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino b/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino index 99840354..831f17fc 100644 --- a/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino +++ b/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino @@ -1,5 +1,5 @@ /* - SCP1000 Barometric Pressure Sensor Display + SCP1000 Barometric Pressure Sensor Display Serves the output of a Barometric Pressure Sensor as a web page. Uses the SPI library. For details on the sensor, see: @@ -60,11 +60,11 @@ long lastReadingTime = 0; void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // start the SPI library: SPI.begin(); @@ -92,7 +92,7 @@ void setup() { // start listening for clients server.begin(); - // initalize the data ready and chip select pins: + // initialize the data ready and chip select pins: pinMode(dataReadyPin, INPUT); pinMode(chipSelectPin, OUTPUT); @@ -104,7 +104,7 @@ void setup() { // give the sensor and Ethernet shield time to set up: delay(1000); - //Set the sensor to high resolution mode tp start readings: + //Set the sensor to high resolution mode to start readings: writeRegister(0x03, 0x0A); } @@ -131,7 +131,7 @@ void getData() { //Read the temperature data int tempData = readRegister(0x21, 2); - // convert the temperature to celsius and display it: + // convert the temperature to Celsius and display it: temperature = (float)tempData / 20.0; //Read the pressure data highest 3 bits: @@ -155,16 +155,16 @@ void listenForEthernetClients() { EthernetClient client = server.available(); if (client) { Serial.println("Got a client"); - // an http request ends with a blank line + // an HTTP request ends with a blank line bool currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); // if you've gotten to the end of the line (received a newline - // character) and the line is blank, the http request has ended, + // character) and the line is blank, the HTTP request has ended, // so you can send a reply if (c == '\n' && currentLineIsBlank) { - // send a standard http response header + // send a standard HTTP response header client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); @@ -216,7 +216,7 @@ void writeRegister(byte registerName, byte registerValue) { //Read register from the SCP1000: unsigned int readRegister(byte registerName, int numBytes) { - byte inByte = 0; // incoming from the SPI read + byte inByte = 0; // incoming from the SPI read unsigned int result = 0; // result to return // SCP1000 expects the register name in the upper 6 bits diff --git a/examples/ChatServer/ChatServer.ino b/examples/ChatServer/ChatServer.ino index 740d7867..3e280607 100644 --- a/examples/ChatServer/ChatServer.ino +++ b/examples/ChatServer/ChatServer.ino @@ -4,7 +4,7 @@ A simple server that distributes any incoming messages to all connected clients. To use, telnet to your device's IP address and type. You can see the client's input in the serial monitor as well. - Using an Arduino Wiznet Ethernet shield. + Using an Arduino WIZnet Ethernet shield. Circuit: * Ethernet shield attached to pins 10, 11, 12, 13 @@ -37,13 +37,13 @@ bool alreadyConnected = false; // whether or not the client was connected previo void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet - // initialize the ethernet device + // initialize the Ethernet device Ethernet.begin(mac, ip, myDns, gateway, subnet); // Open serial communications and wait for port to open: @@ -94,6 +94,3 @@ void loop() { } } } - - - diff --git a/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino b/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino index e40cdfea..612106f9 100644 --- a/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino +++ b/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino @@ -3,7 +3,7 @@ This sketch uses the DHCP extensions to the Ethernet library to get an IP address via DHCP and print the address obtained. - using an Arduino Wiznet Ethernet shield. + using an Arduino WIZnet Ethernet shield. Circuit: Ethernet shield attached to pins 10, 11, 12, 13 @@ -22,17 +22,17 @@ // Enter a MAC address for your controller below. // Newer Ethernet shields have a MAC address printed on a sticker on the shield byte mac[] = { - 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // Open serial communications and wait for port to open: Serial.begin(9600); @@ -92,4 +92,3 @@ void loop() { break; } } - diff --git a/examples/DhcpChatServer/DhcpChatServer.ino b/examples/DhcpChatServer/DhcpChatServer.ino index f4489d61..01cd8140 100644 --- a/examples/DhcpChatServer/DhcpChatServer.ino +++ b/examples/DhcpChatServer/DhcpChatServer.ino @@ -1,10 +1,10 @@ /* - DHCP Chat Server + DHCP Chat Server A simple server that distributes any incoming messages to all connected clients. To use, telnet to your device's IP address and type. You can see the client's input in the serial monitor as well. - Using an Arduino Wiznet Ethernet shield. + Using an Arduino WIZnet Ethernet shield. THis version attempts to get an IP address using DHCP @@ -27,7 +27,7 @@ // The IP address will be dependent on your local network. // gateway and subnet are optional: byte mac[] = { - 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192, 168, 1, 177); IPAddress myDns(192, 168, 1, 1); @@ -41,11 +41,11 @@ bool gotAMessage = false; // whether or not you got a message from the client ye void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // Open serial communications and wait for port to open: Serial.begin(9600); @@ -99,4 +99,3 @@ void loop() { Ethernet.maintain(); } } - diff --git a/examples/LinkStatus/LinkStatus.ino b/examples/LinkStatus/LinkStatus.ino index af62ebbe..84651d09 100644 --- a/examples/LinkStatus/LinkStatus.ino +++ b/examples/LinkStatus/LinkStatus.ino @@ -1,11 +1,12 @@ /* Link Status - This sketch prints the ethernet link status. When the - ethernet cable is connected the link status should go to "ON". - NOTE: Only WizNet W5200 and W5500 are capable of reporting + + This sketch prints the Ethernet link status. When the + Ethernet cable is connected the link status should go to "ON". + NOTE: Only WIZnet W5200 and W5500 are capable of reporting the link status. W5100 will report "Unknown". Hardware: - - Ethernet shield or equivalent board/shield with WizNet 5200/5500 + - Ethernet shield or equivalent board/shield with WIZnet W5200/W5500 Written by Cristian Maglie This example is public domain. */ @@ -16,11 +17,11 @@ void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet Serial.begin(9600); } diff --git a/examples/PagerServer/PagerServer.ino b/examples/PagerServer/PagerServer.ino new file mode 100644 index 00000000..e17ae6ed --- /dev/null +++ b/examples/PagerServer/PagerServer.ino @@ -0,0 +1,71 @@ +/* + Pager Server + + A simple server that echoes any incoming messages to all + connected clients. Connect two or more telnet sessions + to see how server.available() and server.print() works. + + created in September 2020 for the Ethernet library + by Juraj Andrassy https://github.com/jandrassy + +*/ +#include + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// Set the static IP address to use if the DHCP fails to assign +IPAddress ip(192, 168, 0, 177); + +EthernetServer server(2323); + +void setup() { + + Serial.begin(9600); + while (!Serial); + + // start the Ethernet connection: + Serial.println("Initialize Ethernet with DHCP:"); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); + while (true) { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Ethernet cable is not connected."); + } + // try to configure using IP address instead of DHCP: + Ethernet.begin(mac, ip); + } else { + Serial.print(" DHCP assigned IP "); + Serial.println(Ethernet.localIP()); + } + + server.begin(); + + IPAddress ip = Ethernet.localIP(); + Serial.println(); + Serial.print("To access the server, connect with Telnet client to "); + Serial.print(ip); + Serial.println(" 2323"); +} + +void loop() { + + EthernetClient client = server.available(); // returns first client which has data to read or a 'false' client + if (client) { // client is true only if it is connected and has data to read + String s = client.readStringUntil('\n'); // read the message incoming from one of the clients + s.trim(); // trim eventual \r + Serial.println(s); // print the message to Serial Monitor + client.print("echo: "); // this is only for the sending client + server.println(s); // send the message to all connected clients +#ifndef ARDUINO_ARCH_SAM + server.flush(); // flush the buffers +#endif /* !defined(ARDUINO_ARCH_SAM) */ + } +} diff --git a/examples/TelnetClient/TelnetClient.ino b/examples/TelnetClient/TelnetClient.ino index 85386b5f..ff554a5c 100644 --- a/examples/TelnetClient/TelnetClient.ino +++ b/examples/TelnetClient/TelnetClient.ino @@ -1,13 +1,13 @@ /* - Telnet client + Telnet client - This sketch connects to a a telnet server (http://www.google.com) - using an Arduino Wiznet Ethernet shield. You'll need a telnet server + This sketch connects to a telnet server (http://www.google.com) + using an Arduino WIZnet Ethernet shield. You'll need a telnet server to test this with. - Processing's ChatServer example (part of the network library) works well, + Processing's ChatServer example (part of the Network library) works well, running on port 10002. It can be found as part of the examples in the Processing application, available at - http://processing.org/ + https://processing.org/ Circuit: * Ethernet shield attached to pins 10, 11, 12, 13 @@ -39,11 +39,11 @@ EthernetClient client; void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // start the Ethernet connection: Ethernet.begin(mac, ip); @@ -107,7 +107,3 @@ void loop() { } } } - - - - diff --git a/examples/UDPSendReceiveString/UDPSendReceiveString.ino b/examples/UDPSendReceiveString/UDPSendReceiveString.ino index 0c76b022..3995b331 100644 --- a/examples/UDPSendReceiveString/UDPSendReceiveString.ino +++ b/examples/UDPSendReceiveString/UDPSendReceiveString.ino @@ -1,10 +1,11 @@ /* - UDPSendReceiveString: + UDPSendReceiveString + This sketch receives UDP message strings, prints them to the serial port and sends an "acknowledge" string back to the sender A Processing sketch is included at the end of file that can be used to send - and received messages for testing with a computer. + and receive messages for testing with a computer. created 21 Aug 2010 by Michael Margolis @@ -35,11 +36,11 @@ EthernetUDP Udp; void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // start the Ethernet Ethernet.begin(mac, ip); @@ -82,7 +83,7 @@ void loop() { Serial.print(", port "); Serial.println(Udp.remotePort()); - // read the packet into packetBufffer + // read the packet into packetBuffer Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); Serial.println("Contents:"); Serial.println(packetBuffer); @@ -135,5 +136,3 @@ void loop() { println(); } */ - - diff --git a/examples/UdpNtpClient/UdpNtpClient.ino b/examples/UdpNtpClient/UdpNtpClient.ino index ec52717d..1455b40d 100644 --- a/examples/UdpNtpClient/UdpNtpClient.ino +++ b/examples/UdpNtpClient/UdpNtpClient.ino @@ -1,11 +1,10 @@ /* - Udp NTP Client Get the time from a Network Time Protocol (NTP) time server Demonstrates use of UDP sendPacket and ReceivePacket For more on NTP time servers and the messages needed to communicate with them, - see http://en.wikipedia.org/wiki/Network_Time_Protocol + see https://en.wikipedia.org/wiki/Network_Time_Protocol created 4 Sep 2010 by Michael Margolis @@ -42,11 +41,11 @@ EthernetUDP Udp; void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // Open serial communications and wait for port to open: Serial.begin(9600); @@ -144,13 +143,3 @@ void sendNTPpacket(const char * address) { Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); } - - - - - - - - - - diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino index 45e01eed..f4a5d02a 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -1,8 +1,8 @@ /* - Web client + Web client This sketch connects to a website (http://www.google.com) - using an Arduino Wiznet Ethernet shield. + using an Arduino WIZnet Ethernet shield. Circuit: * Ethernet shield attached to pins 10, 11, 12, 13 @@ -43,11 +43,11 @@ bool printWebData = true; // set to false for better speed measurement void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // Open serial communications and wait for port to open: Serial.begin(9600); @@ -69,7 +69,7 @@ void setup() { if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected."); } - // try to congifure using IP address instead of DHCP: + // try to configure using IP address instead of DHCP: Ethernet.begin(mac, ip, myDns); } else { Serial.print(" DHCP assigned IP "); @@ -134,4 +134,3 @@ void loop() { } } } - diff --git a/examples/WebClientRepeating/WebClientRepeating.ino b/examples/WebClientRepeating/WebClientRepeating.ino index 6607f438..1fb11e10 100644 --- a/examples/WebClientRepeating/WebClientRepeating.ino +++ b/examples/WebClientRepeating/WebClientRepeating.ino @@ -1,10 +1,10 @@ /* - Repeating Web client + Repeating Web client - This sketch connects to a a web server and makes a request - using a Wiznet Ethernet shield. You can use the Arduino Ethernet shield, or + This sketch connects to a web server and makes a request + using a WIZnet Ethernet shield. You can use the Arduino Ethernet Shield, or the Adafruit Ethernet shield, either one will work, as long as it's got - a Wiznet Ethernet module on board. + a WIZnet Ethernet module on board. This example uses DNS, by assigning the Ethernet client with a MAC address, IP address, and DNS address. @@ -17,7 +17,7 @@ modified 21 Jan 2014 by Federico Vanzati - http://www.arduino.cc/en/Tutorial/WebClientRepeating + https://www.arduino.cc/en/Tutorial/WebClientRepeating This code is in the public domain. */ @@ -25,7 +25,7 @@ #include #include -// assign a MAC address for the ethernet controller. +// assign a MAC address for the Ethernet controller. // fill in your address here: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED @@ -46,11 +46,11 @@ const unsigned long postingInterval = 10*1000; // delay between updates, in mil void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // start serial port: Serial.begin(9600); @@ -72,7 +72,7 @@ void setup() { if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected."); } - // try to congifure using IP address instead of DHCP: + // try to configure using IP address instead of DHCP: Ethernet.begin(mac, ip, myDns); Serial.print("My IP address: "); Serial.println(Ethernet.localIP()); @@ -104,7 +104,7 @@ void loop() { // this method makes a HTTP connection to the server: void httpRequest() { // close any connection before send a new request. - // This will free the socket on the WiFi shield + // This will free the socket on the Ethernet shield client.stop(); // if there's a successful connection: @@ -124,7 +124,3 @@ void httpRequest() { Serial.println("connection failed"); } } - - - - diff --git a/examples/WebServer/WebServer.ino b/examples/WebServer/WebServer.ino index 06a85576..f3929d2d 100644 --- a/examples/WebServer/WebServer.ino +++ b/examples/WebServer/WebServer.ino @@ -1,8 +1,8 @@ /* - Web Server + Web Server A simple web server that shows the value of the analog input pins. - using an Arduino Wiznet Ethernet shield. + using an Arduino WIZnet Ethernet shield. Circuit: * Ethernet shield attached to pins 10, 11, 12, 13 @@ -35,11 +35,11 @@ EthernetServer server(80); void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields - //Ethernet.init(5); // MKR ETH shield + //Ethernet.init(5); // MKR ETH Shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 - //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet - //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + //Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet + //Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet // Open serial communications and wait for port to open: Serial.begin(9600); @@ -74,17 +74,17 @@ void loop() { EthernetClient client = server.available(); if (client) { Serial.println("new client"); - // an http request ends with a blank line + // an HTTP request ends with a blank line bool 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, + // character) and the line is blank, the HTTP request has ended, // so you can send a reply if (c == '\n' && currentLineIsBlank) { - // send a standard http response header + // send a standard HTTP response header client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); // the connection will be closed after completion of the response @@ -120,4 +120,3 @@ void loop() { Serial.println("client disconnected"); } } - diff --git a/library.properties b/library.properties index de68a94f..ef8982a2 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=Ethernet version=2.0.0 author=Various (see AUTHORS file for details) -maintainer=Paul Stoffregen , Arduino +maintainer=Arduino sentence=Enables network connection (local and Internet) using the Arduino Ethernet Board or Shield. -paragraph=With this library you can use the Arduino Ethernet (shield or board) to connect to Internet. The library provides both Client and server functionalities. The library permits you to connect to a local network also with DHCP and to resolve DNS. +paragraph=With this library you can use the Arduino Ethernet (shield or board) to connect to Internet. The library provides both client and server functionalities. The library permits you to connect to a local network also with DHCP and to resolve DNS. category=Communication -url=http://www.arduino.cc/en/Reference/Ethernet +url=https://www.arduino.cc/en/Reference/Ethernet architectures=* includes=Ethernet.h diff --git a/src/Dns.cpp b/src/Dns.cpp index dfa91125..dca7ce42 100644 --- a/src/Dns.cpp +++ b/src/Dns.cpp @@ -1,4 +1,4 @@ -// Arduino DNS client for WizNet5100-based Ethernet shield +// Arduino DNS client for WIZnet W5100-based Ethernet shield // (c) Copyright 2009-2010 MCQN Ltd. // Released under Apache License, version 2.0 @@ -336,7 +336,7 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) iUdp.flush(); // FIXME return -9;//INVALID_RESPONSE; } - // FIXME: seeems to lock up here on ESP8266, but why?? + // FIXME: seems to lock up here on ESP8266, but why?? iUdp.read(aAddress.raw_address(), 4); return SUCCESS; } else { @@ -351,4 +351,3 @@ uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) // If we get here then we haven't found an answer return -10; //INVALID_RESPONSE; } - diff --git a/src/Dns.h b/src/Dns.h index 5f5b30f5..58f9d2c5 100644 --- a/src/Dns.h +++ b/src/Dns.h @@ -1,4 +1,4 @@ -// Arduino DNS client for WizNet5100-based Ethernet shield +// Arduino DNS client for WIZnet W5100-based Ethernet shield // (c) Copyright 2009-2010 MCQN Ltd. // Released under Apache License, version 2.0 diff --git a/src/Ethernet.h b/src/Ethernet.h index 2a8e91c3..0a2acb8b 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -39,12 +39,12 @@ #define MAX_SOCK_NUM 8 #endif -// By default, each socket uses 2K buffers inside the Wiznet chip. If +// By default, each socket uses 2K buffers inside the WIZnet chip. If // MAX_SOCK_NUM is set to fewer than the chip's maximum, uncommenting -// this will use larger buffers within the Wiznet chip. Large buffers +// this will use larger buffers within the WIZnet chip. Large buffers // can really help with UDP protocols like Artnet. In theory larger // buffers should allow faster TCP over high-latency links, but this -// does not always seem to work in practice (maybe Wiznet bugs?) +// does not always seem to work in practice (maybe WIZnet bugs?) //#define ETHERNET_LARGE_BUFFERS #include @@ -237,42 +237,41 @@ class EthernetUDP : public UDP { class EthernetClient_Base : public Client { public: - EthernetClient_Base() : sockindex(MAX_SOCK_NUM), _timeout(1000) {} - EthernetClient_Base(uint8_t s) : sockindex(s), _timeout(1000) {} - - uint8_t status(); - virtual int connect(IPAddress ip, uint16_t port); - virtual int connect(const char *host, uint16_t port); - virtual int availableForWrite(void); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - virtual int available(); - 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(); - 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_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(); - virtual uint16_t remotePort(); - virtual void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; } - - friend class EthernetServer; - - using Print::write; + 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); + virtual int connect(const char *host, uint16_t port); + virtual int availableForWrite(void); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int available(); + 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(); + 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_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(); + virtual uint16_t remotePort(); + virtual void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; } + + friend class EthernetServer; + + using Print::write; private: - uint8_t sockindex; // MAX_SOCK_NUM means client not in use - uint16_t _timeout; + uint8_t _sockindex; // MAX_SOCK_NUM means client not in use + uint16_t _timeout; }; class EthernetServer_Base : public Server { diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index d6e1f7f9..905ce2ea 100644 --- a/src/EthernetClient.cpp +++ b/src/EthernetClient.cpp @@ -28,11 +28,11 @@ int EthernetClient_Base::connect(const char * host, uint16_t port) DNSClient dns; // Look up the host first IPAddress remote_addr; - if (sockindex < MAX_SOCK_NUM) { - if (Ethernet.socketStatus(sockindex) != SnSR::CLOSED) { - Ethernet.socketDisconnect(sockindex); // TODO: should we call stop()? + if (_sockindex < MAX_SOCK_NUM) { + if (Ethernet.socketStatus(_sockindex) != SnSR::CLOSED) { + Ethernet.socketDisconnect(_sockindex); // TODO: should we call stop()? } - sockindex = MAX_SOCK_NUM; + _sockindex = MAX_SOCK_NUM; } dns.begin(Ethernet.dnsServerIP()); if (!dns.getHostByName(host, remote_addr)) return 0; // TODO: use _timeout @@ -41,38 +41,38 @@ int EthernetClient_Base::connect(const char * host, uint16_t port) int EthernetClient_Base::connect(IPAddress ip, uint16_t port) { - if (sockindex < MAX_SOCK_NUM) { - if (Ethernet.socketStatus(sockindex) != SnSR::CLOSED) { - Ethernet.socketDisconnect(sockindex); // TODO: should we call stop()? + if (_sockindex < MAX_SOCK_NUM) { + if (Ethernet.socketStatus(_sockindex) != SnSR::CLOSED) { + Ethernet.socketDisconnect(_sockindex); // TODO: should we call stop()? } - sockindex = MAX_SOCK_NUM; + _sockindex = MAX_SOCK_NUM; } #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; #endif - sockindex = Ethernet.socketBegin(SnMR::TCP, 0); - if (sockindex >= MAX_SOCK_NUM) return 0; - Ethernet.socketConnect(sockindex, rawIPAddress(ip), port); + _sockindex = Ethernet.socketBegin(SnMR::TCP, 0); + if (_sockindex >= MAX_SOCK_NUM) return 0; + Ethernet.socketConnect(_sockindex, rawIPAddress(ip), port); uint32_t start = millis(); while (1) { - uint8_t stat = Ethernet.socketStatus(sockindex); + uint8_t stat = Ethernet.socketStatus(_sockindex); if (stat == SnSR::ESTABLISHED) return 1; if (stat == SnSR::CLOSE_WAIT) return 1; if (stat == SnSR::CLOSED) return 0; if (millis() - start > _timeout) break; delay(1); } - Ethernet.socketClose(sockindex); - sockindex = MAX_SOCK_NUM; + Ethernet.socketClose(_sockindex); + _sockindex = MAX_SOCK_NUM; return 0; } int EthernetClient_Base::availableForWrite(void) { - if (sockindex >= MAX_SOCK_NUM) return 0; - return Ethernet.socketSendAvailable(sockindex); + if (_sockindex >= MAX_SOCK_NUM) return 0; + return Ethernet.socketSendAvailable(_sockindex); } size_t EthernetClient_Base::write(uint8_t b) @@ -82,97 +82,97 @@ size_t EthernetClient_Base::write(uint8_t b) 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; + if (_sockindex >= MAX_SOCK_NUM) return 0; + if (Ethernet.socketSend(_sockindex, buf, size)) return size; setWriteError(); return 0; } int EthernetClient_Base::available() { - if (sockindex >= MAX_SOCK_NUM) return 0; - return Ethernet.socketRecvAvailable(sockindex); - // TODO: do the Wiznet chips automatically retransmit TCP ACK + if (_sockindex >= MAX_SOCK_NUM) return 0; + return Ethernet.socketRecvAvailable(_sockindex); + // TODO: do the WIZnet chips automatically retransmit TCP ACK // packets if they are lost by the network? Someday this should // be checked by a man-in-the-middle test which discards certain // packets. If ACKs aren't resent, we would need to check for // returning 0 here and after a timeout do another Sock_RECV - // command to cause the Wiznet chip to resend the ACK packet. + // command to cause the WIZnet chip to resend the ACK packet. } int EthernetClient_Base::read(uint8_t *buf, size_t size) { - if (sockindex >= MAX_SOCK_NUM) return 0; - return Ethernet.socketRecv(sockindex, buf, size); + if (_sockindex >= MAX_SOCK_NUM) return 0; + return Ethernet.socketRecv(_sockindex, buf, size); } int EthernetClient_Base::peek() { - if (sockindex >= MAX_SOCK_NUM) return -1; + if (_sockindex >= MAX_SOCK_NUM) return -1; if (!available()) return -1; - return Ethernet.socketPeek(sockindex); + return Ethernet.socketPeek(_sockindex); } int EthernetClient_Base::read() { uint8_t b; - if (Ethernet.socketRecv(sockindex, &b, 1) > 0) return b; + if (Ethernet.socketRecv(_sockindex, &b, 1) > 0) return b; return -1; } void EthernetClient_Base::flush() { - while (sockindex < MAX_SOCK_NUM) { - uint8_t stat = Ethernet.socketStatus(sockindex); + while (_sockindex < MAX_SOCK_NUM) { + uint8_t stat = Ethernet.socketStatus(_sockindex); if (stat != SnSR::ESTABLISHED && stat != SnSR::CLOSE_WAIT) return; - if (Ethernet.socketSendAvailable(sockindex) >= W5100.SSIZE) return; + if (Ethernet.socketSendAvailable(_sockindex) >= W5100.SSIZE) return; } } void EthernetClient_Base::stop() { - if (sockindex >= MAX_SOCK_NUM) return; + if (_sockindex >= MAX_SOCK_NUM) return; // attempt to close the connection gracefully (send a FIN to other side) - Ethernet.socketDisconnect(sockindex); + Ethernet.socketDisconnect(_sockindex); unsigned long start = millis(); // wait up to a second for the connection to close do { - if (Ethernet.socketStatus(sockindex) == SnSR::CLOSED) { - sockindex = MAX_SOCK_NUM; + if (Ethernet.socketStatus(_sockindex) == SnSR::CLOSED) { + _sockindex = MAX_SOCK_NUM; return; // exit the loop } delay(1); } while (millis() - start < _timeout); // if it hasn't closed, close it forcefully - Ethernet.socketClose(sockindex); - sockindex = MAX_SOCK_NUM; + Ethernet.socketClose(_sockindex); + _sockindex = MAX_SOCK_NUM; } uint8_t EthernetClient_Base::connected() { - if (sockindex >= MAX_SOCK_NUM) return 0; + if (_sockindex >= MAX_SOCK_NUM) return 0; - uint8_t s = Ethernet.socketStatus(sockindex); + uint8_t s = Ethernet.socketStatus(_sockindex); return !(s == SnSR::LISTEN || s == SnSR::CLOSED || s == SnSR::FIN_WAIT || (s == SnSR::CLOSE_WAIT && !available())); } uint8_t EthernetClient_Base::status() { - if (sockindex >= MAX_SOCK_NUM) return SnSR::CLOSED; - return Ethernet.socketStatus(sockindex); + if (_sockindex >= MAX_SOCK_NUM) return SnSR::CLOSED; + return Ethernet.socketStatus(_sockindex); } // the next function allows us to use the client returned by // EthernetServer::available() as the condition in an if-statement. bool EthernetClient_Base::operator==(const EthernetClient_Base& rhs) { - if (sockindex != rhs.sockindex) return false; - if (sockindex >= MAX_SOCK_NUM) return false; - if (rhs.sockindex >= MAX_SOCK_NUM) return false; + if (_sockindex != rhs._sockindex) return false; + if (_sockindex >= MAX_SOCK_NUM) return false; + if (rhs._sockindex >= MAX_SOCK_NUM) return false; return true; } @@ -180,22 +180,22 @@ bool EthernetClient_Base::operator==(const EthernetClient_Base& rhs) // from: https://github.com/ntruchsess/Arduino-1/commit/937bce1a0bb2567f6d03b15df79525569377dabd uint16_t EthernetClient_Base::localPort() { - if (sockindex >= MAX_SOCK_NUM) return 0; + if (_sockindex >= MAX_SOCK_NUM) return 0; uint16_t port; SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - port = W5100.readSnPORT(sockindex); + port = W5100.readSnPORT(_sockindex); SPI.endTransaction(); return port; } // https://github.com/per1234/EthernetMod -// returns the remote IP address: http://forum.arduino.cc/index.php?topic=82416.0 +// returns the remote IP address: https://forum.arduino.cc/index.php?topic=82416.0 IPAddress EthernetClient_Base::remoteIP() { - if (sockindex >= MAX_SOCK_NUM) return IPAddress((uint32_t)0); + if (_sockindex >= MAX_SOCK_NUM) return IPAddress((uint32_t)0); uint8_t remoteIParray[4]; SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - W5100.readSnDIPR(sockindex, remoteIParray); + W5100.readSnDIPR(_sockindex, remoteIParray); SPI.endTransaction(); return IPAddress(remoteIParray); } @@ -204,12 +204,10 @@ IPAddress EthernetClient_Base::remoteIP() // from: https://github.com/ntruchsess/Arduino-1/commit/ca37de4ba4ecbdb941f14ac1fe7dd40f3008af75 uint16_t EthernetClient_Base::remotePort() { - if (sockindex >= MAX_SOCK_NUM) return 0; + if (_sockindex >= MAX_SOCK_NUM) return 0; uint16_t port; SPI.beginTransaction(SPI_ETHERNET_SETTINGS); - port = W5100.readSnDPORT(sockindex); + port = W5100.readSnDPORT(_sockindex); SPI.endTransaction(); return port; } - - diff --git a/src/EthernetUdp.cpp b/src/EthernetUdp.cpp index 51669d69..e28791f6 100644 --- a/src/EthernetUdp.cpp +++ b/src/EthernetUdp.cpp @@ -1,5 +1,5 @@ /* - * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. + * Udp.cpp: Library to send/receive UDP packets with the Arduino Ethernet Shield. * This version only offers minimal wrapping of socket.cpp * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ * @@ -188,4 +188,3 @@ uint8_t EthernetUDP::beginMulticast(IPAddress ip, uint16_t port) _remaining = 0; return 1; } - diff --git a/src/EthernetUdp.h b/src/EthernetUdp.h index 2438ec93..16bb062e 100644 --- a/src/EthernetUdp.h +++ b/src/EthernetUdp.h @@ -1,5 +1,5 @@ /* - * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. + * Udp.cpp: Library to send/receive UDP packets with the Arduino Ethernet Shield. * This version only offers minimal wrapping of socket.cpp * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ * diff --git a/src/Ethernet_CI.cpp b/src/Ethernet_CI.cpp index b4e7c598..be785256 100644 --- a/src/Ethernet_CI.cpp +++ b/src/Ethernet_CI.cpp @@ -22,7 +22,7 @@ EthernetHardwareStatus Ethernet_CI::hardwareStatus() { return Ethernet_Base::hardwareStatus(); } -// Manaul configuration +// Manual configuration void Ethernet_CI::begin(uint8_t *mac, IPAddress ip) { return Ethernet_Base::begin(mac, ip); } diff --git a/src/Ethernet_CI.h b/src/Ethernet_CI.h index f9f6bb55..610236b6 100644 --- a/src/Ethernet_CI.h +++ b/src/Ethernet_CI.h @@ -42,7 +42,7 @@ class Ethernet_CI : public Ethernet_Base { static EthernetLinkStatus linkStatus(); static EthernetHardwareStatus hardwareStatus(); - // Manaul configuration + // 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, diff --git a/src/socket.cpp b/src/socket.cpp index bb249d5a..65a11e1e 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -538,4 +538,3 @@ bool EthernetClass::socketSendUDP(uint8_t s) /* Sent ok */ return true; } - diff --git a/src/utility/w5100.cpp b/src/utility/w5100.cpp index 4ae4ee7a..6e7dbd20 100644 --- a/src/utility/w5100.cpp +++ b/src/utility/w5100.cpp @@ -18,13 +18,13 @@ /***************************************************/ // If variant.h or other headers specifically define the -// default SS pin for ethernet, use it. +// default SS pin for Ethernet, use it. #if defined(PIN_SPI_SS_ETHERNET_LIB) #define SS_PIN_DEFAULT PIN_SPI_SS_ETHERNET_LIB // MKR boards default to pin 5 for MKR ETH // Pins 8-10 are MOSI/SCK/MISO on MRK, so don't use pin 10 -#elif defined(USE_ARDUINO_MKR_PIN_LAYOUT) || defined(ARDUINO_SAMD_MKRZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRWAN1300) +#elif defined(USE_ARDUINO_MKR_PIN_LAYOUT) || defined(ARDUINO_SAMD_MKRZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRVIDOR4000) #define SS_PIN_DEFAULT 5 // For boards using AVR, assume shields with SS on pin 10 @@ -132,7 +132,7 @@ uint8_t W5100Class::init(void) writeSnRX_SIZE(i, 0); writeSnTX_SIZE(i, 0); } - // Try W5500 next. Wiznet finally seems to have implemented + // Try W5500 next. WIZnet finally seems to have implemented // SPI well with this chip. It appears to be very resilient, // so try it after the fragile W5200 } else if (isW5500()) { @@ -197,12 +197,12 @@ uint8_t W5100Class::init(void) return 1; // successful init } -// Soft reset the Wiznet chip, by writing to its MR register reset bit +// Soft reset the WIZnet chip, by writing to its MR register reset bit uint8_t W5100Class::softReset(void) { uint16_t count=0; - //Serial.println("Wiznet soft reset"); + //Serial.println("WIZnet soft reset"); // write to reset bit writeMR(0x80); // then wait for soft reset to complete diff --git a/src/utility/w5100.h b/src/utility/w5100.h index 099556a6..b2e8ec83 100644 --- a/src/utility/w5100.h +++ b/src/utility/w5100.h @@ -455,14 +455,23 @@ extern W5100Class W5100; #define UTIL_H #ifndef htons +// The host order of the Arduino platform is little endian. +// Sometimes it is desired to convert to big endian (or +// network order) -#define htons(x) ( (((x)<<8)&0xFF00) | (((x)>>8)&0xFF) ) +// Host to Network short +#define htons(x) ( (((x)&0xFF)<<8) | (((x)>>8)&0xFF) ) + +// Network to Host short #define ntohs(x) htons(x) +// Host to Network long #define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ ((x)<< 8 & 0x00FF0000UL) | \ ((x)>> 8 & 0x0000FF00UL) | \ ((x)>>24 & 0x000000FFUL) ) + +// Network to Host long #define ntohl(x) htonl(x) #endif // !defined(htons) From 89e4416fdb9014f87b63e227e664820d8584eb2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Jul 2022 13:00:49 -0700 Subject: [PATCH 20/21] Bump actions/checkout from 2 to 3 (#15) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/arduino_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/arduino_ci.yml b/.github/workflows/arduino_ci.yml index bc91551f..5a2638cc 100644 --- a/.github/workflows/arduino_ci.yml +++ b/.github/workflows/arduino_ci.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 From b1207fb5f407c1fddf9860843b1a09c35d1785b9 Mon Sep 17 00:00:00 2001 From: James Foster Date: Sun, 3 Jul 2022 13:14:28 -0700 Subject: [PATCH 21/21] Spelling --- src/Ethernet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ethernet.h b/src/Ethernet.h index 0a2acb8b..56aff20b 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -94,7 +94,7 @@ class Ethernet_Base { static EthernetLinkStatus linkStatus(); static EthernetHardwareStatus hardwareStatus(); - // Manaul configuration + // 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,