So I have come to the conclusion that the Ethernet shield can not process more than 16kb/sec with out locking up. I have it setup to receive art-net (light control over Ethernet). There is a super simple code that translates it to a rgb led via 3 pwm outputs. I am using the Ethernet library that comes with Arduino 0022. 16 kb/sec is basically 144 leds at 3 colors each, which is 432 channels. Anything less that that works great, but anything more and the Arduino/Ethernet shield just lock up and I can no longer ping it.
So I have come to the conclusion that the Ethernet shield can not process more than 16kb/sec
It might depend on
- how you sent the data, text or binary, compressed or not.
- creating a socketconnection and closing it per instruction gives other performances than keep a socket open an send commands.
- wrapped in HTTP versus raw sockets
- Communicating over UDP might be faster than TCP,
- blocking code outside the ethernet lib.
- ....
Can you post your code, and describe the protocol a bit, so we can think with you if there are alternatives to increase throughput?
It is UDP. Basically it just reads the first 2 bytes and maps it to an led.
#include <SPI.h>
#include <Ethernet.h>
#include <Udp.h>
/* ETHERNET CONFIGURATION *************************************/
/* ARDUINO: set MAC, IP address of Ethernet shield, its gateway,
and local port to listen on for incoming packets */
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC address to use
byte ip[] = { 192, 168, 0, 2 }; // Arduino's IP address
int localPort = 6454; //local port to listen on
/***************************************************************/
#define MAX_SIZE 530 //maximum packet size
int cont = 1 ;
byte packetBuffer[MAX_SIZE]; //buffer to hold incoming packet
int packetSize; //holds received packet size
byte remoteIp[4]; //holds recvieved packet's originating IP
int remotePort; //holds received packet's originating port
int i;
int r;
void setup() {
Ethernet.begin(mac,ip);
Udp.begin(localPort);
Serial.begin(9600);
}
void loop() {
if(Udp.available()) {
packetSize = Udp.readPacket(packetBuffer,MAX_SIZE,remoteIp,(uint16_t *)&remotePort);
if(packetSize <= MAX_SIZE) {
for(i=0; i<packetSize; i++) {
if(i >= 18) {
if((i-17 == cont) && (packetBuffer[i]!= r)) {
r = packetBuffer[i];
r = map(r, 0, 255, 0, 1023);
analogWrite(5, r);
Serial.println(r);}
} else {
//PANIC - packet too long!
// we've already clobbered mem past our buffer boundary
Serial.println("ERROR");
}
}
}
}
}
There's this thing called bottleneck... and apparently you haven't checked where yours is.
If you have a look at the SPI interface between the Wiznet and ATmega328, you'll notice that's the weakest slowest link on the entire setup and therefore is where the slow speed comes into place.
Of course, this can also be a problem of any of the things rob said, but my guess is in the SPI interface.
In this case, I'd bet on the serial port.
Serial.begin(9600);
:
Serial.println(r);}
I'm surprised that you get as much as 16kb; you ought to be limited to 9.6kb...
Crank up your serial speed and see if things improve.
(Note that UDP has no flow control, so if you're too slow processing packets, eventually the Wiznet chip will have no choice other than to start throwing incoming packets away.)
I'm surprised that you get as much as 16kb; you ought to be limited to 9.6kb...
16kb = ethernetspeed
9600 = serial.
do not need to be the same
Westfw you makes a good point:
some math to enlighten
Serial.println(r);
9600 baud ~~ 1 char per millisecond, printing an int with a return (= 3 bytes) take 3 millis => max 333 ints per second
115200 baud ~~ 12x faster, takes only 0.25 millis (approx) => max ~4000 ints per second
Serial.prinln("ERROR"); takes twice as long . (just printing an E would be sufficient but it looks a bit cryptic)
Serial.println(r);
if the value of r would fit in a byte (<256) you could do
if (r <256) Serial.println((byte)r);
else Serial.println(r);
you would win one byte = 33% of the transmission.
--- Update ---
it is even worse, see post PaulS (Thanks Paul)
Serial.println(r);
9600 baud ~~ 1 char per millisecond, printing an int with a return (= 3 bytes)
If r is an int, it is converted to a string. The resulting string can contain as many as 6 characters ("-32767"). The ln on the end of function means that the function also sends a carriage return AND a line feed. The statement could be sending as many as 8 bytes, not 3.
Isn't the Serial port buffered and transmitting with interrupts?
In the beginning I know it wasn't buffered. Then a few months ago, I think I've seen somewhere that the serial port was implemented with interrupts. Or is it only interrupted for incoming bytes?
If r is an int, it is converted to a string. The resulting string can contain as many as 6 characters ("-32767"). The ln on the end of function means that the function also sends a carriage return AND a line feed. The statement could be sending as many as 8 bytes, not 3.
you're absoluty right, I made a note in my post,
Do you think I could get better performance by removing all of the serial commands completely? They're only for debugging. I just want a smooth led fade.
bubulindo:
...I think I've seen somewhere...
For the current version (arduino-0022), all flavors of HardwareSerial outputs (all of the various kinds of print functions) boil down to the HardwareSerial::write() function:
void HardwareSerial::write(uint8_t c)
{
while (!((*_ucsra) & (1 << _udre)))
;
*_udr = c;
}
This waits until the previous data byte has left the transmit data register (and is being shifted out) before writing the next byte to that register and returning to the calling function.
Bottom line: All HardwareSerial output functions are blocking.
Regards,
Dave
After removing all traces of any serial command, the Ethernet shield does not seem to lock up anymore! Thanks guys!
I can easily push 35 kb/sec to it. that's good for a 200x200 RGB matrix or 120,000 channels!
Isn't the Serial port buffered and transmitting with interrupts?
No, it's not. But even if it was, it wouldn't matter. Buffering only "smooths" irregular output. Eventually (and "rather soon" on an arduino with only about 100 bytes of buffer) you fill up the buffer and start blocking anyway.
Buffering will help if you write at an average rate of much less than the serial port native rate (one 10 byte message every second will no longer take 10ms), but if you're writing continuous data that doesn't have some other bottleneck, you WILL be slowed down by the serial port.
westfw:
Isn't the Serial port buffered and transmitting with interrupts?
No, it's not. But even if it was, it wouldn't matter. Buffering only "smooths" irregular output. Eventually (and "rather soon" on an arduino with only about 100 bytes of buffer) you fill up the buffer and start blocking anyway.
Buffering will help if you write at an average rate of much less than the serial port native rate (one 10 byte message every second will no longer take 10ms), but if you're writing continuous data that doesn't have some other bottleneck, you WILL be slowed down by the serial port.
I get what you're saying about the amount of data and serial speeds. Of course, the buffer size, should be of "adequate" size not to overfill quickly. But, if the transmission is done with interrupts, the main program, although being slowed down by the serial transmission to load UDR with the next char in the buffer, will carry on processing instead of waiting for the transmission of the bit. This becomes even more important as the baud rates get slower and slower.
Care to explain a bit further about the "smooth" part?
bubulindo:
westfw:
Isn't the Serial port buffered and transmitting with interrupts?
No, it's not. But even if it was, it wouldn't matter. Buffering only "smooths" irregular output. Eventually (and "rather soon" on an arduino with only about 100 bytes of buffer) you fill up the buffer and start blocking anyway.
Buffering will help if you write at an average rate of much less than the serial port native rate (one 10 byte message every second will no longer take 10ms), but if you're writing continuous data that doesn't have some other bottleneck, you WILL be slowed down by the serial port.I get what you're saying about the amount of data and serial speeds. Of course, the buffer size, should be of "adequate" size not to overfill quickly. But, if the transmission is done with interrupts, the main program, although being slowed down by the serial transmission to load UDR with the next char in the buffer, will carry on processing instead of waiting for the transmission of the bit. This becomes even more important as the baud rates get slower and slower.
Care to explain a bit further about the "smooth" part?
I need to go to bed. Yes, I get the smooth part.
Once the buffer is full, the main program will take exactly as long busy-waiting for a spot in the buffer as it would have taken to busy-wait for the actual UART register to become empty. A non-interrupt-driven uart output function is pretty exactly equivalent to an interrupt-driven function with a buffer size of one.
There are some papers in the Networking domain that explain why having lots of buffering is not necessarily a good idea. One of the earliest is RFC 970. The latest hubbub is being tagged "buffer bloat." Basically, applications need to understand the physical limitations on their throughput, and "hiding" the limitations by putting a lot of buffering in front of any bottlenecks can be counterproductive.
westfw:
Once the buffer is full, the main program will take exactly as long busy-waiting for a spot in the buffer as it would have taken to busy-wait for the actual UART register to become empty. A non-interrupt-driven uart output function is pretty exactly equivalent to an interrupt-driven function with a buffer size of one.
Unless of course, you program the uart_puts() function to return 0 if buffer is full. That way, you won't wait for it to clear before carrying on with the software. I'm not saying that it is the best approach, but it's the one I use to get a sense if the serial load vs baud rate is good enough.
Luckily, the only data intensive programs I made had to dump the memory values at the end of a test to a computer, so blocking if the buffer was full was a necessity but not a limitation.
westfw:
and "hiding" the limitations by putting a lot of buffering in front of any bottlenecks can be counterproductive.
Kinda like the Arduino interface does?
I'm just saying that Arduino is good to get people interested and put to work their ideas without lots of knowledge, but by starting easily, it kinda limits how far you can go because in reality you don't have a clue about what's a microcontroller and how it does what it does. This is the case here. True, that's what the forum is for, but I believe there is kind of a middle ground that is not explored and should be the next Arduino iteration...
Maybe building an IDE for AVR-GCC and having some graphical menus to configure the controller... oh wait, that's Codevision. But, yeah... something like that (but a bit easier on the configurations) would enable people to learn a bit further and understand the reason behind some of these "limitations". And then choose between writing in the C++ style or going with C.