Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.
Dependents: mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more
Library for ENC28J60 Ethernet modules.
Ported to mbed from Norbert Truchsess's UIPEthernet library for Arduino. Thank you Norbert!
- Full support for persistent (streaming) TCP/IP and UDP connections Client and Server each, ARP, ICMP, DHCP and DNS.
- Works with both Mbed OS 2 and Mbed OS 5.
Usage:
- Import the library into your project.
- Add
#include "UipEthernet.h"
tomain.cpp
- Create one instance of the UipEthernet class initialized with the MAC address you'd like to use and SPI pins of the connected Mbed board.
Example programs:
Import programWebSwitch_ENC28J60
HTTP Server serving a simple webpage which enables to remotely turn a digital output on/off. Compile, download, run and type 'IP_address/secret/' (don't forget the last '/') into your web browser and hit ENTER.
Import programHTTPServer_Echo_ENC28J60
A simple HTTP server echoing received requests. Ethernet connection is over an ENC28J60 board. Usage: Type the server's IP address into you web browser and hit <ENTER>.
Import programTcpServer_ENC28J60
Simple TCP/IP Server using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programTcpClient_ENC28J60
Simple TCP/IP Client using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programUdpServer_ENC28J60
Simple UDP Server using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programUdpClient_ENC28J60
Simple UDP Client using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programMQTT_Hello_ENC28J60
MQTT Client example program. Ethernet connection is via an ENC28J60 module.
Diff: UdpSocket.cpp
- Revision:
- 9:a156d3de5647
- Child:
- 11:647d53d146f1
diff -r 4acb22344932 -r a156d3de5647 UdpSocket.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UdpSocket.cpp Tue Aug 27 15:01:10 2019 +0000 @@ -0,0 +1,374 @@ +/* + UIPUdp.cpp - Arduino implementation of a UIP wrapper class. + Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de> + All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "UipEthernet.h" +#include "UdpSocket.h" +#include "DnsClient.h" + +extern "C" +{ +#include "utility/uip-conf.h" +#include "utility/uip.h" +#include "utility/uip_arp.h" +} +#if UIP_UDP +#define UIP_ARPHDRSIZE 42 +#define UDPBUF ((struct uip_udpip_hdr*) &uip_buf[UIP_LLH_LEN]) + +// Constructor +UdpSocket::UdpSocket() : + _uip_udp_conn(NULL) +{ + memset(&appdata, 0, sizeof(appdata)); +} + +// initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use +uint8_t UdpSocket::begin(uint16_t port) +{ + if (!_uip_udp_conn) { + _uip_udp_conn = uip_udp_new(NULL, 0); + } + + if (_uip_udp_conn) { + uip_udp_bind(_uip_udp_conn, htons(port)); + _uip_udp_conn->appstate = &appdata; + return 1; + } + + return 0; +} + +// Finish with the UDP socket +void UdpSocket::stop() +{ + if (_uip_udp_conn) { + uip_udp_remove(_uip_udp_conn); + _uip_udp_conn->appstate = NULL; + _uip_udp_conn = NULL; + UipEthernet::ethernet->phy.freeBlock(appdata.packet_in); + UipEthernet::ethernet->phy.freeBlock(appdata.packet_next); + UipEthernet::ethernet->phy.freeBlock(appdata.packet_out); + memset(&appdata, 0, sizeof(appdata)); + } +} + +// 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 +int UdpSocket::beginPacket(IpAddress ip, uint16_t port) +{ + UipEthernet::ethernet->tick(); + if (ip && port) { + uip_ipaddr_t ripaddr; + uip_ip_addr(&ripaddr, ip); +#ifdef UIPETHERNET_DEBUG_UDP + printf("udp beginPacket, "); +#endif + if (_uip_udp_conn) { + _uip_udp_conn->rport = htons(port); + uip_ipaddr_copy(_uip_udp_conn->ripaddr, &ripaddr); + } + else { + _uip_udp_conn = uip_udp_new(&ripaddr, htons(port)); + if (_uip_udp_conn) + { +#ifdef UIPETHERNET_DEBUG_UDP + printf("new connection, "); +#endif + _uip_udp_conn->appstate = &appdata; + } + else + { +#ifdef UIPETHERNET_DEBUG_UDP + printf("failed to allocate new connection\r\n"); +#endif + return 0; + } + } + +#ifdef UIPETHERNET_DEBUG_UDP + printf("rip: %s, port: %d\r\n", ip.asString(), port); +#endif + } + + if (_uip_udp_conn) { + if (appdata.packet_out == NOBLOCK) { + appdata.packet_out = UipEthernet::ethernet->phy.allocBlock(UIP_UDP_MAXPACKETSIZE); + appdata.out_pos = UIP_UDP_PHYH_LEN; + if (appdata.packet_out != NOBLOCK) + return 1; +#ifdef UIPETHERNET_DEBUG_UDP + else + printf("failed to allocate memory for packet\r\n"); +#endif + } + +#ifdef UIPETHERNET_DEBUG_UDP + else + printf("previous packet on that connection not sent yet\r\n"); +#endif + } + + return 0; +} + +// 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 +int UdpSocket::beginPacket(const char* host, uint16_t port) +{ + // Look up the host first + int ret = 0; + DnsClient dns; + IpAddress remote_addr; + + dns.begin(UipEthernet::ethernet->dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) { + return beginPacket(remote_addr, port); + } + else { + return ret; + } +} + +// Finish off this packet and send it + +// Returns 1 if the packet was sent successfully, 0 if there was an error +int UdpSocket::endPacket() +{ + if (_uip_udp_conn && appdata.packet_out != NOBLOCK) { + appdata.send = true; + UipEthernet::ethernet->phy.resizeBlock(appdata.packet_out, 0, appdata.out_pos); + uip_udp_periodic_conn(_uip_udp_conn); + if (uip_len > 0) { + _send(&appdata); + return 1; + } + } + + return 0; +} + +// Write a single byte into the packet +size_t UdpSocket::write(uint8_t c) +{ + return write(&c, 1); +} + +// Write size bytes from buffer into the packet +size_t UdpSocket::write(const uint8_t* buffer, size_t size) +{ + if (appdata.packet_out != NOBLOCK) { + size_t ret = UipEthernet::ethernet->phy.writePacket + ( + appdata.packet_out, + appdata.out_pos, + (uint8_t*)buffer, + size + ); + appdata.out_pos += ret; + return ret; + } + + return 0; +} + +// Start processing the next available incoming packet + +// Returns the size of the packet in bytes, or 0 if no packets are available +int UdpSocket::parsePacket() +{ + UipEthernet::ethernet->tick(); +#ifdef UIPETHERNET_DEBUG_UDP + if (appdata.packet_in != NOBLOCK) { + printf("udp parsePacket freeing previous packet: %d\r\n", appdata.packet_in); + } +#endif + UipEthernet::ethernet->phy.freeBlock(appdata.packet_in); + + appdata.packet_in = appdata.packet_next; + appdata.packet_next = NOBLOCK; + +#ifdef UIPETHERNET_DEBUG_UDP + if (appdata.packet_in != NOBLOCK) { + printf("udp parsePacket received packet: %d", appdata.packet_in); + } +#endif + + int size = UipEthernet::ethernet->phy.blockSize(appdata.packet_in); +#ifdef UIPETHERNET_DEBUG_UDP + if (appdata.packet_in != NOBLOCK) { + printf(", size: %d\r\n", size); + } +#endif + return size; +} + +// Number of bytes remaining in the current packet +int UdpSocket::available() +{ + UipEthernet::ethernet->tick(); + return UipEthernet::ethernet->phy.blockSize(appdata.packet_in); +} + +// Read a single byte from the current packet +int UdpSocket::read() +{ + static unsigned char c; + if (read(&c, 1) > 0) { + return c; + } + + return -1; +} + +// 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 UdpSocket::read(unsigned char* buffer, size_t len) +{ + UipEthernet::ethernet->tick(); + if (appdata.packet_in != NOBLOCK) { + memaddress read = UipEthernet::ethernet->phy.readPacket(appdata.packet_in, 0, buffer, len); + if (read == UipEthernet::ethernet->phy.blockSize(appdata.packet_in)) { + UipEthernet::ethernet->phy.freeBlock(appdata.packet_in); + appdata.packet_in = NOBLOCK; + } + else + UipEthernet::ethernet->phy.resizeBlock(appdata.packet_in, read); + return read; + } + + return 0; +} + +// Return the next byte from the current packet without moving on to the next byte +int UdpSocket::peek() +{ + UipEthernet::ethernet->tick(); + if (appdata.packet_in != NOBLOCK) { + unsigned char c; + if (UipEthernet::ethernet->phy.readPacket(appdata.packet_in, 0, &c, 1) == 1) + return c; + } + + return -1; +} + +// Finish reading the current packet +void UdpSocket::flush() +{ + UipEthernet::ethernet->tick(); + UipEthernet::ethernet->phy.freeBlock(appdata.packet_in); + appdata.packet_in = NOBLOCK; +} + +// Return the IP address of the host who sent the current incoming packet +IpAddress UdpSocket::remoteIP() +{ + return _uip_udp_conn ? ip_addr_uip(_uip_udp_conn->ripaddr) : IpAddress(); +} + +// Return the port of the host who sent the current incoming packet +uint16_t UdpSocket::remotePort() +{ + return _uip_udp_conn ? ntohs(_uip_udp_conn->rport) : 0; +} + +// UIP callback function +void uipudp_appcall() +{ + if (uip_udp_userdata_t * data = (uip_udp_userdata_t *) (uip_udp_conn->appstate)) { + if (uip_newdata()) { + if (data->packet_next == NOBLOCK) { + uip_udp_conn->rport = UDPBUF->srcport; + uip_ipaddr_copy(uip_udp_conn->ripaddr, UDPBUF->srcipaddr); + data->packet_next = UipEthernet::ethernet->phy.allocBlock(ntohs(UDPBUF->udplen) - UIP_UDPH_LEN); + + //if we are unable to allocate memory the packet is dropped. udp doesn't guarantee packet delivery + if (data->packet_next != NOBLOCK) { + //discard Linklevel and IP and udp-header and any trailing bytes: + UipEthernet::ethernet->phy.copyPacket + ( + data->packet_next, + 0, + UipEthernet::inPacket, + UIP_UDP_PHYH_LEN, + UipEthernet::ethernet->phy.blockSize(data->packet_next) + ); +#ifdef UIPETHERNET_DEBUG_UDP + printf + ( + "udp, uip_newdata received packet: %d, size: %d\r\n", + data->packet_next, + UIPEthernet.network.blockSize(data->packet_next) + ); +#endif + } + } + } + + if (uip_poll() && data->send) + { + //set uip_slen (uip private) by calling uip_udp_send +#ifdef UIPETHERNET_DEBUG_UDP + printf + ( + "udp, uip_poll preparing packet to send: %d, size: %d\r\n", + data->packet_out, + UIPEthernet.network.blockSize(data->packet_out) + ); +#endif + UipEthernet::uipPacket = data->packet_out; + UipEthernet::uipHeaderLen = UIP_UDP_PHYH_LEN; + uip_udp_send(data->out_pos - (UIP_UDP_PHYH_LEN)); + } + } +} + +/** + * @brief + * @note + * @param + * @retval + */ +void UdpSocket::_send(uip_udp_userdata_t* data) +{ + uip_arp_out(); //add arp + if (uip_len == UIP_ARPHDRSIZE) { + UipEthernet::uipPacket = NOBLOCK; + UipEthernet::packetState &= ~UIPETHERNET_SENDPACKET; +#ifdef UIPETHERNET_DEBUG_UDP + printf("udp, uip_poll results in ARP-packet\r\n"); +#endif + } + else { + //arp found ethaddr for ip (otherwise packet is replaced by arp-request) + data->send = false; + data->packet_out = NOBLOCK; + UipEthernet::packetState |= UIPETHERNET_SENDPACKET; +#ifdef UIPETHERNET_DEBUG_UDP + printf("udp, uip_packet to send: %d\r\n", UIPEthernet::uip_packet); +#endif + } + + UipEthernet::ethernet->network_send(); +} +#endif