diff --git a/libraries/Ethernet/README.adoc b/libraries/Ethernet/README.adoc new file mode 100644 index 000000000..1301ec2e9 --- /dev/null +++ b/libraries/Ethernet/README.adoc @@ -0,0 +1,24 @@ += Ethernet Library for Arduino = + +Enable Ethernet connectivity for Arduino Portenta and a shield/carrier with Ethernet connector. + +For more information about this library please visit us at +http://www.arduino.cc/en/Reference/Ethernet + +== License == + +Copyright (c) 2020 Arduino SA. All right reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/libraries/Ethernet/keywords.txt b/libraries/Ethernet/keywords.txt new file mode 100644 index 000000000..9fd25419c --- /dev/null +++ b/libraries/Ethernet/keywords.txt @@ -0,0 +1,67 @@ +####################################### +# Syntax Coloring Map For Ethernet +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Ethernet KEYWORD1 Ethernet +EthernetClient KEYWORD1 EthernetClient +EthernetServer KEYWORD1 EthernetServer +IPAddress KEYWORD1 EthernetIPAddress + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +status KEYWORD2 +connect KEYWORD2 +write KEYWORD2 +available KEYWORD2 +availableForWrite KEYWORD2 +read KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 +stop KEYWORD2 +connected KEYWORD2 +accept KEYWORD2 +begin KEYWORD2 +beginMulticast KEYWORD2 +beginPacket KEYWORD2 +endPacket KEYWORD2 +parsePacket KEYWORD2 +remoteIP KEYWORD2 +remotePort KEYWORD2 +getSocketNumber KEYWORD2 +localIP KEYWORD2 +localPort KEYWORD2 +maintain KEYWORD2 +linkStatus KEYWORD2 +hardwareStatus KEYWORD2 +MACAddress KEYWORD2 +subnetMask KEYWORD2 +gatewayIP KEYWORD2 +dnsServerIP KEYWORD2 +setMACAddress KEYWORD2 +setLocalIP KEYWORD2 +setSubnetMask KEYWORD2 +setGatewayIP KEYWORD2 +setDnsServerIP KEYWORD2 +setRetransmissionTimeout KEYWORD2 +setRetransmissionCount KEYWORD2 +setConnectionTimeout KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +EthernetLinkStatus LITERAL1 +Unknown LITERAL1 +LinkON LITERAL1 +LinkOFF LITERAL1 +EthernetHardwareStatus LITERAL1 +EthernetNoHardware LITERAL1 +EthernetW5100 LITERAL1 +EthernetW5200 LITERAL1 +EthernetW5500 LITERAL1 diff --git a/libraries/Ethernet/library.properties b/libraries/Ethernet/library.properties new file mode 100644 index 000000000..6be67ec42 --- /dev/null +++ b/libraries/Ethernet/library.properties @@ -0,0 +1,10 @@ +name=Ethernet +version=1.0.0 +author=Arduino +maintainer=Arduino +sentence=Enables network connection (local and Internet) using Ethernet on mbed enabled boards +paragraph=With this library you can connect to Internet via Ethernet. 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 +architectures=* +includes=Ethernet.h diff --git a/libraries/Ethernet/src/Ethernet.cpp b/libraries/Ethernet/src/Ethernet.cpp new file mode 100644 index 000000000..98fb03491 --- /dev/null +++ b/libraries/Ethernet/src/Ethernet.cpp @@ -0,0 +1,143 @@ +#include "Ethernet.h" + +#define SSID_MAX_LENGTH 32 + +arduino::IPAddress arduino::EthernetClass::ipAddressFromSocketAddress(SocketAddress socketAddress) { + nsapi_addr_t address = socketAddress.get_addr(); + return IPAddress(address.bytes[0], address.bytes[1], address.bytes[2], address.bytes[3]); +} + +SocketAddress arduino::EthernetClass::socketAddressFromIpAddress(arduino::IPAddress ip, uint16_t port) { + nsapi_addr_t convertedIP = {NSAPI_IPv4, {ip[0], ip[1], ip[2], ip[3]}}; + return SocketAddress(convertedIP, port); +} + +int arduino::EthernetClass::begin(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) { + if (eth_if == nullptr) { + //Q: What is the callback for? + _initializerCallback(); + if(eth_if == nullptr) return 0; + } + + unsigned long start = millis(); + eth_if->set_blocking(false); + nsapi_error_t result = eth_if->connect(); + + while ((millis() - start < timeout) && (linkStatus() != LinkON)) { + delay(10); + } + + return (linkStatus() != LinkON ? 1 : 0); +} + +void arduino::EthernetClass::end() { + disconnect(); +} + +EthernetLinkStatus arduino::EthernetClass::linkStatus() { + return (eth_if->get_connection_status() == NSAPI_STATUS_GLOBAL_UP ? LinkON : LinkOFF); +} + +EthernetHardwareStatus arduino::EthernetClass::hardwareStatus() { + return EthernetMbed; +} + + +int arduino::EthernetClass::disconnect() { + eth_if->disconnect(); +} + +void arduino::EthernetClass::config(arduino::IPAddress local_ip){ + nsapi_addr_t convertedIP = {NSAPI_IPv4, {local_ip[0], local_ip[1], local_ip[2], local_ip[3]}}; + _ip = SocketAddress(convertedIP); +} + +void arduino::EthernetClass::config(const char *local_ip){ + _ip = SocketAddress(local_ip); +} + +void arduino::EthernetClass::config(IPAddress local_ip, IPAddress dns_server){ + config(local_ip); + setDNS(dns_server); +} + +void arduino::EthernetClass::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway){ + config(local_ip, dns_server); + nsapi_addr_t convertedGatewayIP = {NSAPI_IPv4, {gateway[0], gateway[1], gateway[2], gateway[3]}}; + _gateway = SocketAddress(convertedGatewayIP); +} + +void arduino::EthernetClass::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet){ + config(local_ip, dns_server, gateway); + nsapi_addr_t convertedSubnetMask = {NSAPI_IPv4, {subnet[0], subnet[1], subnet[2], subnet[3]}}; + _netmask = SocketAddress(convertedSubnetMask); +} + +void arduino::EthernetClass::setDNS(IPAddress dns_server1){ + nsapi_addr_t convertedDNSServer = {NSAPI_IPv4, {dns_server1[0], dns_server1[1], dns_server1[2], dns_server1[3]}}; + _dnsServer1 = SocketAddress(convertedDNSServer); +} + +void arduino::EthernetClass::setDNS(IPAddress dns_server1, IPAddress dns_server2){ + setDNS(dns_server1); + nsapi_addr_t convertedDNSServer2 = {NSAPI_IPv4, {dns_server2[0], dns_server2[1], dns_server2[2], dns_server2[3]}}; + _dnsServer2 = SocketAddress(convertedDNSServer2); +} + +uint8_t arduino::EthernetClass::status() { + return _currentNetworkStatus; +} + +int arduino::EthernetClass::hostByName(const char* aHostname, IPAddress& aResult){ + SocketAddress socketAddress = SocketAddress(); + nsapi_error_t returnCode = getNetwork()->gethostbyname(aHostname, &socketAddress); + nsapi_addr_t address = socketAddress.get_addr(); + aResult[0] = address.bytes[0]; + aResult[1] = address.bytes[1]; + aResult[2] = address.bytes[2]; + aResult[3] = address.bytes[3]; + return returnCode == NSAPI_ERROR_OK ? 1 : 0; +} + +uint8_t* arduino::EthernetClass::macAddress(uint8_t* mac) { + const char *mac_str = getNetwork()->get_mac_address(); + for( int b = 0; b < 6; b++ ) + { + uint32_t tmp; + sscanf( &mac_str[b * 2 + (b)], "%02x", &tmp) ; + mac[5-b] = (uint8_t)tmp ; + } + //sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &mac[5], &mac[4], &mac[3], &mac[2], &mac[1], &mac[0]); + return mac; +} + +arduino::IPAddress arduino::EthernetClass::localIP() { + SocketAddress ip; + NetworkInterface *interface = getNetwork(); + interface->get_ip_address(&ip); + return ipAddressFromSocketAddress(ip); +} + +arduino::IPAddress arduino::EthernetClass::subnetMask() { + SocketAddress ip; + NetworkInterface *interface = getNetwork(); + interface->get_netmask(&ip); + return ipAddressFromSocketAddress(ip); +} + +arduino::IPAddress arduino::EthernetClass::gatewayIP() { + SocketAddress ip; + NetworkInterface *interface = getNetwork(); + interface->get_gateway(&ip); + return ipAddressFromSocketAddress(ip); +} + +NetworkInterface *arduino::EthernetClass::getNetwork() { + return eth_if; +} + +unsigned long arduino::EthernetClass::getTime() { + return 0; +} + +arduino::EthernetClass Ethernet; diff --git a/libraries/Ethernet/src/Ethernet.h b/libraries/Ethernet/src/Ethernet.h new file mode 100644 index 000000000..f1ca47a19 --- /dev/null +++ b/libraries/Ethernet/src/Ethernet.h @@ -0,0 +1,128 @@ +/* Copyright 2018 Paul Stoffregen + * Copyright 2020 Arduino SA + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ethernet_h_ +#define ethernet_h_ + +#include "Arduino.h" +#include "api/IPAddress.h" +#include "EthernetClient.h" +#include "EthernetServer.h" +#include "EthernetUdp.h" + +#include "netsocket/NetworkInterface.h" +#include "EthernetInterface.h" + +enum EthernetLinkStatus { + Unknown, + LinkON, + LinkOFF +}; + +enum EthernetHardwareStatus { + EthernetNoHardware, + EthernetMbed = 6 +}; + +namespace arduino { + +typedef void* (*voidPrtFuncPtr)(void); + +class EthernetClass { + +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 + EthernetClass(EthernetInterface* _if) : eth_if(_if) {}; + EthernetClass() {}; + + EthernetClass(voidPrtFuncPtr _cb) : _initializerCallback(_cb) {}; + + int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int maintain(); + EthernetLinkStatus linkStatus(); + EthernetHardwareStatus hardwareStatus(); + + // Manaul configuration + void begin(uint8_t *mac, IPAddress ip) {} + void begin(uint8_t *mac, IPAddress ip, IPAddress dns) {} + void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway) {} + void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) {} + void init(uint8_t sspin = 10); + + void MACAddress(uint8_t *mac_address); + uint8_t* macAddress(uint8_t* mac); + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP() { return ipAddressFromSocketAddress(_dnsServer1); } + + void config(IPAddress local_ip); + void config(const char *local_ip); + void config(IPAddress local_ip, IPAddress dns_server); + void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway); + void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); + void setDNS(IPAddress dns_server1); + void setDNS(IPAddress dns_server1, IPAddress dns_server2); + void setHostname(const char* name); + + int disconnect(void); + void end(void); + + uint8_t status(); + int hostByName(const char* aHostname, IPAddress& aResult); + unsigned long getTime(); + + 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) { _dnsServer1 = socketAddressFromIpAddress(dns_server, 0); } + void setRetransmissionTimeout(uint16_t milliseconds); + void setRetransmissionCount(uint8_t num); + + friend class EthernetClient; + friend class EthernetServer; + friend class EthernetUDP; + + NetworkInterface *getNetwork(); + +private: + + volatile EthernetLinkStatus _currentNetworkStatus = Unknown; + EthernetInterface net; + SocketAddress _ip = nullptr; + SocketAddress _gateway = nullptr; + SocketAddress _netmask = nullptr; + SocketAddress _dnsServer1 = nullptr; + SocketAddress _dnsServer2 = nullptr; + EthernetInterface* eth_if = &net; + voidPrtFuncPtr _initializerCallback; + arduino::IPAddress ipAddressFromSocketAddress(SocketAddress socketAddress); + SocketAddress socketAddressFromIpAddress(arduino::IPAddress ip, uint16_t port); +}; + +} + +extern arduino::EthernetClass Ethernet; + +#endif diff --git a/libraries/Ethernet/src/EthernetClient.cpp b/libraries/Ethernet/src/EthernetClient.cpp new file mode 100644 index 000000000..86a2f5432 --- /dev/null +++ b/libraries/Ethernet/src/EthernetClient.cpp @@ -0,0 +1,142 @@ +#include "EthernetClient.h" + +#ifndef SOCKET_TIMEOUT +#define SOCKET_TIMEOUT 1000 +#endif + +arduino::EthernetClient::EthernetClient() { +} + +uint8_t arduino::EthernetClient::status() { + return _status; +} + +void arduino::EthernetClient::getStatus() { + uint8_t data[256]; + int ret = sock->recv(data, rxBuffer.availableForStore()); + for (int i = 0; i < ret; i++) { + rxBuffer.store_char(data[i]); + } + if (ret < 0 && ret != NSAPI_ERROR_WOULD_BLOCK) { + _status = LinkOFF; + } +} + +int arduino::EthernetClient::connect(SocketAddress socketAddress) { + if (sock == NULL) { + sock = new TCPSocket(); + if(static_cast(sock)->open(Ethernet.getNetwork()) != NSAPI_ERROR_OK){ + return 0; + } + } + //sock->sigio(mbed::callback(this, &EthernetClient::getStatus)); + //sock->set_blocking(false); + sock->set_timeout(SOCKET_TIMEOUT); + nsapi_error_t returnCode = static_cast(sock)->connect(socketAddress); + return returnCode == NSAPI_ERROR_OK ? 1 : 0; +} + +int arduino::EthernetClient::connect(IPAddress ip, uint16_t port) { + return connect(Ethernet.socketAddressFromIpAddress(ip, port)); +} + +int arduino::EthernetClient::connect(const char *host, uint16_t port) { + SocketAddress socketAddress = SocketAddress(); + socketAddress.set_port(port); + Ethernet.getNetwork()->gethostbyname(host, &socketAddress); + return connect(socketAddress); +} + +int arduino::EthernetClient::connectSSL(SocketAddress socketAddress){ + if (sock == NULL) { + sock = new TLSSocket(); + if(static_cast(sock)->open(Ethernet.getNetwork()) != NSAPI_ERROR_OK){ + return 0; + } + } + if (beforeConnect) { + beforeConnect(); + } + sock->set_timeout(SOCKET_TIMEOUT); + nsapi_error_t returnCode = static_cast(sock)->connect(socketAddress); + return returnCode == NSAPI_ERROR_OK ? 1 : 0; +} + +int arduino::EthernetClient::connectSSL(IPAddress ip, uint16_t port) { + return connectSSL(Ethernet.socketAddressFromIpAddress(ip, port)); +} + +int arduino::EthernetClient::connectSSL(const char *host, uint16_t port) { + SocketAddress socketAddress = SocketAddress(); + socketAddress.set_port(port); + Ethernet.getNetwork()->gethostbyname(host, &socketAddress); + return connectSSL(socketAddress); +} + +size_t arduino::EthernetClient::write(uint8_t c) { + sock->send(&c, 1); +} + +size_t arduino::EthernetClient::write(const uint8_t *buf, size_t size) { + sock->send(buf, size); +} + +int arduino::EthernetClient::available() { + if (rxBuffer.available() == 0) { + getStatus(); + } + return rxBuffer.available(); +} + +int arduino::EthernetClient::read() { + if (!available()) { + return -1; + } + + return rxBuffer.read_char(); +} + +int arduino::EthernetClient::read(uint8_t *data, size_t len) { + int avail = available(); + + if (!avail) { + return -1; + } + + if ((int)len > avail) { + len = avail; + } + + for (size_t i = 0; i < len; i++) { + data[i] = rxBuffer.read_char(); + } + + return len; +} + +int arduino::EthernetClient::peek() { + return rxBuffer.peek(); +} + +void arduino::EthernetClient::flush() { + +} + +void arduino::EthernetClient::stop() { + if (sock != NULL) { + sock->close(); + sock = NULL; + } +} + +uint8_t arduino::EthernetClient::connected() { + return _status != LinkOFF; +} + +IPAddress arduino::EthernetClient::remoteIP() { + return IPAddress((uint32_t)0); +} + +uint16_t arduino::EthernetClient::remotePort() { + return 0; +} diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h new file mode 100644 index 000000000..71dd98bd2 --- /dev/null +++ b/libraries/Ethernet/src/EthernetClient.h @@ -0,0 +1,89 @@ +/* + EthernetClient.h + Copyright (c) 2021 Arduino SA. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ethernetclient_h +#define ethernetclient_h + +#include "Ethernet.h" +#include "api/Print.h" +#include "api/Client.h" +#include "api/IPAddress.h" +#include "TLSSocket.h" +#include "TCPSocket.h" + +namespace arduino { + +class EthernetClient : public arduino::Client { + +public: + EthernetClient(); + ~EthernetClient() { + stop(); + } + + uint8_t status(); + int connect(SocketAddress socketAddress); + int connect(IPAddress ip, uint16_t port); + int connect(const char *host, uint16_t port); + int connectSSL(SocketAddress socketAddress); + int connectSSL(IPAddress ip, uint16_t port); + int connectSSL(const char *host, uint16_t port); + size_t write(uint8_t); + size_t write(const uint8_t *buf, size_t size); + int available(); + int read(); + int read(uint8_t *buf, size_t size); + int peek(); + void flush(); + void stop(); + uint8_t connected(); + operator bool() { + return sock != NULL; + } + + void setSocket(Socket* _sock) { + sock = _sock; + } + + IPAddress remoteIP(); + uint16_t remotePort(); + + friend class EthernetServer; + + using Print::write; + +protected: + + void onBeforeConnect(mbed::Callback cb) { + beforeConnect = cb; + } + +private: + static uint16_t _srcport; + Socket* sock; + RingBufferN<256> rxBuffer; + uint8_t _status; + mbed::Callback beforeConnect; + + void getStatus(); +}; + +} + +#endif \ No newline at end of file diff --git a/libraries/Ethernet/src/EthernetServer.cpp b/libraries/Ethernet/src/EthernetServer.cpp new file mode 100644 index 000000000..1e3e260c6 --- /dev/null +++ b/libraries/Ethernet/src/EthernetServer.cpp @@ -0,0 +1,40 @@ +#include "EthernetServer.h" +#include "EthernetClient.h" + +extern arduino::EthernetClass Ethernet; + +arduino::EthernetServer::EthernetServer(uint16_t port) { + _port = port; +} + +uint8_t arduino::EthernetServer::status() { + return 0; +} + +void arduino::EthernetServer::begin() { + if (sock == NULL) { + sock = new TCPSocket(); + ((TCPSocket*)sock)->open(Ethernet.getNetwork()); + } + sock->bind(_port); + sock->listen(5); +} + +size_t arduino::EthernetServer::write(uint8_t c) { + sock->send(&c, 1); +} + +size_t arduino::EthernetServer::write(const uint8_t *buf, size_t size) { + sock->send(buf, size); +} + +arduino::EthernetClient arduino::EthernetServer::available(uint8_t* status) { + EthernetClient client; + nsapi_error_t error; + TCPSocket* clientSocket = sock->accept(&error); + if(status != nullptr) { + *status = error == NSAPI_ERROR_OK ? 1 : 0; + } + client.setSocket(clientSocket); + return client; +} diff --git a/libraries/Ethernet/src/EthernetServer.h b/libraries/Ethernet/src/EthernetServer.h new file mode 100644 index 000000000..1920ded20 --- /dev/null +++ b/libraries/Ethernet/src/EthernetServer.h @@ -0,0 +1,48 @@ +/* + EthernetServer.h + Copyright (c) 2020 Arduino SA. All right reserved. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ethernetserver_h +#define ethernetserver_h + +#include "Ethernet.h" +#include "api/Print.h" +#include "api/Client.h" +#include "api/IPAddress.h" +#include "TLSSocket.h" +#include "TCPSocket.h" + +namespace arduino { + +class EthernetClient; + +class EthernetServer : public arduino::Server { +private: + uint16_t _port; + TCPSocket* sock; +public: + EthernetServer(uint16_t); + arduino::EthernetClient available(uint8_t* status = NULL); + void begin(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + uint8_t status(); + + using Print::write; +}; + +} + +#endif \ No newline at end of file diff --git a/libraries/Ethernet/src/EthernetUdp.cpp b/libraries/Ethernet/src/EthernetUdp.cpp new file mode 100644 index 000000000..603163c53 --- /dev/null +++ b/libraries/Ethernet/src/EthernetUdp.cpp @@ -0,0 +1,201 @@ +#include "EthernetUdp.h" + +extern arduino::EthernetClass WiFi; + +#ifndef ETHERNET_UDP_BUFFER_SIZE +#define ETHERNET_UDP_BUFFER_SIZE 508 +#endif + +arduino::EthernetUDP::EthernetUDP() { + _packet_buffer = new uint8_t[ETHERNET_UDP_BUFFER_SIZE]; + _current_packet = NULL; + _current_packet_size = 0; + // if this allocation fails then ::begin will fail +} + +arduino::EthernetUDP::~EthernetUDP() { + delete[] _packet_buffer; +} + +uint8_t arduino::EthernetUDP::begin(uint16_t port) { + // success = 1, fail = 0 + + nsapi_error_t rt = _socket.open(WiFi.getNetwork()); + if (rt != NSAPI_ERROR_OK) { + return 0; + } + + if (_socket.bind(port) < 0) { + return 0; //Failed to bind UDP Socket to port + } + + if (!_packet_buffer) { + return 0; + } + + _socket.set_timeout(1000); + + return 1; +} + +uint8_t arduino::EthernetUDP::beginMulticast(IPAddress ip, uint16_t port) { + // success = 1, fail = 0 + if(begin(port) != 1){ + return 0; + } + + SocketAddress socketAddress = WiFi.socketAddressFromIpAddress(ip, port); + + if (_socket.join_multicast_group(socketAddress) != NSAPI_ERROR_OK) { + printf("Error joining the multicast group\n"); + return 0; + } + + return 1; +} + +void arduino::EthernetUDP::stop() { + _socket.close(); +} + +int arduino::EthernetUDP::beginPacket(IPAddress ip, uint16_t port) { + _host = WiFi.socketAddressFromIpAddress(ip, port); + //If IP is null and port is 0 the initialization failed + return (_host.get_ip_address() == nullptr && _host.get_port() == 0) ? 0 : 1; +} + +int arduino::EthernetUDP::beginPacket(const char *host, uint16_t port) { + _host = SocketAddress(host, port); + Ethernet.getNetwork()->gethostbyname(host, &_host); + //If IP is null and port is 0 the initialization failed + return (_host.get_ip_address() == nullptr && _host.get_port() == 0) ? 0 : 1; +} + +int arduino::EthernetUDP::endPacket() { + return 1; +} + +// Write a single byte into the packet +size_t arduino::EthernetUDP::write(uint8_t byte) { + uint8_t buffer[1] = { byte }; + return _socket.sendto(_host, buffer, 1); +} + +// Write size bytes from buffer into the packet +size_t arduino::EthernetUDP::write(const uint8_t *buffer, size_t size) { + return _socket.sendto(_host, buffer, size); +} + +int arduino::EthernetUDP::parsePacket() { + nsapi_size_or_error_t ret = _socket.recvfrom(&_remoteHost, _packet_buffer, ETHERNET_UDP_BUFFER_SIZE); + + if (ret == NSAPI_ERROR_WOULD_BLOCK) { + // no data + return 0; + } else if(ret == NSAPI_ERROR_NO_SOCKET){ + // socket was not created correctly. + return -1; + } + // error codes below zero are errors + else if (ret <= 0) { + // something else went wrong, need some tracing info... + return -1; + } + + // set current packet states + _current_packet = _packet_buffer; + _current_packet_size = ret; + + return _current_packet_size; +} + +int arduino::EthernetUDP::available() { + return _current_packet_size; +} + +// Read a single byte from the current packet +int arduino::EthernetUDP::read() { + // no current packet... + if (_current_packet == NULL) { + // try reading the next frame, if there is no data return + if (parsePacket() == 0) return -1; + } + + _current_packet++; + + // check for overflow + if (_current_packet > _packet_buffer + _current_packet_size) { + // try reading the next packet... + if (parsePacket() > 0) { + // if so, read first byte of next packet; + return read(); + } + else { + // no new data... not sure what to return here now + return -1; + } + } + + return _current_packet[0]; +} + +// 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 +int arduino::EthernetUDP::read(unsigned char* buffer, size_t len) { + // Q: does Arduino read() function handle fragmentation? I won't for now... + if (_current_packet == NULL) { + if (parsePacket() == 0) return 0; + } + + // how much data do we have in the current packet? + int offset = _current_packet - _packet_buffer; + if (offset < 0) { + return 0; + } + + int max_bytes = _current_packet_size - offset; + if (max_bytes < 0) { + return 0; + } + + // at the end of the packet? + if (max_bytes == 0) { + // try read next packet... + if (parsePacket() > 0) { + return read(buffer, len); + } + else { + return 0; + } + } + + if (len > max_bytes) len = max_bytes; + + // copy to target buffer + memcpy(buffer, _current_packet, len); + + _current_packet += len; + + return len; +} + +IPAddress arduino::EthernetUDP::remoteIP() { + nsapi_addr_t address = _remoteHost.get_addr(); + return IPAddress(address.bytes[0], address.bytes[1], address.bytes[2], address.bytes[3]); +} + +uint16_t arduino::EthernetUDP::remotePort() { + return _remoteHost.get_port(); +} + +void arduino::EthernetUDP::flush(){ + // TODO: a real check to ensure transmission has been completed +} + +int arduino::EthernetUDP::peek(){ + if (_current_packet_size < 1){ + return -1; + } + + return _current_packet[0]; +} \ No newline at end of file diff --git a/libraries/Ethernet/src/EthernetUdp.h b/libraries/Ethernet/src/EthernetUdp.h new file mode 100644 index 000000000..73efa550d --- /dev/null +++ b/libraries/Ethernet/src/EthernetUdp.h @@ -0,0 +1,96 @@ +/* + EthernetUdp.h + Copyright (c) 2020 Arduino SA. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ethernetudp_h +#define ethernetudp_h + +#include "Ethernet.h" +#include "api/Udp.h" + +#include "netsocket/SocketAddress.h" +#include "netsocket/UDPSocket.h" + +#define UDP_TX_PACKET_MAX_SIZE 24 + +namespace arduino { + +class EthernetUDP : public UDP { +private: + UDPSocket _socket; // Mbed OS socket + SocketAddress _host; // Host to be used to send data + SocketAddress _remoteHost; // Remote host that sent incoming packets + + uint8_t* _packet_buffer; // Raw packet buffer (contains data we got from the UDPSocket) + + // The Arduino APIs allow you to iterate through this buffer, so we need to be able to iterate over the current packet + // these two variables are used to cache the state of the current packet + uint8_t* _current_packet; + size_t _current_packet_size; + +public: + EthernetUDP(); // Constructor + ~EthernetUDP(); + 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 multicast IP address and 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 the port of the host who sent the current incoming packet + virtual uint16_t remotePort(); +}; + +} + +#endif \ No newline at end of file