diff --git a/examples/arduino/EspLinkSample/EspLinkSample.ino b/examples/arduino/EspLinkSample/EspLinkSample.ino index 5fe59bd..937cc07 100644 --- a/examples/arduino/EspLinkSample/EspLinkSample.ino +++ b/examples/arduino/EspLinkSample/EspLinkSample.ino @@ -1,7 +1,88 @@ #include "WebServer.h" + +#define LED_PIN 13 + +int8_t blinking = 0; +int8_t frequency = 10; +uint16_t elapse = 100; +uint32_t next_ts = 0; + +void ledHtmlCallback(WebServerCommand command, char * data, int dataLen); + +const char ledURL[] PROGMEM = "/LED.html.json"; + +const WebMethod PROGMEM methods[] = { + { ledURL, ledHtmlCallback }, + { NULL, NULL }, +}; + +WebServer webServer(Serial, methods); + +void setup() +{ + Serial.begin(57600); + webServer.init(); + + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, false); +} + +void loop() +{ + webServer.loop(); + + if( blinking ) + { + if( next_ts <= millis() ) + { + digitalWrite(LED_PIN, !digitalRead(LED_PIN)); + next_ts += elapse; + } + } +} + + +void ledHtmlCallback(WebServerCommand command, char * data, int dataLen) +{ + switch(command) + { + case BUTTON_PRESS: + if( strcmp_P(data, PSTR("btn_on") ) == 0 ) + { + blinking = 0; + digitalWrite(LED_PIN, true); + } else if( strcmp_P(data, PSTR("btn_off") ) == 0 ) + { + blinking = 0; + digitalWrite(LED_PIN, false); + } else if( strcmp_P(data, PSTR("btn_blink") ) == 0 ) + { + blinking = 1; + next_ts = millis() + elapse; + } + break; + case LOAD: + webServer.setArgNum(1); + case REFRESH: + if( command == REFRESH ) + webServer.setArgNum(1); + + if( blinking ) + webServer.setArgStringP("text", PSTR("LED is blinking")); + else + webServer.setArgStringP("text", digitalRead(LED_PIN) ? PSTR("LED is turned on") : PSTR("LED is turned off")); + break; + default: + break; + } +} + + +/* #include "EspLink.h" + void packetReceived(CmdRequest *req); EspLink espLink(Serial, packetReceived); @@ -97,4 +178,4 @@ void setup() { void loop() { espLink.readLoop(); } - +*/ diff --git a/examples/arduino/EspLinkSample/WebServer.cpp b/examples/arduino/EspLinkSample/WebServer.cpp new file mode 100644 index 0000000..df7e50f --- /dev/null +++ b/examples/arduino/EspLinkSample/WebServer.cpp @@ -0,0 +1,150 @@ +#include "WebServer.h" + +WebServer * WebServer::instance = NULL; + +void webServerCallback(CmdRequest *req) +{ + WebServer::getInstance()->handleRequest(req); +} + +WebServer::WebServer(Stream &streamIn, const WebMethod * PROGMEM methodsIn):espLink(streamIn, webServerCallback),methods(methodsIn),stream(streamIn) +{ + instance = this; +} + +void WebServer::init() +{ + registerCallback(); +} + +void WebServer::loop() +{ + // TODO: resubscribe periodically + espLink.readLoop(); +} + +void WebServer::registerCallback() +{ + espLink.sendPacketStart(CMD_CB_ADD, 100, 1); + espLink.sendPacketArg(5, (uint8_t *)"webCb"); + espLink.sendPacketEnd(); +} + +void WebServer::invokeMethod(RequestReason reason, WebMethod * method, CmdRequest *req) +{ + switch(reason) + { + case WS_BUTTON: + { + uint16_t len = espLink.cmdArgLen(req); + char bf[len+1]; + bf[len] = 0; + espLink.cmdPopArg(req, bf, len); + + method->callback(BUTTON_PRESS, bf, len); + } + break; + case WS_SUBMIT: + { + /* TODO: iterate through fields */ + } + return; + default: + break; + } + + args_to_send = -1; + method->callback( reason == WS_LOAD ? LOAD : REFRESH, NULL, 0); + + if( args_to_send == -1 ) + { + espLink.sendPacketStart(CMD_WEB_JSON_DATA, 100, 2); + espLink.sendPacketArg(4, remote_ip); + espLink.sendPacketArg(2, (uint8_t *)&remote_port); + } + while( args_to_send-- > 0 ) + espLink.sendPacketArg(0, NULL); + espLink.sendPacketEnd(); +} + +void WebServer::handleRequest(CmdRequest *req) +{ + uint16_t shrt; + espLink.cmdPopArg(req, &shrt, 2); + RequestReason reason = (RequestReason)shrt; + + espLink.cmdPopArg(req, &remote_ip, 4); + espLink.cmdPopArg(req, &remote_port, 2); + + { + uint16_t len = espLink.cmdArgLen(req); + char bf[len+1]; + bf[len] = 0; + espLink.cmdPopArg(req, bf, len); + + const WebMethod * meth = methods; + do + { + WebMethod m; + memcpy_P(&m, meth, sizeof(WebMethod)); + if( m.url == NULL || m.callback == NULL ) + break; + + if( strcmp_P(bf, m.url) == 0 ) + { + invokeMethod(reason, &m, req); + return; + } + meth++; + }while(1); + } + + if( reason == WS_SUBMIT ) + return; + + // empty response + espLink.sendPacketStart(CMD_WEB_JSON_DATA, 100, 2); + espLink.sendPacketArg(4, remote_ip); + espLink.sendPacketArg(2, (uint8_t *)&remote_port); + espLink.sendPacketEnd(); +} + +void WebServer::setArgNum(uint8_t num) +{ + espLink.sendPacketStart(CMD_WEB_JSON_DATA, 100, 2 + (args_to_send = num)); + espLink.sendPacketArg(4, remote_ip); + espLink.sendPacketArg(2, (uint8_t *)&remote_port); +} + +void WebServer::setArgString(const char * name, const char * value) +{ + if( args_to_send <= 0 ) + return; + + uint8_t nlen = strlen(name); + uint8_t vlen = strlen(value); + char buf[nlen + vlen + 3]; + buf[0] = WEB_STRING; + strcpy(buf+1, name); + strcpy(buf+2+nlen, value); + espLink.sendPacketArg(nlen+vlen+2, (uint8_t *)buf); + + args_to_send--; +} + +void WebServer::setArgStringP(const char * name, const char * value) +{ + if( args_to_send <= 0 ) + return; + + uint8_t nlen = strlen(name); + uint8_t vlen = strlen_P(value); + char buf[nlen + vlen + 3]; + buf[0] = WEB_STRING; + strcpy(buf+1, name); + strcpy_P(buf+2+nlen, value); + espLink.sendPacketArg(nlen+vlen+2, (uint8_t *)buf); + + args_to_send--; +} + diff --git a/examples/arduino/EspLinkSample/WebServer.h b/examples/arduino/EspLinkSample/WebServer.h index d0869c1..0914b50 100644 --- a/examples/arduino/EspLinkSample/WebServer.h +++ b/examples/arduino/EspLinkSample/WebServer.h @@ -1,11 +1,78 @@ #ifndef WEB_SERVER_H #define WEB_SERVER_H -typedef enum { - LOAD=0, +#include "EspLink.h" + +typedef enum +{ + BUTTON_PRESS, + SET_FIELD, REFRESH, - BUTTON, - SUBMIT, + LOAD, +} WebServerCommand; + +typedef void (*WebServerCallback)(WebServerCommand command, char * data, int dataLen); + +typedef struct +{ + const char * PROGMEM url; + WebServerCallback callback; +} WebMethod; + + +typedef enum { + WS_LOAD=0, + WS_REFRESH, + WS_BUTTON, + WS_SUBMIT, } RequestReason; +typedef enum +{ + WEB_STRING=0, + WEB_NULL, + WEB_INTEGER, + WEB_BOOLEAN, + WEB_FLOAT, + WEB_JSON +} WebValueType; + +class WebServer +{ + friend void webServerCallback(CmdRequest *req); + + private: + const WebMethod * PROGMEM methods; + Stream &stream; + static WebServer * instance; + + void invokeMethod(RequestReason reason, WebMethod * method, CmdRequest *req); + void handleRequest(CmdRequest *req); + + uint8_t remote_ip[4]; + uint16_t remote_port; + + int16_t args_to_send; + + protected: + EspLink espLink; + + public: + WebServer(Stream &stream, const WebMethod * PROGMEM methods); + + void init(); + void loop(); + + void registerCallback(); + + static WebServer * getInstance() { return instance; } + uint8_t * getRemoteIp() { return remote_ip; } + uint16_t getRemotePort() { return remote_port; } + + void setArgNum(uint8_t num); + void setArgString(const char * name, const char * value); + void setArgStringP(const char * name, const char * value); +}; + #endif /* WEB_SERVER_H */ + diff --git a/web-server/web-server.c b/web-server/web-server.c index 877ff2e..f6a8861 100644 --- a/web-server/web-server.c +++ b/web-server/web-server.c @@ -272,6 +272,9 @@ int ICACHE_FLASH_ATTR WEB_CgiJsonHook(HttpdConnData *connData) cmdPopArg(req, buf, len); + if(len == 0) + continue; + if( jsonPtr + 20 + len > sizeof(jsonBuf) ) { errorResponse(connData, 500, "Response too large!"); @@ -365,7 +368,7 @@ void ICACHE_FLASH_ATTR WEB_JsonData(CmdPacket *cmd) CmdRequest req; cmdRequest(&req, cmd); - if (cmdGetArgc(&req) < 3) return; + if (cmdGetArgc(&req) < 2) return; uint8_t ip[4]; cmdPopArg(&req, ip, 4);