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();
}

Share