UDP send/recieve

Hi there,

I recently purchased an Arduino ethernet shield w/ SD reader. Right now I'm just trying to send/recieve UDP messages back and forth between max and arduino.

I started by using the UDPSendRecieveString (http://arduino.cc/en/Tutorial/UDPSendReceiveString) code from the arduino tutorials section.

I downloaded the Spi and Udp libraries and believed to have installed them correctly. However, this is the message I get when compliling

o: In function loop': undefined reference to Udp'o: In function `setup':

Any suggestions on the root of this conundrum?

Cheers,
Zach

If you use Sketch + Import Library..., can you import the Ethernet and Udp libraries? If so, they are installed in the right place. If not, they are not.

Of course, I would expect that you would get a failure about the inability to include Udp.h and/or Ethernet.h if they were not in the right place.

But, it's the first thing to check.

Hi,

Arduino UNO + IDE 0021 running on 64bit Linux: This example compiles OK for me. But I have following problems with it:

Whenever I run it, Udp.available() returns 1024 immediately, without anything actually being sent to that port. Next, the program hangs on Udp.readPacket() call. Serial console displays: "Received packet of size 1016"

I also tried just sending small, constant, udp packets, using Udp.sendPacket(const char[], uint8_t*, uint16_t)
It works well, but occasionally the packet contains just random bytes and is much larger, hundreds of bytes. Looks like buffer gets read from a wrong address. sendPacket method itself reports correct number of bytes sent for those invalid packets.

btw, Should the ReplyBuffer string in that example be zero-terminated to work correctly with that sendPacket method?

Edit: just discovered that invalid packets are sent in bursts of tens of pakets, all by one sendPacket call. Investigating further... (I can upload packet capture + test program if anyone is interested)

Please share the code so we have something to look at. Small typo's can make big difference :slight_smile:

The first, udp.available(), problem happens with unmodified version of the UdpSendReceiveString example that is bundled with IDE 0021.

I'll post the code for my udp send example when I'll get back home in a couple of hours...

OK, here it is, my little pinger...

#include <SPI.h>
#include <Ethernet.h>
#include <Udp.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x20, 0xA4 };
byte ip[] = { 192,168,1,177 };
byte remoteIp[] = { 192,168,1,123 };
unsigned int localPort = 8888;
unsigned int remotePort = 8889;

char sendBuffer[] = "ping\0";

unsigned int sentSize = 0;

void setup() {
  Ethernet.begin(mac, ip);
  Udp.begin(localPort);
  Serial.begin(9600);
  delay(100);
}

void loop() {
   sentSize = Udp.sendPacket(sendBuffer, remoteIp, remotePort); 
   Serial.print("Bytes sent: ");
   Serial.println(sentSize);
   delay(1000);
}

Ethernet and UDP libs are the ones bundled with 0021.

On receiving end, I'll listen in with netcat:

nc -l -u -p 8889

I can see a row or two of "pingpingpingping" as expected, and then couple of pages of trash and then again "pingpingping"... repeat...
Serial console displays "Bytes sent: 4" every second.

[edit]
Packet capture of the problem is downloadable from here: http://byte.ee/udp_send_bug.pcap (74KB)
[/edit]

And here is a test case for Udp.available() problem

#include <SPI.h>
#include <Ethernet.h>
#include <Udp.h>

byte mac[] = {0x90, 0xA2, 0xDA, 0x00, 0x20, 0xA4};
byte ip[] = {192, 168, 1, 177};
unsigned int localPort = 8888;

byte remoteIp[4];
unsigned int remotePort;

char packetBuffer[64];

void setup() {
  Ethernet.begin(mac, ip);
  Udp.begin(localPort);
  Serial.begin(9600);
  delay(1000);
}

void loop() {
  int bytesAvailable = Udp.available();
  Serial.print("Available bytes: ");
  Serial.println(bytesAvailable);
  while(true); 
}

This will always output “Available bytes: 1024” to console, even if the ethernet cable is unplugged.

More info about the UDP trash-sending problem. To debug it I added these lines to the end of W5100Class::send_data_processing() method in libraries/Ethernet/utility/w5100.cpp

...
     ptr += len;
     writeSnTX_WR(s, ptr);
    [glow] Serial.print("TX_WR=");[/glow]
    [glow]Serial.println(ptr);[/glow]
 }

and also a simple println to the buffer wrap-around condition above, because that's where I thought the problem will be. To my surprise, it never gets to the wrap-around. TX_WR register goes from 1028 to 1280, increases nicely by 4 as I'm using 4-byte message. But then after 1280 it will wrap back to 1028 and at that point the trash-packets are sent.

I'm out of ideas here, it starts to look like a hardware problem... :frowning:

Update:
Probably not a harware problem. I was able to test the same program with identical etherShield and it behaved the same. Problem seems to be reading of the Sn_TX_WR register: its internal value seems to be 0 at startup, but somehow it gets read out as 1024... [smiley=shocked.gif]

Is it possible to just subtract 1024, if the value is over 1024?

I'll check it out in the evening after my work.

I'll first verify that the register value reading part (preprocessor generated functions in w5100.h and address calculations) are working correctly. Because the symptom is similar with Udp.available() problem: it reports 1024 if it should report 0.

There's one bit error somewhere out there and I'm pretty sure I'll hunt it down! [smiley=evil.gif]

Some kind of timing problem, ladies & gentelmen?

I modified w5100.h so that I wrote out read/write functions for Sn_TX_WR register:

static void writeSnTX_WR(SOCKET _s, uint16_t _data) {
    writeSn(_s, 0x0024, _data >> 8);
    writeSn(_s, 0x0025, _data & 0xFF);
}

static uint16_t readSnTX_WR(SOCKET _s) {
    uint16_t res = readSn(_s, 0x0024);
    res = (res << 8) + readSn(_s, 0x0025);
    return res;
}

calling readSnTX_WR(0) returns 1024

but when I put in some debugging statements:

static uint16_t readSnTX_WR(SOCKET _s) {
    uint16_t res = readSn(_s, 0x0024);
[glow]    Serial.print("RES1 "); Serial.println(res, HEX);[/glow]
    res = (res << 8) + readSn(_s, 0x0025);
[glow]    Serial.print("RES2 "); Serial.println(res, HEX);[/glow]
    return res;
}

It returns nicely 0 (and of course RES1 and RES2 outputs are both 0 too..

But then again, as the invalid byte gets read before valid one (0x0400) how is it affected by these println-s at all?!?

go figure...

Update:
Also works if I replace println-s there with delay(100);
I also made readSn() method public and called it directly in setup() function, right after initializing Ethernet lib:

uint8_t S0_TX_WR_HI = W5100.readSn(0, 0x24);
uint8_t S0_TX_WR_LO = W5100.readSn(0, 0x25);

... and if called this way, both bytes are zero as they should. But why timing doesn't matter in this case?

original method (doesn't work)

static uint16_t readSnTX_WR(SOCKET _s) {
    uint16_t res = readSn(_s, 0x0024);
    res = (res << 8) + readSn(_s, 0x0025);
    return res;
}

000003f4 <_ZN10W5100Class11readSnTX_WREh>:
 3f4:   ef 92           push    r14
 3f6:   ff 92           push    r15
 3f8:   1f 93           push    r17
 3fa:   cf 93           push    r28
 3fc:   df 93           push    r29
 3fe:   18 2f           mov     r17, r24        ;
 400:   64 e2           ldi     r22, 0x24       ; 36
 402:   70 e0           ldi     r23, 0x00       ; 0
 404:   0e 94 ab 00     call    0x156   ; 0x156 <_ZN10W5100Class6readSnEhj>
[glow] 408:   80 e0           ldi     r24, 0x00       ; 0     !!!1[/glow]
[glow] 40a:   ec 01           movw    r28, r24        ; !!!2[/glow]
 40c:   81 2f           mov     r24, r17
 40e:   65 e2           ldi     r22, 0x25       ; 37
 410:   70 e0           ldi     r23, 0x00       ; 0
 412:   0e 94 ab 00     call    0x156   ; 0x156 <_ZN10W5100Class6readSnEhj>
 416:   c8 0f           add     r28, r24
 418:   d1 1d           adc     r29, r1
 41a:   ce 01           movw    r24, r28
 41c:   df 91           pop     r29
 41e:   cf 91           pop     r28
 420:   1f 91           pop     r17
 422:   ff 90           pop     r15
 424:   ef 90           pop     r14
 426:   08 95           ret

As I understand, readSn method returns its 8-bit result value in r24, but

  • !!!1 - actual result of high-byte gets always zeroed!
  • !!!2 - r25 gets written into r29, which has undefined value at this point, and it ends up in the high byte of the result!

Compiler messes up when trying to write 8-bit method return value to 16-bit variable??!?

Slightly rewritten, but equivalent method which works:

static uint16_t readSnTX_WR(SOCKET _s) {
    uint8_t hi = readSn(_s, 0x0024);
    uint8_t lo = readSn(_s, 0x0025);
    uint16_t res = hi;
    res = (res << 8) + lo;
    return res;
}

000003f4 <_ZN10W5100Class11readSnTX_WREh>:
 3f4:   0f 93           push    r16
 3f6:   1f 93           push    r17
 3f8:   18 2f           mov     r17, r24       
 3fa:   64 e2           ldi     r22, 0x24       ; 36
 3fc:   70 e0           ldi     r23, 0x00       ; 0
 3fe:   0e 94 ab 00     call    0x156   ; 0x156 <_ZN10W5100Class6readSnEhj>
 402:   08 2f           mov     r16, r24
 404:   81 2f           mov     r24, r17
 406:   65 e2           ldi     r22, 0x25       ; 37
 408:   70 e0           ldi     r23, 0x00       ; 0
 40a:   0e 94 ab 00     call    0x156   ; 0x156 <_ZN10W5100Class6readSnEhj>
 40e:   30 2f           mov     r19, r16
 410:   20 e0           ldi     r18, 0x00       ; 0
 412:   28 0f           add     r18, r24
 414:   31 1d           adc     r19, r1
 416:   c9 01           movw    r24, r18
 418:   1f 91           pop     r17
 41a:   0f 91           pop     r16
 41c:   08 95           ret

... assembly looks correct and it's even smaller.

[edit]Added analysis[/edit]

Hi,

Could someone test what this code outputs for you, please (and what version of gcc-avr you're using)

And don't have anything connected to pin 4....

// trick the compiler not to optimize everything out
uint8_t getTheByte(uint8_t pin) {
  uint8_t res = digitalRead(pin); // should return 0
  res++; // increment it to 1
  return res;
}

uint16_t getTheInt(uint8_t pin) {
  uint16_t res = getTheByte(pin);
  res = (res << 8) + getTheByte(pin);
  return res;
}

void setup() {
  delay(3000); // just in case for the UNO serial problem...
  Serial.begin(9600);
  uint16_t value = getTheInt(4);
  Serial.println(value, HEX);   // should write 101
}

void loop() {
   while (true) {
     delay(2000);
   }
}

It should display 101, but for me it displays 1 as the upper byte gets "accidentally" zeroed out.

thanks!

Runned the sketch in IDE 0021 and the output was 101

"C:\Program Files (x86)\arduino-0021\hardware\tools\avr\bin\avr-gcc.exe" --version
avr-gcc.exe (WinAVR 20081205) 4.3.2

Thanks a lot!
It is safe to say that my complier version (avr-gcc (GCC) 4.5.1) is nicely broken...

00000120 <_Z9getTheInth>:
 120:   ef 92           push    r14
 122:   ff 92           push    r15
 124:   1f 93           push    r17
 126:   cf 93           push    r28
 128:   df 93           push    r29
 12a:   18 2f           mov     r17, r24
 12c:   0e 94 8c 00     call    0x118   ; 0x118 <_Z10getTheByteh>
[glow] 130:   80 e0           ldi     r24, 0x00       ; 0  bazinga![/glow]
 132:   ec 01           movw    r28, r24
 134:   81 2f           mov     r24, r17
 136:   0e 94 8c 00     call    0x118   ; 0x118 <_Z10getTheByteh>
 13a:   c8 0f           add     r28, r24
 13c:   d1 1d           adc     r29, r1
 13e:   ce 01           movw    r24, r28
 140:   df 91           pop     r29
 142:   cf 91           pop     r28
 144:   1f 91           pop     r17
 146:   ff 90           pop     r15
 148:   ef 90           pop     r14
 14a:   08 95           ret

Or is 4.5.3. in Beta?

Could you tell me how to generate the (dis)assembly you created?
Which command to use?

Whenever a sketch is built in IDE, /tmp/build.tmp directory is created with compiled object files and also ELF format executabe (.cpp.elf). I create disassembly using avr-objdump, it is part of GNU binutils for avr:

[villuv@tron build4737410445496487641.tmp]$ [glow]avr-objdump -d thesketch.cpp.elf [/glow]

or you can dump the .cpp.o file to just see the code of that module, not the whole thing.

It will output the disassembly to console, so stream it to file or pipe through some pager.

I'll give it a try (tomorrow :slight_smile:

Conclusion:

This small patch

will work around the compiler problem: UDPSendReceiveString and WebServer examples started working. Also compiled code size went down about 30 bytes.