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
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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);