Hi,
I'm working on a simple web server, and am experiencing memory issues. Please see my code and output below.
#include <SdFat.h>
#include <SdFatUtil.h>
#include <Ethernet.h>
#include <SPI.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 206, 5 };
byte gateway[] = { 192, 168, 206, 1 };
byte subnet[] = { 255, 255, 255, 248 };
Server server(80);
boolean waiting = true;
boolean get = false;
boolean post = false;
boolean boundary = false;
/************ SDCARD STUFF ************/
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
void error_P(const char* str) {
PgmPrint("error: ");
SerialPrintln_P(str);
if (card.errorCode()) {
PgmPrint("SD error: ");
Serial.print(card.errorCode(), HEX);
Serial.print(',');
Serial.println(card.errorData(), HEX);
}
while(1);
}
void setup() {
Serial.begin(9600);
PgmPrint("Free RAM: ");
Serial.println(FreeRam());
// initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
// breadboards. use SPI_FULL_SPEED for better performance.
pinMode(10, OUTPUT); // set the SS pin as an output (necessary!)
digitalWrite(10, HIGH); // but turn off the W5100 chip!
if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!");
// initialize a FAT volume
if (!volume.init(&card)) error("vol.init failed!");
PgmPrint("Volume is FAT");
//Serial.println(volume.fatType(),DEC);
//Serial.println();
if (!root.openRoot(&volume)) error("openRoot failed");
// list file in root with date and size
PgmPrintln("Files found in root:");
root.ls(LS_DATE | LS_SIZE);
Serial.println();
// Recursive list of all directories
PgmPrintln("Files found in all dirs:");
root.ls(LS_R);
Serial.println();
PgmPrintln("Done");
// Debugging complete, we start the server!
Ethernet.begin(mac, ip);
server.begin();
}
void loop()
{
if (waiting) {
Serial.print("Waiting for connection ...");
Serial.println(FreeRam());
waiting = false;
}
CheckForConnection();
post = false;
get = false;
boundary = false;
}
void SendFile(String url_args) {
Serial.print("Request to send file...");
Serial.println(FreeRam());
Client client = server.available();
url_args = url_args.substring(5, url_args.length() -9);
url_args = url_args.trim();
if (url_args.length() == 0) { url_args = "index.htm"; };
Serial.println(url_args);
if (url_args.length() > 0) {
if (! file.open(&root, url_args.cstr(), O_READ)) {
client.println("HTTP/1.1 404 Not Found");
client.println();
} else {
Serial.print("Opening file...");
Serial.println(url_args);
client.println("HTTP/1.1 200 OK");
client.print("Content-Type: ");
if (url_args.endsWith(".htm")) {
client.println("text/html");
} else if (url_args.endsWith(".jpg")) {
client.println("image/jpeg");
} else if (url_args.endsWith(".ico")) {
client.println("image/png");
} else if (url_args.endsWith(".png")) {
client.println("image/png");
} else {
client.println("text");
}
client.println();
Serial.print("Sending file... ");
int16_t c;
while ((c = file.read()) >= 0) {
if (client.connected()) {client.print((char)c); continue;}
break;
}
file.close();
Serial.println("done");
}
} else {
client.println("HTTP/1.1 404 Not Found");
client.println();
}
}
void CheckForConnection() {
Client client = server.available();
if (client) {
while (client.connected()) {
String request_line;
while (client.available()) {
char c = client.read();
if (c != '\n' && c != '\r') {
request_line += c;
continue;
}
Serial.println(request_line);
if ((request_line.startsWith("GET /")) && (request_line.endsWith("HTTP/1.1")) && (!get) && (!post)) {
Serial.println("Found GET request");
get = true;
SendFile(request_line.cstr());
} else if ((request_line.startsWith("POST /")) && (request_line.endsWith("HTTP/1.1")) && (!get) && (!post)) {
Serial.println("Found POST request");
post = true;
} else if ((request_line.startsWith("Content-Type:")) && (!get) && (post) && (!boundary)) {
Serial.println("Found boundary");
boundary = true;
int boundary_begin = request_line.indexOf("boundary=");
request_line = request_line.substring(boundary_begin);
request_line = request_line.trim();
Serial.print("Boundary : ");
Serial.println(request_line);
}
request_line = "";
}
delay(1);
client.stop();
waiting = true;
}
}
return;
}
This is what the console output looks like when trying to grab index.htm:
tail -f /dev/ttyUSB0
Free RAM: 827
Volume is FATFiles found in root:
INDEX.HTM 2010-09-13 15:50:30 1318
FAVICON.ICO 2010-09-13 15:53:12 1406
LOGO.JPG 2010-09-10 02:20:04 8249
CONTACT.HTM 2010-09-10 21:09:44 908
PARTS.JPG 2010-09-10 22:29:20 68907
SETUP1.JPG 2010-09-10 22:29:00 38175
SETUP2.JPG 2010-09-10 22:29:00 45070
Files found in all dirs:
INDEX.HTM
FAVICON.ICO
LOGO.JPG
CONTACT.HTM
PARTS.JPG
SETUP1.JPG
SETUP2.JPG
Done
Waiting for connection ...827
GET / HTTP/1.1
Found GET request
Request to send file...616
index.htm
Opening file...index.htm
Sending file... done
User-Agent: curl/7.19.5 (x86_64-pc-linux-gnu) libcurl/7.19.5 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.15
Host: 192.168.206.5
Accept: */*
Waiting for connection ...584
The numbers at the end of "Waiting for connection ..." and "Request to send file..." are the print out of FreeRam();
From what I can tell it would seem that the String request_line is the problem. Its like its not letting go of memory.
If I let it run for a few hours serving the page and images the memory only gets so low.. but never gets close to where it starts in the main loop, typically around 350. I would think that it should go back up to 827 or close to it if everything free's up properly.
I had read posts about memory issues using Strings in that the string wasn't being free'd. I am using the latest Linux build (arduino-0019). I checked WString.h and it has the destructor:
~String() { free(_buffer); _length = _capacity = 0;}
Also note
SendFile(request_line.cstr());
I had to modify WString.h with:
const char * cstr() const {return _buffer;}
So that I could pass my String object properly to :
if (! file.open(&root, url_args.cstr(), O_READ)) {
....
}
I'm still learning C/C++ so its not very apparent where I'm going wrong and working with such tight memory restrictions is showing how poor my coding skills really are.
Any help in the right direction would be appreciated.
--
Shawn