I am running the Ethernet Shield (W5100) on top of the DUE and I am seeing some weird behavior in my own code that i am not seeing in the example code.
Here is my sketch:
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
EthernetClient m_client;
int Send(char * server, int port, char * payload, char * response, int responseLength)
{
int result = 0;
if (m_client.connect(server, port))
{
//send payload
m_client.println(payload);
//wait for a response
uint32_t lastRead = millis();
while (!m_client.available())
{
//exit if timeout exceeded, server didn't respond
if (millis() - lastRead > 5000)
{
m_client.stop();
Serial.println("Timeout reached");
return -1;
}
lastRead = millis();
}
int x = 0;
char c;
while (m_client.available())
{
result++; //we need to read the entire result inorder to calculate how many bytes we received
c = m_client.read();
if (x < responseLength - 1)
{
response[x] = c;
x++;
}
}
//terminate the response with a null char
if (response != NULL)
{
response[x] = '\0';
}
}
else
{
Serial.println("could not connect to server");
result = -2;
}
m_client.stop();
return result;
}
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
// start the Ethernet connection:
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
}
}
void loop()
{
int result = 0;
//build the payload
String strPayload;
strPayload += "GET " + String("/robots.txt") + " HTTP/1.1\r\n";
strPayload += "Host: " + String("www.google.com") + "\r\n";
strPayload += "User-Agent: Arduino/1.0\r\n";
strPayload += "Connection: close\r\n\r\n\r\n";
char payload[strPayload.length()];
strPayload.toCharArray(payload, strPayload.length());
char response[512];
result = Send("www.google.com", 80, payload, response, 512); //results in premature return of send method
//result = Send("www.google.com", 80, payload, NULL, 0); //gets full response of 8078 bytes
Serial.println(result);
delay(1000);
}
www.googe.com\robots.txt is 8078 bytes including http response headers. the Send method returns the number of bytes in the response received.
the problem i am seeing is that if i store the response to my request in a passed in buffer in the Send method, m_client.available() will be false before the 8078 bytes have been returned. when it stops prematurely, it always stops on a multiple of 1024, usually 2048.
if i dont care to store the response, i always receive the full 8078 bytes every time.
I am not sure, but this thread looks to be a similar problem. i also feel like im reading the data from the ethernet to slowly but i am not sure of any other way to read the response.
That is correct. The entire response will be sent in multiple packets. Your code must read each packet (empty the w5100 rx buffer) before the w5100 sends an ACK for that packet, then the sender will send another packet. Between those, there will be no characters in the rx buffer. The signal that the server is finished sending packets is it will close the connection. Wait for that. Here is your code with that part added.
while(m_client.connected())
{
while (m_client.available())
{
result++;
c = m_client.read();
if (x < responseLength - 1)
{
response[x] = c;
x++;
}
}
}
This has no timeout, so it is prone to lockups. Here is my client code with the timeout. Look for the connectLoop variable. It controls the timeout timing. http://playground.arduino.cc/Code/WebClient
SurferTim:
That is correct. The entire response will be sent in multiple packets. Your code must read each packet (empty the w5100 rx buffer) before the w5100 sends an ACK for that packet, then the sender will send another packet. Between those, there will be no characters in the rx buffer. The signal that the server is finished sending packets is it will close the connection. Wait for that. Here is your code with that part added.
while(m_client.connected())
{
while (m_client.available())
{
result++;
c = m_client.read();
if (x < responseLength - 1)
{
response[x] = c;
x++;
}
}
}
This has no timeout, so it is prone to lockups. Here is my client code with the timeout. Look for the connectLoop variable. It controls the timeout timing.
http://playground.arduino.cc/Code/WebClient
Looks like that did the trick. I changed your example code a bit to take advantage of millis() and avoid adding in an artificial delay.
On a side note, why do the code snippets on the playground not use monospace font???
int CM_Ethernet::Send(char * server, int port, char * payload, char * response, int responseLength)
{
int result = 0;
if (m_client.connect(server, port))
{
//send payload
m_client.println(payload);
//wait for a response
uint32_t lastReadTime = millis();
while (!m_client.connected())
{
//exit if timeout exceeded, server didn't respond
if (millis() - lastReadTime > m_receiveTimeout)
{
m_client.stop();
m_serial->println("Connect Timeout reached");
return CM_Ethernet::ConnectTimeout;
}
}
int x = 0;
char c;
lastReadTime = millis();
while (m_client.connected())
{
while (m_client.available())
{
result++; //we need to read the entire result inorder to calculate how many bytes we received
c = m_client.read();
if (x < responseLength - 1)
{
response[x] = c;
x++;
}
lastReadTime = millis();
}
if (millis() - lastReadTime > m_receiveTimeout)
{
m_client.stop();
m_serial->println("Receive timeout reached, closing connection");
return CM_Ethernet::ReceiveTimeout;
}
}
//terminate the response with a null char
if (response != NULL)
{
response[x] = '\0';
}
}
else
{
m_serial->println("could not connect to server");
result = CM_Ethernet::CouldNotConnect;
}
m_client.stop();
return result;
}
The "no delay" will work, but it takes more code (program memory) than setting an integer to zero or an increment and compare. Some of my example code borders on using all the program memory in an Uno as it is.
The millis() call after reading each character will probably take more time than the delay(1) call after each packet (1400 characters on a full packet) anyway.
edit: And the delay(1) call is about the amount of time the w5100 needs to send an ACK and get another packet, so it isn't like it is taking up much time anyway.
There is a time and place to use the "blink without delay" approach, but this wasn't it for me.