/*! \file ELClientWebServer.cpp \brief Constructor and functions for ELClientWebServer */ #include "ELClientWebServer.h" typedef enum { WS_LOAD=0, // page first load WS_REFRESH, // page refresh WS_BUTTON, // button press WS_SUBMIT, // form submission } RequestReason; typedef enum { WEB_STRING=0, // value type string WEB_NULL, // value type null WEB_INTEGER, // value type integer WEB_BOOLEAN, // value type boolean WEB_FLOAT, // value type float WEB_JSON // value type json } WebValueType; ELClientWebServer * ELClientWebServer::instance = 0; /*! ELClientWebServer(ELClient* elClient) @brief Creates a Web-Server instance. @details This method creates a web-server object. @param elClient Reference to ELClient object. @par Example code @code // Initialize a connection to esp-link using the normal hardware serial port // DEBUG is disabled because of performance reasons ELClient esp(&Serial); // Initialize the Web-Server client ELClientWebServer webServer(&esp); @endcode */ ELClientWebServer::ELClientWebServer(ELClient* elc) :_elc(elc),handlers(0), arg_ptr(0) { // save the current packet handler and register a new one instance = this; webServerCb.attach(&ELClientWebServer::webServerPacketHandler); } // packet handler for web-server void ELClientWebServer::webServerPacketHandler(void * response) { ELClientWebServer::getInstance()->processResponse((ELClientResponse*)response); } /*! createURLHandler(const char * URL) @brief Creates and registers an URL handler. @details This method is responsible for creating and registering an URL handler object. @param URL The URL the handler handles. URL is "/" + the HTML file name + ".json". @returns The created URLHandler object. @par Example code @code void myLoadCb(char * url) { ... } void myRefreshCb(char * url) { ... } void myButtonPressCb(char * button_id) { ... } void mySetFieldCb(char * field_id) { ... } void setup() { ... URLHandler *handler = webServer.createURLHandler("/mypage.html.json"); handler->loadCb.attach(&myLoadCb); handler->refreshCb.attach(&myRefreshCb); handler->buttonCb.attach(&myButtonPressCb); handler->setFieldCb.attach(&mySetFieldCb); ... } @endcode */ URLHandler * ELClientWebServer::createURLHandler(const char * URL) { String s = URL; return createURLHandler(s); } /*! createURLHandler(const __FlashStringHelper * URL) @brief Creates and registers an URL handler. @details This method is responsible for creating and registering an URL handler object. @param URL The URL the handler handles. URL is "/" + the HTML file name + ".json". @returns The created URLHandler object. @par Example code @code void myLoadCb(char * url) { ... } void myRefreshCb(char * url) { ... } void myButtonPressCb(char * button_id) { ... } void mySetFieldCb(char * field_id) { ... } void setup() { ... URLHandler *handler = webServer.createURLHandler(F("/mypage.html.json")); handler->loadCb.attach(&myLoadCb); handler->refreshCb.attach(&myRefreshCb); handler->buttonCb.attach(&myButtonPressCb); handler->setFieldCb.attach(&mySetFieldCb); ... } @endcode */ URLHandler * ELClientWebServer::createURLHandler(const __FlashStringHelper * URL) { String s = URL; return createURLHandler(s); } /*! createURLHandler(const String &URL) @brief Creates and registers an URL handler. @details This method is responsible for creating and registering an URL handler object. @param URL The URL the handler handles. URL is "/" + the HTML file name + ".json". @returns The created URLHandler object. @par Example code @code void myLoadCb(char * url) { ... } void myRefreshCb(char * url) { ... } void myButtonPressCb(char * button_id) { ... } void mySetFieldCb(char * field_id) { ... } void setup() { ... String url = F("/mypage.html.json"); URLHandler *handler = webServer.createURLHandler(url); handler->loadCb.attach(&myLoadCb); handler->refreshCb.attach(&myRefreshCb); handler->buttonCb.attach(&myButtonPressCb); handler->setFieldCb.attach(&mySetFieldCb); ... } @endcode */ URLHandler * ELClientWebServer::createURLHandler(const String &URL) { struct URL_HANDLER * hnd = new struct URL_HANDLER(); // "new" is used here instead of malloc to call String destructor at freeing. DOn't use malloc/free. hnd->URL = URL; // handler URL hnd->next = handlers; // next handler handlers = hnd; // change the first handler return hnd; } /*! destroyURLHandler(URLHandler * handler) @brief Unregisters an destroys an URL handler. @details This method is responsible destroying an URL handler object. @param handler The handler to destroy. @par Example code @code URLHandler *handler = ... destroyURLHandler(handler); @endcode */ void ELClientWebServer::destroyURLHandler(URLHandler * handler) { struct URL_HANDLER *prev = 0; struct URL_HANDLER *hnd = handlers; while( hnd != 0 ) { if( hnd == handler ) { if( prev == 0 ) handlers = hnd->next; else prev->next = hnd->next; delete hnd; return; } prev = hnd; hnd = hnd->next; } } /*! setup() @brief Initializes web-server. @details Initialization means to subscribe to Web-Server callback of Esp-Link. @par Example code @code void resetCb(void) { Serial.println("EL-Client (re-)starting!"); bool ok = false; do { ok = esp.Sync(); // sync up with esp-link, blocks for up to 2 seconds if (!ok) Serial.println("EL-Client sync failed!"); } while(!ok); Serial.println("EL-Client synced!"); webServer.setup(); } void setup() { Serial.begin(115200); URLHandler *handler = webServer.createURLHandler(F("/mypage.html.json")); handler->loadCb.attach(&myLoadCb); handler->refreshCb.attach(&myRefreshCb); handler->buttonCb.attach(&myButtonPressCb); handler->setFieldCb.attach(&mySetFieldCb); esp.resetCb = resetCb; resetCb(); } @endcode */ void ELClientWebServer::setup() { // WebServer doesn't send messages to MCU only if asked // register here to the web callback // periodic reregistration is required in case of ESP8266 reset _elc->Request(CMD_WEB_SETUP, 0, 1); uint32_t cb = (uint32_t)&webServerCb; _elc->Request(&cb, 4); _elc->Request(); } void ELClientWebServer::processResponse(ELClientResponse *response) { uint16_t shrt; response->popArg(&shrt, 2); RequestReason reason = (RequestReason)shrt; // request reason response->popArg(remote_ip, 4); // remote IP address response->popArg(&remote_port, 2); // remote port char * url; uint16_t urlLen = response->popArgPtr((void**)&url); struct URL_HANDLER *hnd = handlers; while( hnd != 0 ) { if( hnd->URL.length() == urlLen && memcmp( url, hnd->URL.c_str(), urlLen ) == 0 ) break; hnd = hnd->next; } if( hnd == 0 ) // no handler found for the URL { if( _elc->_debugEn ) { _elc->_debug->print(F("Handler not found for URL:")); for(uint16_t i=0; i < urlLen; i++) _elc->_debug->print( url[i] ); _elc->_debug->println(); } return; } switch(reason) { case WS_BUTTON: // invoked when a button pressed { char * idPtr; int idLen = response->popArgPtr((void**)&idPtr); // add terminating 0 char id[idLen+1]; memcpy(id, idPtr, idLen); id[idLen] = 0; hnd->buttonCb(id); } break; case WS_SUBMIT: // invoked when a form submitted { uint16_t cnt = 4; while( cnt < response->argc() ) { char * idPtr; int idLen = response->popArgPtr((void**)&idPtr); int nameLen = strlen(idPtr+1); int valueLen = idLen - nameLen -2; // add terminating 0 arg_ptr = (char *)malloc(valueLen+1); arg_ptr[valueLen] = 0; memcpy(arg_ptr, idPtr + 2 + nameLen, valueLen); hnd->setFieldCb(idPtr+1); free(arg_ptr); arg_ptr = 0; cnt++; } } return; case WS_LOAD: // invoked at refresh / load case WS_REFRESH: break; default: return; } // the response is generated here with the fields to refresh _elc->Request(CMD_WEB_DATA, 100, VARIABLE_ARG_NUM); _elc->Request(remote_ip, 4); // send remote IP address _elc->Request((uint8_t *)&remote_port, 2); // send remote port if( reason == WS_LOAD ) hnd->loadCb( (char*)hnd->URL.c_str() ); else hnd->refreshCb( (char*)hnd->URL.c_str() ); _elc->Request((uint8_t *)NULL, 0); // end indicator _elc->Request(); // finish packet } /*! setArgJson(const char * name, const char * value) @brief Sets JSON value of a field @details Sets JSON value to display an HTML field (list, table). @param name The name of the field @param value JSON value @par Supported HTML controls @li UL @li OL @li TABLE @warning Use this method only in refreshCb/loadCb. @par Example List HTML @code