new library UIPEthernet: uIP adapted to Arduino for ENC28J60

Existing enc28j60 libraries for Arduino support tcp in 'single-packet-mode' only (EtherCard comes with brief support to stream multiple-packets, but no real persistent connection). To overcome this limitation I've started to merge the code of Adam Dunkels uIP with the ethershield-lib and code from SerialIP calling the new library 'UIPEthernet'

the 'EchoServer'-example allready runs successfully on Arduino-Uno with cheap enc28j60-breakout-board from eBay. The library implements the same EthernetClient/EthernetServer-API as the stock Ethernet-library.

e.g:

#include <UIPEthernet.h>
#include <UIPServer.h>
#include <UIPClient.h>

UIPServer server = UIPServer(1000);

void setup() {

  uint8_t mac[6] = {0x00,0x01,0x02,0x03,0x04,0x05};
  IPAddress myIP(192,168,1,6);

  UIPEthernet.begin(mac,myIP);

  server.begin();
}

void loop() {

  if (UIPClient client = server.available()) {
    if (client) {
      while (client.available()) {
        int c = client.read();
        client.write(c);
      }
    }
  }
}

code is located on github: GitHub - ntruchsess/arduino_uip: UIPEthernet: A plugin-replacement of the stock Arduino Ethernet library for ENC28J60 shields and breakout boards. Full support for persistent (streaming) TCP-connections and UDP (Client and Server each), ARP, ICMP, DHCP and DNS. Build around Adam Dunkels uIP Stack. Further developed version can be found on https://github.com/UIPEthernet/UIPEthernet

Have fun!

  • Norbert
1 Like

I've added EthernetClient and EthernetServer-like classes so reading data from a Socket as a Stream is possible now. (See example above)

hello.

sry to dig up an old topic, but i really have to say: its an awsome lib.

i gave it a short try now (and will try for longer) - and so far its just working fine!

great job ntruchsess

greetings

Thanks, thanks, and thanks again.
A full IP Stack is finally available for the enc28j60.
I've done some test with your lib and it works :slight_smile: And fine. For those are interrested in, you can find them here (in french):http://bibi21000.gallet.info/index.php/fr/domotique/82-arduino-fr/185-arduino-et-enc28j60-ethercard-ou-uipethernet.html

The only small problem is that we must update headers to change the CS pin. Maybe it should be a good idea to pass it in the begin function, like ethercard do.

I've implemented SMTP protocol, and it works :). But I need to solve some memory leaks and optimize memory use. When everything will be done, I can send you a pull request via github.

Thanks ... again

thank you for the feedback :slight_smile:

and thank you for blogging about UIPEthernet. I really do like programming but when it comes to distribute the word about it I'm a bit lazy :wink:

  • Norbert

Your welcome.
I've spent many days trying to implement smtp using EtherCard ... but never succeed.
And only some hours to do it with your lib :slight_smile:
I've fork your repo on githubhere : https://github.com/bibi21000/arduino_uip.
Let me know if you interrested in a pull request.
Implementation of SMTP is done, only a small memory leaks is almost here.
It seems that 15bytes are lost when I try to stop the client :

        delay(80);
        while (iSMTPClient.available()>0) {
                c=iSMTPClient.read();
        }
        delay(80);
        while (iSMTPClient.available()>0) {
                c=iSMTPClient.read();
        }
        iSMTPClient.stop();

I've added some code to empty the client queue, it reduce memory leaks but it is always here.
I've sent about 900 email this night and only 45bytes lost.
Any idea ?

not sure about the memory-leak as you do a lot of dynamic memory-allocation and I didn't analyse this in detail, but I've left some comments on your commits on Github.

  • Norbert

Thanks for your response and comments on github (I've also comment them).
The memory leak does not come from my code, I'm almost sure.
My first test uses array of char, and the memory leak was there too.
It seems that if I close the connection quickly, I lost some bytes (always 15 bytes).
Thanks again
Have a nice day

stop is implemented nonblocking. So if you happen to stop a connection before all packets are sent the connections dynamically allocated memory remains allocated until the packets are actually send. It's freed as soon the latest outgoing packet is acklowledged. You also cannot allocate a new connection on this connection-entry until then (which shouldn't be a problem as there are three more slots available with default-config).

Try calling Ethernet.maintain() (or any other method that dispatches a call to Ethernet.tick()) in a loop for a few 100ms and check for available ram after that.

  • Norbert

Hi All,

Well I used uIP long time ago on other microcontroller projects, this code rocks, thank you guys.

If I remember correctly, I tought there was some option to reduce code size by removing some of function of the library, does this apply to this version too ?

I just say that because I will be short in code size and I want to reduce the library code by removing function, for example I do not need iUP respond to ping nor use UDP protocol.

Is there some include define I should change fot this ?

Anyway, having a begin with the option to set the CS pin of the enc28j60 is a excellent idea :wink:

Thanks for your help.

Charly86:
this code rocks, thank you guys.

thank you for the kudos :wink:

Charly86:
If I remember correctly, I tought there was some option to reduce code size by removing some of function of the library, does this apply to this version too ?

you may reduce by aprox. 5kb by switching off UDP (and at the same time DNS and DHCP as this requires UDP). This can be configured in uipethernet-conf.h

If RAM is an issue you may also configure number of concurrent sockets and number of pakets per socket, but this will not reduce the foodprint in flash.

Excellent,

Thanks for this, just what I needed

Shame on me :blush:, I didn't looked in utility folder...

Good to know for the RAM size also, I will test this tonight :smiley:

Hi guys,

I've done little test with this stack, all works fine and even with REST web server API running, Arduino always reply to ping during processing except when I do some time consuming code.

I have now a little question, if in my main loop I process data as the code below, then if I need to call a function that may take long time, how do I in this function (long_stuff() in example below) I do refresh the stack to uIP continue responding to ping ?

Of course I do not want putting flags and manage all in main loop. The stack does not have interrupt of enc28J60 connected, so I can imagine I need to call a callback or refresh function ?

Would be interested to know which :slight_smile:

Thank you for your help

void quick_stuff (void)
{
  // very quick stuff does not take long time
}

void long_stuff (void)
{
  while (blah blah !!!)
  {
    //do my stuff (may be long)
    
    // So Here, how to I refresh the uIP stack ?
    server.available();
  }
}

void loop()
{
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) 
  {
    while (client.connected()) 
    {
      if (client.available()) 
      {
        if (blah blah)
          quick_stuff();
        else
          long_stuff();
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  }
}

use Ethernet.maintain() for that.

Do you make use of a REST-library and if so which one?

  • Norbert

Norbert,

Thank's, I found it and used it, but it did not solved my problem. But may be I'm in the wrong direction. My problem is that I loose some ping randomly, so I tried to investigate, and searched where are done ping response. Are they done by uIP or by the enc28J60 directly ?

This is very strange, because even with a sketch doing nothing in the main loop (echoserver sample), I loose some ping packet (may be 1 every 50/100 packet).

So I tried ethercard library just to test (spent time to understand that I needed to set CS pin to pin 10 at init because by default it use digital pin 8 ) and then tried the ethernode ino file. No problem, ping are consistent. After I changed the refresh value so my browser will always query the ethercard stack with web server on. Of course after this change, ping varied from 1ms to 50ms but this is normal the app is really stressed (and debug info sent on Arduino Serial) in this case, but I never lost a ping.

Did you encounter this kind of problem ? My desktop and Arduino are located on same cisco switch, I will try to ping from linux computer to be sure, not really always trusting TCP/IP microsoft stack, sometimes I saw strange things.

For the Rest API tried some but I'm writing my own, lot of them used official ethernet shield not the enc28j60 so not the same "process". So instead spending time to search and adapt, I decided to write quickly mine, very basic and simple.

enc28j60 is pretty dumb, it doesn't do any packet-processing by itself. It can only filter incomming packets to decide whether to receive or not, but that's it.
icmp (ping) is handled from UIPEthernet.tick() (which is called from UIPEthernet.maintain()). tick() delegates the processing to uip.c process()-method. If the input-buffer of enc28j60 is filled up, further packets are just dropped. This is not an issue with tcp as tcp does retransmits, but udp, arp and icmp-packets are just lost. It's propably less likely with Ethercard to fill up the receive-buffer as Ethercard doesn't use the enc28j60 memory to store received packets to stream from so there is more memory left free on the chip than with UIPEthernet witch allocates 6k out of 8 for buffering processed by uip but not yet read or sent packets.
You have to call Ethernet.maintain() more often to keep up processing all packets reveived by enc28j60 if you happen to have longer processing steps in between.
I also plan implement an isr (interrupt-service-routing) to call Ethernet.tick() to be enabled by a define in uipethernet-conf.h for those who are willing to wire and use interrupt (and some shields just don't connect it anyway). Using interrupt for ethernet-processing is not suitable in all situation as it might conflict with other libraries that use interrupt as well so you might have to prioritize.
As you say this happens even with EchoServer-example doing nothing, it might also be a hardware-issue. There is a documented bug (in the silicon-errata of enc28j60) that it may miss packets when calculating checksums on the chip (so I don't use that). But I'm using chip-internal DMA to copy received packets to a save memory-location within the chip and DMA is using the same memory-port as checksum-calculation and from what I understand packet-receival does. (This is just speculating as there is no documented conflict in between dma-engine and physical receival facility of enc28j60). Ethercard propably doesn't use dma but reads the received packet as it is (there was no dma-related code in the enc28j60.c-file that is included with Ethercard when I checked this code some month ago).

  • Norbert

Norbert,

Thank's for the explanations, it makes sense to me now.

In fact if I loose some ping packets it's not a problem, my main usage will be for TCP.

And you know what ? I was googling around to see if someone had implemented the ISR because that for my point of view very good way to know when we received a packed (I use them with NRF2401 RF module). And as hardware on Arduino has 2 physical interrupts (D2/D3), it could work fine as soon as other process could be interrupted by ISR without any problem.

If you have a beta with ISR, I can test, no problem.

Thank's again for all your great work.

bibi21000:
The only small problem is that we must update headers to change the CS pin. Maybe it should be a good idea to pass it in the begin function, like ethercard do.

Thanks for your notice, this save my time. I have the same problem with EtherCard on Arduino Mega ADK, where I must connect it to 8pin.

ntruchsess:
Existing enc28j60 libraries for Arduino support tcp in 'single-packet-mode' only (EtherCard comes with brief support to stream multiple-packets, but no real persistent connection). To overcome this limitation I've started to merge the code of Adam Dunkels uIP with the ethershield-lib and code from SerialIP calling the new library 'UIPEthernet'

Hi Norbert,
I must agree with other posts, that this library is great, thanks. Especially the compatibility with official Ethernet shield is nice. I tested it with Xively, in client and server mode and evrething looks good, BUT...

I have two arduinos Arduino Mega ADK and Arduino Mega 2560 R3 and two identical enc28j60 cards. Boths ethernet cards are working with AM ADK, but any with AM 2560 R3. It always stop in this function.

Stack trace:

Ethernet.begin(mac);

void UIPEthernetClass::init(const uint8_t* mac) - network.init((uint8_t*)mac);
void Enc28J60Network::init(uint8_t* macaddr) - writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
void Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) - waitspi();

#define waitspi() while(!(SPSR&(1<<SPIF)))

So its look like the problem is in waitspi()

Previously used library EtherCard works with both.

Do you have any idea where can be problem?

Enc28J60 does not respond to reset-command send on SPI (which is the very first access to enc28j60 SPI from UIPEthernet.init). This can be because ENC28J60_CONTROL_CS is not set to the right pin (defined in utility/Enc28J60Network.h) or you have another device connected to SPI that is not disabled during access to enc28j60 spi and interferes with enc28j60 communication.

  • Norbert