From c53421a4c5b053234f5b5ccaa15e33f27513ef72 Mon Sep 17 00:00:00 2001 From: Yakumo Saki Date: Sat, 31 Dec 2022 22:14:19 +0900 Subject: [PATCH] v46 (#113) * refactor APIs and change URLs * update document and js for api refactor * change build script to ignore embed dirname * delete unused script * Add web update feature * web updater on esp8266 is not supported * Update release shellscript * add setupmode notice * fix backup api URL --- .editorconfig | 4 + .github/workflows/codeql-analysis.yml | 35 +- .gitignore | 3 - build_script/create_embed.py | 49 +- create_release.sh | 101 ++-- devel_writeback.sh | 7 - docs/api.md | 55 +- docs/history.md | 123 +++- docs/how_to_write-en.md | 8 + docs/how_to_write-jp.md | 8 + docs/requirements.txt | 0 embed/config.html | 561 +++++++++--------- embed/config.js | 22 - embed/static/config.js | 375 ++++++++++++ embed/static/style.css | 45 ++ embed/style.css | 29 - embed/update.html | 28 + embed/update_done.html | 18 + embed/update_fail.html | 18 + embed/update_unsupport.html | 17 + include/global.h | 5 + include/http_setup.h | 14 - include/network/api/api_util.h | 11 + include/network/api/basic_api.h | 2 + .../{http_api_config.h => api/v1/config.h} | 1 - .../v1/config_backup.h} | 0 .../v1/config_getset.h} | 1 - .../{http_api_display.h => api/v1/display.h} | 1 - include/network/api/v1/getdata.h | 1 + include/network/api/v1/mhz19b.h | 2 + include/network/goto_setup_api.h | 1 + include/network/http_api.h | 10 - include/network/http_api_base.h | 3 - include/network/http_api_base_json.h | 7 - include/network/http_api_mhz.h | 3 - include/network/http_api_util.h | 5 - include/network/http_not_found.h | 3 + include/network/http_utils.h | 5 + include/network/stastics_api.h | 1 + include/network/{ => web}/http_web_config.h | 0 include/network/web/http_web_updater.h | 3 + include/network/webserver.h | 2 + platformio.ini | 1 + src/ConfigClass.h | 8 +- src/ConfigClass_metadata.cpp | 4 +- src/global.cpp | 12 +- src/main.cpp | 3 + .../api/{http_api_util.cpp => api_util.cpp} | 14 + src/network/api/basic_api.cpp | 60 ++ src/network/api/goto_setup_api.cpp | 35 ++ src/network/api/http_api_base.cpp | 55 -- src/network/api/stastics_api.cpp | 25 + .../{http_api_config.cpp => v1/config.cpp} | 57 +- .../config_backup.cpp} | 2 +- .../config_getset.cpp} | 9 +- .../{http_api_display.cpp => v1/display.cpp} | 6 +- .../getdata.cpp} | 46 +- .../api/{http_api_mhz.cpp => v1/mhz19b.cpp} | 10 +- src/network/http_normal_web.cpp | 29 +- src/network/http_not_found.cpp | 29 + src/network/http_setup_web.cpp | 111 +--- src/network/http_setup_web_get.cpp | 307 ---------- src/network/http_setup_web_post.cpp | 107 ---- src/network/http_utils.cpp | 18 +- src/network/mdns.cpp | 7 +- src/network/web/http_web_config.cpp | 4 +- src/network/web/http_web_updater.cpp | 88 +++ src/network/{http.cpp => webserver.cpp} | 0 src/sensors/delta.cpp | 2 +- 69 files changed, 1513 insertions(+), 1123 deletions(-) delete mode 100755 devel_writeback.sh delete mode 100644 docs/requirements.txt delete mode 100644 embed/config.js create mode 100644 embed/static/config.js create mode 100644 embed/static/style.css delete mode 100644 embed/style.css create mode 100644 embed/update.html create mode 100644 embed/update_done.html create mode 100644 embed/update_fail.html create mode 100644 embed/update_unsupport.html create mode 100644 include/network/api/api_util.h create mode 100644 include/network/api/basic_api.h rename include/network/{http_api_config.h => api/v1/config.h} (50%) rename include/network/{http_api_config_backup.h => api/v1/config_backup.h} (100%) rename include/network/{http_api_config_getset.h => api/v1/config_getset.h} (99%) rename include/network/{http_api_display.h => api/v1/display.h} (50%) create mode 100644 include/network/api/v1/getdata.h create mode 100644 include/network/api/v1/mhz19b.h create mode 100644 include/network/goto_setup_api.h delete mode 100644 include/network/http_api.h delete mode 100644 include/network/http_api_base.h delete mode 100644 include/network/http_api_base_json.h delete mode 100644 include/network/http_api_mhz.h delete mode 100644 include/network/http_api_util.h create mode 100644 include/network/http_not_found.h create mode 100644 include/network/stastics_api.h rename include/network/{ => web}/http_web_config.h (100%) create mode 100644 include/network/web/http_web_updater.h rename src/network/api/{http_api_util.cpp => api_util.cpp} (68%) create mode 100644 src/network/api/basic_api.cpp create mode 100644 src/network/api/goto_setup_api.cpp delete mode 100644 src/network/api/http_api_base.cpp create mode 100644 src/network/api/stastics_api.cpp rename src/network/api/{http_api_config.cpp => v1/config.cpp} (51%) rename src/network/api/{http_api_config_backup.cpp => v1/config_backup.cpp} (85%) rename src/network/api/{http_api_config_getset.cpp => v1/config_getset.cpp} (94%) rename src/network/api/{http_api_display.cpp => v1/display.cpp} (84%) rename src/network/api/{http_api_base_json.cpp => v1/getdata.cpp} (55%) rename src/network/api/{http_api_mhz.cpp => v1/mhz19b.cpp} (86%) create mode 100644 src/network/http_not_found.cpp delete mode 100644 src/network/http_setup_web_get.cpp delete mode 100644 src/network/http_setup_web_post.cpp create mode 100644 src/network/web/http_web_updater.cpp rename src/network/{http.cpp => webserver.cpp} (100%) diff --git a/.editorconfig b/.editorconfig index 2fc657e..78cf7ff 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,6 +14,10 @@ indent_style = space indent_size = 4 indent_style = space +[*.{js}] +indent_size = 4 +indent_style = tab + [*.{yml,yaml}] indent_size = 2 indent_style = space diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 71d6dc0..5e23ed7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ master ] + branches: [ main ] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [ main ] schedule: - cron: '25 10 * * 6' @@ -35,11 +35,17 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 + + - name: generate embedded resources + run: ls -la + + - name: generate embedded resources + run: python build_script/create_embed.py # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -47,10 +53,25 @@ jobs: # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main + - uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + key: ${{ runner.os }}-pio + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Build PlatformIO Project + run: pio run + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # - name: Autobuild + # uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -64,4 +85,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.gitignore b/.gitignore index 7313e5a..f91f9bd 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,6 @@ # embed is auto generated directory. include/embed/** -# temporary place files when develop web ui -embed/static/** - _release _release/** diff --git a/build_script/create_embed.py b/build_script/create_embed.py index b6488d3..65abe0f 100644 --- a/build_script/create_embed.py +++ b/build_script/create_embed.py @@ -12,7 +12,8 @@ def readall(path): return data -def create_name_from_filename(filename): +def create_name_from_filename(path): + filename = os.path.basename(path) ret = filename ret = ret.replace(".", "_") ret = ret.replace(" ", "_") @@ -31,12 +32,6 @@ def create_name_from_filename(filename): print(f"OUT_DIR = {OUT_DIR}") print("=" * SEP_LENGTH) -# access to global build environment -#print(env.Dump()) - -# access to project build environment (is used source files in "src" folder) -#print(projenv.Dump()) - # # Script to build cpp file from embed directory # @@ -49,31 +44,33 @@ def create_name_from_filename(filename): os.mkdir(OUT_DIR) 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 +for root, dirs, files in os.walk(EMBED_DIR): - if file.name.endswith(".md") or file.name.endswith(".sh"): - print(f"ignored markdown file {file.name}") - continue + for filename in files: + print(root, filename) + file = os.path.join(root, filename) - # start generating source - output = template - data = readall(file) + if file.endswith(".md") or file.endswith(".sh"): + print(f"ignored markdown file {file.name}") + continue - output_name = create_name_from_filename(file.name) + # start generating source + output = template + data = readall(file) - # 変数名部分を置換。 変数名は全部大文字とする - output = output.replace("$$REPLACE_NAME$$", output_name.upper()).replace("$$REPLACE_CONTENT$$", data) - output_cpp_path = os.path.join(OUT_DIR, output_name + ".h") + output_name = create_name_from_filename(file) - f = open(output_cpp_path, 'w') - data = f.write(output) - f.close() + # 変数名部分を置換。 変数名は全部大文字とする + output = output.replace("$$REPLACE_NAME$$", output_name.upper()).replace("$$REPLACE_CONTENT$$", data) + output_cpp_path = os.path.join(OUT_DIR, output_name + ".h") + + # todo ファイル名重複チェック。すでにあったらコケる + + f = open(output_cpp_path, 'w') + data = f.write(output) + f.close() - print(f"generated {output_cpp_path}") + print(f"generated {output_cpp_path}") print("=" * SEP_LENGTH) diff --git a/create_release.sh b/create_release.sh index 6bab4b6..7d961bc 100755 --- a/create_release.sh +++ b/create_release.sh @@ -1,10 +1,10 @@ #!/bin/bash -eu MYDIR=$(cd "$(dirname "$0")"; pwd) -RELEASE_BASE_DIR=${MYDIR}/_release/ -RELEASE_DIR=${RELEASE_BASE_DIR}/EnvBoyX +RELEASE_BASE_DIR=${MYDIR}/_release SRC_BUILD_DIR=${MYDIR}/.pio/build -TGT_BUILD_DIR=${RELEASE_DIR}/.pio/build + +# SUBs banner () { echo "" @@ -16,69 +16,76 @@ banner () { copy_src_to_tgt () { banner "Copying board build $1" - # ESP32 dev board - mkdir -p ${TGT_BUILD_DIR}/$1 - cp -v ${SRC_BUILD_DIR}/$1/*.bin ${TGT_BUILD_DIR}/$1/ + # $1 = boardname + cp -v ${SRC_BUILD_DIR}/$1/firmware.bin ${RELEASE_BASE_DIR}/$1_EnvBoyX_$VER$VER_ADDITIONAL.bin } -banner "Delete source build dir and clean build" -rm -rf ${MYDIR}/.pio +# -------------------- +# MAIN +# -------------------- cd ${MYDIR} -pio run - -banner "Delete and create build directory" -rm -rf ${RELEASE_BASE_DIR} - -mkdir -p ${RELEASE_DIR} - -cp ./platformio.ini ${RELEASE_DIR}/ - -BOARDS=( esp32dev esp12e ) -for b in ${BOARDS[@]} ; do - copy_src_to_tgt $b -done - -banner "Add 'how to write' to build directory" -cp ${MYDIR}/docs/how_to_write*.md ${RELEASE_DIR} +# ------------------------------------------------------------------------- +banner "Get Version" +# ------------------------------------------------------------------------- +MAJOR=$(grep " ver =" ./src/global.cpp | sed -r 's/.*"(.*+)".*/\1/') +MINOR=$(grep " minorVer =" ./src/global.cpp | sed -r 's/.*"(.*+)".*/\1/') +VER=$(echo "${MAJOR}.${MINOR}") +echo "Version is ${VER}" -banner "Creating Archive" - -rm -f ${MYDIR}/EnvBoyX*.tar.gz +# ------------------------------------------------------------------------- +banner "Get Version additional info (debug, branch...)" +# ------------------------------------------------------------------------- # git ブランチからファイル名判定 branch=`git symbolic-ref --short HEAD` +if [ $branch != "main" ]; then + echo "git branch is not master/main!" + echo "Appending branch name" + VER_ADDITIONAL=$branch +fi +# DEBUGビルド判定 set +e grep -i "DEBUG_BUILD.*TRUE" ${MYDIR}/src/global.cpp IS_DEBUG=$? + +if [ $IS_DEBUG -eq 0 ] ; then + grep -i "const DEBUG_MODE.*true" ${MYDIR}/embed/static/config.js + IS_DEBUG=$? +fi set -e if [ $IS_DEBUG -eq 0 ] ; then echo "** DEBUG_BUILD IS SET TO TRUE" - RELEASE_FILE_PATH=${MYDIR}/EnvBoyX-DEBUG.tar.gz -elif [ $branch = "main" ]; then - RELEASE_FILE_PATH=${MYDIR}/EnvBoyX.tar.gz -else - echo "git branch is not master/main!" - echo "Appending branch name" - RELEASE_FILE_PATH=${MYDIR}/EnvBoyX-$branch.tar.gz + + if [ ${#VER_ADDITIONAL} -eq 0 ] ; then + VER_ADDITIONAL=DEBUG + else + VER_ADDITIONAL=$(echo "${VER_ADDITIONAL}-DEBUG") + fi +fi + +if [ ${#VER_ADDITIONAL} -gt 0 ] ; then + VER_ADDITIONAL=$(echo "-$VER_ADDITIONAL") fi -rm -f ${MYDIR}/EnvBoyX*.tar.gz -tar -C ${RELEASE_BASE_DIR} -zcvf ${RELEASE_FILE_PATH} . +# ------------------------------------------------------------------------- +banner "Delete and create build directory" +# ------------------------------------------------------------------------- +rm -rf ${RELEASE_BASE_DIR} +mkdir -p ${RELEASE_BASE_DIR} -banner "Release archive created ${RELEASE_FILE_PATH}" +# ------------------------------------------------------------------------- +banner "Build" +# ------------------------------------------------------------------------- -# Warning +BOARDS=( esp32dev esp12e ) -echo "To upload, run:" -echo "tar xvf `basename ${RELEASE_FILE_PATH}`" -echo "cd `basename ${RELEASE_FILE_PATH}`" -echo "pio run -t nobuild -t upload --disable-auto-clean" -echo "If you want to specify board, use below." -echo "pio run -e [esp32dev | esp12e] -t nobuild -t upload --disable-auto-clean" +pio run --target clean +pio run --environment esp32dev --environment esp12e + +for b in ${BOARDS[@]} ; do + copy_src_to_tgt $b +done -if [ $IS_DEBUG -eq 1 ] ; then - banner "RELEASE VERSION ! Nice work !" -fi diff --git a/devel_writeback.sh b/devel_writeback.sh deleted file mode 100755 index 417904b..0000000 --- a/devel_writeback.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -WORKDIR=$(dirname $0) - -cd $WORKDIR/embed - -watch -n 15 cp -v "./static/*" . \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index 5b02071..f3b53db 100644 --- a/docs/api.md +++ b/docs/api.md @@ -9,21 +9,26 @@ EnvBoyX has http web API. | 2.0 | GET | / | JSON | sensor value as JSON.| | 2.0 | GET | /ping | TEXT | returns pong. | | 39.0 | GET | /stastics | JSON | stastics JSON | -| 3.4 | GET | /brightness | JSON | value=0-255 set display brightness (0-255) 0 means OFF (adHoc, not config) | -| 3.4 | GET | /display | JSON | set display power (1 = ON / 0 = OFF) | | 40.0 | POST | /goto_setup | JSON | Go to setup mode at next boot | -| 41.0 | POST | /mhz19b/abc | JSON | value=1 ON value=0 OFF | -| 41.0 | POST | /mhz19b/zeroCalibration | TEXT | __DANGER__ Use if you know what you are doing | -| 42.0 | GET | /config | JSON | Get config | -| 42.0 | POST | /config | JSON | Change config | -| 42.0 | POST | /config/commit | JSON | Save config | -| 42.0 | POST | /config/revert | JSON | Revert to last saved config | -| 43.0 | GET | /config/backup | TEXT | Get current running config on curl command line | -| 44.0 | POST | /config/factory-reset | JSON | Delete ALL configs | +| 3.4 | GET | /api/v1/brightness | JSON | value=0-255 set display brightness (0-255) 0 means OFF (adHoc, not config) | +| 3.4 | GET | /api/v1/display | JSON | set display power (1 = ON / 0 = OFF) | +| 41.0 | POST | /api/v1/mhz19b/abc | JSON | value=1 ON value=0 OFF | +| 41.0 | POST | /api/v1/mhz19b/zeroCalibration | TEXT | __DANGER__ use if you know what you are doing. see below | +| 42.0 | GET | /api/v1/config | JSON | Get config | +| 42.0 | POST | /api/v1/config | JSON | Change config | +| 42.0 | POST | /api/v1/config/commit | JSON | Save config | +| 42.0 | POST | /api/v1/config/revert | JSON | Revert to last saved config | +| 43.0 | GET | /api/v1/config/backup | TEXT | Get current running config on curl command line | +| 44.0 | POST | /api/v1/config/factory-reset | JSON | Delete ALL configs | NOTE: There is no reboot API, because of security reason. -## /config API +## /api/v1/mhz19b/zeroCalibration + +This method may break your MHZ19B accuracy. +By datasheet, place mh-z19b in under 400ppm for at least 20 minutes. + +## /api/v1/config API This API changes config (same thing in setup mode) But some paramters are not changeable. @@ -79,7 +84,7 @@ example: co2alerts.warning1.low #### cURL example ``` -curl -s -X POST -d "displayFlip=yes&displayBrightness=50" "http://ebx32.local/config" +curl -s -X POST -d "displayFlip=yes&displayBrightness=50" "http://ebx32.local/api/v1/config" ``` ## HTTP Web API cURL examples @@ -89,7 +94,8 @@ These are example of calling API ### Simple get API ``` -curl http://[envboy IP or mDNShostname.local]/ +curl http://[envboy IP or mDNShostname.local]/api/v1/getdata + {"product":"EnvBoyX","uptime":"01:18:02","uptimeMills":4682994,"temparature":"28.60","humidity":"35.00","pressure":"976.81","luminous":"16","luminousIr":"2","co2ppm":"-999","co2ppmAccuracy":"","rssi":-12} ``` @@ -106,3 +112,26 @@ OK $ curl -X POST http://[envboy IP or mDNShostname.local]/goto_setup OK ``` + +### Appendix API urls until v45 + +This information is old until v45. + +| since | METHOD | ENDPOINT | RETURN | description | +| ------| ------ | ------------- | -------------------------- | -------| +| 2.0 | GET | / | JSON | sensor value as JSON.| +| 2.0 | GET | /ping | TEXT | returns pong. | +| 39.0 | GET | /stastics | JSON | stastics JSON | +| 3.4 | GET | /brightness | JSON | value=0-255 set display brightness (0-255) 0 means OFF (adHoc, not config) | +| 3.4 | GET | /display | JSON | set display power (1 = ON / 0 = OFF) | +| 40.0 | POST | /goto_setup | JSON | Go to setup mode at next boot | +| 41.0 | POST | /mhz19b/abc | JSON | value=1 ON value=0 OFF | +| 41.0 | POST | /mhz19b/zeroCalibration | TEXT | __DANGER__ Use if you know what you are doing | +| 42.0 | GET | /config | JSON | Get config | +| 42.0 | POST | /config | JSON | Change config | +| 42.0 | POST | /config/commit | JSON | Save config | +| 42.0 | POST | /config/revert | JSON | Revert to last saved config | +| 43.0 | GET | /config/backup | TEXT | Get current running config on curl command line | +| 44.0 | POST | /config/factory-reset | JSON | Delete ALL configs | + +NOTE: There is no reboot API, because of security reason. diff --git a/docs/history.md b/docs/history.md index bd237c3..eb7bcad 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,13 +1,28 @@ # Version History -* FIX: Fix bug, or not friendly behavior -* CHANGE: Change some behavior -* ADD: Add new function -* DROP: Delete some functions -* NOTE: other things +## v46.0: Big Usability improve update + +* Config WebUI is always ON (normal and setup mode) +* Web Updater is added. (only on ESP32) + +### UPGRADE NOTE + +* Almost all API url are changed. +* API URLs are prefixed by /api/v1 is added. (exception: /ping /goto_setup). +* See [API Docs](api.md). + +### CHANGES + +* ADD: Web config both on normal mode and setup mode +* BREAKING CHANGE: All API urls changed. +* Deprecate: MQTT mode will be delete ## v45.0: Config update +### UPGRADE NOTE + +### CHANGES + * ADD: AutoDimmer * ADD: Config class (Internal mechanism) maybe consume 4KB memory. * ADD: Config value validation (Setup web, Web API) @@ -18,12 +33,12 @@ ## v44.0: Internal refactoring Update -### UPGRADE NOTE: +### UPGRADE NOTE * Some config are back to default value. (all alerts) * If using before v42, update to v43 first. and get config backup using `http://[envboy]/config/backup` API -### Changes +### CHANGES * CHANGE: Config version 12 -> 44 (New config version scheme. same as EBX version) * ADD: MHZ-19B Auto Baseline Correction ON/OFF at boot time @@ -37,6 +52,10 @@ ## v43.0: Delta all update +### UPGRADE NOTE + +### CHANGES + * CHANGE: Espressif 32 framework 3.0 -> 3.1 * ADD: Delta display * FIX: Alert config can't save (warning1.high, caution2.low&high) @@ -44,6 +63,10 @@ ## v42.0: Config update +### UPGRADE NOTE + +### CHANGES + * CHANGE: all API results are now JSON * ADD: freeHeap to JSON, value of ESP.getFreeHeap() (update 15sec) * ADD: Config GET / SET API @@ -51,17 +74,29 @@ ## v41.0: MH-Z19B update +### UPGRADE NOTE + +### CHANGES + * ADD: API: MH-Z19B Auto Baseline Calibration ON/OFF * ADD: API: MH-Z19B Zero Calibration ## v40.1: Bugfix release for v40 +### UPGRADE NOTE + +### CHANGES + * FIX: SSD1306: Missing unit when no alerts * CHANGE: Add minor version * CHANGE: Delete startup first screen ## v40: Pressure Delta update +### UPGRADE NOTE + +### CHANGES + * FIX: CO2 ppm alert value is not good. * ADD: Add POST /goto_setup API. * FIX: Delete unused source code. @@ -76,6 +111,10 @@ ## v39: SH1106 Support release +### UPGRADE NOTE + +### CHANGES + * CHANGE: CONFIG: Add SSD1306 / SH1106 switch * CHANGE: HTTP: ESP32: Stop Async Web server. back to standard webserver to reduce code duplicate #42 * ADD: WiFi RSSI to JSON @@ -83,6 +122,10 @@ ## v38: Small update release +### UPGRADE NOTE + +### CHANGES + * CHANGE: CONFIG: ESP32 now uses SPIFFS instead of LITTLEFS but no format or setup required (remove LITTLEFS to SPIFFS wrapper because of compile error) * CHANGE: SSD1306: Move to U8G2 graphic library * CHANGE: SSD1306: Lux unit "lx" to "Lx" (1 and l is very similer griph) @@ -92,6 +135,10 @@ ## v37: Alert update. +### UPGRADE NOTE + +### CHANGES + * VERSION: 37. next version is v38. * FIX: Alert settings are not saved on ESP32. * FIX: Alert settings are not propery saved. @@ -100,6 +147,10 @@ ## v3.6: Bugfix release. +### UPGRADE NOTE + +### CHANGES + * NOTE: LICENSE: EnvBoyX is now under APL 2.0 , Affected to all versions. (license terms are not shown before) * FIX: TSL2561 can't enabled * CHANGE: Serial speed changed to 74880. (was 115200) @@ -108,11 +159,19 @@ ## v3.5 +### UPGRADE NOTE + +### CHANGES + * CHANGE: Config: version 9 (was 8) and using JSON format * ADD: Config: Migration between versions ## v3.4 +### UPGRADE NOTE + +### CHANGES + * CHANGE: Config: version 8 (was 6) * FIX: SSD1306 not shown in Setup Mode * ADD: Default Brightness setting @@ -120,6 +179,10 @@ ## v3.3 +### UPGRADE NOTE + +### CHANGES + * FIX: MH-Z19B wrong message * FIX: Wrong uptime on JSON * ADD: ST7789 Brightness change (via Web API) @@ -128,6 +191,10 @@ ## v3.2 +### UPGRADE NOTE + +### CHANGES + * FIX: Avoid using delay * ADD: I2C scan on startup. * FIX: ESP8266: I2C not working @@ -135,16 +202,28 @@ ## v3.1 +### UPGRADE NOTE + +### CHANGES + * ADD: vertical display mode(bigger font. ST7789 only) * FIX: Refactoring ## v3.0 +### UPGRADE NOTE + +### CHANGES + * ADD: ST7789 Support (T-Display) * NOTE: ST7789 is initial support, some screens are simplifyed. ## v2.7: Display item change update +### UPGRADE NOTE + +### CHANGES + * CHANGE: Delete alive indicator "*" after EnvBoyX string. * ADD: alive indicator. EnvBoyX's "X" character now blinks. * CHANGE: prefix "IP:" before IP address @@ -153,33 +232,63 @@ ## v2.6 +### UPGRADE NOTE + +### CHANGES + * FIX: Fix mDNS not working ## v2.5 +### UPGRADE NOTE + +### CHANGES + * ADD: /display endpoint. ## v2.4 +### UPGRADE NOTE + +### CHANGES + * ADD: /brightness endpoint. ## v2.3 +### UPGRADE NOTE + +### CHANGES + * FIX: Unifing EnvBoyX (ESP8266) and EnvBoyX32 (ESP32) again. ## v2.2 +### UPGRADE NOTE + +### CHANGES + * missing version due to bug. ## v2.1 +### UPGRADE NOTE + +### CHANGES + * FIX: Split EnvBoyX (8266) and EnvBoyX32 (ESP32) ## v2.0 +### UPGRADE NOTE + +### CHANGES + * FIX: Move Arduino IDE to Platform.IO ## before v2.0 +Changelog for EnvboyX before 2.0 is below; + * https://github.com/yakumo-saki/envboy * https://github.com/yakumo-saki/EnvBoyMQTT diff --git a/docs/how_to_write-en.md b/docs/how_to_write-en.md index 2df1de9..54844a1 100644 --- a/docs/how_to_write-en.md +++ b/docs/how_to_write-en.md @@ -27,3 +27,11 @@ pio run -t nobuild -t upload --disable-auto-clean * `-t nobuild` no build * `-t upload` Do write * `--disable-auto-clean` Don't delete firmware.bin + +## Update via web browser + +This method only for ESP32 and EnvBoyX v46.0 or above. + +* download latest release from github. +* Go to http://[envboyx ip address or hostname]/update +* Upload and flash. \ No newline at end of file diff --git a/docs/how_to_write-jp.md b/docs/how_to_write-jp.md index efaa7d0..560ad47 100644 --- a/docs/how_to_write-jp.md +++ b/docs/how_to_write-jp.md @@ -29,6 +29,14 @@ pio run -t nobuild -t upload --disable-auto-clean * `-t upload` 書き込みする * `--disable-auto-clean` ビルドディレクトリを自動的に削除しない(これをつけないと、ビルド済みバイナリが削除されます) +## Update via web browser + +This method only for ESP32 and EnvBoyX v46.0 or above. + +* download latest release from github. +* Go to http://[envboyx ip address or hostname]/update +* Upload and flash. + ## 蛇足 以下は、必要でなければ読む必要はありません。 diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index e69de29..0000000 diff --git a/embed/config.html b/embed/config.html index 6db49e9..c1cee62 100644 --- a/embed/config.html +++ b/embed/config.html @@ -2,290 +2,305 @@ - - - - EnvBoyX setting - - + + + + EnvBoyX setting + + -

EnvBoyX Settings (EBXCFG.v45)

-
-
-
- ネットワーク設定 - WiFi接続情報
- ※ 2.4GHz帯のみ対応しています。
-
-
-
mDNS名(a.k.a Rendezvous, avahi-daemon)
- ※ mDNS名.local で他端末から本機にアクセスすることができます。
-
-
-
- 動作モード設定 - 動作モード
-
- -
-
-
- 表示デバイス共通設定 - 画面反転
- -
- -
+ +

Now loading. Please wait.

+
+ +

EnvBoyX Settings (settingID: EBXCFG.vXX)

+ + + + +
+
+ ネットワーク設定 + WiFi接続情報
+ ※ 2.4GHz帯のみ対応しています。
+ SSID
+ パスワード +
+
mDNS名(a.k.a Rendezvous, avahi-daemon)
+ ※ mDNS名.local で他端末から本機にアクセスすることができます。
+
+
+
+ 動作モード設定 + 動作モード
+
+ +
+
+
+ 表示デバイス共通設定 + 画面反転
+ +
+ +
+ + 明るさ(0-255)
+
+ + Wait for reconfigure画面
+ ※ 起動時にセットアップモードに入るための待ち時間を追加します。
+ +
+ - 明るさ(0-255)
-
- - Wait for reconfigure画面
- ※ 表示しない場合、セットアップモードに入るためにはWeb APIを使用する必要があります。
- -
- - -
+
- 自動消灯する明るさ(暗いと判定するしきい値)
- ※ 光量センサーがない場合は自動的に無効になります。
- ※ 自動消灯を使用しない場合は、999999を入力してください。
-
+ 自動消灯する明るさ(暗いと判定するしきい値)
+ ※ 光量センサーがない場合は自動的に無効になります。
+ ※ 自動消灯を使用しない場合は、999999を入力してください。
+
- 自動消灯判定時間
- ※ しきい値未満の明るさがこの時間継続したら消灯します。
-
-
-
- I2C OLED デバイス設定 - ※ 接続されていない場合無視されます。画面端にゴミが表示されている、または数ドット欠けている場合は変更してください。
- -
+ 自動消灯判定時間
+ ※ しきい値未満の明るさがこの時間継続したら消灯します。
+
+
+
+ I2C OLED デバイス設定 + ※ 接続されていない場合無視されます。画面端にゴミが表示されている、または数ドット欠けている場合は変更してください。
-
-
-
- ST7789 デバイス設定 - ST7789 SPI液晶の有無
- ※ MQTTモードでは無効。
※ SPIピンはここでは指定できません。
- -
- -
- - ST7789 表示モード
- -
+
- -
-
-
- MH-Z19B デバイス設定 - MH-Z19B CO2センサー有無(金色のセンサー)
- -
+
+
+
+ ST7789 デバイス設定 + ST7789 SPI液晶の有無
+ ※ MQTTモードでは無効。
※ SPIピンはここでは指定できません。
- -
+
-
+
- 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. + ST7789 表示モード
+ +
+ + +
+
+
+ MH-Z19B デバイス設定 + MH-Z19B CO2センサー有無(金色のセンサー)
+ +
+ +
+ +
+
+ + MH-Z19BのUARTが接続されているGPIOピン番号
+ ※ MH-Z19Bを使用しない場合は設定不要
※ ESP8266では RX 14 TX 0 で固定
※ ボードによって使用可能なピンが異なるので動作しない場合は他のピンを試してください。
+ RXピン  +
+ TXピン  +
+ +
MH-Z19BのAuto Baseline Correction
+ ※ MH-Z19Bを使用しない場合は無視されます。
+ +
+ +
+
+
+ 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.00.0, Copyright 2018-2022 Yakumo Saki / ziomatrix.org. \ No newline at end of file diff --git a/embed/config.js b/embed/config.js deleted file mode 100644 index 8acdbb6..0000000 --- a/embed/config.js +++ /dev/null @@ -1,22 +0,0 @@ -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/static/config.js b/embed/static/config.js new file mode 100644 index 0000000..f66c2d0 --- /dev/null +++ b/embed/static/config.js @@ -0,0 +1,375 @@ +"use strict"; + +const DEBUG_MODE = false; +const DEBUG_API_HOST = "10.1.0.110"; // デバッグ時、APIを投げる先 + +const CONTENT_JSON = "application/json"; +const CONTENT_TEXT = "text/plain"; +const CONTENT_FORM = "application/x-www-form-urlencoded" +const IGNORE_KEY = ["settingId"] + +let SETUP_MODE = false; + +// 画面ロード時のconfig。保存時に差分を取るために使用する。 +let LAST_CONFIG = null; + +function toPath(relativePath) { + if (!DEBUG_MODE) { + return relativePath; + } + + var path = "http://" + DEBUG_API_HOST; + if (!relativePath.startsWith("/")) { + path = path + "/"; + } + + return path + relativePath; +} + +async function httpGet(relativePath, contentType = CONTENT_JSON) { + if (DEBUG_MODE) { + console.log("httpGet " + toPath(relativePath)); + } + + const ret = await fetch(toPath(relativePath), { + method: "GET", + cache: "no-cache", + headers: { + "Content-Type": contentType, + }, + }); + + if (!ret.ok) { + throw `httpGet: ${relativePath} response is not ok. ${ret.status} ${ret.statusText}` + } + + return ret; +} + +/** + * + * @param {string} relativePath + * @param {object} body + * @param {string} contentType CONTENT_* + * @returns promise + */ +async function httpPost(relativePath, body, contentType = CONTENT_TEXT) { + if (DEBUG_MODE) { + console.log("httpPost " + toPath(relativePath)); + } + + const ret = await fetch(toPath(relativePath), { + method: "POST", + cache: "no-cache", + headers: { + "Content-Type": contentType, + }, + body: body + }); + + if (!ret.ok) { + throw `httpPost: ${relativePath} response is not ok. ${ret.status} ${ret.statusText}` + } + + return ret; +} + +function replaceVersion(replaceName, value) { + const elems = document.querySelectorAll(`span[replace='${replaceName}']`); + elems.forEach((element) => { + element.innerHTML = value; + }); +} + +/** + * ページロード時にEBXのバージョン等を置き換える + */ +async function setPageValues() { + const res = await httpGet("/ping"); + console.log(res); + + const json = await res.json(); + console.log(json); + + SETUP_MODE = json["mode"] == "SETUP"; + + replaceVersion("productVer", `ver.${json["majorVer"]}.${json["minorVer"]}`); + replaceVersion("settingId", json["settingId"]); +} + +async function showSetupModeOnly() { + const elements = document.querySelectorAll(".setupModeOnly"); + elements.forEach(element => { + element.hidden = !SETUP_MODE; + }); + +} + +/** + * configMap の値をドキュメントにセットする + */ +function setConfigValuesToPage(configMap) { + configMap.forEach((v, k) => { + + // console.log("key", k, "val", v); + + const el = document.querySelector(`input[name="${k}"]`); + if (el == null) { + console.log(`key ${k} is not found on document.`); + return; + } + + if (el.type === "radio") { + const radio = document.querySelector( + `input[name="${k}"][value="${v}"]` + ); + if (radio == null) { + console.log( + `radio button name=${k} value=${v} is not found on document.` + ); + } + radio.checked = true; + } else { + // input type=text,password,number + el.value = v; + } + }); +} + +/** + * configのキー、値をWeb上の要素にセット。checkboxには対応していない + * @param {string} key + * @returns boolean 成功 or 失敗 + */ +function setInputValue(key, value) { + + const els = document.querySelectorAll(`input[name='${key}']`); + if (els.length > 1) { + // RADIO + const elements = document.querySelectorAll(`input[name='${key}'][value='${value}']`); + if (elements.length > 1) { + console.log(`failed. multiple elements found ${key}=${value}`); + return false; + } else if (elements.length < 1) { + console.log(`failed. no elements found ${key}=${value}`); + return false; + } + + elements[0].checked = true; + return; + } else if (els.length == 1) { + els[0].value = value; + return true; + } else if (els.length == 0) { + console.log(key, "element not found"); + return false; + } + + return true; +} + + +/** + * Web上の要素から値を取得 + * @param {string} key + * @returns string + */ +function getInputValue(key) { + + const els = document.querySelectorAll(`input[name='${key}']`); + if (els.length > 1) { + // RADIO + const el = document.querySelector(`input[name='${key}']:checked`); + return el.value; + } else if (els.length == 1) { + return els[0].value; + } else if (els.length == 0) { + console.log(key, "element not found"); + return null; + } + + return "INVALID"; +} + +/** + * org -> new で異なっているものだけをMapにして返す + * @param {Map} orgMap + * @param {Map} newMap + * @return {Map} + */ +function createConfigDiff(orgMap, newMap) { + const ret = new Map(); + + orgMap.forEach((val, key) => { + const newVal = newMap.get(key); + // console.log("diff", key, val, newVal); + if (val !== newVal) { + ret.set(key, newVal); + } + }); + + return ret; +} + +/** + * ConfigをEnvBoyに保存するAPIを呼ぶ + * @param {Map} configMap + * @return {object} result + */ +async function saveConfig(configMap) { + const params = new URLSearchParams() + configMap.forEach((v,k) => params.append(k, v)); + + try { + const res = await httpPost("/api/v1/config", params, CONTENT_FORM); + return res; + } catch (err) { + throw `Failed to send config: ${err}`; + } +} + +async function commitRevertConfig(isCommit = true) { + const URL = "/api/v1/config/" + (isCommit ? "commit" : "revert"); + try { + const commit = await httpPost(URL, "", CONTENT_JSON); + return commit; + } catch (err) { + throw `Failed to commit config: ${err}`; + } +} + +async function commitConfig() { + return commitRevertConfig(true); +} + +async function revertConfig() { + return commitRevertConfig(false); +} + + +/** + * 保存ボタンの処理 + */ +async function saveConfigButton() { + + const waitDialog = document.getElementById("waitDialog"); + + const newConfig = new Map(); + + // INPUTから取得 + LAST_CONFIG.forEach((_, key) => { + const val = getInputValue(key); + newConfig.set(key, val); + }); + + console.log(newConfig); + + // 差分作成 + let configDiff = createConfigDiff(LAST_CONFIG, newConfig); + + let confirmMessage = "変更を保存してよろしいですか?"; + if (configDiff.size == 0) { + confirmMessage = confirmMessage + "\n\n※設定内容に変更がありません"; + } + + if (!window.confirm(confirmMessage)) { + return; + } + + try { + waitDialog.showModal(); + + let needReboot = false; + if (configDiff.size > 0) { + const res = await saveConfig(configDiff); + const result = await res.json(); + if (result.success != true) { + alert("設定内容にエラーがあります。設定は反映されていません。\n" + result.message + "\n" + + result.msgs.join("\n")); + return; + } + needReboot = result.needReboot; + } + + const commit = await commitConfig(); + const commitResult = await commit.json(); + if (commitResult.success != true) { + alert("設定の保存に失敗しました。"); + return; + } + + if (needReboot) { + alert("設定を保存しました。\n再起動するまで反映されない設定があります。"); + } else { + alert("設定を保存しました。"); + } + + } catch (err) { + console.log(err); + return; + } finally { + waitDialog.close(); + } + +} + +/** + * configを取得してきたObjectからMapに変換する。 + * ついでに不要な項目は捨てる + * @param {Object} configObj + * @return {Map} + */ +function configObjectToMap(configObj) { + const map = new Map(); + Object.keys(configObj).forEach(k => { + if (IGNORE_KEY.includes(k)) { + return; + } + map.set(k, configObj[k]); + }); + return map; +} + +async function loadConfig() { + try { + const res = await httpGet("/api/v1/config", CONTENT_JSON); + const json = await res.json(); + console.log(json); + + if (!json["success"]) { + alert("エラーが発生しました。"); + return; + } + + const configObj = json["config"]; + const confMap = configObjectToMap(configObj); + + confMap.set("password", ""); // Add password as empty for later use + LAST_CONFIG = confMap; + + setConfigValuesToPage(confMap); + + } catch (err) { + alert(err); + return; + } + +} + +document.addEventListener("DOMContentLoaded", async (event) => { + const waitDialog = document.getElementById("waitDialog"); + waitDialog.showModal(); + + console.log("DOM fully loaded and parsed"); + document.getElementById("submit").addEventListener("click", async (event) => { + console.log("Submit Clicked"); + await saveConfigButton(); + }); + + await setPageValues(); + await showSetupModeOnly(); + await loadConfig(); + + waitDialog.close(); +}); + diff --git a/embed/static/style.css b/embed/static/style.css new file mode 100644 index 0000000..3867c6a --- /dev/null +++ b/embed/static/style.css @@ -0,0 +1,45 @@ +@charset "utf-8"; +body { + background-color: aliceblue; + line-height: 110%; +} + +body.setup_done { + background-color: paleturquoise; +} + +body.setup_err { + background-color: lightsalmon; +} + +span.header { + display: inline-block; + width: 6em; +} + +input[type='text'],input[type='password'] { + width: 15em; +} + +input.num { + width: 6em; +} + +input#submit { + width: 200px; + height: 48px; +} + +a.setup_again { + font-size: 150%; +} + +dialog::backdrop { + background: rgba(0, 0, 0, 0.6); +} + +.notice { + background-color: blanchedalmond; + border: 2px solid black; + padding: 0.5em 1.5em 0.5em 1.5em; +} \ No newline at end of file diff --git a/embed/style.css b/embed/style.css deleted file mode 100644 index d80f0e2..0000000 --- a/embed/style.css +++ /dev/null @@ -1,29 +0,0 @@ -@charset "utf-8"; -body { - background-color: aliceblue; -} - -body.setup_done { - background-color: paleturquoise; -} - -body.setup_err { - background-color: lightsalmon; -} - -input[type='text'] { - width: 200px; -} - -input.num { - width: 50px; -} - -input[type='submit'] { - width: 200px; - height: 48px; -} - -a.setup_again { - font-size: 150%; -} \ No newline at end of file diff --git a/embed/update.html b/embed/update.html new file mode 100644 index 0000000..f3977d2 --- /dev/null +++ b/embed/update.html @@ -0,0 +1,28 @@ + + + + + + + + EnvBoyX updater + + + + + + Firmware Update
+ This is beta feature. proceed with care !
+
+
+ アップロードするファイル
+ (esp32_envboyx_[version].bin
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/embed/update_done.html b/embed/update_done.html new file mode 100644 index 0000000..f81cc85 --- /dev/null +++ b/embed/update_done.html @@ -0,0 +1,18 @@ + + + + + + + + EnvBoyX update completed + + + + + +

Firmware Update completed.

+

EnvBoy is rebooting... please wait

+

NOTE: If not rebooting, reboot manually.

+ + \ No newline at end of file diff --git a/embed/update_fail.html b/embed/update_fail.html new file mode 100644 index 0000000..ed9b8df --- /dev/null +++ b/embed/update_fail.html @@ -0,0 +1,18 @@ + + + + + + + + EnvBoyX update completed + + + + + +

Firmware Update failed.

+

Please reboot now.

+

But EnvBoy might be damaged.

+ + \ No newline at end of file diff --git a/embed/update_unsupport.html b/embed/update_unsupport.html new file mode 100644 index 0000000..651d098 --- /dev/null +++ b/embed/update_unsupport.html @@ -0,0 +1,17 @@ + + + + + + + + EnvBoyX update + + + + + +

Firmware Update is not available

+

Your board is not support web updater

+ + \ No newline at end of file diff --git a/include/global.h b/include/global.h index 5c0ab8e..e85c377 100644 --- a/include/global.h +++ b/include/global.h @@ -22,6 +22,11 @@ extern const String SETTING_ID; extern const bool DEBUG_BUILD; +// SETUPモード OR NORMALモード +extern const int OPERATING_MODE_NORMAL; +extern const int OPERATING_MODE_SETUP; +extern int OPERATING_MODE; + // -------------------------------------------------------------------- // デバイス周りの定数 // -------------------------------------------------------------------- diff --git a/include/http_setup.h b/include/http_setup.h index 9f94b2f..75b0751 100644 --- a/include/http_setup.h +++ b/include/http_setup.h @@ -1,19 +1,5 @@ #include -/* 機種依存部分 */ - -/** - * SETUP GET / - */ -void http_send_setup_post_root_html(); - -/** - * SETUP POST / - */ -void http_send_setup_get_root_html(); - -/* 以下は共通 */ - /** * 初期化 */ diff --git a/include/network/api/api_util.h b/include/network/api/api_util.h new file mode 100644 index 0000000..9fa75ce --- /dev/null +++ b/include/network/api/api_util.h @@ -0,0 +1,11 @@ +#include +#include +#include + +bool parseBooleanString(const String value); +std::vector stringSplit(String value, String delimiter); +String jsonToString(DynamicJsonDocument& json); +String http_normal_ping_json(); + +// millisecond -> 00:00:00 形式の日付文字列を返す +String getTimeString(unsigned long ms); diff --git a/include/network/api/basic_api.h b/include/network/api/basic_api.h new file mode 100644 index 0000000..0fb7eac --- /dev/null +++ b/include/network/api/basic_api.h @@ -0,0 +1,2 @@ + +void http_api_basic_setup(); \ No newline at end of file diff --git a/include/network/http_api_config.h b/include/network/api/v1/config.h similarity index 50% rename from include/network/http_api_config.h rename to include/network/api/v1/config.h index 6047ba5..3786b90 100644 --- a/include/network/http_api_config.h +++ b/include/network/api/v1/config.h @@ -1,3 +1,2 @@ -#include "network/http_api.h" void http_api_config_setup(); \ No newline at end of file diff --git a/include/network/http_api_config_backup.h b/include/network/api/v1/config_backup.h similarity index 100% rename from include/network/http_api_config_backup.h rename to include/network/api/v1/config_backup.h diff --git a/include/network/http_api_config_getset.h b/include/network/api/v1/config_getset.h similarity index 99% rename from include/network/http_api_config_getset.h rename to include/network/api/v1/config_getset.h index 4eb1bfe..ef3272e 100644 --- a/include/network/http_api_config_getset.h +++ b/include/network/api/v1/config_getset.h @@ -1,5 +1,4 @@ #include - #include // Config SET API のエントリポイント diff --git a/include/network/http_api_display.h b/include/network/api/v1/display.h similarity index 50% rename from include/network/http_api_display.h rename to include/network/api/v1/display.h index f102c7a..6a84803 100644 --- a/include/network/http_api_display.h +++ b/include/network/api/v1/display.h @@ -1,3 +1,2 @@ -#include "network/http_api.h" void http_api_display_setup(); \ No newline at end of file diff --git a/include/network/api/v1/getdata.h b/include/network/api/v1/getdata.h new file mode 100644 index 0000000..5481058 --- /dev/null +++ b/include/network/api/v1/getdata.h @@ -0,0 +1 @@ +void http_api_getdata_setup(); \ No newline at end of file diff --git a/include/network/api/v1/mhz19b.h b/include/network/api/v1/mhz19b.h new file mode 100644 index 0000000..7ec1681 --- /dev/null +++ b/include/network/api/v1/mhz19b.h @@ -0,0 +1,2 @@ + +void http_api_mhz_setup(); \ No newline at end of file diff --git a/include/network/goto_setup_api.h b/include/network/goto_setup_api.h new file mode 100644 index 0000000..e556f7f --- /dev/null +++ b/include/network/goto_setup_api.h @@ -0,0 +1 @@ +void http_api_gotosetup_setup(); \ No newline at end of file diff --git a/include/network/http_api.h b/include/network/http_api.h deleted file mode 100644 index 954fb91..0000000 --- a/include/network/http_api.h +++ /dev/null @@ -1,10 +0,0 @@ - -#ifdef ESP32 -#include -typedef WebServer HTTPWEBSERVER; -#endif - -#ifdef ESP8266 -#include -typedef ESP8266WebServer HTTPWEBSERVER; -#endif diff --git a/include/network/http_api_base.h b/include/network/http_api_base.h deleted file mode 100644 index 11d17bc..0000000 --- a/include/network/http_api_base.h +++ /dev/null @@ -1,3 +0,0 @@ -#include "network/http_api.h" - -void http_api_base_setup(); \ No newline at end of file diff --git a/include/network/http_api_base_json.h b/include/network/http_api_base_json.h deleted file mode 100644 index ae38513..0000000 --- a/include/network/http_api_base_json.h +++ /dev/null @@ -1,7 +0,0 @@ -#include - -String http_normal_not_found_html(); - -String http_normal_data_json(); - -String http_normal_ping_json(); diff --git a/include/network/http_api_mhz.h b/include/network/http_api_mhz.h deleted file mode 100644 index 2e7ed7c..0000000 --- a/include/network/http_api_mhz.h +++ /dev/null @@ -1,3 +0,0 @@ -#include "network/http_api.h" - -void http_api_mhz_setup(); \ No newline at end of file diff --git a/include/network/http_api_util.h b/include/network/http_api_util.h deleted file mode 100644 index f4c1ae3..0000000 --- a/include/network/http_api_util.h +++ /dev/null @@ -1,5 +0,0 @@ -#include - -bool parseBooleanString(const String value); -std::vector stringSplit(String value, String delimiter); -String jsonToString(DynamicJsonDocument& json); \ No newline at end of file diff --git a/include/network/http_not_found.h b/include/network/http_not_found.h new file mode 100644 index 0000000..ae89666 --- /dev/null +++ b/include/network/http_not_found.h @@ -0,0 +1,3 @@ +#include + +void http_not_found_setup(); \ No newline at end of file diff --git a/include/network/http_utils.h b/include/network/http_utils.h index 872c605..98328b0 100644 --- a/include/network/http_utils.h +++ b/include/network/http_utils.h @@ -1,3 +1,5 @@ +#include + // HTTP ヘッダ (HTTP 1.1 〜) text/htmlで送信する void sendHttpHeader(); @@ -10,3 +12,6 @@ void sendHtmlHeader(); // HTTP REDIRECTヘッダを送信する。これを実行したあとは何も送れない void sendHttpRedirectHeader(String url); + +// CORS リクエストに応答する(http OPTIONS リクエスト) +void http_handle_cors(); \ No newline at end of file diff --git a/include/network/stastics_api.h b/include/network/stastics_api.h new file mode 100644 index 0000000..f3c5916 --- /dev/null +++ b/include/network/stastics_api.h @@ -0,0 +1 @@ +void http_api_stastics_setup(); \ No newline at end of file diff --git a/include/network/http_web_config.h b/include/network/web/http_web_config.h similarity index 100% rename from include/network/http_web_config.h rename to include/network/web/http_web_config.h diff --git a/include/network/web/http_web_updater.h b/include/network/web/http_web_updater.h new file mode 100644 index 0000000..13f2fe1 --- /dev/null +++ b/include/network/web/http_web_updater.h @@ -0,0 +1,3 @@ +#include + +void http_web_updater_setup(); \ No newline at end of file diff --git a/include/network/webserver.h b/include/network/webserver.h index 4f30a57..cc5f16a 100644 --- a/include/network/webserver.h +++ b/include/network/webserver.h @@ -1,9 +1,11 @@ #ifdef ESP32 #include extern WebServer server; +typedef WebServer HTTPWEBSERVER; #endif #ifdef ESP8266 #include extern ESP8266WebServer server; +typedef ESP8266WebServer HTTPWEBSERVER; #endif diff --git a/platformio.ini b/platformio.ini index ec29a89..08eef68 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,6 +10,7 @@ [platformio] description = "EnvBoyX" +default_envs = esp32dev [env] extra_scripts = diff --git a/src/ConfigClass.h b/src/ConfigClass.h index 1f5f9ec..b828ddb 100644 --- a/src/ConfigClass.h +++ b/src/ConfigClass.h @@ -30,10 +30,10 @@ enum class ConfigValueType { // APIにより起動中に設定を変更する際の後処理を表す enum class RunningConfigChangeFlags { - OK = 0, // 反映された。 - REBOOT_REQ = 1, // 反映されたが、再起動するまで反映されない - DISPLAY_REDRAW_REQ = 2, // 反映されたが、液晶を再描画する必要がある - MDNS_RESTART_REQ = 3, // 反映されたが、mDNSを再起動する必要がある + OK = 0, // 後処理は不要 + REBOOT_REQ = 1, // 再起動するまで反映されない + DISPLAY_REDRAW_REQ = 2, // 液晶を再描画する必要がある + MDNS_RESTART_REQ = 3, // mDNSを再起動する必要がある BLOCKED = 4, // その設定は変更できない }; diff --git a/src/ConfigClass_metadata.cpp b/src/ConfigClass_metadata.cpp index 4e273d4..2a1cbd4 100644 --- a/src/ConfigClass_metadata.cpp +++ b/src/ConfigClass_metadata.cpp @@ -27,14 +27,14 @@ void Config::loadMetadata() { ConfigMeta meta; meta.key = ConfigNames::SSID; meta.type = ConfigValueType::String; - meta.flags = RunningConfigChangeFlags::BLOCKED; + meta.flags = RunningConfigChangeFlags::REBOOT_REQ; // v46 BLOCKED to REBOOT_REQ this->addMeta(meta); } { ConfigMeta meta; meta.key = ConfigNames::PASSWORD; meta.type = ConfigValueType::String; - meta.flags = RunningConfigChangeFlags::BLOCKED; + meta.flags = RunningConfigChangeFlags::REBOOT_REQ; // v46 BLOCKED to REBOOT_REQ this->addMeta(meta); } { diff --git a/src/global.cpp b/src/global.cpp index 8a26d27..7ae69c4 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -16,12 +16,13 @@ extern const String product_short = "EBX"; extern const bool DEBUG_BUILD = false; // EnvBoyX +// note: Also referenced by create_release.sh extern const String product = "EnvBoyX"; -extern const String ver = "45"; // Major +extern const String ver = "46"; // Major extern const String minorVer = "0"; // Bugfix extern const String ver_long = " Ver." + ver + "." + minorVer; -// Setting version +// Setting version (version number when settings changed) extern const String SETTING_ID = "EBXCFG.v45"; // EnvBoyX Ver.53.0 @@ -63,3 +64,10 @@ Config *config; // タイマー // -------------------------------------------------------------------- TimerCall timer = TimerCall(); + +// -------------------------------------------------------------------- +// 動作モード +// -------------------------------------------------------------------- +extern const int OPERATING_MODE_NORMAL = 2; +extern const int OPERATING_MODE_SETUP = 1; +int OPERATING_MODE = 0; diff --git a/src/main.cpp b/src/main.cpp index 7e67f7f..7a25618 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,9 +38,12 @@ void setup() isNormal = has_valid_config(); if (!isNormal) { + OPERATING_MODE = OPERATING_MODE_SETUP; sectionlog(F("Entering setup mode.")); + setup_setupmode(); } else { + OPERATING_MODE = OPERATING_MODE_NORMAL; sectionlog(F("Entering normal mode.")); setup_normal(); diff --git a/src/network/api/http_api_util.cpp b/src/network/api/api_util.cpp similarity index 68% rename from src/network/api/http_api_util.cpp rename to src/network/api/api_util.cpp index bcf2440..053b283 100644 --- a/src/network/api/http_api_util.cpp +++ b/src/network/api/api_util.cpp @@ -4,6 +4,20 @@ #include "log.h" +// millisecond -> 00:00:00 形式の日付文字列を返す +String getTimeString(unsigned long ms) { + unsigned long second = ms / 1000; + unsigned long minute = second / 60; + + int sec = second % 60; + int min = (second / 60) % 60; + int hr = minute / 60; + + char buf[15]; + snprintf (buf, sizeof buf,"%02d:%02d:%02d", hr, min, sec); + return String(buf); +} + String jsonToString(DynamicJsonDocument& json) { String jsonStr; serializeJson(json, jsonStr); diff --git a/src/network/api/basic_api.cpp b/src/network/api/basic_api.cpp new file mode 100644 index 0000000..fb4e1d3 --- /dev/null +++ b/src/network/api/basic_api.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "config.h" + +#include "log.h" +#include "global.h" +#include "display/display.h" + +#include "network/webserver.h" +#include "network/api/basic_api.h" +#include "network/api/api_util.h" +#include "network/http_utils.h" + +extern HTTPWEBSERVER server; + + +String http_normal_ping_json() { + + unsigned long ms = millis(); + String timeString = getTimeString(ms); + + DynamicJsonDocument doc(2000); + doc["product"] = product; + doc["uptime"] = timeString; + doc["uptimeMills"] = ms; + doc["majorVer"] = ver; + doc["minorVer"] = minorVer; + doc["settingId"] = SETTING_ID; + + if (OPERATING_MODE == OPERATING_MODE_NORMAL) { + doc["mode"] = "NORMAL"; + } else if (OPERATING_MODE == OPERATING_MODE_SETUP) { + doc["mode"] = "SETUP"; + } else { + doc["mode"] = "INVALID"; + } + + String json; + serializeJson(doc, json); + + httplog(json); + return json; +} + +void http_handle_ping() { + sendHttpHeader(MimeType::JSON); + String message = http_normal_ping_json(); + server.sendContent(message); +} + +void http_api_basic_setup() { + server.on ( "/ping", HTTP_GET, http_handle_ping); + + // Config web ではPingが最初に実行されるので、ここにOPTION CORSの問い合わせが飛んでくる + // 一度答えればキャッシュされるようなので他のAPIには不要 + server.on ( "/ping", HTTP_OPTIONS, http_handle_cors); + + apilog("Basic API initialized."); +} \ No newline at end of file diff --git a/src/network/api/goto_setup_api.cpp b/src/network/api/goto_setup_api.cpp new file mode 100644 index 0000000..3d81c7a --- /dev/null +++ b/src/network/api/goto_setup_api.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include "config.h" + +#include "log.h" +#include "global.h" +#include "display/display.h" + +#include "network/webserver.h" +#include "network/api/basic_api.h" +#include "network/api/api_util.h" +#include "network/http_utils.h" + +extern HTTPWEBSERVER server; + +void http_handle_goto_setup() { + + remove_configure_flag_file(); + + DynamicJsonDocument json(200); + json["command"] = "GOTO_SETUP"; + json["success"] = true; + json["msg"] = "OK. Entering setup mode at next boot."; + + String jsonStr; + serializeJson(json, jsonStr); + server.send(200, MimeType::JSON, jsonStr); +} + +void http_api_gotosetup_setup() { + server.on ( "/goto_setup", HTTP_POST, http_handle_goto_setup ); + + apilog("Goto Setup API initialized."); +} \ No newline at end of file diff --git a/src/network/api/http_api_base.cpp b/src/network/api/http_api_base.cpp deleted file mode 100644 index bc29246..0000000 --- a/src/network/api/http_api_base.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include - -#include "config.h" - -#include "log.h" -#include "global.h" -#include "display/display.h" -#include "network/http_api.h" -#include "network/http_api_base_json.h" - -extern HTTPWEBSERVER server; - -void http_handle_data() { - String message = http_normal_data_json(); - server.send(200, MimeType::JSON, message); -} - -void http_handle_ping() { - String message = http_normal_ping_json(); - server.send(200, MimeType::JSON, message); -} - -void http_handle_stastics() { - server.send(200, MimeType::JSON, stasticsJSON); -} - -void http_handle_goto_setup() { - - remove_configure_flag_file(); - - DynamicJsonDocument json(200); - json["command"] = "GOTO_SETUP"; - json["success"] = true; - json["msg"] = "OK. Entering setup mode at next boot."; - - String jsonStr; - serializeJson(json, jsonStr); - server.send(200, MimeType::JSON, jsonStr); -} - -void http_handle_not_found() { - String message = http_normal_not_found_html(); - server.send(404, MimeType::HTML, message); -} - -void http_api_base_setup() { - server.on ( "/ping", HTTP_GET, http_handle_ping); - server.on ( "/", HTTP_GET, http_handle_data ); - server.on ( "/stastics", HTTP_GET, http_handle_stastics ); - server.on ( "/goto_setup", HTTP_POST, http_handle_goto_setup ); - - server.onNotFound ( http_handle_not_found ); - apilog("API Base initialized."); -} \ No newline at end of file diff --git a/src/network/api/stastics_api.cpp b/src/network/api/stastics_api.cpp new file mode 100644 index 0000000..6cb4155 --- /dev/null +++ b/src/network/api/stastics_api.cpp @@ -0,0 +1,25 @@ +#include +#include + +#include "config.h" + +#include "log.h" +#include "global.h" +#include "display/display.h" + +#include "network/webserver.h" +#include "network/api/basic_api.h" +#include "network/api/api_util.h" +#include "network/http_utils.h" + +extern HTTPWEBSERVER server; + +void http_handle_stastics() { + server.send(200, MimeType::JSON, stasticsJSON); +} + +void http_api_stastics_setup() { + server.on ( "/stastics", HTTP_GET, http_handle_stastics ); + + apilog("Stastics API initialized."); +} diff --git a/src/network/api/http_api_config.cpp b/src/network/api/v1/config.cpp similarity index 51% rename from src/network/api/http_api_config.cpp rename to src/network/api/v1/config.cpp index d7e8979..c09dd40 100644 --- a/src/network/api/http_api_config.cpp +++ b/src/network/api/v1/config.cpp @@ -4,38 +4,47 @@ #include "log.h" #include "global.h" #include "config.h" -#include "network/http_api.h" -#include "network/http_api_util.h" -#include "network/http_api_config_getset.h" -#include "network/http_api_config_backup.h" #include "sensors/mhz19_uart.h" +#include "network/webserver.h" +#include "network/api/api_util.h" +#include "network/api/v1/config_getset.h" +#include "network/api/v1/config_backup.h" +#include "network//http_utils.h" + extern HTTPWEBSERVER server; void _get_config() { + sendHttpHeader(MimeType::JSON); + String keys = server.arg("key"); std::vector keyArray = stringSplit(keys, ","); - DynamicJsonDocument json = create_config_json(keyArray); + DynamicJsonDocument json(1200); json["command"] = "CONFIG_GET"; json["success"] = true; + json["config"] = create_config_json(keyArray); String jsonStr; serializeJson(json, jsonStr); - server.send(200, MimeType::JSON, jsonStr); + server.sendContent(jsonStr); } void _set_config() { + sendHttpHeader(MimeType::JSON); + String jsonStr = updateConfig(); - server.send(200, MimeType::JSON, jsonStr); + server.sendContent(jsonStr); } void _revert_config() { + sendHttpHeader(MimeType::JSON); + // revertすると何が変更されるかわからないので、全ての反映を実行 reflectConfigAll(); @@ -46,27 +55,36 @@ void _revert_config() { String jsonStr; serializeJson(json, jsonStr); - server.send(200, MimeType::JSON, jsonStr); + server.sendContent(jsonStr); } void _commit_config() { + DynamicJsonDocument json(100); json["command"] = "CONFIG_COMMIT"; json["success"] = true; save_config(); + sendHttpHeader(MimeType::JSON); + String jsonStr; serializeJson(json, jsonStr); - server.send(200, MimeType::JSON, jsonStr); + server.sendContent(jsonStr); } void _backup_config() { + + sendHttpHeader(MimeType::TEXT); + String ret = http_api_backup_config(); - server.send(200, MimeType::TEXT, ret); + server.sendContent(ret); } void _factory_reset() { + + sendHttpHeader(MimeType::JSON); + DynamicJsonDocument json(8000); apilog("FACTORY RESET INITIATED"); @@ -79,16 +97,21 @@ void _factory_reset() { apilog("FACTORY RESET DONE"); - server.send(200, MimeType::JSON, jsonToString(json)); + server.sendContent(jsonToString(json)); } void http_api_config_setup() { - server.on ( "/config", HTTP_GET, _get_config ); - server.on ( "/config", HTTP_POST, _set_config ); - server.on ( "/config/revert", HTTP_POST, _revert_config ); - server.on ( "/config/commit", HTTP_POST, _commit_config ); - server.on ( "/config/backup", HTTP_GET, _backup_config ); - server.on ( "/config/factory-reset", HTTP_POST, _factory_reset ); + server.on ( "/api/v1/config", HTTP_GET, _get_config ); + server.on ( "/api/v1/config", HTTP_POST, _set_config ); + server.on ( "/api/v1/config/revert", HTTP_POST, _revert_config ); + server.on ( "/api/v1/config/commit", HTTP_POST, _commit_config ); + server.on ( "/api/v1/config/backup", HTTP_GET, _backup_config ); + server.on ( "/api/v1/config/factory-reset", HTTP_POST, _factory_reset ); + server.on ( "/api/v1/config", HTTP_OPTIONS, http_handle_cors); + server.on ( "/api/v1/config/revert", HTTP_POST, http_handle_cors ); + server.on ( "/api/v1/config/commit", HTTP_OPTIONS, http_handle_cors ); + server.on ( "/api/v1/config/backup", HTTP_OPTIONS, http_handle_cors ); + apilog("API Config initialized."); } \ No newline at end of file diff --git a/src/network/api/http_api_config_backup.cpp b/src/network/api/v1/config_backup.cpp similarity index 85% rename from src/network/api/http_api_config_backup.cpp rename to src/network/api/v1/config_backup.cpp index eb9f87e..ecbb076 100644 --- a/src/network/api/http_api_config_backup.cpp +++ b/src/network/api/v1/config_backup.cpp @@ -21,7 +21,7 @@ String http_api_backup_config() { ret += keys; ret += "\""; - ret += " http://" + config->get(ConfigNames::MDNS) + ".local/config"; + ret += " http://" + config->get(ConfigNames::MDNS) + ".local/api/v1/config"; return ret; } diff --git a/src/network/api/http_api_config_getset.cpp b/src/network/api/v1/config_getset.cpp similarity index 94% rename from src/network/api/http_api_config_getset.cpp rename to src/network/api/v1/config_getset.cpp index 6b96c18..53806fc 100644 --- a/src/network/api/http_api_config_getset.cpp +++ b/src/network/api/v1/config_getset.cpp @@ -10,8 +10,8 @@ #include "mdns_client.h" -#include "network/http_api.h" -#include "network/http_api_util.h" +#include "network/webserver.h" +#include "network/api/api_util.h" #include "display/display.h" extern unsigned int CONF_JSON_SIZE; @@ -97,6 +97,7 @@ void _reflectConfig(ConfigHookFlags& flags, bool all = false) { if (all || flags.needMDnsRestart) { apilog("Exec mDNS restart."); + apilog("New mDNS name restart." + config->get(ConfigNames::MDNS)); mdns_hostname_change(config->get(ConfigNames::MDNS)); } } @@ -146,9 +147,9 @@ String updateConfig() { json["msgs"] = msgs; if (flags.configFailed) { - json["message"] = "Some error detected. Check msg."; + json["message"] = "Some error detected. Check msgs."; } else { - json["message"] = "Don't forget calling /config/commit."; + json["message"] = "Don't forget calling config/commit. API"; } json.shrinkToFit(); diff --git a/src/network/api/http_api_display.cpp b/src/network/api/v1/display.cpp similarity index 84% rename from src/network/api/http_api_display.cpp rename to src/network/api/v1/display.cpp index dbc4a1b..c279b5c 100644 --- a/src/network/api/http_api_display.cpp +++ b/src/network/api/v1/display.cpp @@ -4,7 +4,7 @@ #include "log.h" #include "global.h" #include "display/display.h" -#include "network/http_api.h" +#include "network/webserver.h" extern HTTPWEBSERVER server; @@ -43,8 +43,8 @@ void http_handle_brightness() { } void http_api_display_setup() { - server.on ( "/brightness", HTTP_GET, http_handle_brightness ); - server.on ( "/display", HTTP_GET, http_handle_power ); + server.on ( "/api/v1/brightness", HTTP_GET, http_handle_brightness ); + server.on ( "/api/v1/display", HTTP_GET, http_handle_power ); apilog("API Display initialized."); } \ No newline at end of file diff --git a/src/network/api/http_api_base_json.cpp b/src/network/api/v1/getdata.cpp similarity index 55% rename from src/network/api/http_api_base_json.cpp rename to src/network/api/v1/getdata.cpp index da54b8f..478a27a 100644 --- a/src/network/api/http_api_base_json.cpp +++ b/src/network/api/v1/getdata.cpp @@ -1,31 +1,21 @@ #include #include +#include "config.h" + #include "log.h" #include "global.h" +#include "display/display.h" +#include "network/webserver.h" +#include "network/api/api_util.h" -String http_normal_not_found_html() { - String message = "File Not Found\n\n"; - return message; -} - -String get_time_string(unsigned long ms) { - unsigned long second = ms / 1000; - unsigned long minute = second / 60; - - int sec = second % 60; - int min = (second / 60) % 60; - int hr = minute / 60; - char buf[15]; - snprintf (buf, sizeof buf,"%02d:%02d:%02d", hr, min, sec); - return String(buf); -} +extern HTTPWEBSERVER server; String http_normal_data_json() { unsigned long ms = millis(); - String timeString = get_time_string(ms); + String timeString = getTimeString(ms); char temp[10], hum[10], pres[10]; char lux[10], luxIr[10],ppm[10]; @@ -49,19 +39,13 @@ String http_normal_data_json() { return json; } -String http_normal_ping_json() { - - unsigned long ms = millis(); - String timeString = get_time_string(ms); - - DynamicJsonDocument doc(2000); - doc["product"] = product; - doc["uptime"] = timeString; - doc["uptimeMills"] = ms; +void http_handle_data() { + String message = http_normal_data_json(); + server.send(200, MimeType::JSON, message); +} - String json; - serializeJson(doc, json); +void http_api_getdata_setup() { + server.on ( "/api/v1/getdata", HTTP_GET, http_handle_data ); - httplog(json); - return json; -} + apilog("API getdata initialized."); +} \ No newline at end of file diff --git a/src/network/api/http_api_mhz.cpp b/src/network/api/v1/mhz19b.cpp similarity index 86% rename from src/network/api/http_api_mhz.cpp rename to src/network/api/v1/mhz19b.cpp index 817f60b..1a37261 100644 --- a/src/network/api/http_api_mhz.cpp +++ b/src/network/api/v1/mhz19b.cpp @@ -3,8 +3,8 @@ #include "log.h" #include "global.h" -#include "network/http_api.h" -#include "network/http_api_util.h" +#include "network/webserver.h" +#include "network/api/api_util.h" #include "sensors/mhz19_uart.h" extern HTTPWEBSERVER server; @@ -72,9 +72,9 @@ void _mhz_zero_calibration() { } void http_api_mhz_setup() { - server.on ( "/mhz19b/abc", HTTP_GET, _get_mhz_abc ); - server.on ( "/mhz19b/abc", HTTP_POST, _set_mhz_abc ); - server.on ( "/mhz19b/zeroCalibration", HTTP_POST, _mhz_zero_calibration ); + server.on ( "/api/v1/mhz19b/abc", HTTP_GET, _get_mhz_abc ); + server.on ( "/api/v1/mhz19b/abc", HTTP_POST, _set_mhz_abc ); + server.on ( "/api/v1/mhz19b/zeroCalibration", HTTP_POST, _mhz_zero_calibration ); apilog("API MHZ-19B initialized."); } \ No newline at end of file diff --git a/src/network/http_normal_web.cpp b/src/network/http_normal_web.cpp index 5af786f..64f5748 100644 --- a/src/network/http_normal_web.cpp +++ b/src/network/http_normal_web.cpp @@ -7,25 +7,36 @@ #include "http_normal.h" -#include "network/http_api.h" -#include "network/http_api_base.h" -#include "network/http_api_display.h" -#include "network/http_api_mhz.h" -#include "network/http_api_config.h" - -#include "network/http_web_config.h" - +#include "network/webserver.h" +#include "network/http_not_found.h" +#include "network/stastics_api.h" +#include "network/goto_setup_api.h" +#include "network/api/basic_api.h" +#include "network/api/v1/display.h" +#include "network/api/v1/mhz19b.h" +#include "network/api/v1/config.h" +#include "network/api/v1/getdata.h" + +#include "network/web/http_web_config.h" +#include "network/web/http_web_updater.h" extern HTTPWEBSERVER server; void http_setup_normal() { httplog("HTTP web server initializing"); + http_not_found_setup(); + http_api_gotosetup_setup(); + http_api_stastics_setup(); + + http_api_basic_setup(); http_api_display_setup(); - http_api_base_setup(); + http_api_getdata_setup(); http_api_mhz_setup(); http_api_config_setup(); + http_web_config_setup(); + http_web_updater_setup(); server.begin(); httplog("HTTP web server initialized"); diff --git a/src/network/http_not_found.cpp b/src/network/http_not_found.cpp new file mode 100644 index 0000000..9f37064 --- /dev/null +++ b/src/network/http_not_found.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include "config.h" + +#include "global.h" + +#include "network/http_utils.h" +#include "network/webserver.h" + +extern HTTPWEBSERVER server; + +void http_handle_not_found() { + // HEADER + server.sendContent("HTTP/1.1 404 FILE NOT FOUND\r\n"); + server.sendContent("Content-Type: text/plain\r\n"); + if (DEBUG_BUILD) { + server.sendContent("Access-Control-Allow-Origin: *\r\n"); + } + server.sendContent("\r\n"); + + // CONTENT + String message = "404 File Not Found\n\n"; + server.sendContent(message); +} + +void http_not_found_setup() { + server.onNotFound ( http_handle_not_found ); +} \ No newline at end of file diff --git a/src/network/http_setup_web.cpp b/src/network/http_setup_web.cpp index 4efce1c..c81c493 100644 --- a/src/network/http_setup_web.cpp +++ b/src/network/http_setup_web.cpp @@ -1,109 +1,36 @@ + #include #include "log.h" #include "global.h" #include "config.h" -#include "http_setup.h" - -#include "halt.h" -#include "utils.h" - -#include "embed/style_css.h" +#include "http_normal.h" #include "network/webserver.h" -#include "network/http_utils.h" - -void http_setup_post_root_error_content(std::vector> errors) { - - sendHttpHeader(); - sendHtmlHeader(); - - String html = ""; - html += ""; - html += "

" + product + " Settings (" + SETTING_ID + ")

"; - html += "

以下の設定値が正しくありません。

"; - html += "
    "; - for (auto& error: errors) { - html += "
  • " + error.first + " = '" + error.second + "'
  • "; - } - html += "
"; - html += "設定にエラーがあるため、保存できませんでした。
"; - html += "以下のリンクから再設定を行ってください。
"; - html += "
"; - html += "再設定"; - html += ""; - html += ""; - - server.sendContent(html); -} +#include "network/http_not_found.h" +#include "network/api/basic_api.h" +#include "network/api/v1/display.h" +#include "network/api/v1/mhz19b.h" +#include "network/api/v1/config.h" +#include "network/api/v1/getdata.h" -/** - * GET 設定画面 - */ -void handle_get_root() { +#include "network/web/http_web_config.h" - // エラー画面からの戻りであればConfigを読まない(すでにセットされているから上書きしてしまう) - if (!server.hasArg("configNoLoad")) { - mainlog("No configNoLoad parameter. loading config."); - read_config(); - } else { - mainlog("configNoLoad parameter specified. Skip loading config."); - } - http_send_setup_get_root_html(); +extern HTTPWEBSERVER server; - // server.send(200, MimeType::HTML, html); -} - -/** - * Post 設定 ( config の post。 ファイルに設定を保存) - */ -void handle_post_root() { +void setup_http_setup() { + httplog("HTTP web server initializing"); - std::vector SKIPABLE_KEY {ConfigNames::SETTING_ID}; - std::vector> errors; - - for (auto &key : config->getKeys()) { - bool argExist = server.hasArg(key); - // debuglog("server arg key=" + key + " ret=" + (argExist ? "EXIST" : "NONE")); - String value = server.arg(key); - if (argExist) { - ConfigSetResult result = config->set(key, value); - if (result == ConfigSetResult::INVALID_VALUE) { - std::pair pair(key, value); - errors.push_back(pair); - } - } else { - if (vectorStringContains(SKIPABLE_KEY, key)) { - httplog("[OK] Skippable key. " + key); - } else { - halt("WebPOST ERR", "NO KEY", key); - } - } - } + http_not_found_setup(); + http_api_basic_setup(); + http_api_config_setup(); - String html; - if (errors.size() == 0) { - httplog("[OK] Config save start"); - save_config(); - httplog("[OK] Sending done HTML"); - http_send_setup_post_root_html(); - } else { - httplog("Send config error page"); - http_setup_post_root_error_content(errors); - } -} - -/** - * 初期化(設定用Webサーバモード) - */ -void setup_http_setup() { - httplog(F("HTTP web server initializing")); - server.on("/", HTTP_GET, handle_get_root); - server.on("/", HTTP_POST, handle_post_root); - server.begin(); - httplog(F("HTTP web server initialized")); + http_web_config_setup(); + + server.begin(); + httplog("HTTP web server initialized"); } void loop_http_setup() { diff --git a/src/network/http_setup_web_get.cpp b/src/network/http_setup_web_get.cpp deleted file mode 100644 index f512455..0000000 --- a/src/network/http_setup_web_get.cpp +++ /dev/null @@ -1,307 +0,0 @@ -#include - -#ifdef ESP32 -#include -#elif defined(ESP8266) -#include -#endif - -#include "log.h" -#include "display/display.h" -#include "global.h" -#include "config.h" - -#include "http_setup.h" - -#include "network/webserver.h" -#include "network/http_utils.h" - -typedef struct { - String label; - String value; -} LabelValue; - -String _create_input_impl(String name, String value, String placeholder = "", String type = "text", String cssClass = "") { - String html = ""; - return html; -} - -String _create_input_nobr(String key, String placeholder = "", String type = "text", String cssClass = "") { - String value = config->get(key); - String html = _create_input_impl(key, value, placeholder, type, cssClass); - - return html + "\n"; -} - -String _create_input(String key, String placeholder = "", String type = "text", String cssClass = "") { - String value = config->get(key); - String html = _create_input_impl(key, value, placeholder, type, cssClass); - html += "
\n"; - - return html; -} - -String _generate_alert(String nameLow, String nameHigh) { - String html = ""; - html += _create_input_nobr(nameLow, "", "number", "num"); - html += " 以上\n"; - html += _create_input_nobr(nameHigh, "", "number", "num"); - html += "未満
\n"; - return html; -} - -String _create_alert_name(String prefix, String name) { - return prefix + "." + name; -} - -String generate_http_setup_alerts_html(String name, String prefix) { - String html; - html += "
" + name + "\n"; - { - html += "注意1: \n"; - String lo = _create_alert_name(prefix, ConfigNames::ALERT_CAUTION1_LO); - String hi = _create_alert_name(prefix, ConfigNames::ALERT_CAUTION1_HI); - html += _generate_alert(lo, hi); - } - - { - html += "注意2: \n"; - String lo = _create_alert_name(prefix, ConfigNames::ALERT_CAUTION2_LO); - String hi = _create_alert_name(prefix, ConfigNames::ALERT_CAUTION2_HI); - html += _generate_alert(lo, hi); - } - - { - html += "警報1: \n"; - String lo = _create_alert_name(prefix, ConfigNames::ALERT_WARN1_LO); - String hi = _create_alert_name(prefix, ConfigNames::ALERT_WARN1_HI); - html += _generate_alert(lo, hi); - } - - { - html += "警報2: \n"; - String lo = _create_alert_name(prefix, ConfigNames::ALERT_WARN2_LO); - String hi = _create_alert_name(prefix, ConfigNames::ALERT_WARN2_HI); - html += _generate_alert(lo, hi); - } - - html += "
\n"; - return html; -} - -/** - * @param name input tag name attribute - * @param value current value - */ -String _create_radiobuttons(String name, std::vector choises) { - String html = ""; - - String value = config->get(name); - - for (unsigned int i = 0; i < choises.size(); i++) - { - auto c = choises[i]; - String id = name + "__" + c.value; - String checked = (value == c.value ? " checked" : ""); - - html += " "; - html += "
\n"; - } - - return html; -} - -void http_send_setup_get_root_html() { - - sendHttpHeader(); - sendHtmlHeader(); - - String html = ""; - - html += "\n"; - html += "

" + product + " Settings (" + SETTING_ID + ")

"; - html += "
\n"; - html += "
"; - - server.sendContent(html); - html = ""; - - html += "
ネットワーク設定\n"; - html += " WiFi接続情報
\n"; - html += " ※ 2.4GHz帯のみ対応しています。
\n"; - html += _create_input(ConfigNames::SSID, "WiFi SSID"); - html += _create_input(ConfigNames::PASSWORD, "WiFi Password"); - html += "
"; - html += " mDNS名(a.k.a Rendezvous, avahi-daemon)
\n"; - html += " ※ mDNS名.local で他端末から本機にアクセスすることができます。
\n"; - #ifdef ESP8266 - html += " ※ ESP8266では動作が安定しません(名前解決に失敗する場合がある)
\n"; - #endif - html += _create_input(ConfigNames::MDNS); - html += "
\n"; - - server.sendContent(html); - html = ""; - - html += "
動作モード設定\n"; - html += " 動作モード
\n"; - #ifdef ESP8266 - html += " ※ ESP8266でMQTTモードを使用するにはIO16ピンとRSTピンを接続する必要があります。
\n"; - #endif - { - std::vector choises; - choises.push_back(LabelValue{"常時起動モード(測定値常時表示, HTTPサーバーあり)", ConfigValues::OPMODE_DISPLAY}); - choises.push_back(LabelValue{"MQTTモード(間欠動作・MQTT送信後ディープスリープ)", ConfigValues::OPMODE_MQTT}); - html += _create_radiobuttons(ConfigNames::OPMODE, choises); - } - html += "
\n"; - - html += "
表示デバイス共通設定\n"; - html += " 画面反転
\n"; - { - std::vector choises; - choises.push_back(LabelValue{"反転しない", ConfigValues::DISPLAY_FLIP_OFF}); - choises.push_back(LabelValue{"反転する", ConfigValues::DISPLAY_FLIP_ON}); - html += _create_radiobuttons(ConfigNames::DISPLAY_FLIP, choises); - } - - html += " 明るさ(0-255)
\n"; - html += _create_input(ConfigNames::DISPLAY_BRIGHTNESS, "brightness 0-255", "number"); - - html += " Wait for reconfigure画面
\n"; - html += " ※ 表示しない場合、セットアップモードに入るためにはWeb APIを使用する必要があります。
\n"; - { - std::vector choises; - choises.push_back(LabelValue{"表示する(推奨)", ConfigValues::DISPLAY_RECONFIG_ON}); - choises.push_back(LabelValue{"表示しない", ConfigValues::DISPLAY_RECONFIG_SKIP}); - html += _create_radiobuttons(ConfigNames::DISPLAY_RECONFIG, choises); - } - - html += " 自動消灯する明るさ(暗いと判定するしきい値)
\n"; - html += " ※ 光量センサーがない場合は自動的に無効になります。
\n"; - html += " ※ 自動消灯を使用しない場合は、999999を入力してください。
\n"; - html += _create_input(ConfigNames::DISPLAY_AUTODIM_LUX, "Lux", "number"); - - html += " 自動消灯判定時間
\n"; - html += " ※ しきい値未満の明るさがこの時間継続したら消灯します。
\n"; - html += _create_input(ConfigNames::DISPLAY_AUTODIM_WAIT_SEC, "second to display sleep", "number"); - - html += "
\n"; - - html += "
I2C OLED デバイス設定\n"; - html += " ※ 接続されていない場合無視されます。画面端にゴミが表示されている、または数ドット欠けている場合は変更してください。
\n"; - { - std::vector choises; - choises.push_back(LabelValue{"SSD1306", ConfigValues::OLED_SSD1306}); - choises.push_back(LabelValue{"SH1106", ConfigValues::OLED_SH1106}); - html += _create_radiobuttons(ConfigNames::OLED_TYPE, choises); - } - - html += "
\n"; - - html += "
ST7789 デバイス設定\n"; - - html += " ST7789 SPI液晶の有無
\n"; - html += " ※ MQTTモードでは無効。
"; - html += " ※ SPIピンはここでは指定できません。
\n"; - { - std::vector choises; - choises.push_back(LabelValue{"使用しない", ConfigValues::ST7789_NOUSE}); - choises.push_back(LabelValue{"使用する", ConfigValues::ST7789_USE}); - html += _create_radiobuttons(ConfigNames::ST7789, choises); - } - - html += " ST7789 表示モード
\n"; - { - std::vector choises; - choises.push_back(LabelValue{"横表示モード", ConfigValues::ST7789_MODE_NORMAL}); - choises.push_back(LabelValue{"縦表示モード(デカ文字)", ConfigValues::ST7789_MODE_BIG}); - html += _create_radiobuttons(ConfigNames::ST7789_MODE, choises); - } - html += "
\n"; - - server.sendContent(html); - html = ""; - - html += "
MH-Z19B デバイス設定\n"; - html += " MH-Z19B CO2センサー有無(金色のセンサー)
\n"; - { - std::vector choises; - choises.push_back(LabelValue{"使用しない", ConfigValues::MHZ_NOUSE}); - choises.push_back(LabelValue{"使用する(UARTモード)", ConfigValues::MHZ_USE_UART}); - // choises.push_back(LabelValue{"使用する(PWMモード)", ConfigValues::MHZ_USE_PWM}); - - html += _create_radiobuttons(ConfigNames::MHZ19B, choises); - } - - //html += "
"; - // html += " MH-Z19BのPWMピンが接続されているGPIOピン番号
"; - // html += "
"; - html += _create_input(ConfigNames::MHZ19B_PWM, "14", "", "hidden"); - - html += " MH-Z19BのUARTが接続されているGPIOピン番号
\n"; - html += " ※ MH-Z19Bを使用しない場合は設定不要
"; - html += " ※ ESP8266では RX 14 TX 0 で固定
"; - html += " ※ ボードによって使用可能なピンが異なるので動作しない場合は他のピンを試してください。
\n"; - html += " RXピン \n"; - html += _create_input(ConfigNames::MHZ19B_RX, "16", "number"); - html += " TXピン \n"; - html += _create_input(ConfigNames::MHZ19B_TX, "17", "number"); - html += "
"; - - html += " 起動時のAuto Baseline Correction
\n"; - html += " ※ MH-Z19Bを使用しない場合・MH-Z19BがUARTモード以外の場合は無視されます。
"; - { - std::vector choises; - choises.push_back(LabelValue{"無効(標準)", ConfigValues::MHZ_ABC_OFF}); - choises.push_back(LabelValue{"有効", ConfigValues::MHZ_ABC_ON}); - html += _create_radiobuttons(ConfigNames::MHZ19B_ABC, choises); - } - - html += "
\n"; - - html += "
MQTTモード専用設定\n"; - html += " MQTTブローカーのIPアドレス
\n"; - html += " ※ ホスト名も可能ですが、mDNS(bonjour, avahi)は使用出来ません。
\n"; - html += _create_input(ConfigNames::MQTT_BROKER, "MQTT Broker"); - html += "
"; - html += " MQTT名
\n"; - html += " ※ クライアント名とトピック名として使用
"; - html += _create_input(ConfigNames::MQTT_NAME, "MQTT Client Name"); - html += "
\n"; - - html += "
アラート設定\n"; - html += generate_http_setup_alerts_html("気温", ConfigNames::TEMP_ALERT); - html += generate_http_setup_alerts_html("湿度", ConfigNames::HUMI_ALERT); - html += generate_http_setup_alerts_html("照度", ConfigNames::LUX_ALERT); - html += generate_http_setup_alerts_html("気圧", ConfigNames::PRES_ALERT); - html += generate_http_setup_alerts_html("CO2濃度", ConfigNames::CO2_ALERT); - - html += "
\n"; - - server.sendContent(html); - html = ""; - - html += "
"; - html += "
\n"; - html += "
\n"; - html += "
"; - html += "
"; - html += product_long + ", Copyright 2018-2021 Yakumo Saki / ziomatrix.org.\n"; - - server.sendContent(html); - html = ""; - - html += "\n"; - html += "\n"; - - server.sendContent(html); - html = ""; -} diff --git a/src/network/http_setup_web_post.cpp b/src/network/http_setup_web_post.cpp deleted file mode 100644 index 9d6077a..0000000 --- a/src/network/http_setup_web_post.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include - -#ifdef ESP32 -#include -#elif defined(ESP8266) -#include -#endif - -#include "log.h" -#include "display/display.h" -#include "global.h" -#include "config.h" - -#include "http_setup.h" -#include "network/webserver.h" -#include "network/http_utils.h" - -/** - * Post 設定 ( config の post。 ファイルに設定を保存) - */ -void http_send_setup_post_root_html() { - - sendHttpHeader(); - sendHtmlHeader(); - - String html = ""; - html += ""; - html += "

" + product + " setting done

"; - if (config->get(ConfigNames::OPMODE) == "always") { - html += "動作モード:常時稼働モード
"; - html += "SSID " + config->get(ConfigNames::SSID) + "
"; - html += "PASS " + config->get(ConfigNames::PASSWORD) + "
"; - html += "mDNS " + config->get(ConfigNames::MDNS) + "
"; - - String oledType = config->get(ConfigNames::OLED_TYPE); - if (oledType == ConfigValues::OLED_SSD1306) { - html += "I2C OLED デバイス: SSD1306
"; - } else if (oledType == ConfigValues::OLED_SH1106) { - html += "I2C OLED デバイス: SH1106
"; - } else if (oledType == ConfigValues::OLED_SH1106) { - html += "【バグ】I2C OLED デバイスの指定が異常です。 " + oledType + "
"; - } - - String st7789 = config->get(ConfigNames::ST7789); - String st7789Mode = config->get(ConfigNames::ST7789_MODE); - if (config->get(ConfigNames::ST7789) == ConfigValues::ST7789_USE) { - html += "ST7789 を使用する
"; - - if (st7789Mode == ConfigValues::ST7789_MODE_BIG) { - html += "ST7789表示モード:縦(デカ文字)
"; - } else if (st7789Mode == ConfigValues::ST7789_MODE_NORMAL) { - html += "ST7789表示モード:横(標準)
"; - } else { - html += "【バグ】ST7789表示モード設定が異常です => " + st7789Mode + "
"; - } - } else if (st7789 == ConfigValues::ST7789_NOUSE) { - html += "ST7789 を使用しない
"; - } else { - html += "【バグ】ST7789設定が異常です => " + st7789 + "
"; - } - - String mhz19b = config->get(ConfigNames::MHZ19B); - String mhz19bABC = config->get(ConfigNames::MHZ19B_ABC); - String mhz19bPwmPin = config->get(ConfigNames::MHZ19B_PWM); - String mhz19bRxPin = config->get(ConfigNames::MHZ19B_RX); - String mhz19bTxPin = config->get(ConfigNames::MHZ19B_TX); - - if (mhz19b == ConfigValues::MHZ_USE_UART) { - html += "MHZ19B を使用する(UART)
"; - html += " RXピン番号=" + String(mhz19bRxPin) + "
"; - html += " TXピン番号=" + String(mhz19bTxPin) + "
"; - if (mhz19bABC == ConfigValues::MHZ_ABC_ON) { - html += " Auto Baseline Correction=有効
"; - } else if (mhz19bABC == ConfigValues::MHZ_ABC_OFF) { - html += " Auto Baseline Correction=オフ
"; - } else { - html += "【バグ】MHZ19B Auto Baseline Correction設定が異常です。 =>" + mhz19bABC + "
"; - } - } else if (mhz19b == ConfigValues::MHZ_USE_PWM) { - html += "MHZ19B を使用する(PWM) ※ 廃止されたモードです"; - html += " GPIOピン番号=" + String(mhz19bPwmPin) + "
"; - } else if (mhz19b == ConfigValues::MHZ_NOUSE) { - html += "MHZ19B を使用しない、または接続されていない
"; - } else { - html += "【バグ】MHZ19B設定が異常です。 =>" + mhz19b + "
"; - } - } else if (config->get(ConfigNames::OPMODE) == ConfigValues::OPMODE_MQTT) { - html += "動作モード:MQTT間欠動作モード
"; - html += "SSID " + config->get(ConfigNames::SSID) + "
"; - html += "PASS " + config->get(ConfigNames::PASSWORD) + "
"; - html += "mDNS " + config->get(ConfigNames::MDNS) + "
"; - html += "MQTT broker " + config->get(ConfigNames::MQTT_BROKER) + "
"; - html += "MQTT name " + config->get(ConfigNames::MQTT_NAME) + "
"; - } else { - html += "【バグ】動作モード:不明" + config->get(ConfigNames::OPMODE) + "
"; - } - - html += "
"; - html += "Restart (power off then power on) to use above setting.
"; - html += "設定が完了しました。リセットボタンを押して再起動して下さい。
"; - html += "
"; - html += "setting again"; - html += ""; - html += ""; - - server.sendContent(html); -} diff --git a/src/network/http_utils.cpp b/src/network/http_utils.cpp index 87b0a02..5f0f2bd 100644 --- a/src/network/http_utils.cpp +++ b/src/network/http_utils.cpp @@ -1,12 +1,28 @@ #include "global.h" +#include "global.h" #include "network/webserver.h" #include "embed/style_css.h" +void http_handle_cors() { + server.sendContent("HTTP/1.1 204 No Content\r\n"); + server.sendContent("Access-Control-Allow-Methods: POST, GET, OPTIONS\r\n"); + server.sendContent("Access-Control-Allow-Headers: content-type\r\n"); + server.sendContent("Access-Control-Max-Age: 86400\r\n"); + + if (DEBUG_BUILD) { + server.sendContent("Access-Control-Allow-Origin: *\r\n"); + } + + server.sendContent("Connection: close\r\n"); +} + void sendHttpHeader(String contentType) { server.sendContent("HTTP/1.1 200 OK\r\n"); server.sendContent("Content-Type: " + contentType + "\r\n"); - server.sendContent("Connection: close\r\n"); + if (DEBUG_BUILD) { + server.sendContent("Access-Control-Allow-Origin: *\r\n"); + } server.sendContent("\r\n"); } diff --git a/src/network/mdns.cpp b/src/network/mdns.cpp index ee360ae..66fa2ed 100644 --- a/src/network/mdns.cpp +++ b/src/network/mdns.cpp @@ -32,7 +32,12 @@ bool start_mdns(String name) { void mdns_hostname_change(String hostname) { MDNS.end(); - MDNS.begin(hostname.c_str()); + bool success = MDNS.begin(hostname.c_str()); + if (success) { + mdnslog("MDNS begin success. hostname is " + hostname); + } else { + mdnslog("MDNS begin failed. hostname is " + hostname); + } } void mdns_setup() { diff --git a/src/network/web/http_web_config.cpp b/src/network/web/http_web_config.cpp index 17efed5..a862c90 100644 --- a/src/network/web/http_web_config.cpp +++ b/src/network/web/http_web_config.cpp @@ -6,7 +6,6 @@ #include "network/webserver.h" -#include "network/http_api.h" #include "network/http_utils.h" #include "embed/config_html.h" @@ -42,6 +41,7 @@ void handle_get_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); + server.on("/", HTTP_GET, handle_get_webconfig); + httplog(F("HTTP web config initialized")); } diff --git a/src/network/web/http_web_updater.cpp b/src/network/web/http_web_updater.cpp new file mode 100644 index 0000000..4da72b1 --- /dev/null +++ b/src/network/web/http_web_updater.cpp @@ -0,0 +1,88 @@ +#include + +#ifdef ESP32 +#include +#endif + +#include "config.h" +#include "global.h" +#include "log.h" +#include "network/http_utils.h" +#include "network/webserver.h" + +#include "watchdog.h" + +// very thanks to +// https://garretlab.web.fc2.com/arduino/esp32/examples/ArduinoOTA/OTAWebUpdater.html + +#ifdef ESP32 + +#include "embed/update_html.h" +#include "embed/update_done_html.h" +#include "embed/update_fail_html.h" + +void handle_post_update() { + server.sendHeader("Connection", "close"); + + if (Update.hasError()) { + server.send(200, "text/html", UPDATE_FAIL_HTML); + } else { + server.send(200, "text/html", UPDATE_DONE_HTML); + } + + delay(3000); + ESP.restart(); +} + +// このメソッドはアップロード中にすごい回数呼ばれる +void handle_post_update_sending() { + watchdog_feed(); + + HTTPUpload& upload = server.upload(); + + if (upload.status == UPLOAD_FILE_START) { + httplog("Update filename:" + upload.filename); + if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { // start with max available size + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + /* flashing firmware to ESP*/ + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_END) { + if (Update.end(true)) { // true to set the size to the current progress + httplog("Update Success size: " + String(upload.totalSize) + " bytes"); + } else { + Update.printError(Serial); + } + } +} + +void handle_get_update() { + sendHttpHeader(); + server.sendContent(String(UPDATE_HTML)); +} + +void http_web_updater_setup() { + + server.on("/update", HTTP_GET, handle_get_update); + server.on("/update", HTTP_POST, handle_post_update, handle_post_update_sending); + + httplog(F("HTTP web updater initialized")); +} +#endif + +#ifndef ESP32 +#include "embed/update_unsupport_html.h" + +void handle_get_update() { + sendHttpHeader(); + server.sendContent(String(UPDATE_UNSUPPORT_HTML)); +} + +void http_web_updater_setup() { + server.on("/update", HTTP_GET, handle_get_update); + httplog(F("HTTP web updater is not supported.")); +} +#endif diff --git a/src/network/http.cpp b/src/network/webserver.cpp similarity index 100% rename from src/network/http.cpp rename to src/network/webserver.cpp diff --git a/src/sensors/delta.cpp b/src/sensors/delta.cpp index 71ca299..f5448ea 100644 --- a/src/sensors/delta.cpp +++ b/src/sensors/delta.cpp @@ -26,7 +26,7 @@ void store_delta() { // 起動直後のhistoryが空の状態のときに、最新の測定値を // historyに追加して、差分の計算を単純にする if (history.empty()) { - deltalog(F("No history. Create first history")); + deltalog(F("First history created.")); history.reserve(MAX_HISTORY); store_history(); } else {