new library UIPEthernet: uIP adapted to Arduino for ENC28J60

Norbert you are a star - that last %d bug has disappeared... lovely.

I've struggled for a couple of days trying to get a TWEET function to work - this uses the Ethernet CLIENT to do stuff.. could NOT get it works - and I just found out why.

The fully automated way is this...

Ethernet.begin(mac);

Works a treat. Assigns IP address, gateway, router....

But there seems to be an assumption out there that I see TIME AND TIME AGAIN that THIS..

Ethernet.begin(mac,ip);

Will ALSO automatically look up the Gateway. In your code Norbert it does NOT - on checking it had picked 192.168.0.1 as the Gateway as against my normal Gateway of 192.168.0.138 - in the first example (only mac provided) it gets it - in the second example (mac and ip provided) it does not

So note THIS example.. http://arduino-tweet.appspot.com/

Won't work - as they specify the IP address. Remove that and it works.. not 100% but it works.

Who is wrong? Seems to ME to be logical that if you only specify the MAC and the IP, that the rest would be automatic - but it isn't so in your otherwise excellent library??

I'm having trouble getting this to work reliably...

#define LIB_DOMAIN "arduino-tweet.appspot.com"

and then in use

if (client.connect(LIB_DOMAIN, 80)) {

That is just failing constantly (works occasionally) - yet calling that domain from a web browser it works every time.

Pete.

The 'only mac is specified' is the only version that makes use of DHCP. All others use hard-coded defaults for dns, gateway and netmask if not specified.

Here you find the documentation of all variants of Ethernet.begin.

Norbert

THanks for that - well, if that's the standard.. I guess the defaults must work for some folk - and I agree you should stick with them.

There is still I think something wrong with the client end.

This example here..

So you add an APP to your Twitter account which essentially lets you do this in a browser.

http://arduino-tweet.appspot.com/update?token=<>&status=<>

Obvious replacing the token with a string they give you - and then your url encoded message.

From the command line of a browser it works EVERY time - returns OK - and sends the TWEET for you...

But trying the actual example - altering the library reference to ETHERNET.H etc... it works - some of the time - other times it sits there for 10 seconds then fails.

I've changed over to fully static IP - makes no difference - occasionally it works - most of the time it doesn't.

Any pointers? Being able to send tweets with this cheap ENC card would be REALLY great.

seeing the code that does not work would help

Ok, sorry, here it is.

Uses THIS http://arduino-tweet.appspot.com/

You get a token for your Twitter account - the library needs one occurrance of ETHERNET.H changing to UIPEthernet.h

And that should be it - as you will see it makes a page call to the pass passing 2 parameters - the token and what you want so say. Done via a browser it works every time (returns the word UK) - but using the code - it works - well, some of the time (you can't send the same message twice incidentally). Most of the time it just fails.

#include <SPI.h>
#include <UIPEthernet.h>
#include <Twitter.h>

#define ENC_RESET 3

// just a simple reset for the ENC chip D3 on the 1284 -scrap if you're using the normal reset line

void reset_enc() // reset the ENC chip inc port setup
{
digitalWrite(ENC_RESET,HIGH); pinMode(ENC_RESET,OUTPUT); delay(100); digitalWrite(ENC_RESET,LOW); delay(100); digitalWrite(ENC_RESET,HIGH); delay(500);
}

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xDC };
byte ip[] = { 192, 168, 0, 123 };
byte mydns[]= { 192,168,0,138 };
byte gateway[]= { 192,168,0,138 };
byte subnet[]= { 255,255,255,0 };

Twitter twitter("<< my token here >>");
char msg[] = "Hello! I am an Arduino!";

void setup()
{
reset_enc();
Ethernet.begin(mac, ip,mydns,gateway,subnet);
Serial.begin(57600);
delay(500);
Serial.println("connecting ...");
if (twitter.post(msg)) {
int status = twitter.wait();
if (status == 200) {
Serial.println("OK.");
} else {
Serial.print("failed : code ");
Serial.println(status);
}
} else {
Serial.println("connection failed.");
}
}

void loop()
{
}

Propably the same issue that I describe here. If you reset the Arduino after a single tcp-connect it is likely you cannot reconnect to the same remote host/port right away.

Move the post to Twitter into the loop and do a Request every minute without resetting the Arduino should do. Don't forget to add a call to 'Ethernet.maintain()' to loop() that is outside the block that calls Twitter every minute.

Thanks Norbert. I tried that without success. Occasionally it gets the first one - then never again. See below. I put in the current second as a character just to ensure no two tweets are the same - makes no difference.. Yet calling the Twitter App from the command line of a browser as below (but with your personal token) works each and every time provided the message is unique - and returns instantly.

http://arduino-tweet.appspot.com/update?token=<<MY_TOKEN>>&status=hello%20therse%20again

Here's my code modified as your suggestion.

void loop()
{
Ethernet.maintain();
if (tryagain<millis())
{
msg[4]=second()+50;
tryagain=millis()+60000;
Serial.println("connecting ...");
if (twitter.post(msg)) {
int status = twitter.wait();
if (status == 200) {
Serial.println("OK.");
} else {
Serial.print("failed : code ");
Serial.println(status);
}
} else {
Serial.println("connection failed.");
}
}
}

Below I've included the Twitter library that it calls

/*
Twitter.cpp - Arduino library to Post messages to Twitter using OAuth.
Copyright (c) NeoCat 2010-2011. All right reserved.

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.
*/

// ver1.2 - Use <string.h>
// ver1.3 - Support IDE 1.0

#include <string.h>
#include "Twitter.h"

#define LIB_DOMAIN "arduino-tweet.appspot.com"

Twitter::Twitter(const char *token) : token(token)
{
}

bool Twitter::post(const char *msg)
{

parseStatus = 0;
statusCode = 0;

if (client.connect(LIB_DOMAIN, 80)) {

client.println("POST http://" LIB_DOMAIN "/update HTTP/1.0");
client.print("Content-Length: ");
client.println(strlen(msg)+strlen(token)+14);
client.println();
client.print("token=");
client.print(token);
client.print("&status=");
client.println(msg);
} else {
return false;
}
return true;
}

bool Twitter::checkStatus(Print *debug)
{
if (!client.connected()) {
if (debug)
while(client.available())
debug->print((char)client.read());
client.flush();
client.stop();
return false;
}
if (!client.available())
return true;
char c = client.read();
if (debug)
debug->print(c);
switch(parseStatus) {
case 0:
if (c == ' ') parseStatus++; break; // skip "HTTP/1.1 "
case 1:
if (c >= '0' && c <= '9') {
statusCode *= 10;
statusCode += c - '0';
} else {
parseStatus++;
}
}
return true;
}

int Twitter::wait(Print *debug)
{
while (checkStatus(debug));
return statusCode;
}

I wonder if anyone has tackled this before.

So you make your server run on a port and initialise the server..

This is done outside of any functions..

EthernetServer server = EthernetServer(MYPORT);

Then in setup you might do this..

Ethernet.begin(mac,myIP);
server.begin();

and all of that is fine IF you want to keep that address. but Let's say you want the PORT and the address stored in EEPROM so that the end user can CHANGE these??

Clearly you could store the IP address in EEPROM - and then on reboot change the address - but what about that port setup - you can't use an EEPROM value in that global setup???

Ok, I've done a lot more work on this and there is definitely something wrong at the client end. The example I gave.. http://arduino-tweet.appspot.com/ works absolutely perfectly on an ETHERNET card, but same hardware but the ENC card and UIPEthernet will not.

So my software powers up, gets it's fixed IP address no problem, reads the time from the web - no problem - but when I call up a client to send a tweet - nothing - won't even open. The SAME, absolutely identical code on an Ethernet board works a treat. Here is the code - clearly I've changed the TOKEN so this example won't work - needs your own token...

So firstly some code to URLENCODE the message - found this on the web, works a treat.

String URLEncode(String bfr)
{
const char *hex = "0123456789abcdef";
char msgbase [160];
char *msg;
String encodedMsg = "";
bfr.toCharArray(msgbase,(bfr.length()+1));
msg=msgbase;
while (*msg!='\0'){
if( ('a' <= *msg && *msg <= 'z')
|| ('A' <= *msg && *msg <= 'Z')
|| ('0' <= *msg && *msg <= '9') ) {
encodedMsg += *msg;
} else {
encodedMsg += '%';
encodedMsg += hex[*msg >> 4];
encodedMsg += hex[*msg & 15];
}
msg++;
}
return encodedMsg;
}

Then the actual TWEET code - taken from a fairly standard example of client use - note the dummy TOKEN

void tweet(String data)
{
EthernetClient client2;
//this is the string of data to be sent by the POST
String data2;
data2="token=YOUR_OWN_TOKEN_HERE&status=";
data2=data2+URLEncode(data);
Serial.println(data2);
//this is the website that is hosting the PHP proxy
char host[]="arduino-tweet.appspot.com";

if (client2.connect(host, 80)) {
Serial.println("connected");
client2.println("POST /update HTTP/1.1");
client2.print("Host: "); // set the Host
client2.println(host);
client2.println("Connection: close");
// calculates and passes the length of the data
client2.print("Content-Length: ");
client2.println(data2.length());
client2.println("Content-Type: application/x-www-form-urlencoded");
// need a blank line so that the server knows that the data comes next
client2.println();
// now we send the data string
client2.println(data2);
Serial.println("sent");
}
else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
}
client2.stop();
}

and finally the code, sent no more than every couple of minutes for testing....

char uu[80];
sprintf(uu,"The time is %.2d%.2d%.2d",hour(),minute(),second());
tweet(uu);

Will not even connect... any ideas???? This could be SO useful... Oh there is no failed connection message either - it just goes away for a walk and eventually my 8 seconds watchdog timer kicks in.

Hi Peter,

I'd be happy to help you if you post a sketch that allows to reproduce your issue, but is broken down to the point that it does not require to register at some third-party side or to understand and/or debug twitter protocol.

regards,

Norbert

Hi

I can give you a complete example ready to go with my ID in the code - all you'd need to do is stick it in a board and run it... but I'd rather not place that ID in a public forum - any way to send privately? Can't see a way here??

Pete.

Actually I don't believe it has anything to do with Twitter at all - the issue is not what happens when one connects- it just won't connect...

If you look at the code below - down to the point where I put " if (client2.connect(host, 80)) {" - it's not connecting - and hence the data and protocols are irrelevant - it simply won't connect to that site. Yet, the Ethernet version will.

At the point of this routine I have a client open (as a socket) and I've already grabbed the time successfully from an NTP server.... so it's not as if there's a problem with connectivity. If you like I'll happily send all the details privately so you can use the account - it's not doing anything else yet but see below...

void tweet(String data)
{
EthernetClient client2;
//this is the string of data to be sent by the POST
String data2;
data2="token=YOUR_OWN_TOKEN_HERE&status=";
data2=data2+URLEncode(data);
Serial.println(data2);
//this is the website that is hosting the PHP proxy
char host[]="arduino-tweet.appspot.com";

** if (client2.connect(host, 80)) {**
** Serial.println("connected");**

It propably will not make a big difference here, but you should use 'if (client2.connect(host, 80) > 0) {' as this namelockup based connect may return negative values if namelookup fails.

Please provide a ready-to-compile sketch that does not rely on libraries I have download and install first and I'm pretty sure I can find the reason.

regards,

Norbert

Sent by email last night including my "things" Twitter account details to test. Can reliably initiate a Tweet from any Browser to the intermediate service - but not from the program. I have it getting the time first from an NTP server to prove there is a connection. Hope this is useful.

Pete.

Any joy?

Hi there!

Thanks for the efforts for making this library! I'm trying now to convert a project I have controlling servos over udp from the Wiznet W5100 to using the ENC28J60 and this library. It works sort of like an RC car, that is, I'm sending a lot of values to go to the servos, and this works fine with the W5100, but now with the ENC28J60 I'm getting a lot of dropouts

Reading through this thread that the W5100 does some processing of the packages by itself, while the ENC28J60 leaves this for the Arduino makes me hope that this problem is somehow fixable...

Thus I'm wondering if this is a problem with the ENC28J60 itself, the UIPEthernet library, or my inexperienced coding not being optimized.

So I would like to ask if anyone could have a look at my code and see if it can be made better. Any input would be greatly apprechiated :slight_smile:

The messages sent over UDP are short strings, SR10 to SR1127, SR1 meaning Servo nr 1, and then the value 0-127.

#include <UIPEthernet.h>
#include <Servo.h>        // Servo library

EthernetUDP udp;

IPAddress ip(172,16,3,200); 

unsigned int localPort = 8888;      // local port to listen on

// initialization of the servos - pin 10 is used by the Ethernetshield
Servo Servo1;  // Servo 1 on pin 7
Servo Servo2;  // Servo 2 on Pin 8 

void setup() {

  Serial.begin(9600);

  uint8_t mac[6] = {0x00,0x01,0x02,0x03,0x04,0x05};

  Ethernet.begin(mac,IPAddress(172,16,3,200));
  Servo1.attach(7);  // Servo 1 on pin 7
  Servo2.attach(8);  // Servo 2 on Pin 8
  int success = udp.begin(8888);

  Serial.print("initialize: ");
  Serial.println(success ? "success" : "failed");

}

void loop() {
  
  // initialization of the variables
  char command[4];      // commandstring
  unsigned int vnumber; // the value as integer
  
  Ethernet.maintain();
  
  //check for new udp-packet:
  int size = udp.parsePacket();
  if (size > 3) {

        char msg[7];
        int len;
        memset(msg, '\0', 7);   // Set all elements to null
        len = udp.read(msg,7);
        msg[len] = '\0';

        vnumber = atoi(&msg[3]); // convert to integer

        Serial.print("received: ");
        Serial.println(msg);
        
          command[0]=msg[0];
          command[1]=msg[1];
          command[2]=msg[2];
          command[3]='\0';

    //finish reading this packet:
    udp.flush();
    
        Serial.print("command: ");
        Serial.println(command);
        Serial.print("value: ");
        Serial.println(vnumber);
    
    // write to servos
    if (String(command) == "SR1")
    {
      Servo1.write(vnumber);
      //Serial.println("servo 1 go!");
    }

    if (String(command) == "SR2")
    {
      Servo2.write(vnumber);
    }

  }
  delay(10);
  Ethernet.maintain();
}

I keep losing the connection when I set up a server on my arduino using the ENC28J60, whereas it stays connected for hours with the W5100. I'm using the server example from the arduino library, and in the case of the ENC28J60, the UIPEthernet library file and functions.

So, to answer myself -

I redid the code with the Ethercard library and now it's working smoothly, no dropped UDP packages so far.

I was worried that it might have been the ENC28J60 or the cheap fake 4 euro "arduino pro mini" I'd gotted over ebay that was causing the dropouts, but no! So now I'm really happy that I have a fully working 7 euro ethernet arduino :smiley:

Here's the code in case anyone else is in the same situation:
(but beware, its using the Ethercard lib)

// Modified from the Ethercard udpServer example
// This code takes simple strings like SR145 where SR1 means "servo 1" and 45 is the value
// I use pin 10 for CS from the ENC28J60. Pin 8 is default in the Ethercard library. This has to be defined in ether.begin


#include <EtherCard.h>
#include <IPAddress.h>
#include <Servo.h>        // Servo library

// ethernet interface ip address
static byte myip[] = { 172,16,3,10 };
// gateway ip address
static byte gwip[] = { 172,16,3,17 };


// ethernet mac address - must be unique on your network
static byte mymac[] = { 0x70,0x69,0x69,0x2D,0x30,0x05 };

byte Ethernet::buffer[500]; // tcp/ip send and receive buffer

// Servo init
Servo Servo1;  // Servo 1 on pin 7
Servo Servo2;  // Servo 2 on Pin 6  



//callback that prints received packets to the serial port
void udpSerialPrint(word port, byte ip[4], const char *data, word len) {
  
  char command[4];      // commandstring
  unsigned int vnumber; // the value as integer
  
  //IPAddress src(ip[0], ip[1], ip[2], ip[3]);
  //Serial.println(src);
  //Serial.println(port);
  //Serial.println(data);
  //Serial.println(len);
  
  if (len > 0) {
    
        //Serial.print("received: ");
        Serial.println(data); // print all received data
        
          // store SR1 in command
          command[0]=data[0];
          command[1]=data[1];
          command[2]=data[2];
          command[3]='\0';
          
          // store value here
          vnumber = atoi(&data[3]); // convert to integer
    
        //Serial.print("command: ");
        //Serial.println(command);
        //Serial.print("value: ");
        //Serial.println(vnumber);
    
    // write to the servos
    if (String(command) == "SR1")
    {
      Servo1.write(vnumber);
      Serial.println("servo 1 go!");
    }

    if (String(command) == "SR2")
    {
      Servo2.write(vnumber);
    }

  }
}

void setup(){
  Serial.begin(9600);
  Serial.println("\n[backSoon]");

  if (ether.begin(sizeof Ethernet::buffer, mymac, 10) == 0) // the 10 because I use pin 10 for CS, not 8 as is default in Ethercard
    {Serial.println( "Failed to access Ethernet controller"); };

  Serial.println("ether begin ok");

  ether.staticSetup(myip, gwip);

  Serial.println("static ip set");
  //ether.printIp("IP:  ", ether.myip);
  //ether.printIp("GW:  ", ether.gwip);
  //ether.printIp("DNS: ", ether.dnsip);

  //register udpSerialPrint() to port 8888
  ether.udpServerListenOnPort(&udpSerialPrint, 8888);
  
  Servo1.attach(7);  // Servo 1 on pin 7
  Servo2.attach(6);  // Servo 2 on Pin 6
  Serial.println("setup ok");
}

void loop(){
  //this must be called for ethercard functions to work.
  ether.packetLoop(ether.packetReceive());
}

I'm having similar issues.

This code runs perfectly on a standard Arduino ethernet shield but It just stops communicating after around 40 seconds on my ENC28J60. I've tried 4 arduino's and 3 ethernet adapters.

I can see by the serial output that the sketch keeps running but it vanishes of my network completely.

Any idea's?

#include <SPI.h>
#include <UIPEthernet.h>

//linked from here
//http://resources.annikken.com/index.php?title=ENC28J60_Ethernet_Module
// Set your own MAC address for the Ethernet module here:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Set your own IP address for the Ethernet module here:
IPAddress ip(192,168,16,198);
EthernetServer server(80);
// You can access your Arduino server at http://192.168.16.198/

void setup()
{
  // Start Ethernet connection and web server
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.begin(9600);
}

void loop() 
{
  
  // Listen for incoming connections
  EthernetClient client = server.available();
  long time = millis();
  
  Serial.print("Server has been running for ");
  Serial.println(time);
  
  if (client) // If a user is connected
  {
    // An HTTP request from the client ends with two end
    // of line characters (\n x2). We'll need code to check
    // for that.
    boolean currentLineIsBlank = true;
    // This flag is used to check if the first end of line character
    // was sent by the client.
    
    while (client.connected()) // While web client is connected
    {
      if (client.available())
      {
        char c = client.read();

        // Received two end of line characters (\n). Client has finished
        // sending the request. 
        if (c == '\n' && currentLineIsBlank) 
        {
          // Send a standard HTTP response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // Close the connection once the page is fully loaded
	  client.println("Refresh: 2");  // Refresh the page automatically every 2 seconds
          client.println(); // End of HTTP Response header
          
          // Send HTML page
          client.println("<!DOCTYPE HTML>"); // Start of HTML document
          client.println("<html>"); // Start of HTML code
          client.println("<head><title>Hello World!</title></head>"); // Set page title
          client.println("<body>"); // Start of HTML body
          client.println("<h1>Hello World!</h1>"); // Set header

          // Output the value of each analog input pin
          for (int analogChannel = 0; analogChannel < 6; analogChannel++) 
          {
            int sensorReading = analogRead(analogChannel);
            client.print("Analog Input ");
            client.print(analogChannel);
            client.print(": ");
            client.print(sensorReading);
            client.println("
");       
          }

          // Display server uptime
          client.println("<p>Server has been running for ");
          client.println(time);
          client.println(" ms.</p>");
          client.println("</body></html>"); // End of body and HTML page
          
          break; // End of transmission
        }
        
        // If client sent one end of line character (\n),
        // raise the flag and see if the next character is another
        // end of line character
        if (c == '\n')
        {
          currentLineIsBlank = true;
        } 
        // If client sends a character that isn't an end of line character,
        // reset the flag and wait for the end of line character.
        else if (c != '\r')
        {
          currentLineIsBlank = false;
        }
      }
    }
    // Give the web browser time to receive the data
    delay(1);
    
    // Close the connection
    client.stop();
  }
}