Playground.arduino.cc will be read-only starting December 31st, 2018. For more info please look at this Forum Post

the care less do more webserver

Thanks to technologies like Ajax, todays powerfull webbrowsers, Cascadinf Stylesheets and frameworks like jQuery we can create awesome user-interfaces without running out of space or cycles on the Arduino platform. Take a minute to learn how this is done and enjoy the more user-friendly interfaces of your self-made gadgets in the future.

Examples https://www.danrl.de/danweb.php Fork it https://github.com/danrl/danweb

Serving JSON-formatted datasets is efficient and fast, all the eyecandy stuff can be done in the browser. In the meantime the Arduino sure has better things to do.

if (danweb_request("json")) {
	danweb_send_special_header(client, "application/json");
	client.print("[");
	client.print("{ \"id\": 1, \"name\": \"hello\"},");
	client.print("{ \"id\": 2, \"name\": \"world\"}");
	client.print("]");
}

You can create your own API, simple calls are made via GET-request. E.g. http://example.com/apicall?bar will result in the webstate-variables being updated to request=apicall and argv=foo.

if (danweb_request("apicall")) {
	danweb_send_header(client);
	apicall_foo(webstate.argv);
	client.println("success!");
}

Of course simple file-serving is also possible.

if (danweb_request("jquery.js")) {
	danweb_send_special_header(client, "text/javascript");
	danweb_send_file(client, "jquery.js");
}

Full source (see GitHub for latest version):

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

/*
 * -----------------------------------------------------------------------------
 * Webserver danweb
 * -----------------------------------------------------------------------------
 */
/* configuration */
#define DANWEB_MAC	{ 0x90, 0xa2, 0xda, 0x00, 0x91, 0x14 }
#define DANWEB_IP4	{ 94, 45, 236, 200 }
#define DANWEB_PORT	80
#define DANWEB_SERBAUD	9600
#define DANWEB_PINSS	10
#define DANWEB_CSELECT	4
#define DANWEB_BUFFER	512
#define DANWEB_REQSIZE	32
#define DANWEB_ARGSIZE	64

Server server(DANWEB_PORT);

struct danweb_webstate {
	char	request[DANWEB_REQSIZE+1];
	char	argv[DANWEB_ARGSIZE+1];
};

struct danweb_webstate webstate;

static int danweb_init ()
{
	Serial.begin(DANWEB_SERBAUD);
	Serial.println("danweb 0.1");

	// start ethernet and server
	byte mac[] = DANWEB_MAC;
	byte ip4[] = DANWEB_IP4;
	Ethernet.begin(mac, ip4);
	server.begin();

	// setting SS-pin as output
	pinMode(DANWEB_PINSS, OUTPUT);

	// init SD-Card
	Serial.print("SD init... ");
	if (SD.begin(DANWEB_CSELECT)) {
		Serial.println("done!");
	} else {
		Serial.println("failed!");
		return -1;
	}
	return 1;
}

static int danweb_readline_client (Client client, char *buf, int length)
{
	if (!client.available())
		return -1;

	int count = 0;
	char c;
	while ((c = client.read()) > 0) {
		if (c == '\r')
			continue;
		if (c == '\n')
			break;
		if (count >= length)
			break;
		buf[count++] = c;
	}
	buf[count] = 0;
	return count;
}

static int danweb_read_file (File file, char *buf, int length)
{
	int count = 0;
	int16_t c;
	while ((c = file.read()) > 0) {
		buf[count++] = (char) c;
		if (count >= length)
			break;
	}
	buf[count] = 0;
	return count;
}

static int danweb_is_get_request (char *buf)
{
	// a very simple test, but it works for now
	if (strstr(buf, "GET ") != 0)
		return 1;
	return 0;
}
static void danweb_parse_get_request (char *buffer)
{
	// cut off the protocol version
	(strchr(&(buffer[5]), ' '))[0] = 0x0;
	// copy and truncate request
	strncpy(webstate.request, &(buffer[5]), DANWEB_REQSIZE);
	(strchr(webstate.request, '?'))[0] = 0x0;
	// arguments string
	strncpy(webstate.argv, strchr(&(buffer[5]), '?') +1, DANWEB_ARGSIZE);
}

static void danweb_send_header (Client client)
{
		client.println("HTTP/1.1 200 OK");
		client.println("Content-Type: text/html");
		client.println();
}

static void danweb_send_404 (Client client)
{
		client.println("HTTP/1.1 404 Not Found");
		client.println("Content-Type: text/html");
		client.println();
		client.println("404 Gone for good");
}

static void danweb_send_special_header (Client client, char *contenttype)
{
		client.println("HTTP/1.1 200 OK");
		client.print("Content-Type: ");
		client.println(contenttype);
		client.println();
}

static void danweb_send_file (Client client, char* path)
{
	char fullpath[25];
	strncpy(fullpath, "/danweb/", 24);
	strncat(fullpath, path, 24);
	if (SD.exists(fullpath)) {
		File f = SD.open(fullpath, FILE_READ);
		int16_t c;
		char buffer[DANWEB_BUFFER+1];
		int i = 0;
		do {
			danweb_read_file(f, buffer, sizeof(buffer));
			client.print(buffer);
		} while (strlen(buffer));
		f.close();

	} else {
		Serial.print("file not found: ");
		Serial.println(fullpath);
	}
}

static int danweb_idle (Client client)
{
	if (!client) {
		delay(1);
		client.stop();
		return 1;
	}
	char buffer[DANWEB_BUFFER];
	while (client.connected() && client.available()) {
		danweb_readline_client(client, buffer, sizeof(buffer));
		if (danweb_is_get_request(buffer)) {
			danweb_parse_get_request(buffer);
			Serial.print("request=");
			Serial.println(webstate.request);
			Serial.print("argv=");
			Serial.println(webstate.argv);
		}
	}
	return 0;
}

static int danweb_request (char *s)
{
	if (strncmp(webstate.request, s, DANWEB_REQSIZE) == 0)
		return 1;
	return 0;
}
/*
 * -----------------------------------------------------------------------------
 */

void setup()
{
	/* ----- initialize webserver ----- */
	if (!danweb_init())
		return;
	/* -------------------------------- */


	// your setup stuff goes here
	// serial line is already up


	/* -------------------------------- */
}

void loop()
{
	/* -------------------------------- */


	// your loop stuff goes here
	// have fun


	/* -------------------------------- */
	Client client = server.available();
	if (danweb_idle(client))
		return;
	/* -------------------------------- */
	if (danweb_request("")) {
		danweb_send_header(client);
		danweb_send_file(client, "index.htm");

	} else if (danweb_request("version")) {
		danweb_send_header(client);
		client.println("Version 0.1");

	} else if (danweb_request("json")) {
		danweb_send_special_header(client, "application/json");
		client.print("[");
		client.print("{ \"id\": 1, \"name\": \"hello\"},");
		client.print("{ \"id\": 2, \"name\": \"world\"}");
		client.print("]");

	} else if (danweb_request("apicall")) {
		danweb_send_header(client);
		client.print("success! ");
		client.print(webstate.argv);

	/* some file serving */
	} else if (danweb_request("style.css")) {
		danweb_send_special_header(client, "text/css");
		danweb_send_file(client, "style.css");

	} else if (danweb_request("jquery.js")) {
		danweb_send_special_header(client, "text/javascript");
		danweb_send_file(client, "jquery.js");

	} else {
		danweb_send_404(client);
	}
	client.stop();
}