diff --git a/Basic_WebSkeletonApp/.cproject b/Basic_WebSkeletonApp/.cproject new file mode 100644 index 0000000000..d6cf498965 --- /dev/null +++ b/Basic_WebSkeletonApp/.cproject @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + + all + true + true + true + + + make + + rebuild + true + true + true + + + make + + flash + true + true + true + + + + + + + + + + diff --git a/Basic_WebSkeletonApp/.project b/Basic_WebSkeletonApp/.project new file mode 100644 index 0000000000..76421a4d40 --- /dev/null +++ b/Basic_WebSkeletonApp/.project @@ -0,0 +1,27 @@ + + + MeteoControl + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/Basic_WebSkeletonApp/Makefile b/Basic_WebSkeletonApp/Makefile new file mode 100644 index 0000000000..16d76cd676 --- /dev/null +++ b/Basic_WebSkeletonApp/Makefile @@ -0,0 +1,24 @@ +##################################################################### +#### Please don't change this file. Use Makefile-user.mk instead #### +##################################################################### +# Including user Makefile. +# Should be used to set project-specific parameters +include ./Makefile-user.mk + +# Important parameters check. +# We need to make sure SMING_HOME and ESP_HOME variables are set. +# You can use Makefile-user.mk in each project or use enviromental variables to set it globally. + +ifndef SMING_HOME +$(error SMING_HOME is not set. Please configure it in Makefile-user.mk) +endif +ifndef ESP_HOME +$(error ESP_HOME is not set. Please configure it in Makefile-user.mk) +endif + +# Include main Sming Makefile +ifeq ($(RBOOT_ENABLED), 1) +include $(SMING_HOME)/Makefile-rboot.mk +else +include $(SMING_HOME)/Makefile-project.mk +endif diff --git a/Basic_WebSkeletonApp/Makefile-user.mk b/Basic_WebSkeletonApp/Makefile-user.mk new file mode 100644 index 0000000000..265efd479c --- /dev/null +++ b/Basic_WebSkeletonApp/Makefile-user.mk @@ -0,0 +1,29 @@ +## Local build configuration +## Parameters configured here will override default and ENV values. +## Uncomment and change examples: + +## ESP_HOME sets the path where ESP tools and SDK are located. +## Windows: +# ESP_HOME = c:/Espressif + +## MacOS / Linux: +#ESP_HOME = /opt/esp-open-sdk + +## SMING_HOME sets the path where Sming framework is located. +## Windows: +# SMING_HOME = c:/tools/sming/Sming + +# MacOS / Linux +# SMING_HOME = /opt/sming/Sming + +## COM port parameter is reqruied to flash firmware correctly. +## Windows: +# COM_PORT = COM3 + +# MacOS / Linux: +# COM_PORT = /dev/tty.usbserial + +# Com port speed +# COM_SPEED = 115200 + +SPIFF_SIZE = 196608 diff --git a/Basic_WebSkeletonApp/README.md b/Basic_WebSkeletonApp/README.md new file mode 100644 index 0000000000..bef3ae8e83 --- /dev/null +++ b/Basic_WebSkeletonApp/README.md @@ -0,0 +1,13 @@ +Basic application that can be used as a start point for some useful App. + +Features: + +* can setup wifi ssid and wifi password for STA (wifi client) mode either from own AP or as connected to some wifi network +* if preconfigured wifi network is unreachable after 20 seconds start AP named TyTherm with hardcoded password (see source) +* can enable/disable STA (wifi client) mode +* own AP autodisable after successful connection to preconfigured wifi network +* form population and sending is done with json+ajax +* demonstrate usage of getting raw http request body to be processed as json +* demonstrate how to fill html template on client side with more flexible than Smings Templating - JavaScript + +App called TyTherm because it is base for TinY TermOmeter :) diff --git a/Basic_WebSkeletonApp/app/application.cpp b/Basic_WebSkeletonApp/app/application.cpp new file mode 100644 index 0000000000..9760b66dca --- /dev/null +++ b/Basic_WebSkeletonApp/app/application.cpp @@ -0,0 +1,54 @@ +#include +#include + +Timer counterTimer; +void counter_loop(); +unsigned long counter = 0; + +void init() +{ + spiffs_mount(); // Mount file system, in order to work with files + Serial.begin(SERIAL_BAUD_RATE); // 115200 by default + Serial.systemDebugOutput(false); + Serial.commandProcessing(false); + + //SET higher CPU freq & disable wifi sleep + system_update_cpu_freq(SYS_CPU_160MHZ); + wifi_set_sleep_type(NONE_SLEEP_T); + + ActiveConfig = loadConfig(); + + if (ActiveConfig.StaEnable) + { + WifiStation.waitConnection(StaConnectOk, StaConnectTimeout, StaConnectFail); + WifiStation.enable(true); + WifiStation.config(ActiveConfig.StaSSID, ActiveConfig.StaPassword); + } + else + { + WifiStation.enable(false); + } + + startWebServer(); + + counterTimer.initializeMs(1000, counter_loop).start(); +} + +void counter_loop() +{ + counter++; +} + +void StaConnectOk() +{ + Serial.println("connected to AP"); + WifiAccessPoint.enable(false); +} + +void StaConnectFail() +{ + Serial.println("connection FAILED"); + WifiStation.disconnect(); + WifiAccessPoint.config("TyTherm", "ENTERYOURPASSWD", AUTH_WPA2_PSK); + WifiAccessPoint.enable(true); +} diff --git a/Basic_WebSkeletonApp/app/configuration.cpp b/Basic_WebSkeletonApp/app/configuration.cpp new file mode 100644 index 0000000000..b7b99e5ea1 --- /dev/null +++ b/Basic_WebSkeletonApp/app/configuration.cpp @@ -0,0 +1,48 @@ +#include + +ThermConfig ActiveConfig; + +ThermConfig loadConfig() +{ + StaticJsonBuffer jsonBuffer; + ThermConfig cfg; + if (fileExist(THERM_CONFIG_FILE)) + { + int size = fileGetSize(THERM_CONFIG_FILE); + char* jsonString = new char[size + 1]; + fileGetContent(THERM_CONFIG_FILE, jsonString, size + 1); + JsonObject& root = jsonBuffer.parseObject(jsonString); + + JsonObject& network = root["network"]; + cfg.StaSSID = String((const char*)network["StaSSID"]); + cfg.StaPassword = String((const char*)network["StaPassword"]); + cfg.StaEnable = network["StaEnable"]; + + delete[] jsonString; + } + else + { + //Factory defaults if no config file present + cfg.StaSSID = WIFI_SSID; + cfg.StaPassword = WIFI_PWD; + } + return cfg; +} + +void saveConfig(ThermConfig& cfg) +{ + StaticJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.createObject(); + + JsonObject& network = jsonBuffer.createObject(); + root["network"] = network; + network["StaSSID"] = cfg.StaSSID.c_str(); + network["StaPassword"] = cfg.StaPassword.c_str(); + network["StaEnable"] = cfg.StaEnable; + + char buf[ConfigFileBufferSize]; + root.prettyPrintTo(buf, sizeof(buf)); + fileSetContent(THERM_CONFIG_FILE, buf); +} + + diff --git a/Basic_WebSkeletonApp/app/webserver.cpp b/Basic_WebSkeletonApp/app/webserver.cpp new file mode 100644 index 0000000000..35ed3fc6e0 --- /dev/null +++ b/Basic_WebSkeletonApp/app/webserver.cpp @@ -0,0 +1,120 @@ +#include + + +bool serverStarted = false; +HttpServer server; + +void onIndex(HttpRequest &request, HttpResponse &response) +{ + response.setCache(86400, true); // It's important to use cache for better performance. + response.sendFile("index.html"); +} + +void onConfiguration(HttpRequest &request, HttpResponse &response) +{ + + if (request.getRequestMethod() == RequestMethod::POST) + { + debugf("Update config"); + // Update config + if (request.getBody() == NULL) + { + debugf("NULL bodyBuf"); + return; + } + else + { + StaticJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(request.getBody()); + root.prettyPrintTo(Serial); //Uncomment it for debuging + + if (root["StaSSID"].success()) // Settings + { + uint8_t PrevStaEnable = ActiveConfig.StaEnable; + + ActiveConfig.StaSSID = String((const char *)root["StaSSID"]); + ActiveConfig.StaPassword = String((const char *)root["StaPassword"]); + ActiveConfig.StaEnable = root["StaEnable"]; + + if (PrevStaEnable && ActiveConfig.StaEnable) + { + WifiStation.waitConnection(StaConnectOk, StaConnectTimeout, StaConnectFail); + WifiStation.config(ActiveConfig.StaSSID, ActiveConfig.StaPassword); + } + else if (ActiveConfig.StaEnable) + { + WifiStation.waitConnection(StaConnectOk, StaConnectTimeout, StaConnectFail); + WifiStation.enable(true); + WifiStation.config(ActiveConfig.StaSSID, ActiveConfig.StaPassword); + } + else + { + WifiStation.disconnect(); + WifiAccessPoint.config("TyTherm", "ENTERYOURPASSWD", AUTH_WPA2_PSK); + WifiAccessPoint.enable(true); + } + } + } + saveConfig(ActiveConfig); + } + else + { + response.setCache(86400, true); // It's important to use cache for better performance. + response.sendFile("config.html"); + } +} + +void onConfiguration_json(HttpRequest &request, HttpResponse &response) +{ + JsonObjectStream* stream = new JsonObjectStream(); + JsonObject& json = stream->getRoot(); + + json["StaSSID"] = ActiveConfig.StaSSID; + json["StaPassword"] = ActiveConfig.StaPassword; + json["StaEnable"] = ActiveConfig.StaEnable; + + response.sendJsonObject(stream); +} +void onFile(HttpRequest &request, HttpResponse &response) +{ + String file = request.getPath(); + if (file[0] == '/') + file = file.substring(1); + + if (file[0] == '.') + response.forbidden(); + else + { + response.setCache(86400, true); // It's important to use cache for better performance. + response.sendFile(file); + } +} + +void onAJAXGetState(HttpRequest &request, HttpResponse &response) +{ + JsonObjectStream* stream = new JsonObjectStream(); + JsonObject& json = stream->getRoot(); + + json["counter"] = counter; + + response.sendJsonObject(stream); +} + + +void startWebServer() +{ + if (serverStarted) return; + + server.listen(80); + server.addPath("/", onIndex); + server.addPath("/config", onConfiguration); + server.addPath("/config.json", onConfiguration_json); + server.addPath("/state", onAJAXGetState); + server.setDefaultHandler(onFile); + serverStarted = true; + + if (WifiStation.isEnabled()) + debugf("STA: %s", WifiStation.getIP().toString().c_str()); + if (WifiAccessPoint.isEnabled()) + debugf("AP: %s", WifiAccessPoint.getIP().toString().c_str()); +} diff --git a/Basic_WebSkeletonApp/files/bootstrap.min.css.gz b/Basic_WebSkeletonApp/files/bootstrap.min.css.gz new file mode 100644 index 0000000000..80c9946433 Binary files /dev/null and b/Basic_WebSkeletonApp/files/bootstrap.min.css.gz differ diff --git a/Basic_WebSkeletonApp/files/config.html b/Basic_WebSkeletonApp/files/config.html new file mode 100644 index 0000000000..6a93eb7bef --- /dev/null +++ b/Basic_WebSkeletonApp/files/config.html @@ -0,0 +1,69 @@ + + + + + + + + TyTherm configuration + + + + + + + + + + +
+
+ +

TyTherm

+
+ +
+

 

+
+ +
+
+
+

Network

+
+
+
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+ + + +
+
+ +
+ + \ No newline at end of file diff --git a/Basic_WebSkeletonApp/files/config.js b/Basic_WebSkeletonApp/files/config.js new file mode 100644 index 0000000000..2ff2d1e4d3 --- /dev/null +++ b/Basic_WebSkeletonApp/files/config.js @@ -0,0 +1,38 @@ +function get_config() { + $.getJSON('/config.json', + function(data) { + $.each(data, function(key, value){ + document.getElementById(key).value = value; + if (data.StaEnable == 1) { + document.getElementById('StaEnable').checked = true; + } + else + document.getElementById('StaEnable').checked = false; + }); + }); +} + + +function post_netcfg(event) { + event.preventDefault(); + var formData = { + 'StaSSID' : document.getElementById('StaSSID').value, + 'StaPassword' : document.getElementById('StaPassword').value, + 'StaEnable' : (document.getElementById('StaEnable').checked ? 1 : 0) + }; + $.ajax({ + type : 'POST', + url : '/config', + contentType : 'application/json; charset=utf-8', + data : JSON.stringify(formData), + dataType : 'json' + }) +} + + +$( document ).ready(function() { + get_config(); + + document.getElementById('form_netcfg').addEventListener('submit', post_netcfg); + document.getElementById('netcfg_cancel').addEventListener('click', get_config); +}); \ No newline at end of file diff --git a/Basic_WebSkeletonApp/files/index.html b/Basic_WebSkeletonApp/files/index.html new file mode 100644 index 0000000000..eb890a0140 --- /dev/null +++ b/Basic_WebSkeletonApp/files/index.html @@ -0,0 +1,51 @@ + + + + + + + + TyTherm Status + + + + + + + + + + +
+
+ +

TyTherm

+
+ +
+

 

+
+
+
+
+

Counter

+
+
+

{counter}

+
+
+
+ +
+
+ +
+ + \ No newline at end of file diff --git a/Basic_WebSkeletonApp/files/index.js b/Basic_WebSkeletonApp/files/index.js new file mode 100644 index 0000000000..4733242d66 --- /dev/null +++ b/Basic_WebSkeletonApp/files/index.js @@ -0,0 +1,9 @@ +$( document ).ready(function() { + + (function worker() { + $.getJSON('/state', function(data) { + document.getElementById('counter').textContent = data.counter; + setTimeout(worker, 5000); + }); + })(); +}); \ No newline at end of file diff --git a/Basic_WebSkeletonApp/files/jquery-2.1.4.min.js.gz b/Basic_WebSkeletonApp/files/jquery-2.1.4.min.js.gz new file mode 100644 index 0000000000..bc77dafe10 Binary files /dev/null and b/Basic_WebSkeletonApp/files/jquery-2.1.4.min.js.gz differ diff --git a/Basic_WebSkeletonApp/include/configuration.h b/Basic_WebSkeletonApp/include/configuration.h new file mode 100644 index 0000000000..43d5c929c2 --- /dev/null +++ b/Basic_WebSkeletonApp/include/configuration.h @@ -0,0 +1,30 @@ +#ifndef INCLUDE_CONFIGURATION_H_ +#define INCLUDE_CONFIGURATION_H_ + +#include +#include + +const char THERM_CONFIG_FILE[] = ".therm.conf"; // leading point for security reasons :) + +struct ThermConfig +{ + ThermConfig() + { + StaEnable = 1; //Enable WIFI Client + } + + String StaSSID; + String StaPassword; + uint8_t StaEnable; + +// ThermControl settings + + +}; + +ThermConfig loadConfig(); +void saveConfig(ThermConfig& cfg); + +extern ThermConfig ActiveConfig; + +#endif /* INCLUDE_CONFIGURATION_H_ */ diff --git a/Basic_WebSkeletonApp/include/tytherm.h b/Basic_WebSkeletonApp/include/tytherm.h new file mode 100644 index 0000000000..6fe5045df2 --- /dev/null +++ b/Basic_WebSkeletonApp/include/tytherm.h @@ -0,0 +1,24 @@ +#ifndef INCLUDE_TYTHERM_H_ +#define INCLUDE_TYTHERM_H_ +#include +#include +#include + +//OneWire stuff +const uint8_t onewire_pin = 2; +extern OneWire ds; + +extern unsigned long counter; // Kind of heartbeat counter + +const uint8_t ConfigJsonBufferSize = 200; // Application configuration JsonBuffer size ,increase it if you have large config +const uint16_t ConfigFileBufferSize = 2048; // Application configuration FileBuffer size ,increase it if you have large config + +//Webserver +void startWebServer(); + +//STA disconnecter +const uint8_t StaConnectTimeout = 20; //15 sec to connect in STA mode +void StaConnectOk(); +void StaConnectFail(); + +#endif /* INCLUDE_HEATCONTROL_H_ */ diff --git a/Basic_WebSkeletonApp/include/user_config.h b/Basic_WebSkeletonApp/include/user_config.h new file mode 100644 index 0000000000..1c6ac36703 --- /dev/null +++ b/Basic_WebSkeletonApp/include/user_config.h @@ -0,0 +1,45 @@ +#ifndef __USER_CONFIG_H__ +#define __USER_CONFIG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + // UART config + #define SERIAL_BAUD_RATE 115200 + + // ESP SDK config + #define LWIP_OPEN_SRC + #define USE_US_TIMER + + // Default types + #define __CORRECT_ISO_CPP_STDLIB_H_PROTO + #include + #include + + // Override c_types.h include and remove buggy espconn + #define _C_TYPES_H_ + #define _NO_ESPCON_ + + // Updated, compatible version of c_types.h + // Just removed types declared in + #include + + // System API declarations + #include + + // C++ Support + #include + // Extended string conversion for compatibility + #include + // Network base API + #include + + // Beta boards + #define BOARD_ESP01 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Sming/SmingCore/Network/HttpRequest.cpp b/Sming/SmingCore/Network/HttpRequest.cpp index 36c63b193a..f0d9e9bcc8 100644 --- a/Sming/SmingCore/Network/HttpRequest.cpp +++ b/Sming/SmingCore/Network/HttpRequest.cpp @@ -19,6 +19,7 @@ HttpRequest::HttpRequest() cookies = NULL; postDataProcessed = 0; combinePostFrag = false; + bodyBuf = NULL; } HttpRequest::~HttpRequest() @@ -28,6 +29,10 @@ HttpRequest::~HttpRequest() delete requestPostParameters; delete cookies; postDataProcessed = 0; + if (bodyBuf != NULL) + { + os_free(bodyBuf); + } } String HttpRequest::getQueryParameter(String parameterName, String defaultValue /* = "" */) @@ -229,6 +234,22 @@ bool HttpRequest::extractParsingItemsList(pbuf* buf, int startPos, int endPos, c } return continued; } +void HttpRequest::parseRawData(HttpServer *server, pbuf* buf) +{ + bodyBuf = (char *) os_zalloc(sizeof(char) * buf->tot_len); + int headerEnd = NetUtils::pbufFindStr(buf, "\r\n\r\n"); + if (headerEnd + getContentLength() > NETWORK_MAX_HTTP_PARSING_LEN) + { + debugf("NETWORK_MAX_HTTP_PARSING_LEN"); + return; + } + pbuf_copy_partial(buf, bodyBuf, buf->tot_len, headerEnd + 4); +} + +char* HttpRequest::getBody() +{ + return bodyBuf; +} bool HttpRequest::isAjax() { diff --git a/Sming/SmingCore/Network/HttpRequest.h b/Sming/SmingCore/Network/HttpRequest.h index 9317522eba..ef444af857 100644 --- a/Sming/SmingCore/Network/HttpRequest.h +++ b/Sming/SmingCore/Network/HttpRequest.h @@ -42,6 +42,7 @@ class HttpRequest String getPostParameter(String parameterName, String defaultValue = ""); String getHeader(String headerName, String defaultValue = ""); String getCookie(String cookieName, String defaultValue = ""); + char* getBody(); public: HttpParseResult parseHeader(HttpServer *server, pbuf* buf); @@ -49,6 +50,7 @@ class HttpRequest bool extractParsingItemsList(pbuf* buf, int startPos, int endPos, char delimChar, char endChar, HashMap *resultItems); + void parseRawData(HttpServer *server, pbuf* buf); private: String method; @@ -59,6 +61,7 @@ class HttpRequest HashMap *cookies; int postDataProcessed; bool combinePostFrag; + char *bodyBuf; friend class TemplateFileStream; }; diff --git a/Sming/SmingCore/Network/HttpServerConnection.cpp b/Sming/SmingCore/Network/HttpServerConnection.cpp index 9a7261d423..022c44c256 100644 --- a/Sming/SmingCore/Network/HttpServerConnection.cpp +++ b/Sming/SmingCore/Network/HttpServerConnection.cpp @@ -61,7 +61,12 @@ err_t HttpServerConnection::onReceive(pbuf *buf) if (request.getContentLength() > 0 && contType.indexOf(ContentType::FormUrlEncoded) != -1) state = eHCS_ParsePostData; else + { + request.parseRawData(server, buf); state = eHCS_ParsingCompleted; + TcpConnection::onReceive(buf); + return ERR_OK; + } } } else if (state == eHCS_WebSocketFrames) @@ -86,7 +91,10 @@ err_t HttpServerConnection::onReceive(pbuf *buf) state = eHCS_ParsingCompleted; } } - +// else if (state == eHCS_ParsingCompleted && request.getContentLength() > 0) +// { +// request.parseRawData(server, buf); +// } // Fire callbacks TcpConnection::onReceive(buf);