Skip to content

Initial: porting Ethernet library #114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 8, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions libraries/Ethernet/README.adoc
Original file line number Diff line number Diff line change
@@ -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
67 changes: 67 additions & 0 deletions libraries/Ethernet/keywords.txt
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions libraries/Ethernet/library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name=Ethernet
version=1.0.0
author=Arduino
maintainer=Arduino <[email protected]>
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
143 changes: 143 additions & 0 deletions libraries/Ethernet/src/Ethernet.cpp
Original file line number Diff line number Diff line change
@@ -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;
128 changes: 128 additions & 0 deletions libraries/Ethernet/src/Ethernet.h
Original file line number Diff line number Diff line change
@@ -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
142 changes: 142 additions & 0 deletions libraries/Ethernet/src/EthernetClient.cpp
Original file line number Diff line number Diff line change
@@ -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<TCPSocket*>(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<TCPSocket*>(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<TLSSocket*>(sock)->open(Ethernet.getNetwork()) != NSAPI_ERROR_OK){
return 0;
}
}
if (beforeConnect) {
beforeConnect();
}
sock->set_timeout(SOCKET_TIMEOUT);
nsapi_error_t returnCode = static_cast<TLSSocket*>(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;
}
89 changes: 89 additions & 0 deletions libraries/Ethernet/src/EthernetClient.h
Original file line number Diff line number Diff line change
@@ -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<int(void)> cb) {
beforeConnect = cb;
}

private:
static uint16_t _srcport;
Socket* sock;
RingBufferN<256> rxBuffer;
uint8_t _status;
mbed::Callback<int(void)> beforeConnect;

void getStatus();
};

}

#endif
40 changes: 40 additions & 0 deletions libraries/Ethernet/src/EthernetServer.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
48 changes: 48 additions & 0 deletions libraries/Ethernet/src/EthernetServer.h
Original file line number Diff line number Diff line change
@@ -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
201 changes: 201 additions & 0 deletions libraries/Ethernet/src/EthernetUdp.cpp
Original file line number Diff line number Diff line change
@@ -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];
}
96 changes: 96 additions & 0 deletions libraries/Ethernet/src/EthernetUdp.h
Original file line number Diff line number Diff line change
@@ -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