diff --git a/.editorconfig b/.editorconfig index 94cbdc8..2fc657e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,23 @@ -[*.cpp,*.h, *.hpp] -indent_size = 2; -indent_style = space \ No newline at end of file +root = true + +charset = utf-8 + +[*.{cpp,h,hpp}] +indent_size = 2 +indent_style = space + +[*.{py}] +indent_size = 4 +indent_style = space + +[*.{htm,html,css}] +indent_size = 4 +indent_style = space + +[*.{yml,yaml}] +indent_size = 2 +indent_style = space + +[*.{ini}] +indent_size = 4 +indent_style = tab diff --git a/.gitignore b/.gitignore index e6ddf65..7313e5a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,16 @@ .vscode/launch.json .vscode/ipch .vscode + # embed is auto generated directory. include/embed/** + +# temporary place files when develop web ui +embed/static/** + _release _release/** + EnvBoyX.zip EnvBoyX.tar.gz EnvBoyX-*.tar.gz \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0f0d740..080e70d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,10 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ] -} +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index a875c5f..4572a33 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -46,5 +46,8 @@ "cinttypes": "cpp", "typeinfo": "cpp", "atomic": "cpp" - } + }, + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ] } \ No newline at end of file diff --git a/README.md b/README.md index a3043a1..30cb9ce 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,13 @@ EnvBoyX has HTTP web API. * WeMos D1mini * NodeMCU V3 +## build note + +* Use python script to store some html, css +* Script is stored in build_script +* Script is automatically executed by platformio +* See platform.io [env] extra_script for details. + ## Version History see [Version History](docs/history.md) for details. diff --git a/_how-to-develop.md b/_how-to-develop.md new file mode 100644 index 0000000..4763683 --- /dev/null +++ b/_how-to-develop.md @@ -0,0 +1,28 @@ +# how to develop web ui + +## copy files except config.html to static/ + +``` +cd embed +rsync -v --exclude=config.html ./* static +``` + +## run python http.server or some simple web server on embed + +``` +cd embed +python -m http.server +``` + +## Access web via web browser + +Use firefox or chrome newer version. + +# after development + +Dont forget to write back all files in static/ to embed. + +``` +cd embed/static +cp -v ./* ../ +``` \ No newline at end of file diff --git a/build_script/create_embed.py b/build_script/create_embed.py index 93f65a1..b6488d3 100644 --- a/build_script/create_embed.py +++ b/build_script/create_embed.py @@ -50,6 +50,16 @@ def create_name_from_filename(filename): template = readall(TEMPLATE_FILE) for file in pathlib.Path(EMBED_DIR).iterdir(): + #print(file.name) + if file.is_dir(): + print(f"ignored sub directory {file.name}") + continue + + if file.name.endswith(".md") or file.name.endswith(".sh"): + print(f"ignored markdown file {file.name}") + continue + + # start generating source output = template data = readall(file) diff --git a/build_script/template.cpp b/build_script/template.cpp index ae1a0f5..6ccc208 100644 --- a/build_script/template.cpp +++ b/build_script/template.cpp @@ -1,5 +1,4 @@ #include -static const char $$REPLACE_NAME$$[] PROGMEM = R"=====( -$$REPLACE_CONTENT$$ +static const char $$REPLACE_NAME$$[] PROGMEM = R"=====($$REPLACE_CONTENT$$ )====="; diff --git a/devel_web.sh b/devel_web.sh new file mode 100755 index 0000000..1401ddf --- /dev/null +++ b/devel_web.sh @@ -0,0 +1,7 @@ +#!/bin/bash -eu + +WORKDIR=$(dirname $0) + +cd $WORKDIR/embed + +python -m http.server 8080 \ No newline at end of file diff --git a/devel_writeback.sh b/devel_writeback.sh new file mode 100755 index 0000000..417904b --- /dev/null +++ b/devel_writeback.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +WORKDIR=$(dirname $0) + +cd $WORKDIR/embed + +watch -n 15 cp -v "./static/*" . \ No newline at end of file diff --git a/embed/config.html b/embed/config.html new file mode 100644 index 0000000..6db49e9 --- /dev/null +++ b/embed/config.html @@ -0,0 +1,291 @@ + + + + + + + + EnvBoyX setting + + + + + +

EnvBoyX Settings (EBXCFG.v45)

+
+
+
+ ネットワーク設定 + WiFi接続情報
+ ※ 2.4GHz帯のみ対応しています。
+
+
+
mDNS名(a.k.a Rendezvous, avahi-daemon)
+ ※ mDNS名.local で他端末から本機にアクセスすることができます。
+
+
+
+ 動作モード設定 + 動作モード
+
+ +
+
+
+ 表示デバイス共通設定 + 画面反転
+ +
+ +
+ + 明るさ(0-255)
+
+ + Wait for reconfigure画面
+ ※ 表示しない場合、セットアップモードに入るためにはWeb APIを使用する必要があります。
+ +
+ + +
+ + 自動消灯する明るさ(暗いと判定するしきい値)
+ ※ 光量センサーがない場合は自動的に無効になります。
+ ※ 自動消灯を使用しない場合は、999999を入力してください。
+
+ + 自動消灯判定時間
+ ※ しきい値未満の明るさがこの時間継続したら消灯します。
+
+
+
+ I2C OLED デバイス設定 + ※ 接続されていない場合無視されます。画面端にゴミが表示されている、または数ドット欠けている場合は変更してください。
+ +
+ +
+
+
+ ST7789 デバイス設定 + ST7789 SPI液晶の有無
+ ※ MQTTモードでは無効。
※ SPIピンはここでは指定できません。
+ +
+ +
+ + ST7789 表示モード
+ +
+ + +
+
+
+ MH-Z19B デバイス設定 + MH-Z19B CO2センサー有無(金色のセンサー)
+ +
+ + +
+ +
+ + MH-Z19BのUARTが接続されているGPIOピン番号
+ ※ MH-Z19Bを使用しない場合は設定不要
※ ESP8266では RX 14 TX 0 で固定
※ ボードによって使用可能なピンが異なるので動作しない場合は他のピンを試してください。
+ RXピン  +
+ TXピン  +
+ +
起動時のAuto Baseline Correction
+ ※ MH-Z19Bを使用しない場合・MH-Z19BがUARTモード以外の場合は無視されます。
+ +
+ +
+
+
+ MQTTモード専用設定 + MQTTブローカーのIPアドレス
+ ※ ホスト名も可能ですが、mDNS(bonjour, avahi)は使用出来ません。
+
+
MQTT名
+ ※ クライアント名とトピック名として使用

+
+
+ アラート設定 +
+ 気温 + 注意1:  + + 以上 + + 未満
+ 注意2:  + + 以上 + + 未満
+ 警報1:  + + 以上 + + 未満
+ 警報2:  + + 以上 + + 未満
+
+
+ 湿度 + 注意1:  + + 以上 + + 未満
+ 注意2:  + + 以上 + + 未満
+ 警報1:  + + 以上 + + 未満
+ 警報2:  + + 以上 + + 未満
+
+
+ 照度 + 注意1:  + + 以上 + + 未満
+ 注意2:  + + 以上 + + 未満
+ 警報1:  + + 以上 + + 未満
+ 警報2:  + + 以上 + + 未満
+
+
+ 気圧 + 注意1:  + + 以上 + + 未満
+ 注意2:  + + 以上 + + 未満
+ 警報1:  + + 以上 + + 未満
+ 警報2:  + + 以上 + + 未満
+
+
+ CO2濃度 + 注意1:  + + 以上 + + 未満
+ 注意2:  + + 以上 + + 未満
+ 警報1:  + + 以上 + + 未満
+ 警報2:  + + 以上 + + 未満
+
+
+

+
+
+
EnvBoyX Ver.45.0, Copyright 2018-2021 Yakumo Saki / ziomatrix.org. + + + \ No newline at end of file diff --git a/embed/config.js b/embed/config.js new file mode 100644 index 0000000..8acdbb6 --- /dev/null +++ b/embed/config.js @@ -0,0 +1,22 @@ +const DEBUG_MODE = true; +const DEBUG_API_HOST = "127.0.0.1"; + +function loadConfig() { +await fetch('http://10.1.0.130', { + method: "GET" + }) + .then(response => { + if (response.ok) { + return response.json(); + } + // 404 や 500 ステータスならここに到達する + throw new Error('Network response was not ok.'); + }) + .then(resJson => { + console.log(JSON.stringify(resJson)); + }) + .catch(error => { + // ネットワークエラーの場合はここに到達する + console.error(error); + }) +} \ No newline at end of file diff --git a/embed/style.css b/embed/style.css index 2bf3782..d80f0e2 100644 --- a/embed/style.css +++ b/embed/style.css @@ -1,3 +1,4 @@ +@charset "utf-8"; body { background-color: aliceblue; } diff --git a/examples/config/misc_10_goto_setup.sh b/examples/config/misc_10_goto_setup.sh new file mode 100644 index 0000000..eedbb91 --- /dev/null +++ b/examples/config/misc_10_goto_setup.sh @@ -0,0 +1 @@ +curl -s -X POST "http://10.1.0.130/api/goto_setup" diff --git a/include/SimpleMap.h b/include/SimpleMap.h index 758c916..41c8dca 100644 --- a/include/SimpleMap.h +++ b/include/SimpleMap.h @@ -1,6 +1,8 @@ #pragma once #include +#include + #include "log.h" template @@ -38,7 +40,7 @@ template class SimpleMap { } int findIndex(String key) { - for (int i = 0; i < vector.size(); i++) { + for (unsigned int i = 0; i < vector.size(); i++) { // debuglog("NOW=" + vector[i].key + " target=" + key); if (vector[i].key == key) return i; } diff --git a/include/global_MimeType.h b/include/global_MimeType.h index 7cff944..0a65111 100644 --- a/include/global_MimeType.h +++ b/include/global_MimeType.h @@ -8,4 +8,5 @@ class MimeType { static const String TEXT; static const String JSON; static const String CSS; + static const String JAVASCRIPT; }; \ No newline at end of file diff --git a/include/network/http_utils.h b/include/network/http_utils.h index ba59dce..872c605 100644 --- a/include/network/http_utils.h +++ b/include/network/http_utils.h @@ -1,7 +1,12 @@ -// HTTP ヘッダ (HTTP 1.1 〜) -// を送信する +// HTTP ヘッダ (HTTP 1.1 〜) text/htmlで送信する void sendHttpHeader(); +// HTTP ヘッダ (HTTP 1.1 〜)をコンテントタイプ(text/html..)を送信する +void sendHttpHeader(String contentType); + // HTMLヘッダ (~まで)を送信する。 // 〜 は呼び出し元で送信する必要がある -void sendHtmlHeader(); \ No newline at end of file +void sendHtmlHeader(); + +// HTTP REDIRECTヘッダを送信する。これを実行したあとは何も送れない +void sendHttpRedirectHeader(String url); diff --git a/include/network/http_web_config.h b/include/network/http_web_config.h new file mode 100644 index 0000000..db93acc --- /dev/null +++ b/include/network/http_web_config.h @@ -0,0 +1,3 @@ +#include + +void http_web_config_setup(); \ No newline at end of file diff --git a/include/utils.h b/include/utils.h index 6b4f97e..09eb6c6 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,4 +1,5 @@ #include +#include bool parseBooleanString(const String val); -bool vectorStringContains(const std::vector &keyArray, const String& key); \ No newline at end of file +bool vectorStringContains(const std::vector &keyArray, const String& key); diff --git a/lib/TimerCall/TimerCall.h b/lib/TimerCall/TimerCall.h index 279f1df..8a841d2 100644 --- a/lib/TimerCall/TimerCall.h +++ b/lib/TimerCall/TimerCall.h @@ -1,6 +1,7 @@ #pragma once #include "Arduino.h" +#include class TimerCall { diff --git a/platformio.ini b/platformio.ini index d921edc..ec29a89 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ description = "EnvBoyX" [env] extra_scripts = - post:build_script/create_embed.py + pre:build_script/create_embed.py [env:esp32dev] platform = espressif32 diff --git a/src/global_MimeType.cpp b/src/global_MimeType.cpp index c76ee22..df407a6 100644 --- a/src/global_MimeType.cpp +++ b/src/global_MimeType.cpp @@ -5,3 +5,4 @@ const String MimeType::HTML = "text/html"; const String MimeType::TEXT = "text/plain"; const String MimeType::JSON = "application/json"; const String MimeType::CSS = "text/css"; +const String MimeType::JAVASCRIPT = "text/javascript"; diff --git a/src/network/api/http_api_base_json.cpp b/src/network/api/http_api_base_json.cpp index 6a6021f..da54b8f 100644 --- a/src/network/api/http_api_base_json.cpp +++ b/src/network/api/http_api_base_json.cpp @@ -17,7 +17,7 @@ String get_time_string(unsigned long ms) { int min = (second / 60) % 60; int hr = minute / 60; - char buf[10]; + char buf[15]; snprintf (buf, sizeof buf,"%02d:%02d:%02d", hr, min, sec); return String(buf); } diff --git a/src/network/api/http_api_util.cpp b/src/network/api/http_api_util.cpp index 4ac48e7..bcf2440 100644 --- a/src/network/api/http_api_util.cpp +++ b/src/network/api/http_api_util.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "log.h" diff --git a/src/network/http_normal_web.cpp b/src/network/http_normal_web.cpp index 7dd17a5..5af786f 100644 --- a/src/network/http_normal_web.cpp +++ b/src/network/http_normal_web.cpp @@ -13,6 +13,7 @@ #include "network/http_api_mhz.h" #include "network/http_api_config.h" +#include "network/http_web_config.h" extern HTTPWEBSERVER server; @@ -24,7 +25,8 @@ void http_setup_normal() { http_api_base_setup(); http_api_mhz_setup(); http_api_config_setup(); - + http_web_config_setup(); + server.begin(); httplog("HTTP web server initialized"); } @@ -32,5 +34,3 @@ void http_setup_normal() { void http_loop_normal() { server.handleClient(); } - - diff --git a/src/network/http_utils.cpp b/src/network/http_utils.cpp index 71cc41c..87b0a02 100644 --- a/src/network/http_utils.cpp +++ b/src/network/http_utils.cpp @@ -3,13 +3,23 @@ #include "network/webserver.h" #include "embed/style_css.h" -void sendHttpHeader() { +void sendHttpHeader(String contentType) { server.sendContent("HTTP/1.1 200 OK\r\n"); - server.sendContent("Content-Type: text/html\r\n"); + server.sendContent("Content-Type: " + contentType + "\r\n"); server.sendContent("Connection: close\r\n"); server.sendContent("\r\n"); } +void sendHttpRedirectHeader(String url) { + server.sendContent("HTTP/1.0 301 Moved Permanently\r\n"); + server.sendContent("Location: " + url + "\r\n"); + server.sendContent("\r\n"); +} + +void sendHttpHeader() { + sendHttpHeader(MimeType::HTML); +} + void sendHtmlHeader() { String html = ""; html += ""; diff --git a/src/network/web/http_web_config.cpp b/src/network/web/http_web_config.cpp new file mode 100644 index 0000000..17efed5 --- /dev/null +++ b/src/network/web/http_web_config.cpp @@ -0,0 +1,47 @@ +#include + +#include "log.h" +#include "global.h" +#include "config.h" + +#include "network/webserver.h" + +#include "network/http_api.h" +#include "network/http_utils.h" + +#include "embed/config_html.h" +#include "embed/style_css.h" +#include "embed/config_js.h" + +extern HTTPWEBSERVER server; + +/** + * GET 設定画面 + */ +void handle_get_webconfig() { + + sendHttpHeader(); + + server.sendContent(String(CONFIG_HTML)); +} + +void handle_get_style_css() { + + sendHttpHeader(MimeType::CSS); + + server.sendContent(String(STYLE_CSS)); +} + +void handle_get_config_js() { + + sendHttpHeader(MimeType::JAVASCRIPT); + + server.sendContent(String(CONFIG_JS)); +} + +void http_web_config_setup() { + server.on("/static/style.css", HTTP_GET, handle_get_style_css); + server.on("/static/config.js", HTTP_GET, handle_get_config_js); + server.on("/webconfig", HTTP_GET, handle_get_webconfig); + httplog(F("HTTP web config initialized")); +} diff --git a/src/network/wifi.cpp b/src/network/wifi.cpp index 923e18e..2c7ba22 100644 --- a/src/network/wifi.cpp +++ b/src/network/wifi.cpp @@ -1,4 +1,6 @@ #include +#include +#include // WiFi #ifdef ESP32 diff --git a/src/sensors/delta.cpp b/src/sensors/delta.cpp index 38104d4..71ca299 100644 --- a/src/sensors/delta.cpp +++ b/src/sensors/delta.cpp @@ -11,7 +11,7 @@ const int MAX_HISTORY = 30; // ここに毎分のデータを保存する。 -std::vector history(MAX_HISTORY); +std::vector history; void store_history() { if (history.size() >= MAX_HISTORY) { @@ -22,54 +22,36 @@ void store_history() { // deltalog("Pressure history pushed. historySize = " + String(history.size())); } -void store_zero_delta() { - deltaValues.pressure = 0.0; - deltaValues.temperature = 0.0; - deltaValues.humidity = 0.0; - deltaValues.pressure = 0.0; - deltaValues.lux = 0.0; - deltaValues.co2ppm = 0; -} - void store_delta() { - if (history.size() == 0) { - store_zero_delta(); - deltalog(F("No history. Assume delta = 0")); - return; + // 起動直後のhistoryが空の状態のときに、最新の測定値を + // historyに追加して、差分の計算を単純にする + if (history.empty()) { + deltalog(F("No history. Create first history")); + history.reserve(MAX_HISTORY); + store_history(); + } else { + // statlog("history size=" + String(history.size()) + // + " Capacity=" + String(history.capacity())); } - const sensor_values_t* base = nullptr; - - for (const sensor_values_t v : history) { - // 有効なデータが入ってるかチェックして、入っていればdeltaを計算する元にする - if (v.temperature != 0.0 || v.pressure != 0.0) { - base = &v; - break; - } - } + const sensor_values_t *base = nullptr; + base = &history.front(); - if (base != nullptr) { - // 計算 - deltaValues.pressure = sensorValues.pressure - base->pressure; - deltaValues.temperature = sensorValues.temperature - base->temperature; - deltaValues.humidity = sensorValues.humidity - base->humidity; - deltaValues.lux = sensorValues.lux - base->lux; - deltaValues.co2ppm = sensorValues.co2ppm - base->co2ppm; + // 計算 + deltaValues.pressure = sensorValues.pressure - base->pressure; + deltaValues.temperature = sensorValues.temperature - base->temperature; + deltaValues.humidity = sensorValues.humidity - base->humidity; + deltaValues.lux = sensorValues.lux - base->lux; + deltaValues.co2ppm = sensorValues.co2ppm - base->co2ppm; #ifdef SENSOR_VALUE_LOG - deltalog("Pressure Delta=" + String(sensorValues.pressure) - + " PRES=" + String(sensorValues.pressure, 2) - + " BASE=" + String(base->pressure, 2) - + " FORMATTED=" + format_delta_value(deltaValues.pressure)); - deltalog("temperature Delta=" + String(sensorValues.temperature) - + " PRES=" + String(sensorValues.temperature, 2) - + " BASE=" + String(base->temperature, 2) - + " FORMATTED=" + format_delta_value(deltaValues.temperature)); + deltalog("Pressure Delta=" + String(sensorValues.pressure) + + " PRES=" + String(sensorValues.pressure, 2) + + " BASE=" + String(base->pressure, 2) + + " FORMATTED=" + format_delta_value(deltaValues.pressure)); + deltalog("temperature Delta=" + String(sensorValues.temperature) + + " PRES=" + String(sensorValues.temperature, 2) + + " BASE=" + String(base->temperature, 2) + + " FORMATTED=" + format_delta_value(deltaValues.temperature)); #endif - } else { - // 計算 - store_zero_delta(); - } - - } diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 496297b..0d1ed36 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -1,4 +1,5 @@ #include +#include #include "log.h" @@ -23,7 +24,7 @@ bool parseBooleanString(const String val) { return false; } -// +// returns string exist in vector bool vectorStringContains(const std::vector &keyArray, const String& key) { for (const String k : keyArray) {