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.

/media/uploads/hudakz/enc28j60_module01.jpg

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" to main.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.

UIPEthernet.cpp

Committer:
hudakz
Date:
2014-09-15
Revision:
0:5350a66d5279
Child:
2:049ce85163c5

File content as of revision 0:5350a66d5279:

/*
 UIPEthernet.cpp - Arduino implementation of a uIP wrapper class.
 Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
 All rights reserved.

 Modified (ported to mbed) by Zoltan Hudak <hudakz@inbox.com>

 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/>.
  */
#pragma once
#include <mbed.h>
#include "UIPEthernet.h"
#include "Enc28J60Network.h"

#if (defined UIPETHERNET_DEBUG || defined UIPETHERNET_DEBUG_CHKSUM)
    #include "HardwareSerial.h"
#endif
extern "C"
{
#include "uip-conf.h"
#include "uip.h"
#include "uip_arp.h"
#include "uip_timer.h"
}
#define ETH_HDR ((struct uip_eth_hdr*) &uip_buf[0])

// Because uIP isn't encapsulated within a class we have to use global

// variables, so we can only have one TCP/IP stack per program.
UIPEthernetClass::UIPEthernetClass(PinName mosi, PinName miso, PinName sck, PinName cs) :
    network(mosi, miso, sck, cs),
    fn_uip_cb(NULL),
    fn_uip_udp_cb(NULL),
    in_packet(NOBLOCK),
    uip_packet(NOBLOCK),
    uip_hdrlen(0),
    packetstate(0),
    _dhcp(NULL)
{ }

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int UIPEthernetClass::begin(const uint8_t* mac) {
    static DhcpClass    s_dhcp;
    _dhcp = &s_dhcp;

    // Initialise the basic info
    init(mac);

    // Now try to get our config info from a DHCP server
    int ret = _dhcp->beginWithDHCP((uint8_t*)mac);
    if(ret == 1) {

        // We've successfully found a DHCP server and got our configuration info, so set things
        // accordingly
        configure(_dhcp->getLocalIp(), _dhcp->getDnsServerIp(), _dhcp->getGatewayIp(), _dhcp->getSubnetMask());
    }

    return ret;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip) {
    IPAddress   dns = ip;
    dns[3] = 1;
    begin(mac, ip, dns);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns) {
    IPAddress   gateway = ip;
    gateway[3] = 1;
    begin(mac, ip, dns, gateway);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway) {
    IPAddress   subnet(255, 255, 255, 0);
    begin(mac, ip, dns, gateway, subnet);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) {
    init(mac);
    configure(ip, dns, gateway, subnet);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int UIPEthernetClass::maintain(void) {
    tick();

    int rc = DHCP_CHECK_NONE;
    if(_dhcp != NULL) {

        //we have a pointer to dhcp, use it
        rc = _dhcp->checkLease();
        switch(rc) {
        case DHCP_CHECK_NONE:
            //nothing done
            break;

        case DHCP_CHECK_RENEW_OK:
        case DHCP_CHECK_REBIND_OK:
            //we might have got a new IP.
            configure(_dhcp->getLocalIp(), _dhcp->getDnsServerIp(), _dhcp->getGatewayIp(), _dhcp->getSubnetMask());
            break;

        default:
            //this is actually a error, it will retry though
            break;
        }
    }

    return rc;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
IPAddress UIPEthernetClass::localIP(void) {
    IPAddress       ret;
    uip_ipaddr_t    a;
    uip_gethostaddr(a);
    return ip_addr_uip(a);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
IPAddress UIPEthernetClass::subnetMask(void) {
    IPAddress       ret;
    uip_ipaddr_t    a;
    uip_getnetmask(a);
    return ip_addr_uip(a);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
IPAddress UIPEthernetClass::gatewayIP(void) {
    IPAddress       ret;
    uip_ipaddr_t    a;
    uip_getdraddr(a);
    return ip_addr_uip(a);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
IPAddress UIPEthernetClass::dnsServerIP(void) {
    return _dnsServerAddress;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::tick(void) {
    if(in_packet == NOBLOCK) {
        in_packet = network.receivePacket();
#ifdef UIPETHERNET_DEBUG
        if(in_packet != NOBLOCK) {
            Serial.print("--------------\nreceivePacket: ");
            Serial.println(in_packet);
        }
#endif
    }

    if(in_packet != NOBLOCK) {
        packetstate = UIPETHERNET_FREEPACKET;
        uip_len = network.blockSize(in_packet);
        if(uip_len > 0) {
            network.readPacket(in_packet, 0, (uint8_t*)uip_buf, UIP_BUFSIZE);
            if(ETH_HDR->type == HTONS(UIP_ETHTYPE_IP)) {
                uip_packet = in_packet;
#ifdef UIPETHERNET_DEBUG
                Serial.print("readPacket type IP, uip_len: ");
                Serial.println(uip_len);
#endif
                uip_arp_ipin();
                uip_input();
                if(uip_len > 0) {
                    uip_arp_out();
                    network_send();
                }
            }
            else
            if(ETH_HDR->type == HTONS(UIP_ETHTYPE_ARP))
            {
#ifdef UIPETHERNET_DEBUG
                Serial.print("readPacket type ARP, uip_len: ");
                Serial.println(uip_len);
#endif
                uip_arp_arpin();
                if(uip_len > 0) {
                    network_send();
                }
            }
        }

        if(in_packet != NOBLOCK && (packetstate & UIPETHERNET_FREEPACKET))
        {
#ifdef UIPETHERNET_DEBUG
            Serial.print("freeing packet: ");
            Serial.println(in_packet);
#endif
            network.freePacket();
            in_packet = NOBLOCK;
        }
    }

    if(uip_timer_expired(&periodic_timer)) {
        uip_timer_restart(&periodic_timer);
        for(int i = 0; i < UIP_CONNS; i++) {
            uip_periodic(i);

            // If the above function invocation resulted in data that
            // should be sent out on the network, the global variable
            // uip_len is set to a value > 0.
            if(uip_len > 0) {
                uip_arp_out();
                network_send();
            }
        }

#if UIP_UDP
        for(int i = 0; i < UIP_UDP_CONNS; i++) {
            uip_udp_periodic(i);

            // If the above function invocation resulted in data that
            // should be sent out on the network, the global variable
            // uip_len is set to a value > 0. */
            if(uip_len > 0) {
                network_send();
            }
        }
#endif /* UIP_UDP */
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
bool UIPEthernetClass::network_send(void) {
    if(packetstate & UIPETHERNET_SENDPACKET)
    {
#ifdef UIPETHERNET_DEBUG
        Serial.print("network_send uip_packet: ");
        Serial.print(uip_packet);
        Serial.print(", hdrlen: ");
        Serial.println(uip_hdrlen);
#endif
        network.writePacket(uip_packet, 0, uip_buf, uip_hdrlen);
        goto sendandfree;
    }

    uip_packet = network.allocBlock(uip_len);
    if(uip_packet != NOBLOCK)
    {
#ifdef UIPETHERNET_DEBUG
        Serial.print("network_send uip_buf (uip_len): ");
        Serial.print(uip_len);
        Serial.print(", packet: ");
        Serial.println(uip_packet);
#endif
        network.writePacket(uip_packet, 0, uip_buf, uip_len);
        goto sendandfree;
    }

    return false;
sendandfree:
    network.sendPacket(uip_packet);
    network.freeBlock(uip_packet);
    uip_packet = NOBLOCK;
    return true;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::init(const uint8_t* mac) {
    uip_timer_set(&this->periodic_timer, CLOCK_SECOND / 4);

    network.init((uint8_t*)mac);
    uip_seteth_addr(mac);

    uip_init();
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) {
    uip_ipaddr_t    ipaddr;

    uip_ip_addr(ipaddr, ip);
    uip_sethostaddr(ipaddr);

    uip_ip_addr(ipaddr, gateway);
    uip_setdraddr(ipaddr);

    uip_ip_addr(ipaddr, subnet);
    uip_setnetmask(ipaddr);

    _dnsServerAddress = dns;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::set_uip_callback(fn_uip_cb_t fn) {
    this->fn_uip_cb = fn;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::uip_callback(void) {
    struct uipethernet_state*   s = &(uip_conn->appstate);

    if(this->fn_uip_cb) {

        // The sketch wants to handle all uIP events itself, using uIP functions.
        this->fn_uip_cb(s); //->p, &s->user);
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::set_uip_udp_callback(fn_uip_udp_cb_t fn) {
    this->fn_uip_udp_cb = fn;
}

#if UIP_UDP

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void UIPEthernetClass::uip_udp_callback(void) {
    struct uipudp_state*    s = &(uip_udp_conn->appstate);

    if(this->fn_uip_udp_cb) {

        // The sketch wants to handle all uIP events itself, using uIP functions.
        this->fn_uip_udp_cb(s); //->p, &s->user);
    }
}
#endif
//UIPEthernetClass UIPEthernet;

// uIP callback function
void uipethernet_appcall(void) {
    UIPEthernet.uip_callback();
}

#if UIP_UDP

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void uipudp_appcall(void) {
    UIPEthernet.uip_udp_callback();
}
#endif

/*---------------------------------------------------------------------------*/
uint16_t UIPEthernetClass::chksum(uint16_t sum, const uint8_t* data, uint16_t len) {
    uint16_t        t;
    const uint8_t*  dataptr;
    const uint8_t*  last_byte;

    dataptr = data;
    last_byte = data + len - 1;

    while(dataptr < last_byte) {

        /* At least two more bytes */
        t = (dataptr[0] << 8) + dataptr[1];
        sum += t;
        if(sum < t) {
            sum++;  /* carry */
        }

        dataptr += 2;
    }

    if(dataptr == last_byte) {
        t = (dataptr[0] << 8) + 0;
        sum += t;
        if(sum < t) {
            sum++;  /* carry */
        }
    }

    /* Return sum in host byte order. */
    return sum;
}

/*---------------------------------------------------------------------------*/
uint16_t UIPEthernetClass::ipchksum(void) {
    uint16_t    sum;

    sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN);
    return(sum == 0) ? 0xffff : htons(sum);
}

/*---------------------------------------------------------------------------*/
uint16_t UIPEthernetClass::upper_layer_chksum(uint8_t proto) {
    uint16_t    upper_layer_len;
    uint16_t    sum;

#if UIP_CONF_IPV6
    upper_layer_len = (((u16_t) (BUF->len[0]) << 8) + BUF->len[1]);
#else /* UIP_CONF_IPV6 */
    upper_layer_len = (((u16_t) (BUF->len[0]) << 8) + BUF->len[1]) - UIP_IPH_LEN;
#endif /* UIP_CONF_IPV6 */

    /* First sum pseudoheader. */

    /* IP protocol and length fields. This addition cannot carry. */
    sum = upper_layer_len + proto;

    /* Sum IP source and destination addresses. */
    sum = chksum(sum, (u8_t*) &BUF->srcipaddr[0], 2 * sizeof(uip_ipaddr_t));

    uint8_t upper_layer_memlen;
    switch(proto) {
    case UIP_PROTO_ICMP:
    case UIP_PROTO_ICMP6:
        upper_layer_memlen = upper_layer_len;
        break;

    case UIP_PROTO_TCP:
        upper_layer_memlen = (BUF->tcpoffset >> 4) << 2;
        break;
#if UIP_UDP

    case UIP_PROTO_UDP:
        upper_layer_memlen = UIP_UDPH_LEN;
        break;
#endif
    }

    sum = chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], upper_layer_memlen);
#ifdef UIPETHERNET_DEBUG_CHKSUM
    Serial.print("chksum uip_buf[");
    Serial.print(UIP_IPH_LEN + UIP_LLH_LEN);
    Serial.print("-");
    Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen);
    Serial.print("]: ");
    Serial.println(htons(sum), HEX);
#endif
    if(upper_layer_memlen < upper_layer_len) {
        sum = network.chksum
            (
                sum,
                uip_packet,
                UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen,
                upper_layer_len - upper_layer_memlen
            );
#ifdef UIPETHERNET_DEBUG_CHKSUM
        Serial.print("chksum uip_packet(");
        Serial.print(uip_packet);
        Serial.print(")[");
        Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen);
        Serial.print("-");
        Serial.print(UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_len);
        Serial.print("]: ");
        Serial.println(htons(sum), HEX);
#endif
    }

    return(sum == 0) ? 0xffff : htons(sum);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
uint16_t uip_ipchksum(void) {
    return UIPEthernet.ipchksum();
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
uint16_t uip_tcpchksum(void) {
    uint16_t    sum = UIPEthernet.upper_layer_chksum(UIP_PROTO_TCP);
    return sum;
}

#if UIP_UDP

/**
 * @brief
 * @note
 * @param
 * @retval
 */
uint16_t uip_udpchksum(void) {
    uint16_t    sum = UIPEthernet.upper_layer_chksum(UIP_PROTO_UDP);
    return sum;
}
#endif