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