Spark Core Web Server [Solved]

Hi there,

I am using the Webduino port to implement a web server on the Spark Core. This works quite well with a few elements but seems to hang if the html code grows beyond 10 or 15 lines.

Is this a limitation to the Core or is it a limitation to the software?
Can the Core handle a more complex web server?

Thanks,
Kyle

Hi @kklumper

One of the problems on Spark (and Arduino) is that client.print("this is some data"); produces a network packet for each character in the string by default.

I know that this is getting fixed but in the mean time you can try sending your data using the client.write(buf, len); method instead. If you look around here in the forum you will find others have tried that it worked out for them.

Thanks for the quick reply bko!

I attempted the change but my browser timed out. I’m not sure if I implemented it properly. I’ve got a fairly shaky grasp of web servers so I modified some example code from the Webduino library. This is what I have so far:

#include "application.h"
#include "WebServer/WebServer.h"
#include "SparkIntervalTimer/SparkIntervalTimer.h"

SYSTEM_MODE(AUTOMATIC);

#define PREFIX "/buzz"
WebServer webserver(PREFIX, 80);

String index_part1("<!DOCTYPE html> <html> <head> <title>Uni Web UI Mobile</title> <link rel='icon' type='image/png' href='images/icon.ico'> <style> html, body { background-color: blue; padding:0; margin:0; height:100%; } </style> </head>  <body> <table> <tr> <th></th> <th>Start Time</th> <th>Stop Time</th>		<th>Picture Frequency</th> </tr> <tr> <td>Sunday</td> <td><input type='time' value='--:--'></td> <td><input type='time' value='--:--'></td>		<td><input type='number' style='width:50px'> minutes</td> </tr> <tr> <td>Monday</td> <td><input type='time' value='--:--'></td> <td><input type='time' value='--:--'></td>		<td><input type='number' style='width:50px'> minutes</td> </tr> <tr> <td>Tuesday</td> <td><input type='time' value='--:--'></td> <td><input type='time' value='--:--'></td>		<td><input type='number' style='width:50px'> minutes</td> </tr> <tr> <td>Wednesday</td> <td><input type='time' value='--:--'></td> <td><input type='time' value='--:--'></td>		<td><input type='number' style='width:50px'> minutes</td> </tr> <tr> <td>Thursday</td> <td><input type='time' value='--:--'></td> <td><input type='time' value='--:--'></td>		<td><input type='number' style='width:50px'> minutes</td> </tr> <tr> <td>Friday</td> <td><input type='time' value='--:--'></td> <td><input type='time' value='--:--'></td>		<td><input type='number' style='width:50px'> minutes</td> </tr> <tr> <td>Saturday</td> <td><input type='time' value='--:--'></td> <td><input type='time' value='--:--'></td>		<td><input type='number' style='width:50px'> minutes</td> </tr> </table> </body> </html>");
    
int timeLapse = 0;

void timeCmd(WebServer &server, WebServer::ConnectionType type, char *, bool)
{
  if (type == WebServer::POST)
  {
    bool repeat;
    char name[16], value[16];
    do
    {
      repeat = server.readPOSTparam(name, 16, value, 16);

      /* this is a standard string comparison function.  It returns 0
       * when there's an exact match.  We're looking for a parameter
       * named "buzz" here. */
      if (strcmp(name, "buzz") == 0)
      {
	/* use the STRing TO Unsigned Long function to turn the string
	 * version of the delay number into our integer timeLapse
	 * variable */
        timeLapse = strtoul(value, NULL, 10);
        EEPROM.write(0, timeLapse);
      }
    } while (repeat);
    
    // after procesing the POST data, tell the web browser to reload
    // the page using a GET method. 
    server.httpSeeOther(PREFIX);
    return;
  }

  /* for a GET or HEAD, send the standard "it's all OK headers" */
  server.httpSuccess();

  /* we don't output the body for a HEAD request */
  if (type == WebServer::GET)
  {
      server.write((uint8_t*)index_part1.c_str(),index_part1.length());
  }
}

int ledPin     = D7;
int pushButton = D5;
int blinkCount = 0;

IntervalTimer myTimer;
// Pre-define ISR callback functions
void blinkLED(void);

// Callback for Timer 1
void blinkLED(void) {
  blinkCount++;
}


/* This function is called once at start up ----------------------------------*/
void setup()
{

    webserver.setDefaultCommand(&timeCmd);
    webserver.begin();

    // Digital I/O
    pinMode(ledPin, OUTPUT);
    pinMode(pushButton, INPUT);
    
    // AUTO allocate blinkLED to run every 500ms (1000 * .5ms period)
    myTimer.begin(blinkLED, 2000, hmSec);
    
     timeLapse = EEPROM.read(0);
}

void loop()
{

    // process incoming connections one at a time forever
    webserver.processConnection();

    if (blinkCount >= timeLapse)
    {
        blinkCount = 0;
        digitalWrite(ledPin, HIGH);
        delay(100);
    }else
    {
        digitalWrite(ledPin, LOW);
    }
}

I am not sending any POST data in this example so it’s mainly just supposed to spit out a webpage when a GET request is sent from a browser.

1 Like

This is looking good–did it work?

You can save RAM by making your index_part1 a const char array instead a string (assuming you don’t need to change it) since then it will live in flash and not RAM.

const char index_part1[] = "<!DOCTYPE html> <html>...";

...
server.write((uint8_t*)index_part1,strlen(index_part1));
...
1 Like

Unfortunately no… As a sanity check I replaced the html string with a shorter one and it did work.

const char index_part2[] = "<!DOCTYPE html> <html><head><title>Uni Web UI</title> <body><h1>Take Picture Every</h1><form action='/uni' method='POST'><p><button name='time' value='1'>1 second</button></p><p><button name='time' value='10'>10 seconds</button></p></form></body></html>";

Could it be that the Core isn’t powerful enough to serve large pages?

I didn’t check the length, but if it is longer than about 1400 bytes, you should split it into 1400 byte chunks.

1 Like

It works! The original length was 1560, I broke it into two chunks and it worked without hanging. Is a 1400 byte length a general rule for serving pages on the Core?

Hi @kklumper

Glad it is working for you!

When you are using the client.write(buf,len) interface, you are directly generating packets and the TI part has a 1468 byte limit on packet size (called the MTU setting). It should be pretty easy to write a loop to chunk up a larger array in to packets that are less than 1468 bytes each.

1 Like

Good to know. Thanks for your help bko!

You have to manually break up the info into chunks smaller than the MTU? Isn’t that what OS network stacks are for? :slight_smile:

Not on small microcontrollers. There is no large RAM buffer pool available you have to do more things yourself.