Pages

Arduino and EtherShield – Sending UDP broadcast messages

With the ENC28J60 EtherShield, it is possible to create packets other than the regular HTTP and web packets. The UDP packet is one of these. It is simple and connectionless making minimum overhead needed to create packets. In developing the UDP broadcasts I’ve had to expose a number of functions in my EtherShield library, these are for building the packets and calculating checksums.

The broadcast example featured here is included as an example in the library. The new library is compatible with my previous versions available from this site.

Setting up the ethernet requires a number of addresses to be configured. The broadcast address used is the network broadcast for the local LAN, in this example is it 10.0.0.255:

// Define mac and IP addresses to use
static uint8_t mymac[6] = { 0x54,0x55,0x58,0x10,0x00,0x28 };
static uint8_t myip[4] = { 10,0,0,28 };
static uint8_t broadcastip[4] = { 10,0,0,255 };
// Destination Port 52240
#define DEST_PORT_L  0x10
#define DEST_PORT_H  0xCC

The UDP Payload in the example is stored in a simple struct, this is then copied into the packet and broadcast to all those that are listening on the LAN:

struct UDPPayload {
  uint32_t time;            // Time
  uint16_t temperature;     // Temp in 1/10 degree
  uint16_t data[10];        //watts data
  uint16_t errorCount;      // count of errors in the XML.
  uint16_t timeout_count;   // count of all protocol lockups
};
UDPPayload udpPayload;

Initialise the payload:

  // Initialise data in the payload structure
  for( int i=0; i<10; i++ ) {
    udpPayload.data[i] = i;
  }
  udpPayload.time = millis();
  udpPayload.temperature = 0;

The final step is to build and send the broadcast message:

void broadcastData( void ) {
  uint8_t i=0;
  uint16_t ck;
  // Setup the MAC addresses for ethernet header
  while(i<6){
    buf[ETH_DST_MAC +i]= 0xff; // Broadcsat address
    buf[ETH_SRC_MAC +i]=mymac[i];
    i++;
  }
  buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V;
  buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V;
  es.ES_fill_buf_p(&buf[IP_P],9,iphdr);

  // IP Header
  buf[IP_TOTLEN_L_P]=28+sizeof(UDPPayload);
  buf[IP_PROTO_P]=IP_PROTO_UDP_V;
  i=0;
  while(i<4){
    buf[IP_DST_P+i]=broadcastip[i];
    buf[IP_SRC_P+i]=myip[i];
    i++;
  }
  es.ES_fill_ip_hdr_checksum(buf);
  buf[UDP_DST_PORT_H_P]=DEST_PORT_H;
  buf[UDP_DST_PORT_L_P]=DEST_PORT_L;
  buf[UDP_SRC_PORT_H_P]=10;
  buf[UDP_SRC_PORT_L_P]=srcport; // lower 8 bit of src port
  buf[UDP_LEN_H_P]=0;
  buf[UDP_LEN_L_P]=8+sizeof(UDPPayload); // fixed len
  // zero the checksum
  buf[UDP_CHECKSUM_H_P]=0;
  buf[UDP_CHECKSUM_L_P]=0;
  // copy the data:
  i=0;
  // most fields are zero, here we zero everything and fill later
  uint8_t* b = (uint8_t*)&udpPayload;
  while(i< sizeof( UDPPayload ) ){ 
    buf[UDP_DATA_P+i]=*b++;
    i++;
  }
  // Create correct checksum
  ck=es.ES_checksum(&buf[IP_SRC_P], 16 + sizeof( UDPPayload ),1);
  buf[UDP_CHECKSUM_H_P]=ck>>8;
  buf[UDP_CHECKSUM_L_P]=ck& 0xff;
  es.ES_enc28j60PacketSend(42 + sizeof( UDPPayload ), buf);
}

The payload could be a structure or a text string, just replace the sizeof() functions with strlen(). With using text strings of the correct format is is then possible to create Syslog messages. The only issue here is that you would need a real time clock to provide the correct timestamp header. In practice I have seen my linux syslog daemon accept syslog messages with invalid or missing timestamps and replace it with its own.

Receiving broadcasts using Perl on Linux
Using a simple perl script the broadcast messages can be received and processed. The example below is receiving the broadcast packets and extracting currentcost data. The format of the payload is similar to the example shown above.

#!/usr/bin/perl -w
# cclisten - UDP Currentcost listener
use strict;
use IO::Socket;
my($msglen,$sock, $newmsg, $MAXLEN, $PORTNO);
my($lasttimestamp,$timestamp,$tempr,$watts,$errcount,$timeouts);

# Pachube.com values
my $pachubeApiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
my $pachubeFeed = "nnnn";

# Keep track of how many sample and total for updating pachube once a minute
my $sampleCount = 0;
my $totalWatts = 0;

$MAXLEN = 1024;
$PORTNO = 52240;
$sock = IO::Socket::INET->new(LocalPort => $PORTNO,
        Proto => 'udp',
        LocalAddr => '10.0.0.255',
        Broadcast => 1)
    or die "socket: $@";
print "Awaiting UDP messages on port $PORTNO\n";
$lasttimestamp = 0;
while ($sock->recv($newmsg, $MAXLEN)) {
    $msglen = length($newmsg);
    $timestamp = unpack("L", substr( $newmsg, 0, 4));
    $tempr = unpack("S", substr( $newmsg, 4, 2)) / 10;
    $watts = unpack("S", substr( $newmsg, 6, 2));
    $errcount = unpack("S", substr( $newmsg, 26, 2));
    $timeouts = unpack("S", substr( $newmsg, 28, 2));
    if ( $timestamp != $lasttimestamp ) {
        $lasttimestamp = $timestamp;
        $sampleCount++;
        $totalWatts += $watts;
        print "Time $timestamp temp = $tempr errors $errcount Timeouts $timeouts Ch0 Watts $watts\n";
        # Update RRD
        system( "/usr/bin/rrdtool update currentcost.rrd N:$watts:0");
        # Update pachube once a minute with a minute's worth of averaged data
        if ( $sampleCount > 10 ) {
                # Do a little formatting on the output
                my $averageWatts = sprintf("%.2f",$totalWatts / $sampleCount);
                system("curl -s --request PUT --header 'X-PachubeApiKey: $pachubeApiKey' --data $averageWatts 'http://www.pachube.com/api/$pachubeFeed.csv'");

                # Reset the counts
                $totalWatts = 0;
                $sampleCount = 0;
        }
    }
}
die "recv: $!";

Downloads

Bookmark this post: bookmark bookmark bookmark bookmark bookmark bookmark bookmark bookmark bookmark bookmark

9 comments to Arduino and EtherShield – Sending UDP broadcast messages

  • maxlock

    Hi Andy,

    Thanks for posting this improved library. I now have no excuse for not finishing my xAP automation node stuff I previously posted about now 🙂

    -Cheers Max.

  • jagsilva

    Hello.
    Can you help me? I’m trying to start with a similar project but i have some problems. I download your library but when i compile (ethershieldclient)i got an error
    in this line: buf[TCP_DATA_P+plen]=client_ip[i]; The error is:
    In function ‘uint16_t gen_client_request(uint8_t*)’:
    error: ‘TCP_DATA_P’ was not declared in this scope In function ‘void client_process()’:

    Can you help me please? I use arduino 17 because 16 don’t start.
    jagsilva

  • Andy,

    I’ve been using your library as the stock one that comes with it is a little lacking. I’m able to get a web server up and working and processing different URLs no problem. What I’d really like is to be able to output the status of various pins. Something like:

    for (int i = 0; i <=5 ; i++) {
    plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("Pin #"));
    plen=es.ES_fill_tcp_data_p(buf,plen,PSTR(i));
    plen=es.ES_fill_tcp_data_p(buf,plen,PSTR(" is status "));
    plen=es.ES_fill_tcp_data_p(buf,plen,PSTR(digitalRead(i)));
    }

    The compiler complains about error: initializer fails to determine size of '__c'

    PSTR() seems to ONLY want strings. Is there a way to get it to output anything else? Where is PSTR defined, I can't find reference to it to see what it expects.

  • Administrator

    Hi Scott,

    Looks like your problem is in the line:

    plen=es.ES_fill_tcp_data_p(buf,plen,PSTR(i));

    and

    plen=es.ES_fill_tcp_data_p(buf,plen,PSTR(digitalRead(i)));

    Here you are trying to access program memory. The PSTR references program memory and is where the static strings are stored in the code above, but for dynamic values, e.g. i in this case it wornt work.

    As the code doesnt implement the print class you’d need to format the value if i into a small string buffer then add it to the ether shield packet buffer:

    plen=es.ES_fill_tcp_data_p(buf,plen, numberBuffer );

    Where numberBuffer is a small character buffer that the value of i has been formatted into. Note here, there is no PSTR as numberBuffer is in data memory. There should be examples of this in the examples included in the library.

    Hope this helps if you’ve not already figured it out!

    Andy

  • […] Arduino reading a CSV pachube feed with the ENC28J60 The question of how to do GET requests with the ENC28J60 arose recently so here is an example. This is using the etherShield from Nuelectronics and @andrewdlindsay’s etherShield library. […]

  • KirillN

    Thank you very much!

  • Omnimusha

    no puedo publicar

  • Omnimusha

    h?ello, how I can send and resivir UDP packets from the program
    processing 1.5?

    I found an example, but not as to figure it with this library for arduino.

    from already thank you very much