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.

Revision:
0:5350a66d5279
Child:
2:049ce85163c5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Dhcp.cpp	Mon Sep 15 11:12:30 2014 +0000
@@ -0,0 +1,538 @@
+// DHCP Library v0.3 - April 25, 2009
+// Author: Jordan Terrell - blog.jordanterrell.com
+#include <string.h>
+#include <stdlib.h>
+#include "Dhcp.h"
+#include "util.h"
+#include "uip_clock.h"
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int DhcpClass::beginWithDHCP(uint8_t* mac, unsigned long timeout, unsigned long responseTimeout) {
+    _dhcpLeaseTime = 0;
+    _dhcpT1 = 0;
+    _dhcpT2 = 0;
+    _lastCheck = 0;
+    _timeout = timeout;
+    _responseTimeout = responseTimeout;
+
+    // zero out _dhcpMacAddr
+    memset(_dhcpMacAddr, 0, 6);
+    reset_DHCP_lease();
+
+    memcpy((void*)_dhcpMacAddr, (void*)mac, 6);
+    _dhcp_state = STATE_DHCP_START;
+    return request_DHCP_lease();
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void DhcpClass::reset_DHCP_lease(void) {
+
+    // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp
+
+    memset(_dhcpLocalIp, 0, 20);
+}
+
+//return:0 on error, 1 if request is sent and response is received
+int DhcpClass::request_DHCP_lease(void) {
+    uint8_t messageType = 0;
+
+    // Pick an initial transaction ID
+
+    _dhcpTransactionId = (rand() * 2000UL) / RAND_MAX + 1;
+    _dhcpInitialTransactionId = _dhcpTransactionId;
+
+    _dhcpUdpSocket.stop();
+    if(_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) {
+
+        // Couldn't get a socket
+        return 0;
+    }
+
+    presend_DHCP();
+
+    int             result = 0;
+
+    unsigned long   startTime = clock_time();
+
+    while(_dhcp_state != STATE_DHCP_LEASED) {
+        if(_dhcp_state == STATE_DHCP_START) {
+            _dhcpTransactionId++;
+
+            send_DHCP_MESSAGE(DHCP_DISCOVER, ((clock_time() - startTime) / 1000));
+            _dhcp_state = STATE_DHCP_DISCOVER;
+        }
+        else
+        if(_dhcp_state == STATE_DHCP_REREQUEST) {
+            _dhcpTransactionId++;
+            send_DHCP_MESSAGE(DHCP_REQUEST, ((clock_time() - startTime) / 1000));
+            _dhcp_state = STATE_DHCP_REQUEST;
+        }
+        else
+        if(_dhcp_state == STATE_DHCP_DISCOVER) {
+            uint32_t    respId;
+            messageType = parseDHCPResponse(_responseTimeout, respId);
+            if(messageType == DHCP_OFFER) {
+
+                // We'll use the transaction ID that the offer came with,
+                // rather than the one we were up to
+                _dhcpTransactionId = respId;
+                send_DHCP_MESSAGE(DHCP_REQUEST, ((clock_time() - startTime) / 1000));
+                _dhcp_state = STATE_DHCP_REQUEST;
+            }
+        }
+        else
+        if(_dhcp_state == STATE_DHCP_REQUEST) {
+            uint32_t    respId;
+            messageType = parseDHCPResponse(_responseTimeout, respId);
+            if(messageType == DHCP_ACK) {
+                _dhcp_state = STATE_DHCP_LEASED;
+                result = 1;
+
+                //use default lease time if we didn't get it
+                if(_dhcpLeaseTime == 0) {
+                    _dhcpLeaseTime = DEFAULT_LEASE;
+                }
+
+                //calculate T1 & T2 if we didn't get it
+                if(_dhcpT1 == 0) {
+
+                    //T1 should be 50% of _dhcpLeaseTime
+                    _dhcpT1 = _dhcpLeaseTime >> 1;
+                }
+
+                if(_dhcpT2 == 0) {
+
+                    //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime
+                    _dhcpT2 = _dhcpT1 << 1;
+                }
+
+                _renewInSec = _dhcpT1;
+                _rebindInSec = _dhcpT2;
+            }
+            else
+            if(messageType == DHCP_NAK)
+                _dhcp_state = STATE_DHCP_START;
+        }
+
+        if(messageType == 255) {
+            messageType = 0;
+            _dhcp_state = STATE_DHCP_START;
+        }
+
+        if(result != 1 && ((clock_time() - startTime) > _timeout))
+            break;
+    }
+
+    // We're done with the socket now
+    _dhcpUdpSocket.stop();
+    _dhcpTransactionId++;
+
+    return result;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void DhcpClass::presend_DHCP(void)
+{ }
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) {
+    uint8_t buffer[32];
+    memset(buffer, 0, 32);
+
+    IPAddress   dest_addr(255, 255, 255, 255);  // Broadcast address
+
+    if(-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) {
+
+        // FIXME Need to return errors
+        return;
+    }
+
+    buffer[0] = DHCP_BOOTREQUEST;       // op
+    buffer[1] = DHCP_HTYPE10MB;         // htype
+    buffer[2] = DHCP_HLENETHERNET;      // hlen
+    buffer[3] = DHCP_HOPS;              // hops
+
+    // xid
+    unsigned long   xid = htonl(_dhcpTransactionId);
+    memcpy(buffer + 4, &(xid), 4);
+
+    // 8, 9 - seconds elapsed
+    buffer[8] = ((secondsElapsed & 0xff00) >> 8);
+    buffer[9] = (secondsElapsed & 0x00ff);
+
+    // flags
+    unsigned short  flags = htons(DHCP_FLAGSBROADCAST);
+    memcpy(buffer + 10, &(flags), 2);
+
+    // ciaddr: already zeroed
+    // yiaddr: already zeroed
+    // siaddr: already zeroed
+    // giaddr: already zeroed
+    //put data in W5100 transmit buffer
+    _dhcpUdpSocket.write(buffer, 28);
+
+    memset(buffer, 0, 32);              // clear local buffer
+    memcpy(buffer, _dhcpMacAddr, 6);    // chaddr
+
+    //put data in W5100 transmit buffer
+    _dhcpUdpSocket.write(buffer, 16);
+
+    memset(buffer, 0, 32);      // clear local buffer
+
+    // leave zeroed out for sname && file
+    // put in W5100 transmit buffer x 6 (192 bytes)
+    for(int i = 0; i < 6; i++) {
+        _dhcpUdpSocket.write(buffer, 32);
+    }
+
+    // OPT - Magic Cookie
+    buffer[0] = (uint8_t) ((MAGIC_COOKIE >> 24) & 0xFF);
+    buffer[1] = (uint8_t) ((MAGIC_COOKIE >> 16) & 0xFF);
+    buffer[2] = (uint8_t) ((MAGIC_COOKIE >> 8) & 0xFF);
+    buffer[3] = (uint8_t) (MAGIC_COOKIE & 0xFF);
+
+    // OPT - message type
+    buffer[4] = dhcpMessageType;
+    buffer[5] = 0x01;
+    buffer[6] = messageType;    //DHCP_REQUEST;
+
+    // OPT - client identifier
+    buffer[7] = dhcpClientIdentifier;
+    buffer[8] = 0x07;
+    buffer[9] = 0x01;
+    memcpy(buffer + 10, _dhcpMacAddr, 6);
+
+    // OPT - host name
+    buffer[16] = hostName;
+    buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address
+    strcpy((char*) &(buffer[18]), HOST_NAME);
+
+    printByte((char*) &(buffer[24]), _dhcpMacAddr[3]);
+    printByte((char*) &(buffer[26]), _dhcpMacAddr[4]);
+    printByte((char*) &(buffer[28]), _dhcpMacAddr[5]);
+
+    //put data in W5100 transmit buffer
+    _dhcpUdpSocket.write(buffer, 30);
+
+    if(messageType == DHCP_REQUEST) {
+        buffer[0] = dhcpRequestedIPaddr;
+        buffer[1] = 0x04;
+        buffer[2] = _dhcpLocalIp[0];
+        buffer[3] = _dhcpLocalIp[1];
+        buffer[4] = _dhcpLocalIp[2];
+        buffer[5] = _dhcpLocalIp[3];
+
+        buffer[6] = dhcpServerIdentifier;
+        buffer[7] = 0x04;
+        buffer[8] = _dhcpDhcpServerIp[0];
+        buffer[9] = _dhcpDhcpServerIp[1];
+        buffer[10] = _dhcpDhcpServerIp[2];
+        buffer[11] = _dhcpDhcpServerIp[3];
+
+        //put data in W5100 transmit buffer
+        _dhcpUdpSocket.write(buffer, 12);
+    }
+
+    buffer[0] = dhcpParamRequest;
+    buffer[1] = 0x06;
+    buffer[2] = subnetMask;
+    buffer[3] = routersOnSubnet;
+    buffer[4] = dns;
+    buffer[5] = domainName;
+    buffer[6] = dhcpT1value;
+    buffer[7] = dhcpT2value;
+    buffer[8] = endOption;
+
+    //put data in W5100 transmit buffer
+    _dhcpUdpSocket.write(buffer, 9);
+
+    _dhcpUdpSocket.endPacket();
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId) {
+    uint8_t         type = 0;
+    uint8_t         opt_len = 0;
+
+    unsigned long   startTime = clock_time();
+
+    while(_dhcpUdpSocket.parsePacket() <= 0) {
+        if((clock_time() - startTime) > responseTimeout) {
+            return 255;
+        }
+
+        wait(0.050);
+    }
+
+    // start reading in the packet
+    RIP_MSG_FIXED   fixedMsg;
+    _dhcpUdpSocket.read((uint8_t*) &fixedMsg, sizeof(RIP_MSG_FIXED));
+
+    if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) {
+        transactionId = ntohl(fixedMsg.xid);
+        if
+        (
+            memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0
+        ||  (transactionId < _dhcpInitialTransactionId)
+        ||  (transactionId > _dhcpTransactionId)
+        ) {
+
+            // Need to read the rest of the packet here regardless
+            _dhcpUdpSocket.flush();
+            return 0;
+        }
+
+        memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4);
+
+        // Skip to the option part
+        // Doing this a byte at a time so we don't have to put a big buffer
+        // on the stack (as we don't have lots of memory lying around)
+        for(int i = 0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++) {
+            _dhcpUdpSocket.read();  // we don't care about the returned byte
+        }
+
+        while(_dhcpUdpSocket.available() > 0) {
+            switch(_dhcpUdpSocket.read()) {
+            case endOption:
+                break;
+
+            case padOption:
+                break;
+
+            case dhcpMessageType:
+                opt_len = _dhcpUdpSocket.read();
+                type = _dhcpUdpSocket.read();
+                break;
+
+            case subnetMask:
+                opt_len = _dhcpUdpSocket.read();
+                _dhcpUdpSocket.read(_dhcpSubnetMask, 4);
+                break;
+
+            case routersOnSubnet:
+                opt_len = _dhcpUdpSocket.read();
+                _dhcpUdpSocket.read(_dhcpGatewayIp, 4);
+                for(int i = 0; i < opt_len - 4; i++) {
+                    _dhcpUdpSocket.read();
+                }
+                break;
+
+            case dns:
+                opt_len = _dhcpUdpSocket.read();
+                _dhcpUdpSocket.read(_dhcpDnsServerIp, 4);
+                for(int i = 0; i < opt_len - 4; i++) {
+                    _dhcpUdpSocket.read();
+                }
+                break;
+
+            case dhcpServerIdentifier:
+                opt_len = _dhcpUdpSocket.read();
+                if(*((uint32_t*)_dhcpDhcpServerIp) == 0 || IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP()) {
+                    _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp));
+                }
+                else {
+
+                    // Skip over the rest of this option
+                    while(opt_len--) {
+                        _dhcpUdpSocket.read();
+                    }
+                }
+                break;
+
+            case dhcpT1value:
+                opt_len = _dhcpUdpSocket.read();
+                _dhcpUdpSocket.read((uint8_t*) &_dhcpT1, sizeof(_dhcpT1));
+                _dhcpT1 = ntohl(_dhcpT1);
+                break;
+
+            case dhcpT2value:
+                opt_len = _dhcpUdpSocket.read();
+                _dhcpUdpSocket.read((uint8_t*) &_dhcpT2, sizeof(_dhcpT2));
+                _dhcpT2 = ntohl(_dhcpT2);
+                break;
+
+            case dhcpIPaddrLeaseTime:
+                opt_len = _dhcpUdpSocket.read();
+                _dhcpUdpSocket.read((uint8_t*) &_dhcpLeaseTime, sizeof(_dhcpLeaseTime));
+                _dhcpLeaseTime = ntohl(_dhcpLeaseTime);
+                _renewInSec = _dhcpLeaseTime;
+                break;
+
+            default:
+                opt_len = _dhcpUdpSocket.read();
+
+                // Skip over the rest of this option
+                while(opt_len--) {
+                    _dhcpUdpSocket.read();
+                }
+                break;
+            }
+        }
+    }
+
+    // Need to skip to end of the packet regardless here
+    _dhcpUdpSocket.flush();
+
+    return type;
+}
+
+/*
+    returns:
+    0/DHCP_CHECK_NONE: nothing happened
+    1/DHCP_CHECK_RENEW_FAIL: renew failed
+    2/DHCP_CHECK_RENEW_OK: renew success
+    3/DHCP_CHECK_REBIND_FAIL: rebind fail
+    4/DHCP_CHECK_REBIND_OK: rebind success
+*/
+int DhcpClass::checkLease(void) {
+
+    //this uses a signed / unsigned trick to deal with millis overflow
+    unsigned long   now = clock_time();
+    signed long     snow = (long)now;
+    int             rc = DHCP_CHECK_NONE;
+    if(_lastCheck != 0) {
+        signed long factor;
+        //calc how many ms past the timeout we are
+
+        factor = snow - (long)_secTimeout;
+
+        //if on or passed the timeout, reduce the counters
+        if(factor >= 0) {
+
+            //next timeout should be now plus 1000 ms minus parts of second in factor
+            _secTimeout = snow + 1000 - factor % 1000;
+
+            //how many seconds late are we, minimum 1
+            factor = factor / 1000 + 1;
+
+            //reduce the counters by that mouch
+            //if we can assume that the cycle time (factor) is fairly constant
+            //and if the remainder is less than cycle time * 2
+            //do it early instead of late
+            if(_renewInSec < factor * 2)
+                _renewInSec = 0;
+            else
+                _renewInSec -= factor;
+
+            if(_rebindInSec < factor * 2)
+                _rebindInSec = 0;
+            else
+                _rebindInSec -= factor;
+        }
+
+        //if we have a lease but should renew, do it
+        if(_dhcp_state == STATE_DHCP_LEASED && _renewInSec <= 0) {
+            _dhcp_state = STATE_DHCP_REREQUEST;
+            rc = 1 + request_DHCP_lease();
+        }
+
+        //if we have a lease or is renewing but should bind, do it
+        if((_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <= 0) {
+
+            //this should basically restart completely
+            _dhcp_state = STATE_DHCP_START;
+            reset_DHCP_lease();
+            rc = 3 + request_DHCP_lease();
+        }
+    }
+    else {
+        _secTimeout = snow + 1000;
+    }
+
+    _lastCheck = now;
+    return rc;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+IPAddress DhcpClass::getLocalIp(void) {
+    return IPAddress(_dhcpLocalIp);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+IPAddress DhcpClass::getSubnetMask(void) {
+    return IPAddress(_dhcpSubnetMask);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+IPAddress DhcpClass::getGatewayIp(void) {
+    return IPAddress(_dhcpGatewayIp);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+IPAddress DhcpClass::getDhcpServerIp(void) {
+    return IPAddress(_dhcpDhcpServerIp);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+IPAddress DhcpClass::getDnsServerIp(void) {
+    return IPAddress(_dhcpDnsServerIp);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void DhcpClass::printByte(char* buf, uint8_t n) {
+    char*   str = &buf[1];
+    buf[0] = '0';
+    do
+    {
+        unsigned long   m = n;
+        n /= 16;
+
+        char    c = m - 16 * n;
+        *str-- = c < 10 ? c + '0' : c + 'A' - 10;
+    } while(n);
+}