diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea78..1cbff930 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,6 +10,11 @@ assignees: '' **Describe the bug** A clear and concise description of what the bug is. +**Device information** +- Screenshot of Zigbee and Device tab from root page of device +- JSON object returned on [http://xzg.local/api?action=1¶m=all](http://xzg.local/api?action=1¶m=all) + *You **should** remove your credentials first* + **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' @@ -23,16 +28,5 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - **Additional context** Add any other context about the problem here. diff --git a/.github/scripts/update_devices.py b/.github/scripts/update_devices.py new file mode 100644 index 00000000..513c2857 --- /dev/null +++ b/.github/scripts/update_devices.py @@ -0,0 +1,129 @@ +import re + +def read_hw_file(hw_file_path): + with open(hw_file_path, 'r') as file: + return file.read() + +def parse_brd_configs(hw_content): + pattern = re.compile(r'BrdConfigStruct brdConfigs\[\] = \{(.*?)\};', re.DOTALL) + match = pattern.search(hw_content) + if match: + print("Found brdConfigs") + return match.group(1) + print("brdConfigs not found") + return "" + +def extract_devices(brd_configs, mist_configs): + devices = [] + device_pattern = re.compile(r'\{\s*"([^"]+)",\s*\.ethConfigIndex = (-?\d+),\s*\.zbConfigIndex = -?\d+,\s*\.mistConfigIndex = (-?\d+)\s*\}', re.DOTALL) + for device_match in device_pattern.finditer(brd_configs): + device_name = device_match.group(1) + eth_config_index = int(device_match.group(2)) + mist_config_index = int(device_match.group(3)) + + eth_is = eth_config_index > -1 + btn_is = mist_configs[mist_config_index]['btnPin'] > -1 + led_is = mist_configs[mist_config_index]['ledModePin'] > -1 or mist_configs[mist_config_index]['ledPwrPin'] > -1 + + device = { + 'name': device_name, + 'eth': ':white_check_mark:' if eth_is else ':x:', + 'button': ':white_check_mark:' if btn_is else ':x:', + 'led': ':white_check_mark:' if led_is else ':x:', + 'network_usb': ':white_check_mark:' + } + devices.append(device) + print(f"Extracted device: {device}") + return devices + +def parse_mist_configs(hw_content): + pattern = re.compile(r'MistConfig mistConfigs\[\] = \{(.*?)\};', re.DOTALL) + match = pattern.search(hw_content) + if match: + print("Found mistConfigs") + mist_configs = match.group(1) + return extract_mist_configs(mist_configs) + print("mistConfigs not found") + return [] + +def extract_mist_configs(mist_configs): + configs = [] + config_pattern = re.compile(r'\{\s*\.btnPin = (-?\d+),\s*\.btnPlr = \d+,\s*\.uartSelPin = -?\d+,\s*\.uartSelPlr = \d+,\s*\.ledModePin = (-?\d+),\s*\.ledModePlr = \d+,\s*\.ledPwrPin = (-?\d+),\s*\.ledPwrPlr = \d+\s*\}', re.DOTALL) + for config_match in config_pattern.finditer(mist_configs): + config = { + 'btnPin': int(config_match.group(1)), + 'ledModePin': int(config_match.group(2)), + 'ledPwrPin': int(config_match.group(3)), + } + configs.append(config) + print(f"Extracted mistConfig: {config}") + return configs + +def read_features_file(features_file_path): + with open(features_file_path, 'r') as file: + return file.read() + +def extract_existing_links(features_content): + device_links = {} + table_pattern = re.compile(r'\| \[(.*?)\]\((.*?)\)', re.DOTALL) + for match in table_pattern.finditer(features_content): + device_name = match.group(1) + link = match.group(2) + device_links[device_name] = link + print(f"Found link: {device_name} -> {link}") + return device_links + +def update_features_content(features_content, devices, device_links): + # Define the regular expression to match the whole Supported devices section + table_pattern = re.compile(r'(.*## 🎮 Supported devices)(\s+\| .+\|.+?)(\* Some devices do not support all features.*)', re.DOTALL) + match = table_pattern.search(features_content) + if match: + + header = match.group(1) + footer = match.group(3) + + updated_devices_table = "| Device Name | Button | ESP32 LEDs | Remote Network / USB mode selection | Ethernet |\n" + updated_devices_table += "| :---------------------------------------------------------- | :----------------: | :----------------: | :---------------------------------: | :----------------: |\n" + + for device in devices: + device_name = device['name'] + link = device_links.get(device_name, "") + if link: + device_name = f"[{device_name}]({link})" + device_row = f"| {device_name} | {device['button']} | {device['led']} | {device['network_usb']} | {device['eth']} |\n" + updated_devices_table += device_row + + updated_features_content = header + "\n\n" + updated_devices_table + "\n" + footer + print("Updated features content") + return updated_features_content + print("Supported devices section not found") + return features_content + +def write_features_file(features_file_path, updated_content): + with open(features_file_path, 'w') as file: + file.write(updated_content) + print(f"Updated features file: {features_file_path}") + +def main(): + hw_file_path = 'main_branch/src//const/hw.cpp' + features_file_path = 'mkdocs_branch/docs/features.md' + + hw_content = read_hw_file(hw_file_path) + print(f"Read hw.cpp content: {len(hw_content)} characters") + + brd_configs = parse_brd_configs(hw_content) + mist_configs = parse_mist_configs(hw_content) + + devices = extract_devices(brd_configs, mist_configs) + + features_content = read_features_file(features_file_path) + print(f"Read features.md content: {len(features_content)} characters") + + device_links = extract_existing_links(features_content) + + updated_content = update_features_content(features_content, devices, device_links) + + write_features_file(features_file_path, updated_content) + +if __name__ == "__main__": + main() diff --git a/.github/workflows/build_fw.yml b/.github/workflows/build_fw.yml index 41e1e54e..681bfeea 100644 --- a/.github/workflows/build_fw.yml +++ b/.github/workflows/build_fw.yml @@ -17,6 +17,7 @@ jobs: uses: actions/checkout@v3 with: submodules: "recursive" + fetch-depth: 0 - name: Get Release tag id: get_tag @@ -53,7 +54,7 @@ jobs: python-version: "3.9" - name: Install PlatformIO Core - run: pip install --upgrade platformio==6.1.11 + run: pip install --upgrade platformio==6.1.15 - name: Build PlatformIO Project run: pio run @@ -97,6 +98,29 @@ jobs: EOF echo "Manifest file created." + - name: Get the latest commit SHA for the whole project + id: get_fw_commit_sha + run: echo "::set-output name=fw_commit_sha::$(git log -n 1 --pretty=format:%h)" + + - name: Get the latest commit SHA in src/websrc + id: get_fs_commit_sha + run: echo "::set-output name=fs_commit_sha::$(git log -n 1 --pretty=format:%h -- src/websrc)" + + - name: Create xzg.json for firmware update + run: | + cat << EOF > xzg.json + { + "name": "XZG Firmware", + "version": "${{ steps.get_tag.outputs.tag }}", + "fw_sha": "${{ steps.get_fw_commit_sha.outputs.fw_commit_sha }}", + "fs_sha": "${{ steps.get_fs_commit_sha.outputs.fs_commit_sha }}" + } + EOF + echo "xzg.json file created." + + - name: Display xzg.json + run: cat xzg.json + - name: Release uses: softprops/action-gh-release@v1 with: @@ -106,6 +130,7 @@ jobs: files: | bin/XZG_${{ steps.get_tag.outputs.tag }}.ota.bin bin/XZG_${{ steps.get_tag.outputs.tag }}.full.bin + bin/XZG_${{ steps.get_tag.outputs.tag }}.fs.bin - name: Checkout releases branch uses: actions/checkout@v3 @@ -120,14 +145,32 @@ jobs: cp manifest.json releases/${{ steps.get_tag.outputs.tag }}/ echo "Files copied to releases directory." + - name: Prepare latest release directory + run: | + if [ -d releases/latest ]; then + rm -rf releases/latest + fi + mkdir -p releases/latest + + - name: Copy file to latest release directory + run: | + cp xzg.json releases/latest/ + # cp ./bin/XZG_${{ steps.get_tag.outputs.tag }}.ota.bin releases/latest/XZG.ota.bin + # cp ./bin/XZG_${{ steps.get_tag.outputs.tag }}.fs.bin releases/latest/XZG.fs.bin + echo "File copied to latest release directory." + - name: Commit and push files to releases branch run: | cd releases git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git add . - git commit -m "Add firmware and manifest for version ${{ steps.get_tag.outputs.tag }}" - git push origin releases + if [[ -n $(git status --porcelain) ]]; then + git add . + git commit -m "${{ steps.get_tag.outputs.tag }}" + git push origin releases + else + echo "No changes to commit" + fi - name: Send Telegram Notification about release uses: appleboy/telegram-action@master @@ -142,7 +185,7 @@ jobs: - name: Send Discord Notification about release run: | + json_payload=$(jq -n --arg content "https://github.com/${{ github.repository }}/releases/tag/${{ steps.get_tag.outputs.tag }}" '{content: $content}') curl -H "Content-Type: application/json" \ - -d "{\"content\": \"${{ env.commitMessage }}\n\nhttps://github.com/${{ github.repository }}/releases/tag/${{ env.tag }}\"}" \ - ${{ secrets.DISCORD_WEBHOOK }} - + -d "$json_payload" \ + ${{ secrets.DISCORD_WEBHOOK }} \ No newline at end of file diff --git a/.github/workflows/build_wiki.yml b/.github/workflows/build_wiki.yml new file mode 100644 index 00000000..8276f2f6 --- /dev/null +++ b/.github/workflows/build_wiki.yml @@ -0,0 +1,38 @@ +name: Build Wiki +on: + workflow_dispatch: + workflow_run: + workflows: ["Update devices in wiki"] + types: + - completed + push: + branches: + - mkdocs + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: mkdocs + - name: Configure Git Credentials + run: | + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v4 + with: + key: mkdocs-material-${{ env.cache_id }} + path: .cache + restore-keys: | + mkdocs-material- + - run: pip install mkdocs-material + - run: pip install -r requirements.txt + - run: mkdocs gh-deploy --force diff --git a/.github/workflows/update_devices.yml b/.github/workflows/update_devices.yml new file mode 100644 index 00000000..f1455ba6 --- /dev/null +++ b/.github/workflows/update_devices.yml @@ -0,0 +1,56 @@ +name: Update devices in wiki + +permissions: + contents: write + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - src/const/hw.cpp + +jobs: + update-devices: + runs-on: ubuntu-latest + + steps: + - name: Checkout main branch + uses: actions/checkout@v2 + with: + ref: main + path: main_branch + + - name: Checkout mkdocs branch + uses: actions/checkout@v2 + with: + ref: mkdocs + path: mkdocs_branch + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + + - name: Run update_devices.py + run: python main_branch/.github/scripts/update_devices.py + + - name: Commit and push changes + run: | + cd mkdocs_branch + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + if [[ -n $(git status --porcelain) ]]; then + git add docs/features.md + git commit -m 'Update features.md with new device data' + git push origin mkdocs + else + echo "No changes to commit" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 873cc680..2033b33e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,8 @@ todo* !lib !platformio.ini **/__pycache__ -tools/.no_web_update* \ No newline at end of file +tools/.no_web_update* +managed_components +old? +data/* +/tools/webfilesbuilder/.npm_install_marker* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..7e97b898 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.16.0) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(XZG) + diff --git a/README.md b/README.md index db725fa9..ee7130b5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # XZG Firmware
-GitHub version +GitHub version GitHub Actions Workflow Status -GitHub download +GitHub download GitHub Issues or Pull Requests License
@@ -17,22 +17,6 @@ By focusing the community's efforts on enhancing one product, XZG aims to stream thereby improving the capabilities and efficiency of your Zigbee Gateways. 🌍

- - - - - - - - - - - - - - -
Previous versions:
ZigStarGW-FWUZG-01SLZB-06
ZigStarGW-FW's downloadUZG-01 Firmware's downloadSLZB-06 Firmware's download
-
## 🍓 Firmware features @@ -52,8 +36,7 @@ Please follow the installation guide tailored to your hardware. ## 🛠️ Compiling from source -### Local - +### VS Code - You need npm and Python installed - Install Visual Studio Code (VSC) - Install PlatformIO extension to VSC @@ -62,6 +45,23 @@ Please follow the installation guide tailored to your hardware. - Open `XZG.code-workspace` in VSC - Press "PlatformIO: Build" and wait until XZG*.bin are generated +### Linux CLI +- You need npm ad Python installed +- Install PlatformIO Core (it's in many package managers) +- Clone this repository + `git clone --recurse-submodules https://github.com/xyzroe/XZG.git` +- Use `pio run` to build default environment +- Binaries output to .pio/build/name_of_env/ +- Use `pio run -t upload` to build and upload + firmware image +- Use `-e` flag to select a specific build: + `pio run -e env_name -t upload` + +### Language Server Setup +- LSP (e.g. clangd and Neovim) users need to run + `pio run -t compiledb` to + generate a "compile_commands.json" + ### Github - Fork this repository; - Made your changes; @@ -93,11 +93,8 @@ Contributions are welcome! If you'd like to help improve the XZG Firmware, you c Thanks to all the developers and contributors who make this project possible, and special thanks to [@mercenaruss](https://github.com/mercenaruss/) for **Zig Star devices development**. -#### All projects contributors: +#### All contributors: - - - Special thanks to all third-party library authors. Their work has significantly contributed to this project: diff --git a/XZG.code-workspace b/XZG.code-workspace index 206e598d..6cfeea7d 100644 --- a/XZG.code-workspace +++ b/XZG.code-workspace @@ -11,8 +11,17 @@ ], "cSpell.useGitignore": false, "files.associations": { - "*.tpp": "cpp" + "*.tpp": "cpp", + "typeinfo": "cpp", + "*.ipp": "cpp" }, + "i18n-ally.keysInUse": [ + "p.lo.mlo", + "p.lo.mwc", + "p.lo.mnl", + "p.mq.disc.ph", + "p.mq.disc.n" + ], }, "extensions": { "recommendations": [ diff --git a/bin/.gitignore b/bin/.gitignore index 625356ff..a0c907ca 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -1 +1,2 @@ -*debug* \ No newline at end of file +*debug* +XZG*bin \ No newline at end of file diff --git a/dependencies.lock b/dependencies.lock new file mode 100644 index 00000000..6efb4782 --- /dev/null +++ b/dependencies.lock @@ -0,0 +1,319 @@ +dependencies: + chmorgan/esp-libhelix-mp3: + component_hash: cbb76089dc2c5749f7b470e2e70aedc44c9da519e04eb9a67d4c7ec275229e53 + dependencies: + - name: idf + require: private + version: '>=4.1.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.3 + espressif/cbor: + component_hash: 440f4ee4504841cc9b4f3a8ef755776a612ac9dace355514c68b999868f990ff + dependencies: + - name: idf + require: private + version: '>=4.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.6.0~1 + espressif/esp-dsp: + component_hash: fa7fe74305df6da25867437ebcd4213e047cbfc0556cf92067ab657fce537c6e + dependencies: + - name: idf + require: private + version: '>=4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.2 + espressif/esp-modbus: + component_hash: 2168e6b4cbda4d0281a2a2d1a40a3848e231473b2690d73217e3600fd2c98c12 + dependencies: + - name: idf + require: private + version: '>=4.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.16 + espressif/esp-serial-flasher: + component_hash: dcc42a16712a1a636509cf0bf90e14032d7f2141784b533613b267b6aa318d52 + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.0.11 + espressif/esp-zboss-lib: + component_hash: ceb89aaab088a3bc037c608bb7b7925e7116b0a7dda7c3476f44d9b06ff66c0a + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.0 + espressif/esp-zigbee-lib: + component_hash: 238c29955025d3e4f430fd7243ad7f9f038b97775746032478f34532ad5979aa + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.0 + espressif/esp_diag_data_store: + component_hash: 8849195251dbb8a2d7268335277cfa310cef36e4ac1e90cd59ad3be4269a30d7 + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.1 + espressif/esp_diagnostics: + component_hash: 2ea46a907cad1842e41a74b9efbd533ddd4908d3ccd06370c1f8355d41dd0342 + dependencies: + - name: espressif/rmaker_common + registry_url: https://components.espressif.com/ + require: private + version: ~1.4.0 + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.2.0 + espressif/esp_insights: + component_hash: c2b5219b3343e79e6d62aa5589a204612416abe8bdb6ba11224dca1b9a3c62ba + dependencies: + - name: espressif/cbor + registry_url: https://components.espressif.com/ + require: private + rules: + - if: idf_version >=5.0 + version: ~0.6 + - name: espressif/esp_diag_data_store + registry_url: https://components.espressif.com/ + require: private + version: ~1.0 + - name: espressif/esp_diagnostics + registry_url: https://components.espressif.com/ + require: private + version: '>=1.2.0' + - name: espressif/rmaker_common + registry_url: https://components.espressif.com/ + require: private + version: ~1.4.0 + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.2.0 + espressif/esp_modem: + component_hash: e48da33fee082dd9d9a97a354a228057e07a14ac108766b40ad84e018205410a + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.0 + espressif/esp_rainmaker: + component_hash: f89a4759347f3909417fb33059452f36c86befae9d10bda78b5417b7a5d19d11 + dependencies: + - name: espressif/esp_rcp_update + registry_url: https://components.espressif.com/ + require: private + rules: + - if: idf_version >= 5.1 + version: ~1.2.0 + - name: espressif/esp_schedule + registry_url: https://components.espressif.com/ + require: private + version: ~1.2.0 + - name: espressif/esp_secure_cert_mgr + registry_url: https://components.espressif.com/ + require: private + rules: + - if: idf_version >=4.3 + version: ^2.2.1 + - name: espressif/json_generator + registry_url: https://components.espressif.com/ + require: private + version: ~1.1.1 + - name: espressif/json_parser + registry_url: https://components.espressif.com/ + require: private + version: ~1.0.3 + - name: espressif/mdns + registry_url: https://components.espressif.com/ + require: private + rules: + - if: idf_version >=5.0 + version: ^1.2.0 + - name: espressif/network_provisioning + registry_url: https://components.espressif.com/ + require: private + rules: + - if: idf_version >= 5.1 + version: ~1.0.0 + - name: espressif/rmaker_common + registry_url: https://components.espressif.com/ + require: private + version: ~1.4.6 + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.0 + espressif/esp_rcp_update: + component_hash: c10afbd54a17f27eed880e61262b161656e6d36ad63376c307f9273e99d0abcd + dependencies: + - name: espressif/esp-serial-flasher + registry_url: https://components.espressif.com/ + require: private + version: ~0.0.0 + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.2.0 + espressif/esp_schedule: + component_hash: e202a9c688f7f1ab601efb91d682e4bcfaebc508dcceee1a1e0a0d2d1ca75a26 + dependencies: + - name: espressif/rmaker_common + registry_url: https://components.espressif.com/ + require: private + version: ~1.4.2 + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.2.0 + espressif/esp_secure_cert_mgr: + component_hash: a20007d67e65a000670ab77e45d7554c943eb8dcb0abeada0a57dd9adac3a703 + dependencies: + - name: idf + require: private + version: '>=4.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.4.1 + espressif/jsmn: + component_hash: d80350c41bbaa827c98a25b6072df00884e72f54885996fab4a4f0aebce6b6c3 + dependencies: + - name: idf + require: private + version: '>=4.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.0 + espressif/json_generator: + component_hash: 45033e1c199b13f1c8c1b544fb7d4e2df6a8e3071ebdcb1b22582b61a7974ff2 + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.2 + espressif/json_parser: + component_hash: d74b81729ad06ec11ff5eb5b1b0d7df1d00e6027fc11471f4b139c70dcf1b1e4 + dependencies: + - name: espressif/jsmn + registry_url: https://components.espressif.com/ + require: private + rules: + - if: idf_version >=5.0 + version: ~1.1 + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.3 + espressif/libsodium: + component_hash: f6e982479a2389cb6868e8fb761cf23aba6c355a8090b3e906299807775f58a3 + dependencies: + - name: idf + require: private + version: '>=4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.20~1 + espressif/mdns: + component_hash: af6306fe65d637a3683d1cf671508fcedd6b05f9ca029a8815abeab64001fb8d + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.4.0 + espressif/network_provisioning: + component_hash: ef2e10182fd1861e68b821491916327c25416ca7ae70e5a6e43313dbc71fe993 + dependencies: + - name: idf + require: private + version: '>=5.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.2 + espressif/qrcode: + component_hash: 3b493771bc5d6ad30cbf87c25bf784aada8a08c941504355b55d6b75518ed7bc + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.1.0~2 + espressif/rmaker_common: + component_hash: a3a1df881278d0351fc850b77792fe8a196ddd6dcacbea203d606329cc6a0239 + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.4.6 + idf: + source: + type: idf + version: 5.1.4 + joltwallet/littlefs: + component_hash: 362f1f5beb5087b0c60169aff82676d2d0ffc991ead975212b0cba95959181c5 + dependencies: + - name: idf + require: private + version: '>=4.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.14.8 +direct_dependencies: +- chmorgan/esp-libhelix-mp3 +- espressif/esp-dsp +- espressif/esp-modbus +- espressif/esp-zboss-lib +- espressif/esp-zigbee-lib +- espressif/esp_insights +- espressif/esp_modem +- espressif/esp_rainmaker +- espressif/libsodium +- espressif/mdns +- espressif/network_provisioning +- espressif/qrcode +- espressif/rmaker_common +- idf +- joltwallet/littlefs +manifest_hash: 6e90f5b15283407daa0c179aac46415cf48b3dd974c63431ebecfcee6f2c1510 +target: esp32 +version: 2.0.0 diff --git a/lib/CCTools/library.json b/lib/CCTools/library.json index 38792b35..59339e3c 100644 --- a/lib/CCTools/library.json +++ b/lib/CCTools/library.json @@ -18,5 +18,5 @@ "type": "git", "url": "https://github.com/xyzroe/CCTools" }, - "version": "0.0.5" + "version": "0.0.6" } \ No newline at end of file diff --git a/lib/CCTools/src/CCTools.cpp b/lib/CCTools/src/CCTools.cpp index 8d125009..654a4312 100644 --- a/lib/CCTools/src/CCTools.cpp +++ b/lib/CCTools/src/CCTools.cpp @@ -60,17 +60,17 @@ bool CommandInterface::_wait_for_ack(unsigned long timeout = 1) uint8_t received = _stream.read(); if (received == ACK_BYTE) { - // Serial.println("ACK received"); + // DEBUG_PRINTLN("ACK received"); return true; } else if (received == NACK_BYTE) { - // Serial.println("NACK received"); + // DEBUG_PRINTLN("NACK received"); return false; } } } - Serial.println("Timeout waiting for ACK/NACK"); + DEBUG_PRINTLN("Timeout waiting for ACK/NACK"); return false; } @@ -119,7 +119,7 @@ uint32_t CommandInterface::_cmdGetChipId() { // 4 byte answ, the 2 LSB hold chip ID byte *version = _receivePacket(); - // Serial.println("size " + String(sizeof(version))); + // DEBUG_PRINTLN("size " + String(sizeof(version))); if (_checkLastCmd()) { @@ -128,12 +128,12 @@ uint32_t CommandInterface::_cmdGetChipId() value |= uint32_t(version[2]) << 8; value |= uint32_t(version[1]) << 16; value |= uint32_t(version[0]) << 24; // Most significant byte - // Serial.print("ChipId "); - // Serial.println(value, HEX); + // DEBUG_PRINT("ChipId "); + // DEBUG_PRINTLN(value, HEX); if (sizeof(version) != 4) { - Serial.println("Unreasonable chip. Looks upper"); // repr(version) ? + DEBUG_PRINTLN("Unreasonable chip. Looks upper"); // repr(version) ? return uint32_t(0); } @@ -160,7 +160,7 @@ byte *CommandInterface::_cmdGetStatus() byte *stat = _receivePacket(); return stat; } - Serial.print("Error _cmdGetStatus"); + DEBUG_PRINT("Error _cmdGetStatus"); return nullptr; } @@ -170,27 +170,28 @@ bool CommandInterface::_checkLastCmd() byte *stat = _cmdGetStatus(); if (stat == nullptr) { - Serial.println("No response from target on status request."); - Serial.println("(Did you disable the bootloader?)"); + DEBUG_PRINTLN("No response from target on status request."); + DEBUG_PRINTLN("(Did you disable the bootloader?)"); return 0; } else { if (stat[0] == COMMAND_RET_SUCCESS) { - // Serial.println("Command Successful"); + // DEBUG_PRINTLN("Command Successful"); return 1; } else { const char *stat_str = _getStatusString(stat[0]); - if (stat_str == "Unknown") + //if (stat_str == "Unknown") + if (strcmp(stat_str, "Unknown") == 0) { - Serial.println("Warning: unrecognized status returned 0x" + String(stat[0])); + DEBUG_PRINTLN("Warning: unrecognized status returned 0x" + String(stat[0])); } else { - Serial.println("Target returned: 0x" + String(stat[0]) + " " + String(stat_str)); + DEBUG_PRINTLN("Target returned: 0x" + String(stat[0]) + " " + String(stat_str)); } return 0; } @@ -241,9 +242,9 @@ bool CommandInterface::_cmdDownload(uint32_t address, unsigned long size) if (size % 4 != 0) { // If size is not a multiple of 4, handle the error - Serial.print("Invalid data size: "); - Serial.print(size); - Serial.println(". Size must be a multiple of 4."); + DEBUG_PRINT("Invalid data size: "); + DEBUG_PRINT(size); + DEBUG_PRINTLN(". Size must be a multiple of 4."); return false; } @@ -265,7 +266,7 @@ bool CommandInterface::_cmdDownload(uint32_t address, unsigned long size) _stream.write(sizeBytes[2]); // # send size _stream.write(sizeBytes[3]); // # send size - // Serial.println("*** Mem Read (0x2A)"); + // DEBUG_PRINTLN("*** Mem Read (0x2A)"); if (_wait_for_ack()) { byte *data = _receivePacket(); @@ -285,9 +286,9 @@ bool CommandInterface::_cmdSendData(byte *data, unsigned int dataSize) // Check if data size exceeds maximum limit if (dataSize > maxDataSize) { - Serial.print("Data size too large: "); - Serial.print(dataSize); - Serial.println(". Maximum size allowed is 252 bytes."); + DEBUG_PRINT("Data size too large: "); + DEBUG_PRINT(dataSize); + DEBUG_PRINTLN(". Maximum size allowed is 252 bytes."); return false; } @@ -310,7 +311,7 @@ bool CommandInterface::_cmdSendData(byte *data, unsigned int dataSize) } // Optionally print debug information to the serial monitor - // Serial.println("*** Send Data (0x24)"); + // DEBUG_PRINTLN("*** Send Data (0x24)"); // Assume _wait_for_ack() and _checkLastCmd() are implemented similarly to your previous method if (_wait_for_ack()) @@ -348,7 +349,7 @@ byte *CommandInterface::_cmdMemRead(uint32_t address) _stream.write(1); // # send width, 4 bytes _stream.write(1); // # send number of reads - // Serial.println("*** Mem Read (0x2A)"); + // DEBUG_PRINTLN("*** Mem Read (0x2A)"); if (_wait_for_ack()) { byte *data = _receivePacket(); @@ -379,10 +380,10 @@ byte *CommandInterface::_receivePacket() return nullptr; } - // Debugging output, might use Serial.print in Arduino - // Serial.print(F("*** received ")); - // Serial.print(size, HEX); - // Serial.println(F(" bytes")); + // Debugging output, might use DEBUG_PRINT in Arduino + // DEBUG_PRINT(F("*** received ")); + // DEBUG_PRINT(size, HEX); + // DEBUG_PRINTLN(F(" bytes")); // Calculate checksum byte calculatedChks = 0; @@ -475,7 +476,7 @@ bool CommandInterface::_ledToggle(bool ledState) bool CommandInterface::_nvram_osal_delete(uint16_t nvid) { // DEBUG_PRINT("Checking OsalNvIds ID: "); - // Serial.print(nvid, HEX); + // DEBUG_PRINT(nvid, HEX); // DEBUG_PRINT(" - "); const uint8_t cmd1 = 0x21; @@ -504,21 +505,21 @@ bool CommandInterface::_nvram_osal_delete(uint16_t nvid) _stream.flush(); - // Serial.println(""); - // Serial.println("> " + String(cmd1, HEX) + " " + String(cmd2, HEX) + " " + String(lowByte, HEX) + " " + String(highByte, HEX) + " " + String(fcs, HEX)); + // DEBUG_PRINTLN(""); + // DEBUG_PRINTLN("> " + String(cmd1, HEX) + " " + String(cmd2, HEX) + " " + String(lowByte, HEX) + " " + String(highByte, HEX) + " " + String(fcs, HEX)); std::unique_ptr data(_receive_SRSP()); if (!data) { return false; } - // Serial.println("< " + String(data[0], HEX) + " " + String(data[1], HEX) + " " + String(data[2], HEX) + " " + String(data[3], HEX) + " " + String(data[4], HEX)); + // DEBUG_PRINTLN("< " + String(data[0], HEX) + " " + String(data[1], HEX) + " " + String(data[2], HEX) + " " + String(data[3], HEX) + " " + String(data[4], HEX)); if (data[2] > 0 || data[3] > 0) { /* DEBUG_PRINT("* Deleting OsalNvIds ID: "); - Serial.print(nvid, HEX); + DEBUG_PRINT(nvid, HEX); DEBUG_PRINT(" - "); */ @@ -547,15 +548,15 @@ bool CommandInterface::_nvram_osal_delete(uint16_t nvid) _stream.flush(); - // Serial.println(""); - // Serial.println("> " + String(cmd1, HEX) + " " + String(cmd2, HEX) + " " + String(lowByte, HEX) + " " + String(highByte, HEX) + " " + String(data[2], HEX) + " " + String(data[3], HEX) + " " + String(fcs, HEX)); + // DEBUG_PRINTLN(""); + // DEBUG_PRINTLN("> " + String(cmd1, HEX) + " " + String(cmd2, HEX) + " " + String(lowByte, HEX) + " " + String(highByte, HEX) + " " + String(data[2], HEX) + " " + String(data[3], HEX) + " " + String(fcs, HEX)); std::unique_ptr data(_receive_SRSP()); if (!data) { return false; } - // Serial.println("< " + String(data[0], HEX) + " " + String(data[1], HEX) + " " + String(data[2], HEX) + " " + String(data[3], HEX)); + // DEBUG_PRINTLN("< " + String(data[0], HEX) + " " + String(data[1], HEX) + " " + String(data[2], HEX) + " " + String(data[3], HEX)); } return true; } @@ -565,9 +566,9 @@ bool CommandInterface::_nvram_ex_delete(uint16_t nvid, uint16_t subID) /* DEBUG_PRINT("Deleting ExNvIds sub ID: "); - Serial.print(nvid, HEX); + DEBUG_PRINT(nvid, HEX); DEBUG_PRINT(" "); - Serial.print(subID, HEX); + DEBUG_PRINT(subID, HEX); DEBUG_PRINT(" - "); */ const uint8_t cmd1 = 0x21; @@ -611,7 +612,7 @@ bool CommandInterface::_nvram_ex_delete(uint16_t nvid, uint16_t subID) { return false; } - // Serial.println(String(data[2], HEX)); + // DEBUG_PRINTLN(String(data[2], HEX)); if (data[2] == 0x0A) { // error @@ -649,7 +650,7 @@ CommandInterface::zbInfoStruct CommandInterface::_checkFwVer() chip.transportrev = zbVerBuf[0]; _cleanBuffer(); - DEBUG_PRINTLN("ZB v: " + String(chip.fwRev) + " Main: " + chip.maintrel + " Min: " + chip.minorrel + " Maj: " + chip.majorrel + " T: " + chip.transportrev + " P: " + chip.product); + // DEBUG_PRINTLN("ZB v: " + String(chip.fwRev) + " Main: " + chip.maintrel + " Min: " + chip.minorrel + " Maj: " + chip.majorrel + " T: " + chip.transportrev + " P: " + chip.product); return chip; } @@ -735,10 +736,10 @@ bool CCTools::detectChipInfo() uint32_t chip_id = _cmdGetChipId(); - // Serial.println(chip_id, HEX); + // DEBUG_PRINTLN(chip_id, HEX); byte *device_id = _cmdMemRead(ICEPICK_DEVICE_ID); - // Serial.println(sizeof(device_id)); + // DEBUG_PRINTLN(sizeof(device_id)); uint32_t wafer_id = (((device_id[3] & 0x0F) << 16) + (device_id[2] << 8) + @@ -748,33 +749,33 @@ bool CCTools::detectChipInfo() // We can now detect the exact device - // Serial.print("wafer_id: "); - // Serial.println(wafer_id, HEX); - // Serial.print("pg_rev: "); - // Serial.println(pg_rev, HEX); + // DEBUG_PRINT("wafer_id: "); + // DEBUG_PRINTLN(wafer_id, HEX); + // DEBUG_PRINT("pg_rev: "); + // DEBUG_PRINTLN(pg_rev, HEX); byte *user_id = _cmdMemRead(FCFG_USER_ID); - // Serial.println("Package: " + _getPackageString(user_id[2])); + // DEBUG_PRINTLN("Package: " + _getPackageString(user_id[2])); byte protocols = user_id[1] >> 4; - // Serial.print("protocols: "); - // Serial.println(protocols, HEX); + // DEBUG_PRINT("protocols: "); + // DEBUG_PRINTLN(protocols, HEX); byte *flash_size = _cmdMemRead(FLASH_SIZE); - // Serial.print("flash_size: "); - // Serial.println(flash_size[0], HEX); + // DEBUG_PRINT("flash_size: "); + // DEBUG_PRINTLN(flash_size[0], HEX); // byte *ram_size = _cmdMemRead(PRCM_RAMHWOPT); - // Serial.print("ram_size: "); - // Serial.println(ram_size[0], HEX); + // DEBUG_PRINT("ram_size: "); + // DEBUG_PRINTLN(ram_size[0], HEX); byte *ieee_b1 = _cmdMemRead(addr_ieee_address_primary + 4); byte *ieee_b2 = _cmdMemRead(addr_ieee_address_primary); if (ieee_b1 == nullptr || ieee_b2 == nullptr) { - Serial.println("Error read IEEE"); + DEBUG_PRINTLN("Error read IEEE"); return false; } @@ -796,10 +797,11 @@ bool CCTools::detectChipInfo() delete[] ieee_b2; String chip_str; - if (protocols & PROTO_MASK_IEEE == PROTO_MASK_IEEE) + //if (protocols & PROTO_MASK_IEEE == PROTO_MASK_IEEE) + if ((protocols & PROTO_MASK_IEEE) == PROTO_MASK_IEEE) { uint32_t test = 360372; - // Serial.print(test, HEX); + // DEBUG_PRINT(test, HEX); byte *b_val = _cmdMemRead(test); chip.hwRev = _getChipDescription(chip_id, wafer_id, pg_rev, b_val[1]); @@ -811,22 +813,16 @@ bool CCTools::detectChipInfo() chip.flashSize = flash_size[0] * page_size; test = chip.flashSize - 88 + 0xC; - Serial.print(test, HEX); + //DEBUG_PRINT(test, HEX); b_val = _cmdMemRead(test); - Serial.print(" MODE_CONF: "); - Serial.print(b_val[0], HEX); - Serial.print(b_val[1], HEX); - Serial.print(b_val[2], HEX); - Serial.println(b_val[3], HEX); + + chip.modeCfg = _decodeAddr(b_val[3], b_val[2], b_val[1], b_val[0]); uint32_t bsl_adr = chip.flashSize - 88 + 0x30; - Serial.print(bsl_adr, HEX); + //DEBUG_PRINT(bsl_adr, HEX); byte *bsl_val = _cmdMemRead(bsl_adr); - Serial.print(" bsl_val: "); - Serial.print(bsl_val[0], HEX); - Serial.print(bsl_val[1], HEX); - Serial.print(bsl_val[2], HEX); - Serial.println(bsl_val[3], HEX); + + chip.bslCfg = _decodeAddr(bsl_val[3], bsl_val[2], bsl_val[1], bsl_val[0]); return true; } @@ -874,6 +870,7 @@ bool CCTools::checkFirmwareVersion() { restart(); } + _cleanBuffer(); zbInfoStruct temp = _checkFwVer(); chip.fwRev = temp.fwRev; chip.maintrel = temp.maintrel; @@ -971,4 +968,9 @@ bool CCTools::nvram_reset(void (*logFunction)(const String&)) restart(); return success; +} + +void CCTools::cleanBuffer() +{ + _cleanBuffer(); } \ No newline at end of file diff --git a/lib/CCTools/src/CCTools.h b/lib/CCTools/src/CCTools.h index 0e139f7a..394f3de5 100644 --- a/lib/CCTools/src/CCTools.h +++ b/lib/CCTools/src/CCTools.h @@ -416,6 +416,8 @@ class CommandInterface uint8_t majorrel; uint8_t product; uint8_t transportrev; + unsigned long modeCfg; + unsigned long bslCfg; }; static const uint8_t ACK_BYTE = 0xCC; @@ -614,6 +616,7 @@ class CCTools : public CommandInterface bool checkFirmwareVersion(); bool ledToggle(); bool nvram_reset(void (*logFunction)(const String&)); + void cleanBuffer(); }; #endif // CCTools_DETECT_H \ No newline at end of file diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 00000000..608eb781 --- /dev/null +++ b/partitions.csv @@ -0,0 +1,13 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +# Раздел для хранения данных NVS (Non-Volatile Storage), 20 KB +otadata, data, ota, 0xe000, 0x2000, +# Раздел для хранения данных OTA (Over-the-Air) обновлений, 8 KB +app0, app, ota_0, 0x10000, 0x180000, +# Первый раздел для приложения, используемый для OTA обновлений, 1.5 MB (1572864 байт) +app1, app, ota_1, 0x190000,0x180000, +# Второй раздел для приложения, используемый для OTA обновлений, 1.5 MB (1572864 байт) +eeprom, data, 0x99, 0x310000,0x1000, +# Раздел для хранения данных EEPROM, 4 KB +spiffs, data, spiffs, 0x311000,0xAF000, +# Раздел для файловой системы SPIFFS, 704 KB (720896 байт) \ No newline at end of file diff --git a/partitions.default.csv b/partitions.default.csv new file mode 100644 index 00000000..e1fd12be --- /dev/null +++ b/partitions.default.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags ; Комментарии +nvs, data, nvs, 0x9000, 0x5000, ; Раздел для хранения данных NVS (Non-Volatile Storage), 20 KB +otadata, data, ota, 0xe000, 0x2000, ; Раздел для хранения данных OTA (Over-the-Air) обновлений, 8 KB +app0, app, ota_0, 0x10000, 0x140000, ; Первый раздел для приложения, используемый для OTA обновлений, 1.25 MB (1310720 байт) +app1, app, ota_1, 0x150000,0x140000, ; Второй раздел для приложения, используемый для OTA обновлений, 1.25 MB (1310720 байт) +spiffs, data, spiffs, 0x290000,0x160000, ; Раздел для файловой системы SPIFFS, 1.5 MB (1441792 байт) +coredump, data, coredump,0x3F0000,0x10000, ; Раздел для хранения дампов ядра, 64 KB (65536 байт) \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index a8e2fb53..cf6de6a7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,88 +8,110 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html -;It was difficult to find working platform for https get function on solo board. Here are some logs: -;https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5/platform-espressif32-2.0.5.zip ;- old build. - unkown board -;https://github.com/tasmota/platform-espressif32/releases/download/2023.01.00/platform-espressif32.zip ; fuck yeaar. it works on solo -;https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip ; fuck yeaar. it also works on solo -;https://github.com/tasmota/platform-espressif32/releases/download/2023.08.00/platform-espressif32.zip ; bitch. did't work -1 -;https://github.com/tasmota/platform-espressif32/releases/download/2023.10.01/platform-espressif32.zip ;- build but -1 -;https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32.zip ;- last builds version but -1 -;https://github.com/tasmota/platform-espressif32/releases/download/2023.10.05/platform-espressif32.zip ;- WiFiClientSecure.h: No such file or directory -;https://github.com/tasmota/platform-espressif32/releases/download/2023.10.10/platform-espressif32.zip ; - logs2.txt -;espressif32 @ ^6.4.0 ;- unkown board -;espressif32 @ 5.1.0 ; - works but no solo - [platformio] -default_envs = prod-solo +default_envs = prod-dual [env] framework = arduino lib_deps = bblanchon/ArduinoJson@6.21.3 rlogiacco/CircularBuffer@>=1.4.0 - sstaub/Ticker@>=4.4.0 Martin-Laclaustra/CronAlarms + mathieucarbou/AsyncTCP@3.2.5 + marvinroger/AsyncMqttClient@^0.9.0 + robtillaart/DS18B20@^0.2.3 + ;husarnet/Husarnet ESP32 @ 1.2.0-5 ;husarnet example - marvinroger/AsyncMqttClient @ ^0.9.0 - me-no-dev/AsyncTCP@1.1.1 + ;husarnet/esp_husarnet^0.0.11 + ;me-no-dev/AsyncTCP@1.1.1 + ;elims/PsychicMqttClient@0.2.0 + ;sstaub/Ticker@>=4.4.0 ;plerup/EspSoftwareSerial@8.1.0 ;marian-craciunescu/ESP32Ping@>=1.7 ;me-no-dev/ESPAsyncWebServer@1.2.3 -monitor_filters = direct; log2file ; esp32_exception_decoder, default ; + ;milesburton/DallasTemperature @ ^3.11.0 +monitor_filters = direct, esp32_exception_decoder, log2file ; default ; ; ; monitor_speed = 115200 upload_speed = 460800 ;platform_packages = ; framework-arduinoespressif32 @ https://github.com/husarnet/arduino-esp32/releases/download/1.0.4-1/arduino-husarnet-esp32.zip ;husarnet example +platform_packages = + tool-mklittlefs + ;framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/3.0.4.240826/framework-arduinoespressif32.zip + ;framework-arduinoespressif32-solo1 @ https://github.com/tasmota/arduino-esp32/releases/download/3.0.4.240826/framework-arduinoespressif32-solo1.zip extra_scripts = + ;pre:tools/build/build_fs.py pre:tools/build/pre_build.py pre:tools/build/version_update.py pre:tools/webfilesbuilder/build_html.py + pre:tools/export_compile_commands.py post:tools/build/build.py + build_flags = -DBUILD_ENV_NAME=$PIOENV - + -Os + +[solo] +platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.09.10/platform-espressif32.zip ; Platform 2024.08.11 Tasmota Arduino Core 3.0.4 based on IDF 5.1.4+ +build_flags = + -DFRAMEWORK_ARDUINO_SOLO1 + -DTASMOTA_PLATFORM + +[dual] +platform = espressif32 @ 6.8.1 +;platform = espressif32@2.1.0 ;husarnet example + +[prod] +build_flags = + -DCORE_DEBUG_LEVEL=0 + +[debug] +build_flags = + -DCORE_DEBUG_LEVEL=2 + -DDEBUG [env:prod-solo] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip +platform = ${solo.platform} board = esp32-solo1 +board_build.filesystem = littlefs build_flags = ${env.build_flags} - -DFRAMEWORK_ARDUINO_SOLO1 + ${solo.build_flags} + ${prod.build_flags} board_build.f_cpu = 160000000L extra_scripts = ${env.extra_scripts} [env:debug-solo] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip +platform = ${solo.platform} board = esp32-solo1 +board_build.filesystem = littlefs build_flags = - -DDEBUG - ${env.build_flags} - -DFRAMEWORK_ARDUINO_SOLO1 + ${env.build_flags} + ${solo.build_flags} + ${debug.build_flags} board_build.f_cpu = 160000000L extra_scripts = - ${env.extra_scripts} - + ${env.extra_scripts} +monitor_speed = 115200 -[env:prod] -platform = espressif32 @ 6.4.0 +[env:prod-dual] +platform = ${dual.platform} board = esp32dev +board_build.filesystem = littlefs build_flags = ${env.build_flags} + ${prod.build_flags} extra_scripts = ${env.extra_scripts} - -[env:debug] -platform = espressif32 @ 6.6.0 -;platform = espressif32@2.1.0 ;husarnet example +[env:debug-dual] +platform = ${dual.platform} board = esp32dev +board_build.partitions = partitions.csv +board_build.filesystem = littlefs build_flags = - -DDEBUG ${env.build_flags} + ${debug.build_flags} extra_scripts = - ${env.extra_scripts} -lib_deps = - ${env.lib_deps} - + ${env.extra_scripts} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..483bc0cf --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,6 @@ +# This file was automatically generated for projects +# without default 'CMakeLists.txt' file. + +FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) + +idf_component_register(SRCS ${app_sources}) diff --git a/src/config.cpp b/src/config.cpp index 7d700abd..f5fef4db 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -23,8 +23,6 @@ extern struct NetworkConfigStruct networkCfg; extern struct VpnConfigStruct vpnCfg; extern struct MqttConfigStruct mqttCfg; -String tag = "NVS"; - void getNvsStats(int *total, int *used) { nvs_stats_t nvsStats; @@ -75,7 +73,8 @@ String makeJsonConfig(const NetworkConfigStruct *networkCfg, const VpnConfigStruct *vpnCfg, const MqttConfigStruct *mqttCfg, const SystemConfigStruct *systemCfg, - const SysVarsStruct *systemVars) + const SysVarsStruct *systemVars, + const ThisConfigStruct *hwConfig) { StaticJsonDocument<2048> doc; @@ -109,6 +108,12 @@ String makeJsonConfig(const NetworkConfigStruct *networkCfg, serializeSysVarsToJson(*systemVars, varsJson); } + if (hwConfig != nullptr) + { + JsonObject hw = doc.createNestedObject(hwConfigKey); + serializeHwConfigToJson(*hwConfig, hw); + } + String output; serializeJsonPretty(doc, output); return output; @@ -127,6 +132,8 @@ void saveNetworkConfig(const NetworkConfigStruct &config) preferences.putString(wifiGateKey, config.wifiGate.toString()); preferences.putString(wifiDns1Key, config.wifiDns1.toString()); preferences.putString(wifiDns2Key, config.wifiDns2.toString()); + preferences.putInt(wifiPwrKey, static_cast(config.wifiPower)); + preferences.putInt(wifiModeKey, config.wifiMode); preferences.putBool(ethEnblKey, config.ethEnable); preferences.putBool(ethDhcpKey, config.ethDhcp); @@ -153,6 +160,9 @@ void loadNetworkConfig(NetworkConfigStruct &config) config.wifiDns1.fromString(preferences.getString(wifiDns1Key, DNS_SERV_1)); config.wifiDns2.fromString(preferences.getString(wifiDns2Key, DNS_SERV_2)); + config.wifiPower = static_cast(preferences.getInt(wifiPwrKey, WIFI_POWER_19_5dBm)); + config.wifiMode = preferences.getInt(wifiModeKey, WIFI_PROTOCOL_11B); + config.ethEnable = preferences.getBool(ethEnblKey, true); config.ethDhcp = preferences.getBool(ethDhcpKey, true); config.ethIp.fromString(preferences.getString(ethIpKey)); @@ -216,6 +226,78 @@ void loadVpnConfig(VpnConfigStruct &config) preferences.end(); } +void saveHwConfig(const ThisConfigStruct &config) +{ + LOGD("saveHwConfig"); + preferences.begin(hwConfigKey, false); + /* + char board[50]; + EthConfig eth; + ZbConfig zb; + MistConfig mist; + */ + + preferences.putString(boardKey, config.board); + preferences.putInt(addrKey, config.eth.addr); + preferences.putInt(pwrPinKey, config.eth.pwrPin); + preferences.putInt(mdcPinKey, config.eth.mdcPin); + preferences.putInt(mdiPinKey, config.eth.mdiPin); + preferences.putInt(phyTypeKey, config.eth.phyType); + preferences.putInt(clkModeKey, config.eth.clkMode); + // preferences.putInt(pwrAltPinKey, config.eth.pwrAltPin); + + preferences.putInt(zbTxPinKey, config.zb.txPin); + preferences.putInt(zbRxPinKey, config.zb.rxPin); + preferences.putInt(zbRstPinKey, config.zb.rstPin); + preferences.putInt(zbBslPinKey, config.zb.bslPin); + + preferences.putInt(btnPinKey, config.mist.btnPin); + preferences.putInt(btnPlrKey, config.mist.btnPlr); + preferences.putInt(uartSelPinKey, config.mist.uartSelPin); + preferences.putInt(uartSelPlrKey, config.mist.uartSelPlr); + preferences.putInt(ledModePinKey, config.mist.ledModePin); + preferences.putInt(ledModePlrKey, config.mist.ledModePlr); + preferences.putInt(ledPwrPinKey, config.mist.ledPwrPin); + preferences.putInt(ledPwrPlrKey, config.mist.ledPwrPlr); + + preferences.end(); + LOGD("saveHwConfig end"); +} + +void loadHwConfig(ThisConfigStruct &config) +{ + preferences.begin(hwConfigKey, true); + + strlcpy(config.board, preferences.getString(boardKey).c_str(), sizeof(config.board)); + + config.eth.addr = preferences.getInt(addrKey, 0); + config.eth.pwrPin = preferences.getInt(pwrPinKey, 0); + config.eth.mdcPin = preferences.getInt(mdcPinKey, -1); + config.eth.mdiPin = preferences.getInt(mdiPinKey, -1); + config.eth.phyType = static_cast(preferences.getInt(phyTypeKey, ETH_PHY_LAN8720)); + config.eth.clkMode = static_cast(preferences.getInt(clkModeKey, ETH_CLOCK_GPIO0_IN)); + // config.eth.pwrAltPin = preferences.getInt(pwrAltPinKey, -1); + + config.zb.txPin = preferences.getInt(zbTxPinKey, -1); + config.zb.rxPin = preferences.getInt(zbRxPinKey, -1); + config.zb.rstPin = preferences.getInt(zbRstPinKey, -1); + config.zb.bslPin = preferences.getInt(zbBslPinKey, -1); + + config.mist.btnPin = preferences.getInt(btnPinKey, -1); + config.mist.btnPlr = preferences.getInt(btnPlrKey, -1); + config.mist.uartSelPin = preferences.getInt(uartSelPinKey, -1); + config.mist.uartSelPlr = preferences.getInt(uartSelPlrKey, -1); + config.mist.ledModePin = preferences.getInt(ledModePinKey, -1); + config.mist.ledModePlr = preferences.getInt(ledModePlrKey, -1); + config.mist.ledPwrPin = preferences.getInt(ledPwrPinKey, -1); + config.mist.ledPwrPlr = preferences.getInt(ledPwrPlrKey, -1); + + preferences.end(); + + String cfg = makeJsonConfig(NULL, NULL, NULL, NULL, NULL, &config); + LOGI("\n%s", cfg.c_str()); +} + void saveMqttConfig(const MqttConfigStruct &config) { preferences.begin(mqttConfigKey, false); @@ -262,13 +344,14 @@ void saveSystemConfig(const SystemConfigStruct &config) { preferences.begin(systemConfigKey, false); - preferences.putBool(keepWebKey, config.keepWeb); + // preferences.putBool(keepWebKey, config.keepWeb); preferences.putBool(disableWebKey, config.disableWeb); preferences.putBool(webAuthKey, config.webAuth); preferences.putString(webUserKey, config.webUser); preferences.putString(webPassKey, config.webPass); preferences.putBool(fwEnabledKey, config.fwEnabled); preferences.putString(fwIpKey, config.fwIp.toString()); + preferences.putString(fwMaskKey, config.fwMask.toString()); preferences.putInt(serialSpeedKey, config.serialSpeed); preferences.putInt(socketPortKey, config.socketPort); preferences.putInt(tempOffsetKey, config.tempOffset); @@ -285,6 +368,13 @@ void saveSystemConfig(const SystemConfigStruct &config) // preferences.putInt(prevWorkModeKey, static_cast(config.prevWorkMode)); preferences.putInt(workModeKey, static_cast(config.workMode)); + preferences.putInt(zbRoleKey, static_cast(config.zbRole)); + preferences.putString(zbFwKey, config.zbFw); + + preferences.putString(updCheckTimeKey, config.updCheckTime); + preferences.putString(updCheckDayKey, config.updCheckDay); + preferences.putBool(updAutoInstKey, config.updAutoInst); + preferences.end(); } @@ -292,19 +382,20 @@ void loadSystemConfig(SystemConfigStruct &config) { preferences.begin(systemConfigKey, true); - config.keepWeb = preferences.getBool(keepWebKey, true); + // config.keepWeb = preferences.getBool(keepWebKey, true); config.disableWeb = preferences.getBool(disableWebKey, false); config.webAuth = preferences.getBool(webAuthKey, false); strlcpy(config.webUser, preferences.getString(webUserKey, "").c_str(), sizeof(config.webUser)); strlcpy(config.webPass, preferences.getString(webPassKey, "").c_str(), sizeof(config.webPass)); config.fwEnabled = preferences.getBool(fwEnabledKey, false); config.fwIp.fromString(preferences.getString(fwIpKey, "0.0.0.0")); + config.fwMask.fromString(preferences.getString(fwMaskKey, "0.0.0.0")); config.serialSpeed = preferences.getInt(serialSpeedKey, ZB_SERIAL_SPEED); config.socketPort = preferences.getInt(socketPortKey, ZB_TCP_PORT); config.tempOffset = preferences.getInt(tempOffsetKey, 0); config.disableLedUSB = preferences.getBool(disableLedUSBKey, false); config.disableLedPwr = preferences.getBool(disableLedPwrKey, false); - config.refreshLogs = preferences.getInt(refreshLogsKey, 1); + config.refreshLogs = preferences.getInt(refreshLogsKey, 2); strlcpy(config.hostname, preferences.getString(hostnameKey, "XZG").c_str(), sizeof(config.hostname)); /// to do add def host name!! strlcpy(config.timeZone, preferences.getString(timeZoneKey, NTP_TIME_ZONE).c_str(), sizeof(config.timeZone)); strlcpy(config.ntpServ1, preferences.getString(ntpServ1Key, NTP_SERV_1).c_str(), sizeof(config.ntpServ1)); @@ -317,6 +408,13 @@ void loadSystemConfig(SystemConfigStruct &config) // config.prevWorkMode = static_cast(preferences.getInt(prevWorkModeKey, WORK_MODE_NETWORK)); config.workMode = static_cast(preferences.getInt(workModeKey, WORK_MODE_NETWORK)); + config.zbRole = static_cast(preferences.getInt(zbRoleKey, UNDEFINED)); + strlcpy(config.zbFw, preferences.getString(zbFwKey, "?").c_str(), sizeof(config.zbFw)); + + strlcpy(config.updCheckTime, preferences.getString(updCheckTimeKey, UPD_CHK_TIME).c_str(), sizeof(config.updCheckTime)); + strlcpy(config.updCheckDay, preferences.getString(updCheckDayKey, UPD_CHK_DAY).c_str(), sizeof(config.updCheckDay)); + config.updAutoInst = preferences.getBool(updAutoInstKey, false); + preferences.end(); } @@ -340,7 +438,6 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne { const char *pageId = "pageId"; const char *on = "on"; - const char *contTypeText = "text/plain"; if (serverWeb.hasArg(pageId)) { @@ -348,7 +445,7 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne { case API_PAGE_GENERAL: { - if (serverWeb.hasArg(coordMode)) + /*if (serverWeb.hasArg(coordMode)) { const uint8_t mode = serverWeb.arg(coordMode).toInt(); if (mode <= 2 && mode >= 0) @@ -360,7 +457,7 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne } } - configSys.keepWeb = serverWeb.hasArg(keepWebKey) == true; + configSys.keepWeb = serverWeb.hasArg(keepWebKey) == true;*/ configSys.disableLedPwr = serverWeb.hasArg(disableLedPwrKey) == true; @@ -400,19 +497,33 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne if (serverWeb.hasArg(nmStartHourKey)) { - LOGD("nmStartHourKey %s", String(serverWeb.arg(nmStartHourKey))); + // LOGD("nmStartHourKey %s", String(serverWeb.arg(nmStartHourKey))); // Serial.println(convertTimeToCron(serverWeb.arg(nmStartHourKey))); strncpy(configSys.nmStart, serverWeb.arg(nmStartHourKey).c_str(), sizeof(configSys.nmStart) - 1); configSys.nmStart[sizeof(configSys.nmStart) - 1] = '\0'; // Guarantee a null terminator at the end } if (serverWeb.hasArg(nmEndHourKey)) { - LOGD("nmEndHourKey %s", String(serverWeb.arg(nmEndHourKey))); + // LOGD("nmEndHourKey %s", String(serverWeb.arg(nmEndHourKey))); // Serial.println(convertTimeToCron(serverWeb.arg(nmEndHourKey))); strncpy(configSys.nmEnd, serverWeb.arg(nmEndHourKey).c_str(), sizeof(configSys.nmEnd) - 1); configSys.nmEnd[sizeof(configSys.nmEnd) - 1] = '\0'; // Guarantee a null terminator at the end } + if (serverWeb.hasArg(updCheckTimeKey)) + { + strncpy(configSys.updCheckTime, serverWeb.arg(updCheckTimeKey).c_str(), sizeof(configSys.updCheckTime) - 1); + configSys.updCheckTime[sizeof(configSys.updCheckTime) - 1] = '\0'; // Guarantee a null terminator at the end + } + + if (serverWeb.hasArg(updCheckDayKey)) + { + strncpy(configSys.updCheckDay, serverWeb.arg(updCheckDayKey).c_str(), sizeof(configSys.updCheckDay) - 1); + configSys.updCheckDay[sizeof(configSys.updCheckDay) - 1] = '\0'; // Guarantee a null terminator at the end + } + + configSys.updAutoInst = serverWeb.hasArg(updAutoInstKey) == true; + saveSystemConfig(configSys); } break; @@ -451,6 +562,16 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne configNet.wifiDhcp = serverWeb.hasArg(wifiDhcpKey) == true; + if (serverWeb.hasArg(wifiModeKey)) + { + configNet.wifiMode = serverWeb.arg(wifiModeKey).toInt(); + } + if (serverWeb.hasArg(wifiPwrKey)) + { + const uint8_t pwr = serverWeb.arg(wifiPwrKey).toInt(); + configNet.wifiPower = static_cast(pwr); + } + if (serverWeb.arg(wifiSsidKey)) { strncpy(configNet.wifiSsid, serverWeb.arg(wifiSsidKey).c_str(), sizeof(configNet.wifiSsid) - 1); @@ -489,7 +610,7 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne } saveNetworkConfig(configNet); - + serverWeb.send(HTTP_CODE_OK, contTypeText, "ok"); if (configNet.wifiEnable) { WiFi.persistent(false); @@ -507,13 +628,22 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne break; case API_PAGE_ZIGBEE: { + if (serverWeb.hasArg(coordMode)) + { + const uint8_t mode = serverWeb.arg(coordMode).toInt(); + if (mode <= 2 && mode >= 0) + { + configSys.workMode = static_cast(mode); + } + } + const char *baud = "baud"; if (serverWeb.hasArg(baud)) { configSys.serialSpeed = serverWeb.arg(baud).toInt(); } - if (serverWeb.hasArg(baud)) + if (serverWeb.hasArg(portKey)) { configSys.socketPort = serverWeb.arg(portKey).toInt(); } @@ -527,15 +657,32 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne configSys.webAuth = serverWeb.hasArg(webAuthKey) == true; + const char *defaultCreds = "admin"; + const char *defaultFwIp = "0.0.0.0"; + if (serverWeb.hasArg(webUserKey)) { - strncpy(configSys.webUser, serverWeb.arg(webUserKey).c_str(), sizeof(configSys.webUser) - 1); + if (serverWeb.arg(webUserKey).length() > 0) + { + strncpy(configSys.webUser, serverWeb.arg(webUserKey).c_str(), sizeof(configSys.webUser) - 1); + } + else + { + strncpy(configSys.webUser, defaultCreds, sizeof(configSys.webUser) - 1); + } configSys.webUser[sizeof(configSys.webUser) - 1] = '\0'; // Guarantee a null terminator at the end } if (serverWeb.hasArg(webPassKey)) { - strncpy(configSys.webPass, serverWeb.arg(webPassKey).c_str(), sizeof(configSys.webPass) - 1); + if (serverWeb.arg(webPassKey).length() > 0) + { + strncpy(configSys.webPass, serverWeb.arg(webPassKey).c_str(), sizeof(configSys.webPass) - 1); + } + else + { + strncpy(configSys.webPass, defaultCreds, sizeof(configSys.webPass) - 1); + } configSys.webPass[sizeof(configSys.webPass) - 1] = '\0'; // Guarantee a null terminator at the end } @@ -543,7 +690,26 @@ void updateConfiguration(WebServer &serverWeb, SystemConfigStruct &configSys, Ne if (serverWeb.hasArg(fwIpKey)) { - configSys.fwIp.fromString(serverWeb.arg(fwIpKey)); + if (serverWeb.arg(fwIpKey).length() > 0) + { + configSys.fwIp.fromString(serverWeb.arg(fwIpKey)); + } + else + { + configSys.fwIp.fromString(defaultFwIp); + } + } + + if (serverWeb.hasArg(fwMaskKey)) + { + if (serverWeb.arg(fwMaskKey).length() > 0) + { + configSys.fwMask.fromString(serverWeb.arg(fwMaskKey)); + } + else + { + configSys.fwMask.fromString(defaultFwIp); + } } saveSystemConfig(configSys); @@ -702,6 +868,8 @@ void serializeNetworkConfigToJson(const NetworkConfigStruct &config, JsonObject obj[wifiGateKey] = config.wifiGate.toString(); obj[wifiDns1Key] = config.wifiDns1.toString(); obj[wifiDns2Key] = config.wifiDns2.toString(); + obj[wifiPwrKey] = config.wifiPower; + obj[wifiModeKey] = config.wifiMode; obj[ethEnblKey] = config.ethEnable; obj[ethDhcpKey] = config.ethDhcp; obj[ethIpKey] = config.ethIp.toString(); @@ -754,13 +922,14 @@ void serializeMqttConfigToJson(const MqttConfigStruct &config, JsonObject obj) // Serialization SystemConfigStruct into JSON void serializeSystemConfigToJson(const SystemConfigStruct &config, JsonObject obj) { - obj[keepWebKey] = config.keepWeb; + // obj[keepWebKey] = config.keepWeb; obj[disableWebKey] = config.disableWeb; obj[webAuthKey] = config.webAuth; obj[webUserKey] = config.webUser; obj[webPassKey] = config.webPass; obj[fwEnabledKey] = config.fwEnabled; obj[fwIpKey] = config.fwIp.toString(); + obj[fwMaskKey] = config.fwMask.toString(); obj[serialSpeedKey] = config.serialSpeed; obj[socketPortKey] = config.socketPort; obj[tempOffsetKey] = config.tempOffset; @@ -776,6 +945,13 @@ void serializeSystemConfigToJson(const SystemConfigStruct &config, JsonObject ob obj[nmEndHourKey] = config.nmEnd; // obj[prevWorkModeKey] = static_cast(config.prevWorkMode); obj[workModeKey] = static_cast(config.workMode); + + obj[zbRoleKey] = static_cast(config.zbRole); + obj[zbFwKey] = config.zbFw; + + obj[updCheckTimeKey] = config.updCheckTime; + obj[updCheckDayKey] = config.updCheckDay; + obj[updAutoInstKey] = config.updAutoInst; } // Serializing system variables to JSON @@ -784,7 +960,7 @@ void serializeSysVarsToJson(const SysVarsStruct &vars, JsonObject obj) obj[hwBtnIsKey] = vars.hwBtnIs; obj[hwLedUsbIsKey] = vars.hwLedUsbIs; obj[hwLedPwrIsKey] = vars.hwLedPwrIs; - obj[hwUartSelIsKey] = vars.hwUartSelIs; + // obj[hwUartSelIsKey] = vars.hwUartSelIs; obj[hwZigbeeIsKey] = vars.hwZigbeeIs; obj[connectedClientsKey] = vars.connectedClients; @@ -805,128 +981,181 @@ void serializeSysVarsToJson(const SysVarsStruct &vars, JsonObject obj) obj[disableLedsKey] = vars.disableLeds; // obj[zbLedStateKey] = vars.zbLedState; - // obj[zbFlashingKey] = vars.zbFlashing; + obj[zbFlashingKey] = vars.zbFlashing; obj[deviceIdKey] = vars.deviceId; + + obj[espUpdAvailKey] = vars.updateEspAvail; + obj[rcpUpdAvailKey] = vars.updateZbAvail; +} + +void serializeHwConfigToJson(const ThisConfigStruct &config, JsonObject obj) +{ + obj[boardKey] = config.board; + obj[addrKey] = config.eth.addr; + obj[pwrPinKey] = config.eth.pwrPin; + obj[mdcPinKey] = config.eth.mdcPin; + obj[mdiPinKey] = config.eth.mdiPin; + obj[phyTypeKey] = config.eth.phyType; + obj[clkModeKey] = config.eth.clkMode; + // obj[pwrAltPin] = config.eth.pwrAltPin; + obj[btnPinKey] = config.mist.btnPin; + obj[btnPlrKey] = config.mist.btnPlr; + obj[uartSelPinKey] = config.mist.uartSelPin; + obj[uartSelPlrKey] = config.mist.uartSelPlr; + obj[ledModePinKey] = config.mist.ledModePin; + obj[ledModePlrKey] = config.mist.ledModePlr; + obj[ledPwrPinKey] = config.mist.ledPwrPin; + obj[ledPwrPlrKey] = config.mist.ledPwrPlr; + obj[zbTxPinKey] = config.zb.txPin; + obj[zbRxPinKey] = config.zb.rxPin; + obj[zbRstPinKey] = config.zb.rstPin; + obj[zbBslPinKey] = config.zb.bslPin; } -bool loadFileConfigHW() +bool loadFileConfigHW() // Support for old config HW files { - String tag = "HW"; - const char *board = "board"; - const char *addr = "addr"; - const char *pwrPin = "pwrPin"; - const char *mdcPin = "mdcPin"; - const char *mdiPin = "mdiPin"; - const char *phyType = "phyType"; - const char *clkMode = "clkMode"; - const char *pwrAltPin = "pwrAltPin"; - const char *btnPin = "btnPin"; - const char *btnPlr = "btnPlr"; - const char *uartSelPin = "uartSelPin"; - const char *uartSelPlr = "uartSelPlr"; - const char *ledModePin = "ledModePin"; - const char *ledModePlr = "ledModePlr"; - const char *ledPwrPin = "ledPwrPin"; - const char *ledPwrPlr = "ledPwrPlr"; - const char *zbTxPin = "zbTxPin"; - const char *zbRxPin = "zbRxPin"; - const char *zbRstPin = "zbRstPin"; - const char *zbBslPin = "zbBslPin"; File configFile = LittleFS.open(configFileHw, FILE_READ); - if (!configFile) + /*if (!configFile) { + if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED, "/lfs2", 10)) + { + LOGD("Error with LITTLEFS"); + } DynamicJsonDocument config(300); - config[board] = ""; + config[boardKey] = ""; writeDefaultConfig(configFileHw, config); configFile = LittleFS.open(configFileHw, FILE_READ); - } + }*/ DynamicJsonDocument config(1024); - deserializeJson(config, configFile); - configFile.close(); - - strlcpy(hwConfig.board, config[board] | "", sizeof(hwConfig.board)); - hwConfig.eth.addr = config[addr]; - hwConfig.eth.pwrPin = config[pwrPin]; - hwConfig.eth.mdcPin = config[mdcPin]; - hwConfig.eth.mdiPin = config[mdiPin]; - hwConfig.eth.phyType = config[phyType]; - hwConfig.eth.clkMode = config[clkMode]; - hwConfig.eth.pwrAltPin = config[pwrAltPin]; - hwConfig.mist.btnPin = config[btnPin]; - hwConfig.mist.btnPlr = config[btnPlr]; - hwConfig.mist.uartSelPin = config[uartSelPin]; - hwConfig.mist.uartSelPlr = config[uartSelPlr]; - hwConfig.mist.ledModePin = config[ledModePin]; - hwConfig.mist.ledModePlr = config[ledModePlr]; - hwConfig.mist.ledPwrPin = config[ledPwrPin]; - hwConfig.mist.ledPwrPlr = config[ledPwrPlr]; - hwConfig.zb.txPin = config[zbTxPin]; - hwConfig.zb.rxPin = config[zbRxPin]; - hwConfig.zb.rstPin = config[zbRstPin]; - hwConfig.zb.bslPin = config[zbBslPin]; - - if (hwConfig.board[0] != '\0' && strlen(hwConfig.board) > 0) + if (configFile) { - LOGD("LOAD - OK"); - return true; - } - else - { - LOGI("LOAD - ERROR"); - int searchId = 0; - if (config["searchId"]) + deserializeJson(config, configFile); + + configFile.close(); + + strlcpy(hwConfig.board, config[boardKey] | "", sizeof(hwConfig.board)); + hwConfig.eth.addr = config[addrKey]; + hwConfig.eth.pwrPin = config[pwrPinKey]; + hwConfig.eth.mdcPin = config[mdcPinKey]; + hwConfig.eth.mdiPin = config[mdiPinKey]; + hwConfig.eth.phyType = config[phyTypeKey]; + hwConfig.eth.clkMode = config[clkModeKey]; + if (hwConfig.eth.pwrPin == -1) { - searchId = config["searchId"]; + hwConfig.eth.pwrPin = config[pwrAltPinKey]; } - ThisConfigStruct *newConfig = findBrdConfig(searchId); - if (newConfig) + // hwConfig.eth.pwrAltPin = config[pwrAltPin]; + hwConfig.mist.btnPin = config[btnPinKey]; + hwConfig.mist.btnPlr = config[btnPlrKey]; + hwConfig.mist.uartSelPin = config[uartSelPinKey]; + hwConfig.mist.uartSelPlr = config[uartSelPlrKey]; + hwConfig.mist.ledModePin = config[ledModePinKey]; + hwConfig.mist.ledModePlr = config[ledModePlrKey]; + hwConfig.mist.ledPwrPin = config[ledPwrPinKey]; + hwConfig.mist.ledPwrPlr = config[ledPwrPlrKey]; + hwConfig.zb.txPin = config[zbTxPinKey]; + hwConfig.zb.rxPin = config[zbRxPinKey]; + hwConfig.zb.rstPin = config[zbRstPinKey]; + hwConfig.zb.bslPin = config[zbBslPinKey]; + + LOGD("Removing HW config file"); + LittleFS.remove(configFileHw); + if (hwConfig.board[0] != '\0' && strlen(hwConfig.board) > 0) { - LOGD("Find. Saving config"); - - DynamicJsonDocument config(512); - config[board] = newConfig->board; - config[addr] = newConfig->eth.addr; - config[pwrPin] = newConfig->eth.pwrPin; - config[mdcPin] = newConfig->eth.mdcPin; - config[mdiPin] = newConfig->eth.mdiPin; - config[phyType] = newConfig->eth.phyType; - config[clkMode] = newConfig->eth.clkMode; - config[pwrAltPin] = newConfig->eth.pwrAltPin; - config[btnPin] = newConfig->mist.btnPin; - config[btnPlr] = newConfig->mist.btnPlr; - config[uartSelPin] = newConfig->mist.uartSelPin; - config[uartSelPlr] = newConfig->mist.uartSelPlr; - config[ledModePin] = newConfig->mist.ledModePin; - config[ledModePlr] = newConfig->mist.ledModePlr; - config[ledPwrPin] = newConfig->mist.ledPwrPin; - config[ledPwrPlr] = newConfig->mist.ledPwrPlr; - config[zbTxPin] = newConfig->zb.txPin; - config[zbRxPin] = newConfig->zb.rxPin; - config[zbRstPin] = newConfig->zb.rstPin; - config[zbBslPin] = newConfig->zb.bslPin; - writeDefaultConfig(configFileHw, config); - - LOGD("Calc and save temp offset"); - float CPUtemp = getCPUtemp(true); - int offset = CPUtemp - 30; - systemCfg.tempOffset = int(offset); - saveSystemConfig(systemCfg); - - LOGD("Restarting..."); - ESP.restart(); + LOGD("Load HW - OK"); + saveHwConfig(hwConfig); + return true; + } + else + { + LOGI("Load HW - ERROR. File is empty"); + return false; } } - return false; + else + { + return false; + } +} +/* +if (hwConfig.board[0] != '\0' && strlen(hwConfig.board) > 0) +{ + delay(3000); + LOGD("Load HW - OK"); + saveHwConfig(hwConfig); + return true; } +else +{ + LOGI("Load HW - ERROR"); + + int searchId = 0; + if (config["searchId"]) + { + searchId = config["searchId"]; + } + + + String chipId = ESP.getChipModel(); + LOGW("%s", chipId.c_str()); + if (chipId == "ESP32-D0WDQ6") + { + searchId = 12; + } + + + ThisConfigStruct *newConfig = findBrdConfig(searchId); + if (newConfig) + { + LOGD("Find. Saving config"); + saveHwConfig(*newConfig); + + + DynamicJsonDocument config(512); + config[boardKey] = newConfig->board; + config[addrKey] = newConfig->eth.addr; + config[pwrPinKey] = newConfig->eth.pwrPin; + config[mdcPinKey] = newConfig->eth.mdcPin; + config[mdiPinKey] = newConfig->eth.mdiPin; + config[phyTypeKey] = newConfig->eth.phyType; + config[clkModeKey] = newConfig->eth.clkMode; + // config[pwrAltPin] = newConfig->eth.pwrAltPin; + config[btnPinKey] = newConfig->mist.btnPin; + config[btnPlrKey] = newConfig->mist.btnPlr; + config[uartSelPinKey] = newConfig->mist.uartSelPin; + config[uartSelPlrKey] = newConfig->mist.uartSelPlr; + config[ledModePinKey] = newConfig->mist.ledModePin; + config[ledModePlrKey] = newConfig->mist.ledModePlr; + config[ledPwrPinKey] = newConfig->mist.ledPwrPin; + config[ledPwrPlrKey] = newConfig->mist.ledPwrPlr; + config[zbTxPinKey] = newConfig->zb.txPin; + config[zbRxPinKey] = newConfig->zb.rxPin; + config[zbRstPinKey] = newConfig->zb.rstPin; + config[zbBslPinKey] = newConfig->zb.bslPin; + writeDefaultConfig(configFileHw, config); + + + LOGD("Calc and save temp offset"); + float CPUtemp = getCPUtemp(true); + int offset = CPUtemp - 30; + systemCfg.tempOffset = int(offset); + saveSystemConfig(systemCfg); + + restartDevice(); + } +} +return false; +*/ /* Previous firmware read config support. start */ +/* const char *msg_file_rm = "OK. Remove old file"; const char *msg_open_f = "Error. open failed"; @@ -942,7 +1171,7 @@ void fileReadError(DeserializationError error, const char *fileName) { fileContent += (char)configFile.read(); } - LOGI("%s - %s - %s", fileName, error.f_str(), fileContent.c_str()); + LOGI("%s - %s - %s", fileName, error.c_str(), fileContent.c_str()); configFile.close(); if (error == DeserializationError::EmptyInput) { @@ -951,12 +1180,13 @@ void fileReadError(DeserializationError error, const char *fileName) } } + bool loadFileSystemVar() { File configFile = LittleFS.open(configFileSystem, FILE_READ); if (!configFile) { - LOGD("%s %s", configFileSystem, msg_open_f); + // LOGD("%s %s", configFileSystem, msg_open_f); return false; } @@ -991,7 +1221,7 @@ bool loadFileConfigWifi() File configFile = LittleFS.open(configFileWifi, FILE_READ); if (!configFile) { - LOGD("%s %s", configFileWifi, msg_open_f); + // LOGD("%s %s", configFileWifi, msg_open_f); return false; } @@ -1030,7 +1260,7 @@ bool loadFileConfigEther() File configFile = LittleFS.open(configFileEther, FILE_READ); if (!configFile) { - LOGD("%s %s", configFileEther, msg_open_f); + // LOGD("%s %s", configFileEther, msg_open_f); return false; } @@ -1061,7 +1291,7 @@ bool loadFileConfigGeneral() File configFile = LittleFS.open(configFileGeneral, FILE_READ); if (!configFile) { - LOGD("%s %s", configFileGeneral, msg_open_f); + // LOGD("%s %s", configFileGeneral, msg_open_f); return false; } @@ -1091,7 +1321,7 @@ bool loadFileConfigGeneral() systemCfg.disableLedPwr = (uint8_t)doc[disableLedPwrKey]; systemCfg.disableLedUSB = (uint8_t)doc[disableLedUSBKey]; vars.disableLeds = (uint8_t)doc[disableLedsKey]; - systemCfg.keepWeb = (uint8_t)doc[keepWebKey]; + // systemCfg.keepWeb = (uint8_t)doc[keepWebKey]; strlcpy(systemCfg.timeZone, doc[timeZoneKey] | NTP_TIME_ZONE, sizeof(systemCfg.timeZone)); configFile.close(); @@ -1106,7 +1336,7 @@ bool loadFileConfigSecurity() File configFile = LittleFS.open(configFileSecurity, FILE_READ); if (!configFile) { - LOGD("%s %s", configFileSecurity, msg_open_f); + // LOGD("%s %s", configFileSecurity, msg_open_f); return false; } @@ -1134,6 +1364,7 @@ bool loadFileConfigSecurity() return true; } + bool loadFileConfigSerial() { const char *baud = "baud"; @@ -1141,7 +1372,7 @@ bool loadFileConfigSerial() File configFile = LittleFS.open(configFileSerial, FILE_READ); if (!configFile) { - LOGD("%s %s", configFileSerial, msg_open_f); + // LOGD("%s %s", configFileSerial, msg_open_f); return false; } @@ -1170,7 +1401,7 @@ bool loadFileConfigMqtt() File configFile = LittleFS.open(configFileMqtt, FILE_READ); if (!configFile) { - LOGD("%s %s", configFileMqtt, msg_open_f); + // LOGD("%s %s", configFileMqtt, msg_open_f); return false; } @@ -1212,7 +1443,7 @@ bool loadFileConfigWg() File configFile = LittleFS.open(configFileWg, FILE_READ); if (!configFile) { - LOGD("%s %s", configFileWg, msg_open_f); + // LOGD("%s %s", configFileWg, msg_open_f); return false; } @@ -1242,4 +1473,6 @@ bool loadFileConfigWg() return true; } +*/ + /* Previous firmware read config support. end */ \ No newline at end of file diff --git a/src/config.h b/src/config.h index a7a8cb50..34e5a9bc 100644 --- a/src/config.h +++ b/src/config.h @@ -8,13 +8,14 @@ #include #include #include +#include "const/hw.h" #define DEBOUNCE_TIME 70 #define MAX_DEV_ID_LONG 50 #define ZB_TCP_PORT 6638 // any port ever. later setup from config file #define ZB_SERIAL_SPEED 115200 -#define NTP_TIME_ZONE "Europe/Kiev" +#define NTP_TIME_ZONE "Europe/Berlin" #define NTP_SERV_1 "pool.ntp.org" #define NTP_SERV_2 "time.google.com" #define DNS_SERV_1 "1.1.1.1" @@ -23,6 +24,8 @@ #define NETWORK_ZERO "0.0.0.0" #define NM_START_TIME "23:00" #define NM_END_TIME "07:00" +#define UPD_CHK_TIME "01:00" +#define UPD_CHK_DAY "*" #define MAX_SOCKET_CLIENTS 5 @@ -34,7 +37,7 @@ #define NEED_BSL_PIN 15 // CC2652 pin number (FOR BSL VALIDATION!) #define NEED_BSL_LEVEL 1 // 0-ERROR 1-LOW 2-HIGH -const int16_t overseerInterval = 5 * 1000; // check lan or wifi connection every 5sec +const int16_t overseerInterval = 5; // check lan or wifi connection every 5sec const uint8_t overseerMaxRetry = 3; // 5x12 = 60sec delay for AP start enum WORK_MODE_t : uint8_t @@ -50,6 +53,14 @@ enum LED_t : uint8_t ZB_LED }; +enum ZB_ROLE_t : uint8_t +{ + UNDEFINED, + COORDINATOR, + ROUTER, + OPENTHREAD +}; + extern const char *coordMode; // coordMode node name extern const char *configFileSystem; extern const char *configFileWifi; @@ -66,14 +77,16 @@ struct SysVarsStruct bool hwBtnIs = false; bool hwLedUsbIs = false; bool hwLedPwrIs = false; - bool hwUartSelIs = false; + // bool hwUartSelIs = false; bool hwZigbeeIs = false; + bool oneWireIs = false; bool connectedSocket[MAX_SOCKET_CLIENTS]; //[10] int connectedClients; unsigned long socketTime; bool connectedEther = false; + bool ethIPv6 = false; bool apStarted = false; bool wifiWebSetupInProgress = false; @@ -90,11 +103,24 @@ struct SysVarsStruct bool disableLeds; // bool zbLedState; - // bool zbFlashing; + bool zbFlashing; char deviceId[MAX_DEV_ID_LONG]; bool updateEspAvail; + bool updateZbAvail; + + char lastESPVer[20]; + char lastZBVer[20]; + //IPAddress savedWifiDNS; + //IPAddress savedEthDNS; + + bool firstUpdCheck = false; + + uint32_t last1wAsk = 0; + float temp1w = 0; + + bool needFsDownload = false; }; // Network configuration structure @@ -102,8 +128,11 @@ struct NetworkConfigStruct { // Wi-Fi bool wifiEnable; + // int wifiPower; + wifi_power_t wifiPower; + int wifiMode; char wifiSsid[50]; - char wifiPass[50]; + char wifiPass[80]; bool wifiDhcp; IPAddress wifiIp; IPAddress wifiMask; @@ -175,7 +204,7 @@ void loadMqttConfig(MqttConfigStruct &config); struct SystemConfigStruct { - bool keepWeb; // when usb mode active + // bool keepWeb; // when usb mode active bool disableWeb; // when socket connected bool webAuth; @@ -183,7 +212,8 @@ struct SystemConfigStruct char webPass[50]; bool fwEnabled; // firewall for socket connection - IPAddress fwIp; // allowed IP + IPAddress fwIp; // allowed IP base + IPAddress fwMask; // allowed mask int serialSpeed; int socketPort; @@ -205,7 +235,14 @@ struct SystemConfigStruct char nmEnd[6]; // WORK_MODE_t prevWorkMode; // for button // WORK_MODE_t - WORK_MODE_t workMode; // for button // WORK_MODE_t + WORK_MODE_t workMode; + + ZB_ROLE_t zbRole; + char zbFw[30]; + + char updCheckTime[6]; + char updCheckDay[3]; + bool updAutoInst; }; // Function prototypes for SystemConfigStruct @@ -218,6 +255,7 @@ void serializeVpnConfigToJson(const VpnConfigStruct &config, JsonObject obj); void serializeMqttConfigToJson(const MqttConfigStruct &config, JsonObject obj); void serializeSystemConfigToJson(const SystemConfigStruct &config, JsonObject obj); void serializeSysVarsToJson(const SysVarsStruct &vars, JsonObject obj); +void serializeHwConfigToJson(const ThisConfigStruct &config, JsonObject obj); void updateConfiguration(WebServer &server, SystemConfigStruct &configSys, NetworkConfigStruct &configNet, VpnConfigStruct &configVpn, MqttConfigStruct &configMqtt); @@ -232,11 +270,16 @@ String makeJsonConfig(const NetworkConfigStruct *networkCfg = nullptr, const VpnConfigStruct *vpnCfg = nullptr, const MqttConfigStruct *mqttCfg = nullptr, const SystemConfigStruct *systemCfg = nullptr, - const SysVarsStruct *systemVars = nullptr); + const SysVarsStruct *systemVars = nullptr, + const ThisConfigStruct *hwCfg = nullptr); bool loadFileConfigHW(); +void saveHwConfig(const ThisConfigStruct &config); +void loadHwConfig(ThisConfigStruct &config); + /* Previous firmware read config support. start */ +/* bool loadFileSystemVar(); bool loadFileConfigWifi(); bool loadFileConfigEther(); @@ -245,6 +288,7 @@ bool loadFileConfigSecurity(); bool loadFileConfigSerial(); bool loadFileConfigMqtt(); bool loadFileConfigWg(); +*/ /* Previous firmware read config support. end */ /* ----- Define functions | START -----*/ @@ -274,16 +318,16 @@ uint8_t temprature_sens_read(); // Set the current logging level here #define CURRENT_LOG_LEVEL LOG_LEVEL_DEBUG -#define DEBUG_PRINT(x) Serial.print(String(x)) -#define DEBUG_PRINTLN(x) Serial.println(String(x)) +//#define DEBUG_PRINT(x) Serial.print(String(x)) +//#define DEBUG_PRINTLN(x) Serial.println(String(x)) #else // Set the current logging level here #define CURRENT_LOG_LEVEL LOG_LEVEL_INFO -#define DEBUG_PRINT(x) -#define DEBUG_PRINTLN(x) +//#define DEBUG_PRINT(x) +//#define DEBUG_PRINTLN(x) #endif #endif @@ -297,19 +341,31 @@ uint8_t temprature_sens_read(); // Conditional logging macros #if CURRENT_LOG_LEVEL >= LOG_LEVEL_WARN -#define LOGW(format, ...) Serial.printf(ANSI_COLOR_PURPLE "%d " ANSI_COLOR_RESET ANSI_COLOR_RED "[%s] " ANSI_COLOR_RESET format "\n", millis(), __func__, ##__VA_ARGS__) +#define LOGW(format, ...) \ + if (systemCfg.workMode == WORK_MODE_NETWORK) \ + { \ + Serial.printf(ANSI_COLOR_PURPLE "%lu " ANSI_COLOR_RESET ANSI_COLOR_RED "[%s] " ANSI_COLOR_RESET format "\n", millis(), __func__, ##__VA_ARGS__); \ + } #else #define LOGW(format, ...) // Nothing #endif #if CURRENT_LOG_LEVEL >= LOG_LEVEL_INFO -#define LOGI(format, ...) Serial.printf(ANSI_COLOR_PURPLE "%d " ANSI_COLOR_RESET ANSI_COLOR_GREEN "[%s] " ANSI_COLOR_RESET format "\n", millis(), __func__, ##__VA_ARGS__) +#define LOGI(format, ...) \ + if (systemCfg.workMode == WORK_MODE_NETWORK) \ + { \ + Serial.printf(ANSI_COLOR_PURPLE "%lu " ANSI_COLOR_RESET ANSI_COLOR_GREEN "[%s] " ANSI_COLOR_RESET format "\n", millis(), __func__, ##__VA_ARGS__); \ + } #else #define LOGI(format, ...) // Nothing #endif #if CURRENT_LOG_LEVEL >= LOG_LEVEL_DEBUG -#define LOGD(format, ...) Serial.printf(ANSI_COLOR_PURPLE "%d " ANSI_COLOR_RESET ANSI_COLOR_YELLOW "[%s] " ANSI_COLOR_RESET format "\n", millis(), __func__, ##__VA_ARGS__) +#define LOGD(format, ...) \ + if (systemCfg.workMode == WORK_MODE_NETWORK) \ + { \ + Serial.printf(ANSI_COLOR_PURPLE "%lu " ANSI_COLOR_RESET ANSI_COLOR_YELLOW "[%s] " ANSI_COLOR_RESET format "\n", millis(), __func__, ##__VA_ARGS__); \ + } #else #define LOGD(format, ...) // Nothing #endif @@ -343,8 +399,14 @@ struct LEDControl LEDSettings powerLED; }; -enum usbMode +enum usbMode : uint8_t { XZG, ZIGBEE }; + +enum updInfoType : uint8_t +{ + UPD_ESP, + UPD_ZB +}; \ No newline at end of file diff --git a/src/const/hw.cpp b/src/const/hw.cpp index f47991f9..d5c57532 100644 --- a/src/const/hw.cpp +++ b/src/const/hw.cpp @@ -1,46 +1,51 @@ #include "const/hw.h" // Ethernet configurations -// !!! Don't forget to edit ETH_CFG_CNT !!! +// Don't forget to edit ETH_CFG_CNT ! EthConfig ethConfigs[] = { - {.addr = 0, .pwrPin = 12, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO17_OUT, .pwrAltPin = -1}, // 0 Olimex-ESP32-POE - {.addr = 1, .pwrPin = 16, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO0_IN, .pwrAltPin = -1}, // 1 WT32-ETH01 - {.addr = 0, .pwrPin = -1, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO17_OUT, .pwrAltPin = 5}, // 2 T-Internet-POE + {.addr = 0, .pwrPin = 12, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO17_OUT}, // .pwrAltPin = -1}, // 0 Olimex-ESP32-POE + {.addr = 1, .pwrPin = 16, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO0_IN}, // .pwrAltPin = -1}, // 1 WT32-ETH01 / SLZB-06 + {.addr = 0, .pwrPin = 5, .mdcPin = 23, .mdiPin = 18, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO17_OUT}, // .pwrAltPin = -1}, // 2 T-Internet-POE / UZG-01 / HamGeek POE Plus }; // ZigBee configurations -// !!! Don't forget to edit ZB_CFG_CNT !!! +// Don't forget to edit ZB_CFG_CNT ! ZbConfig zbConfigs[] = { - {.txPin = 4, .rxPin = 36, .rstPin = 16, .bslPin = 32}, // 0 - {.txPin = 17, .rxPin = 5, .rstPin = 33, .bslPin = 32}, // 1 - {.txPin = 33, .rxPin = 32, .rstPin = 12, .bslPin = 14}, // 2 - {.txPin = 4, .rxPin = 36, .rstPin = 5, .bslPin = 16}, // 3 - {.txPin = 5, .rxPin = 17, .rstPin = 33, .bslPin = 32}, // 4 - {.txPin = 16, .rxPin = 5, .rstPin = 33, .bslPin = 32}, // 5 - {.txPin = 16, .rxPin = 5, .rstPin = 13, .bslPin = 4}, // 6 - {.txPin = 4, .rxPin = 36, .rstPin = 5, .bslPin = 16}, // 7 + {.txPin = 4, .rxPin = 36, .rstPin = 16, .bslPin = 32}, // 0 UZG-01 / LilyZig / Olizig + {.txPin = 17, .rxPin = 5, .rstPin = 33, .bslPin = 32}, // 1 ZigStar LAN / SLZB-06 / TubesZB-eth + {.txPin = 33, .rxPin = 32, .rstPin = 12, .bslPin = 14}, // 2 No name China-GW + {.txPin = 5, .rxPin = 17, .rstPin = 33, .bslPin = 32}, // 3 TubesZB-eth_usb + {.txPin = 16, .rxPin = 5, .rstPin = 33, .bslPin = 32}, // 4 TubesZB-poe + {.txPin = 16, .rxPin = 5, .rstPin = 13, .bslPin = 4}, // 5 TubesZB-poe-2022 + {.txPin = 4, .rxPin = 36, .rstPin = 5, .bslPin = 16}, // 6 TubesZB-poe-2023 + {.txPin = 23, .rxPin = 22, .rstPin = 18, .bslPin = 19}, // 7 SLS-classic }; // Mist configurations -// !!! Don't forget to edit MIST_CFG_CNT !!! +// Don't forget to edit MIST_CFG_CNT ! MistConfig mistConfigs[] = { - {.btnPin = -1, .btnPlr = 0, .uartSelPin = -1, .uartSelPlr = 0, .ledModePin = -1, .ledModePlr = 0, .ledPwrPin = -1, .ledPwrPlr = 0}, // 0 - {.btnPin = 35, .btnPlr = 1, .uartSelPin = 33, .uartSelPlr = 1, .ledModePin = 12, .ledModePlr = 1, .ledPwrPin = 14, .ledPwrPlr = 1}, // 1 - {.btnPin = 35, .btnPlr = 1, .uartSelPin = 4, .uartSelPlr = 1, .ledModePin = 12, .ledModePlr = 1, .ledPwrPin = 14, .ledPwrPlr = 1}, // 2 + {.btnPin = -1, .btnPlr = 0, .uartSelPin = -1, .uartSelPlr = 0, .ledModePin = -1, .ledModePlr = 0, .ledPwrPin = -1, .ledPwrPlr = 0}, // 0 No mist cfg + {.btnPin = 35, .btnPlr = 1, .uartSelPin = 33, .uartSelPlr = 1, .ledModePin = 12, .ledModePlr = 1, .ledPwrPin = 14, .ledPwrPlr = 1}, // 1 UZG-01 / CZC-1.0 + {.btnPin = 35, .btnPlr = 1, .uartSelPin = 4, .uartSelPlr = 1, .ledModePin = 12, .ledModePlr = 1, .ledPwrPin = 14, .ledPwrPlr = 1}, // 2 SLZB-06 + {.btnPin = 33, .btnPlr = 1, .uartSelPin = -1, .uartSelPlr = 0, .ledModePin = -1, .ledModePlr = 0, .ledPwrPin = -1, .ledPwrPlr = 0}, // 3 SLS-classic + {.btnPin = 14, .btnPlr = 1, .uartSelPin = -1, .uartSelPlr = 0, .ledModePin = -1, .ledModePlr = 0, .ledPwrPin = -1, .ledPwrPlr = 0}, // 4 T-Internet-POE }; // Board configurations -// !!! Don't forget to edit BOARD_CFG_CNT !!! +// Don't forget to edit BOARD_CFG_CNT ! BrdConfigStruct brdConfigs[] = { - {"UZG-01", .ethConfigIndex = 2, .zbConfigIndex = 0, .mistConfigIndex = 1}, - {"SLZB-06", .ethConfigIndex = 1, .zbConfigIndex = 1, .mistConfigIndex = 2}, - {"WT32-ETH01", .ethConfigIndex = 1, .zbConfigIndex = 1, .mistConfigIndex = 0}, - {"T-Internet-POE", .ethConfigIndex = 2, .zbConfigIndex = 0, .mistConfigIndex = 0}, - {"Olimex-ESP32-POE", .ethConfigIndex = 0, .zbConfigIndex = 0, .mistConfigIndex = 0}, - {"China-GW", .ethConfigIndex = 0, .zbConfigIndex = 2, .mistConfigIndex = 0}, - {"TubesZB-eth", .ethConfigIndex = 1, .zbConfigIndex = 1, .mistConfigIndex = 0}, - {"TubesZB-eth_usb", .ethConfigIndex = 1, .zbConfigIndex = 4, .mistConfigIndex = 0}, - {"TubesZB-poe", .ethConfigIndex = 0, .zbConfigIndex = 5, .mistConfigIndex = 0}, - {"TubesZB-poe-2022", .ethConfigIndex = 0, .zbConfigIndex = 6, .mistConfigIndex = 0}, - {"TubesZB-poe-2023", .ethConfigIndex = 0, .zbConfigIndex = 7, .mistConfigIndex = 0}, -}; + {"SLS-classic", -1, 7, 3}, // 0 + {"UZG-01", 2, 0, 1}, // 1 + {"SLZB-06", 1, 1, 2}, // 2 + {"ZigStar LAN", 1, 1, 0}, // 3 + {"LilyZig", 2, 0, 4}, // 4 + {"Olizig", 0, 0, 0}, // 5 + {"China-GW", 0, 2, 0}, // 6 + {"TubesZB-eth", 1, 1, 0}, // 7 + {"TubesZB-eth_usb", 1, 3, 0}, // 8 + {"TubesZB-poe", 0, 4, 0}, // 9 + {"TubesZB-poe-2022", 0, 5, 0}, // 10 + {"TubesZB-poe-2023", 0, 6, 0}, // 11 + {"CZC-1.0", 2, 0, 1}, // 12 + {"HG POE Plus", 2, 0, 1}, // 13 +}; \ No newline at end of file diff --git a/src/const/hw.h b/src/const/hw.h index 357307b0..15058231 100644 --- a/src/const/hw.h +++ b/src/const/hw.h @@ -1,3 +1,6 @@ +#ifndef HW_H +#define HW_H + #include // Ethernet settings structure @@ -9,7 +12,7 @@ struct EthConfig int mdiPin; eth_phy_type_t phyType; eth_clock_mode_t clkMode; - int pwrAltPin; + //int pwrAltPin; }; // ZigBee settings structure @@ -45,8 +48,8 @@ struct BrdConfigStruct #define ETH_CFG_CNT 3 #define ZB_CFG_CNT 8 -#define MIST_CFG_CNT 3 -#define BOARD_CFG_CNT 13 +#define MIST_CFG_CNT 5 +#define BOARD_CFG_CNT 14 struct ThisConfigStruct { @@ -54,4 +57,6 @@ struct ThisConfigStruct EthConfig eth; ZbConfig zb; MistConfig mist; -}; \ No newline at end of file +}; + +#endif // HW_H \ No newline at end of file diff --git a/src/const/keys.cpp b/src/const/keys.cpp index a7d2d4ef..9290d775 100644 --- a/src/const/keys.cpp +++ b/src/const/keys.cpp @@ -10,6 +10,9 @@ const char *wifiMaskKey = "wifiMask"; const char *wifiGateKey = "wifiGate"; const char *wifiDns1Key = "wifiDns1"; const char *wifiDns2Key = "wifiDns2"; +const char *wifiPwrKey = "wifiPwr"; +const char *wifiModeKey = "wifiMode"; +const char *wifiIPv6Key = "wifiIPv6"; const char *ethEnblKey = "ethEnbl"; const char *ethDhcpKey = "ethDhcp"; const char *ethIpKey = "ethIp"; @@ -17,6 +20,7 @@ const char *ethMaskKey = "ethMask"; const char *ethGateKey = "ethGate"; const char *ethDns1Key = "ethDns1"; const char *ethDns2Key = "ethDns2"; +const char *ethIPv6Key = "ethIPv6"; const char *vpnConfigKey = "vpn-config"; const char *wgEnableKey = "wgEnable"; @@ -51,13 +55,14 @@ const char *discoveryKey = "discovery"; const char *reconnectIntKey = "reconnectInt"; const char *systemConfigKey = "system-config"; -const char *keepWebKey = "keepWeb"; +// const char *keepWebKey = "keepWeb"; const char *disableWebKey = "disableWeb"; const char *webAuthKey = "webAuth"; const char *webUserKey = "webUser"; const char *webPassKey = "webPass"; const char *fwEnabledKey = "fwEnabled"; const char *fwIpKey = "fwIp"; +const char *fwMaskKey = "fwMask"; const char *serialSpeedKey = "serialSpeed"; const char *socketPortKey = "socketPort"; const char *tempOffsetKey = "tempOffset"; @@ -73,11 +78,15 @@ const char *nmStartHourKey = "startHour"; const char *nmEndHourKey = "endHour"; const char *nmEnableKey = "nightMode"; +const char *updCheckTimeKey = "updHour"; +const char *updCheckDayKey = "updDays"; +const char *updAutoInstKey = "autoIns"; + const char *systemVarsKey = "system-vars"; const char *hwBtnIsKey = "hwBtnIs"; const char *hwLedUsbIsKey = "hwLedUsbIs"; const char *hwLedPwrIsKey = "hwLedPwrIs"; -const char *hwUartSelIsKey = "hwUartSelIs"; +// const char *hwUartSelIsKey = "hwUartSelIs"; const char *hwZigbeeIsKey = "hwZigbeeIs"; const char *workModeKey = "workMode"; const char *connectedSocketKey = "connectedSocket"; @@ -94,8 +103,41 @@ const char *vpnHnInitKey = "vpnHnInit"; const char *mqttConnKey = "mqttConn"; const char *mqttReconnectTimeKey = "mqttReconnectTime"; const char *mqttHeartbeatTimeKey = "mqttHeartbeatTime"; -//const char *zbLedStateKey = "zbLedState"; -//const char *zbFlashingKey = "zbFlashing"; +// const char *zbLedStateKey = "zbLedState"; +const char *zbFlashingKey = "zbFlashing"; const char *deviceIdKey = "deviceId"; +const char *timeZoneNameKey = "timeZoneName"; +const char *zbRoleKey = "zbRole"; +const char *zbFwKey = "zbFw"; +const char *rcpUpdAvailKey = "rcpUpdAvail"; +const char *espUpdAvailKey = "espUpdAvail"; + +const char *tagZB_FW_info = "zb.fi"; +const char *tagZB_FW_file = "zb.ff"; +const char *tagZB_FW_err = "zb.fe"; +const char *tagZB_FW_prgs = "zb.fp"; +const char *tagZB_NV_prgs = "zb.nv"; +const char *tagESP_FW_prgs = "esp.fp"; -const char *timeZoneNameKey = "timeZoneName"; \ No newline at end of file +const char *hwConfigKey = "hw-config"; +const char *boardKey = "board"; +const char *addrKey = "addr"; +const char *pwrPinKey = "pwrPin"; +const char *mdcPinKey = "mdcPin"; +const char *mdiPinKey = "mdiPin"; +const char *phyTypeKey = "phyType"; +const char *clkModeKey = "clkMode"; +const char *pwrAltPinKey = "pwrAltPin"; +const char *btnPinKey = "btnPin"; +const char *btnPlrKey = "btnPlr"; +const char *uartSelPinKey = "uartSelPin"; +const char *uartSelPlrKey = "uartSelPlr"; +const char *ledModePinKey = "ledModePin"; +const char *ledModePlrKey = "ledModePlr"; +const char *ledPwrPinKey = "ledPwrPin"; +const char *ledPwrPlrKey = "ledPwrPlr"; +const char *zbTxPinKey = "zbTxPin"; +const char *zbRxPinKey = "zbRxPin"; +const char *zbRstPinKey = "zbRstPin"; +const char *zbBslPinKey = "zbBslPin"; +const char *contTypeText = "text/plain"; \ No newline at end of file diff --git a/src/const/keys.h b/src/const/keys.h index 567abc26..1496d3c3 100644 --- a/src/const/keys.h +++ b/src/const/keys.h @@ -12,6 +12,9 @@ extern const char *wifiMaskKey; extern const char *wifiGateKey; extern const char *wifiDns1Key; extern const char *wifiDns2Key; +extern const char *wifiPwrKey; +extern const char *wifiModeKey; +extern const char *wifiIPv6Key; extern const char *ethEnblKey; extern const char *ethDhcpKey; extern const char *ethIpKey; @@ -19,6 +22,7 @@ extern const char *ethMaskKey; extern const char *ethGateKey; extern const char *ethDns1Key; extern const char *ethDns2Key; +extern const char *ethIPv6Key; extern const char *vpnConfigKey; extern const char *wgEnableKey; extern const char *wgLocalIPKey; @@ -56,6 +60,7 @@ extern const char *webUserKey; extern const char *webPassKey; extern const char *fwEnabledKey; extern const char *fwIpKey; +extern const char *fwMaskKey; extern const char *serialSpeedKey; extern const char *socketPortKey; extern const char *tempOffsetKey; @@ -70,6 +75,9 @@ extern const char *ntpServ2Key; extern const char *nmStartHourKey; extern const char *nmEndHourKey; extern const char *nmEnableKey; +extern const char *updCheckTimeKey; +extern const char *updCheckDayKey; +extern const char *updAutoInstKey; extern const char *systemVarsKey; extern const char *hwBtnIsKey; extern const char *hwLedUsbIsKey; @@ -95,5 +103,37 @@ extern const char *zbLedStateKey; extern const char *zbFlashingKey; extern const char *deviceIdKey; extern const char *timeZoneNameKey; +extern const char *zbRoleKey; +extern const char *zbFwKey; +extern const char *rcpUpdAvailKey; +extern const char *espUpdAvailKey; +extern const char *tagZB_FW_info; +extern const char *tagZB_FW_file; +extern const char *tagZB_FW_err; +extern const char *tagZB_FW_prgs; +extern const char *tagZB_NV_prgs; +extern const char *tagESP_FW_prgs; +extern const char *hwConfigKey; +extern const char *boardKey; +extern const char *addrKey; +extern const char *pwrPinKey; +extern const char *mdcPinKey; +extern const char *mdiPinKey; +extern const char *phyTypeKey; +extern const char *clkModeKey; +extern const char *pwrAltPinKey; +extern const char *btnPinKey; +extern const char *btnPlrKey; +extern const char *uartSelPinKey; +extern const char *uartSelPlrKey; +extern const char *ledModePinKey; +extern const char *ledModePlrKey; +extern const char *ledPwrPinKey; +extern const char *ledPwrPlrKey; +extern const char *zbTxPinKey; +extern const char *zbRxPinKey; +extern const char *zbRstPinKey; +extern const char *zbBslPinKey; +extern const char *contTypeText; #endif // XZG_KEYS_H diff --git a/src/etc.cpp b/src/etc.cpp index 7bbc6e49..8da401db 100644 --- a/src/etc.cpp +++ b/src/etc.cpp @@ -16,6 +16,7 @@ #include "const/zones.h" // #include "const/hw.h" #include "zb.h" +#include "main.h" #include static WireGuard wg; @@ -41,14 +42,14 @@ extern CCTools CCTool; const char *coordMode = "coordMode"; // coordMode node name ?? not name but text field with mode // const char *prevCoordMode = "prevCoordMode"; // prevCoordMode node name ?? not name but text field with mode -const char *configFileSystem = "/config/system.json"; +/*const char *configFileSystem = "/config/system.json"; const char *configFileWifi = "/config/configWifi.json"; const char *configFileEther = "/config/configEther.json"; const char *configFileGeneral = "/config/configGeneral.json"; const char *configFileSecurity = "/config/configSecurity.json"; const char *configFileSerial = "/config/configSerial.json"; const char *configFileMqtt = "/config/configMqtt.json"; -const char *configFileWg = "/config/configWg.json"; +const char *configFileWg = "/config/configWg.json";*/ const char *configFileHw = "/configHw.json"; #include "mbedtls/md.h" @@ -88,6 +89,106 @@ String sha1(String payloadStr) return hashStr; } +// #include +// #include + +// OneWire *oneWire = nullptr; +// DallasTemperature *sensor = nullptr; +#include "DS18B20.h" + +OneWire *oneWire = nullptr; +DS18B20 *sensor = nullptr; + +int check1wire() +{ + int pin = -1; + vars.oneWireIs = false; + if (hwConfig.eth.mdcPin != 33 && hwConfig.eth.mdiPin != 33 && hwConfig.eth.pwrPin != 33) + { + if (hwConfig.zb.rxPin != 33 && hwConfig.zb.txPin != 33 && hwConfig.zb.bslPin != 33 && hwConfig.zb.rstPin != 33) + { + if (hwConfig.mist.btnPin != 33 && hwConfig.mist.uartSelPin != 33 && hwConfig.mist.ledModePin != 33 && hwConfig.mist.ledPwrPin != 33) + { + pin = 33; + vars.oneWireIs = true; + } + } + } + return pin; +} + +void setup1wire(int pin) +{ + + if (pin > 0) + { + if (oneWire != nullptr) + { + delete oneWire; + } + if (sensor != nullptr) + { + delete sensor; + } + + oneWire = new OneWire(pin); + sensor = new DS18B20(oneWire); + + sensor->begin(); + sensor->setResolution(10); + +#ifdef DEBUG + uint32_t start, stop; + + start = micros(); + sensor->requestTemperatures(); + + int n = 0; + // wait until sensor ready, do some counting for fun. + while (!sensor->isConversionComplete()) + n++; + + stop = micros(); + LOGD("Convert %lu\t%d", stop - start, n); + + // delay(100); + start = micros(); + float f = sensor->getTempC(); + stop = micros(); + + LOGD("getTemp %lu\t%.2f", stop - start, f); +#endif + + get1wire(); + } +} + +float get1wire() +{ + + if (sensor == nullptr) + { + LOGW("1w not init"); + vars.oneWireIs = false; + return -127.0; + } + if (millis() - vars.last1wAsk > 5000) + { + sensor->requestTemperatures(); + vars.temp1w = sensor->getTempC(); + LOGD("Temp is %f", vars.temp1w); + vars.last1wAsk = millis(); + } + + if (vars.temp1w == -127) + { + vars.oneWireIs = false; + LOGW("1w not connected"); + } + + return vars.temp1w; +} + void getReadableTime(String &readableTime, unsigned long beginTime) { unsigned long currentMillis; @@ -167,6 +268,11 @@ void zigbeeEnableBSL() printLogMsg("ZB enable BSL"); CCTool.enterBSL(); printLogMsg("Now you can flash CC2652!"); + if (systemCfg.workMode == WORK_MODE_USB) + { + Serial.updateBaudRate(500000); + Serial2.updateBaudRate(500000); + } } void zigbeeRestart() @@ -174,30 +280,44 @@ void zigbeeRestart() printLogMsg("ZB RST begin"); CCTool.restart(); printLogMsg("ZB restart was done"); + if (systemCfg.workMode == WORK_MODE_USB) + { + Serial.updateBaudRate(systemCfg.serialSpeed); + Serial2.updateBaudRate(systemCfg.serialSpeed); + } } void usbModeSet(usbMode mode) { - if (vars.hwUartSelIs) + // if (vars.hwUartSelIs) + //{ + // String modeStr = (mode == ZIGBEE) ? "ZIGBEE" : "ESP"; + bool pinValue = (mode == ZIGBEE) ? HIGH : LOW; + // String msg = "Switched USB to " + modeStr + ""; + // printLogMsg(msg); + + if (mode == ZIGBEE) { - String modeStr = (mode == ZIGBEE) ? "ZIGBEE" : "ESP"; - bool pinValue = (mode == ZIGBEE) ? HIGH : LOW; - String msg = "Switched USB to " + modeStr + ""; - printLogMsg(msg); - digitalWrite(hwConfig.mist.uartSelPin, pinValue); - if (pinValue) - { - ledControl.modeLED.mode = LED_ON; - } - else - { - ledControl.modeLED.mode = LED_OFF; - } + Serial.updateBaudRate(systemCfg.serialSpeed); } else { - LOGD("NO vars.hwUartSelIs"); + Serial.updateBaudRate(115200); } + // digitalWrite(hwConfig.mist.uartSelPin, pinValue); + if (pinValue) + { + ledControl.modeLED.mode = LED_ON; + } + else + { + ledControl.modeLED.mode = LED_OFF; + } + //} + // else + //{ + // LOGD("NO vars.hwUartSelIs"); + //} } void getDeviceID(char *arr) @@ -230,10 +350,10 @@ void getDeviceID(char *arr) // sprintf(arr, "%s-%s", hwConfig.board, buf); String devicePref = "XZG"; // hwConfig.board - snprintf(arr, MAX_DEV_ID_LONG, "%s-%s", devicePref, buf); + snprintf(arr, MAX_DEV_ID_LONG + 1, "%s-%s", devicePref.c_str(), buf); } -void writeDefaultConfig(const char *path, DynamicJsonDocument &doc) +/*void writeDefaultConfig(const char *path, DynamicJsonDocument &doc) { LOGD("Write defaults to %s", path); serializeJsonPretty(doc, Serial); @@ -244,8 +364,7 @@ void writeDefaultConfig(const char *path, DynamicJsonDocument &doc) if (LittleFS.mkdir(path)) { LOGD("Config dir created"); - delay(500); - ESP.restart(); + estartDevice(); } else { @@ -258,7 +377,7 @@ void writeDefaultConfig(const char *path, DynamicJsonDocument &doc) serializeJsonPretty(doc, configFile); } configFile.close(); -} +}*/ void factoryReset() { @@ -268,37 +387,41 @@ void factoryReset() ledControl.powerLED.mode = LED_FLASH_3Hz; ledControl.modeLED.mode = LED_FLASH_3Hz; - for (uint8_t i = 0; i < TIMEOUT_FACTORY_RESET; i++) + /*for (uint8_t i = 0; i < TIMEOUT_FACTORY_RESET; i++) { LOGD("%d, sec", TIMEOUT_FACTORY_RESET - i); delay(1000); - } + }*/ + /*LittleFS.format(); if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED, "/lfs2", 10)) // change to format anyway { LOGD("Error with LITTLEFS"); - } - LittleFS.remove(configFileSerial); + }*/ + + /*LittleFS.remove(configFileSerial); LittleFS.remove(configFileSecurity); LittleFS.remove(configFileGeneral); LittleFS.remove(configFileEther); LittleFS.remove(configFileWifi); LittleFS.remove(configFileSystem); - LittleFS.remove(configFileWg); - LittleFS.remove(configFileHw); LittleFS.remove(configFileMqtt); - LOGD("FS Done"); + LittleFS.remove(configFileWg);*/ + // LittleFS.remove(configFileHw); + + // LOGD("FS Done"); + eraseNVS(); LOGD("NVS Done"); ledControl.powerLED.mode = LED_OFF; ledControl.modeLED.mode = LED_OFF; - delay(500); - ESP.restart(); + restartDevice(); } void setClock(void *pvParameters) { + // checkDNS(); configTime(0, 0, systemCfg.ntpServ1, systemCfg.ntpServ2); const time_t targetTime = 946684800; // 946684800 - is 01.01.2000 in timestamp @@ -321,7 +444,7 @@ void setClock(void *pvParameters) { // LOGD("Current GMT time: %s", String(asctime(&timeinfo)).c_str()); - char *zoneToFind = const_cast("Europe/Kiev"); + char *zoneToFind = const_cast(NTP_TIME_ZONE); if (systemCfg.timeZone) { zoneToFind = systemCfg.timeZone; @@ -354,7 +477,7 @@ void setLedsDisable(bool all) { if (vars.hwLedPwrIs || vars.hwLedUsbIs) { - LOGD("setLedsDisable", "%s", String(all)); + LOGD("[setLedsDisable] %s", String(all).c_str()); if (all) { ledControl.powerLED.active = false; @@ -379,12 +502,128 @@ void nmDeactivate() LOGD("end"); setLedsDisable(); } +/* +bool checkDNS(bool setup) +{ + const char *wifiKey = "WiFi"; + const char *ethKey = "ETH"; + const char *savedKey = "Saved"; + const char *restoredKey = "Restored"; + const char *dnsTagKey = "[DNS]"; + char buffer[100]; + + if (networkCfg.wifiEnable) + { + IPAddress currentWifiDNS = WiFi.dnsIP(); + if (currentWifiDNS != vars.savedWifiDNS) + { + char dnsStrW[16]; + snprintf(dnsStrW, sizeof(dnsStrW), "%u.%u.%u.%u", currentWifiDNS[0], currentWifiDNS[1], currentWifiDNS[2], currentWifiDNS[3]); + + int lastDot = -1; + for (int i = 0; dnsStrW[i] != '\0'; i++) + { + if (dnsStrW[i] == '.') + { + lastDot = i; + } + } + + int fourthPartW = atoi(dnsStrW + lastDot + 1); + + if (setup && fourthPartW != 0) + { + vars.savedWifiDNS = currentWifiDNS; + snprintf(buffer, sizeof(buffer), "%s %s %s - %s", dnsTagKey, savedKey, wifiKey, dnsStrW); + printLogMsg(buffer); + } + else + { + if (vars.savedWifiDNS) + { + WiFi.config(WiFi.localIP(), WiFi.gatewayIP(), WiFi.subnetMask(), vars.savedWifiDNS); + snprintf(buffer, sizeof(buffer), "%s %s %s - %s", dnsTagKey, restoredKey, wifiKey, vars.savedWifiDNS.toString().c_str()); + printLogMsg(buffer); + } + } + } + } + + if (networkCfg.ethEnable) + { + IPAddress currentEthDNS = ETH.dnsIP(); + if (currentEthDNS != vars.savedEthDNS) + { + char dnsStrE[16]; + snprintf(dnsStrE, sizeof(dnsStrE), "%u.%u.%u.%u", currentEthDNS[0], currentEthDNS[1], currentEthDNS[2], currentEthDNS[3]); + + int lastDot = -1; + for (int i = 0; dnsStrE[i] != '\0'; i++) + { + if (dnsStrE[i] == '.') + { + lastDot = i; + } + } + + int fourthPartE = atoi(dnsStrE + lastDot + 1); + + if (setup && fourthPartE != 0) + { + vars.savedEthDNS = currentEthDNS; + snprintf(buffer, sizeof(buffer), "%s %s %s - %s", dnsTagKey, savedKey, ethKey, dnsStrE); + printLogMsg(buffer); + } + else + { + if (vars.savedEthDNS) + { + ETH.config(ETH.localIP(), ETH.gatewayIP(), ETH.subnetMask(), vars.savedEthDNS); + snprintf(buffer, sizeof(buffer), "%s %s %s - %s", dnsTagKey, restoredKey, ethKey, vars.savedEthDNS.toString().c_str()); + printLogMsg(buffer); + } + } + } + } + return true; +} +*/ + +/*void reCheckDNS() +{ + checkDNS(); +}*/ void setupCron() { - // Cron.create(const_cast("0 */1 * * * *"), cronTest, false); + // Cron.create(const_cast("30 */1 * * * *"), reCheckDNS, false); + + // const String time = systemCfg.updCheckTime; + static char formattedTime[16]; + int seconds, hours, minutes; + + String wday = systemCfg.updCheckDay; + + if (wday != "0") + { + seconds = random(1, 59); + + // char timeArray[6]; + // String(systemCfg.updCheckTime).toCharArray(timeArray, sizeof(timeArray)); + + sscanf(systemCfg.updCheckTime, "%d:%d", &hours, &minutes); + + snprintf(formattedTime, sizeof(formattedTime), "%d %d %d * * %s", seconds, minutes, hours, wday.c_str()); - Cron.create(const_cast("0 0 */1 * * *"), checkEspUpdateAvail, false); + // LOGD("UPD cron %s", String(formattedTime)); + printLogMsg("[UPD_CHK] cron " + String(formattedTime)); + + Cron.create(const_cast(formattedTime), checkUpdateAvail, false); // 0 0 */6 * * * + } + else + { + printLogMsg("[UPD_CHK] cron disabled"); + } if (systemCfg.nmEnable) { @@ -430,8 +669,8 @@ void setupCron() char endCron[30]; strcpy(startCron, convertTimeToCron(String(systemCfg.nmStart))); strcpy(endCron, convertTimeToCron(String(systemCfg.nmEnd))); - LOGD("cron", "NM start %s", startCron); - LOGD("cron", "NM end %s", endCron); + LOGD("[cron] NM start %s", const_cast(startCron)); + LOGD("[cron] NM end %s", const_cast(endCron)); Cron.create(const_cast(startCron), nmActivate, false); Cron.create(const_cast(endCron), nmDeactivate, false); @@ -449,7 +688,7 @@ void setTimezone(String timezone) String timeNow = asctime(&timeinfo); timeNow.remove(timeNow.length() - 1); - printLogMsg("Local time: " + timeNow); + printLogMsg("[Time] " + timeNow); } const char *getGmtOffsetForZone(const char *zone) @@ -482,7 +721,7 @@ char *convertTimeToCron(const String &time) static char formattedTime[16]; int hours, minutes; - char timeArray[6]; + char timeArray[6]; time.toCharArray(timeArray, sizeof(timeArray)); sscanf(timeArray, "%d:%d", &hours, &minutes); @@ -500,7 +739,7 @@ ThisConfigStruct *findBrdConfig(int searchId = 0) bool zbOk = false; static ThisConfigStruct bestConfig; - bestConfig.eth = {.addr = -1, .pwrPin = -1, .mdcPin = -1, .mdiPin = -1, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO17_OUT, .pwrAltPin = -1}; + bestConfig.eth = {.addr = -1, .pwrPin = -1, .mdcPin = -1, .mdiPin = -1, .phyType = ETH_PHY_LAN8720, .clkMode = ETH_CLOCK_GPIO17_OUT}; // .pwrAltPin = -1}; bestConfig.zb = {.txPin = -1, .rxPin = -1, .rstPin = -1, .bslPin = -1}; memset(&bestConfig.mist, -1, sizeof(bestConfig.mist)); strlcpy(bestConfig.board, "Unknown", sizeof(bestConfig.board)); @@ -513,95 +752,115 @@ ThisConfigStruct *findBrdConfig(int searchId = 0) LOGI("Try brd: %d - %s", brdIdx, brdConfigs[brdIdx].board); - if (brdIdx == 3) // T-Internet-POE + if (ethIdx == -1) { - pinMode(ethConfigs[ethIdx].pwrAltPin, OUTPUT); - delay(50); - digitalWrite(ethConfigs[ethIdx].pwrAltPin, LOW); - delay(50); - pinMode(ethConfigs[ethIdx].pwrAltPin, INPUT); - delay(50); - bool pwrPinState = digitalRead(ethConfigs[ethIdx].pwrAltPin); - if (pwrPinState) - { - LOGW("%s", "Looks like not T-Internet-POE!"); - continue; - } - } - - if (ETH.begin(ethConfigs[ethIdx].addr, ethConfigs[ethIdx].pwrPin, ethConfigs[ethIdx].mdcPin, ethConfigs[ethIdx].mdiPin, ethConfigs[ethIdx].phyType, ethConfigs[ethIdx].clkMode, ethConfigs[ethIdx].pwrAltPin)) + ethOk = true; + LOGD("NO ethernet OK: %d", ethIdx); + } // egin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_POWER, ETH_CLK_MODE); + +#ifdef TASMOTA_PLATFORM + else if (ETH.begin(ethConfigs[ethIdx].phyType, ethConfigs[ethIdx].addr, ethConfigs[ethIdx].mdcPin, ethConfigs[ethIdx].mdiPin, ethConfigs[ethIdx].pwrPin, ethConfigs[ethIdx].clkMode)) // ethConfigs[ethIdx].pwrAltPin)) +#else + else if (ETH.begin(ethConfigs[ethIdx].addr, ethConfigs[ethIdx].pwrPin, ethConfigs[ethIdx].mdcPin, ethConfigs[ethIdx].mdiPin, ethConfigs[ethIdx].phyType, ethConfigs[ethIdx].clkMode)) // ethConfigs[ethIdx].pwrAltPin)) +#endif { ethOk = true; - LOGD("Ethernet config OK: %d", ethIdx); - bestConfig.eth = ethConfigs[ethIdx]; + LOGD("Ethernet config OK: %d", ethIdx); + } - if (mistConfigs[mistIdx].btnPin > 0) + if (mistConfigs[mistIdx].btnPin > 0) + { + pinMode(mistConfigs[mistIdx].btnPin, INPUT); + int press = 0; + for (int y = 0; y < 10; y++) { - pinMode(mistConfigs[mistIdx].btnPin, INPUT); - int press = 0; - for (int y = 0; y < 10; y++) + int state = digitalRead(mistConfigs[mistIdx].btnPin); + if (state != mistConfigs[mistIdx].btnPlr) { - int state = digitalRead(mistConfigs[mistIdx].btnPin); - if (state != mistConfigs[mistIdx].btnPlr) - { - press++; - } - delay(100); + press++; } - btnOk = (press <= 5); + delay(100); + } + btnOk = (press <= 5); - if (!btnOk) - { - LOGD("Button pin ERROR"); - ethOk = false; - } - else - { - LOGD("Button pin OK"); - } + if (!btnOk) + { + LOGD("Button pin ERROR"); + ethOk = false; } else { - btnOk = true; + LOGD("Button pin OK"); } + } + else + { + btnOk = true; + } - if (btnOk) - { - LOGD("Trying Zigbee: %d", zbIdx); - esp_task_wdt_reset(); - Serial2.begin(systemCfg.serialSpeed, SERIAL_8N1, zbConfigs[zbIdx].rxPin, zbConfigs[zbIdx].txPin); + if (btnOk) + { + LOGD("Trying Zigbee: %d", zbIdx); + esp_task_wdt_reset(); + Serial2.begin(systemCfg.serialSpeed, SERIAL_8N1, zbConfigs[zbIdx].rxPin, zbConfigs[zbIdx].txPin); - int BSL_PIN_MODE = 0; - if (CCTool.begin(zbConfigs[zbIdx].rstPin, zbConfigs[zbIdx].bslPin, BSL_PIN_MODE)) + int BSL_PIN_MODE = 0; + if (CCTool.begin(zbConfigs[zbIdx].rstPin, zbConfigs[zbIdx].bslPin, BSL_PIN_MODE)) + { + if (CCTool.detectChipInfo()) { - if (CCTool.detectChipInfo()) - { - zbOk = true; - LOGD("Zigbee config OK: %d", zbIdx); + zbOk = true; + LOGD("Zigbee config OK: %d", zbIdx); + + bestConfig.zb = zbConfigs[zbIdx]; + bestConfig.mist = mistConfigs[mistIdx]; - bestConfig.zb = zbConfigs[zbIdx]; - bestConfig.mist = mistConfigs[mistIdx]; - strlcpy(bestConfig.board, brdConfigs[brdIdx].board, sizeof(bestConfig.board)); - return &bestConfig; + bool multiCfg = false; + int brdNewId = -1; + for (int brdNewIdx = 0; brdNewIdx < BOARD_CFG_CNT; brdNewIdx++) + { + LOGD("%d %d", brdIdx, brdNewIdx); + if (brdIdx != brdNewIdx && brdConfigs[brdNewIdx].ethConfigIndex == ethIdx && brdConfigs[brdNewIdx].zbConfigIndex == zbIdx && brdConfigs[brdNewIdx].mistConfigIndex == mistIdx) + { + multiCfg = true; + brdNewId = brdNewIdx; + break; + } + } + const char *nameBrd; + if (!multiCfg) + { + nameBrd = brdConfigs[brdIdx].board; + strlcpy(bestConfig.board, nameBrd, sizeof(bestConfig.board)); } else { - zbOk = false; - LOGD("Zigbee config ERROR"); + String nameBrdStr = ("Multi_" + String(brdNewId)); + nameBrd = nameBrdStr.c_str(); + // LOGW("%s", nameBrdStr); + strlcpy(bestConfig.board, nameBrd, sizeof(bestConfig.board)); } + + return &bestConfig; + } + else + { + zbOk = false; + LOGD("Zigbee config ERROR"); } } } LOGI("Config error with: %s", brdConfigs[brdIdx].board); - DynamicJsonDocument config(300); - config["searchId"] = brdIdx + 1; - writeDefaultConfig(configFileHw, config); + // DynamicJsonDocument config(300); + // config["searchId"] = brdIdx + 1; + // writeDefaultConfig(configFileHw, config); - LOGD("Restarting..."); - delay(500); - ESP.restart(); + snprintf(hwConfig.board, sizeof(hwConfig.board), "i%02d", brdIdx + 1); + saveHwConfig(hwConfig); + + restartDevice(); return nullptr; } @@ -620,6 +879,7 @@ ThisConfigStruct *findBrdConfig(int searchId = 0) void wgBegin() { + // checkDNS(); if (!wg.is_initialized()) { @@ -705,7 +965,8 @@ void wgLoop() { LOGD("Peer disconnect"); } - vars.vpnWgPeerIp.clear(); + // vars.vpnWgPeerIp.clear(); + vars.vpnWgPeerIp.fromString("0.0.0.0"); vars.vpnWgConnect = false; } } @@ -809,18 +1070,289 @@ void ledTask(void *parameter) } } -void checkEspUpdateAvail() +int compareVersions(String v1, String v2) { - String latestReleaseUrl = fetchGitHubReleaseInfo(); - String latestVersion = extractVersionFromURL(latestReleaseUrl); + int v1_major = v1.substring(0, v1.indexOf('.')).toInt(); + int v2_major = v2.substring(0, v2.indexOf('.')).toInt(); - if (latestVersion.length() > 0 && latestVersion > VERSION) + if (v1_major != v2_major) { - vars.updateEspAvail = true; + return v1_major - v2_major; + } + + String v1_suffix = v1.substring(v1.indexOf('.') + 1); + String v2_suffix = v2.substring(v2.indexOf('.') + 1); + + if (v1_suffix.length() == 0) + return -1; + if (v2_suffix.length() == 0) + return 1; + + return v1_suffix.compareTo(v2_suffix); +} + +void checkUpdateAvail() +{ + if (!vars.apStarted) + { + const char *ESPkey = "ESP"; + const char *ZBkey = "ZB"; + const char *FoundKey = "Found "; + const char *NewFwKey = " new fw: "; + const char *TryKey = "try to install"; + // String latestReleaseUrlEsp = fetchLatestEspFw(); + String latestReleaseUrlZb = fetchLatestZbFw(); + + // String latestVersionEsp = extractVersionFromURL(latestReleaseUrlEsp); + + FirmwareInfo esp_fw = fetchLatestEspFw(); + String latestVersionEsp = esp_fw.version; + // String latestVersionEsp = "1.2.3"; + + String latestVersionZb = extractVersionFromURL(latestReleaseUrlZb); + + strncpy(vars.lastESPVer, latestVersionEsp.c_str(), sizeof(vars.lastESPVer) - 1); + vars.lastESPVer[sizeof(vars.lastESPVer) - 1] = '\0'; + + LOGD("lastESPVer %s", vars.lastESPVer); + + strncpy(vars.lastZBVer, latestVersionZb.c_str(), sizeof(vars.lastZBVer) - 1); + vars.lastZBVer[sizeof(vars.lastZBVer) - 1] = '\0'; + + LOGD("lastZBVer %s", vars.lastZBVer); + + if (latestVersionEsp.length() > 0 && compareVersions(latestVersionEsp, VERSION) > 0) + { + vars.updateEspAvail = true; + + printLogMsg(String(FoundKey) + String(ESPkey) + String(NewFwKey) + latestVersionEsp); + if (systemCfg.updAutoInst) + { + printLogMsg(String(TryKey)); + // getEspUpdate(latestReleaseUrlEsp); + } + } + else + { + vars.updateEspAvail = false; + } + + if (CCTool.chip.fwRev > 0 && latestVersionZb.length() > 0 && compareVersions(latestVersionZb, String(CCTool.chip.fwRev)) > 0) + { + vars.updateZbAvail = true; + + printLogMsg(String(FoundKey) + String(ZBkey) + String(NewFwKey) + latestVersionZb); + if (systemCfg.updAutoInst) + { + printLogMsg(String(TryKey)); + // flashZbUrl(latestReleaseUrlZb); + // restartDevice(); + } + } + else + { + vars.updateZbAvail = false; + } } else { - vars.updateEspAvail = false; + LOGW("AP is started, skip update check"); } - LOGD("%s", String(vars.updateEspAvail)); } + +bool isIpInSubnet(IPAddress ip, IPAddress subnetBase, IPAddress subnetMask) +{ + for (int i = 0; i < 4; i++) + { + if ((ip[i] & subnetMask[i]) != (subnetBase[i] & subnetMask[i])) + { + return false; + } + } + return true; +} + +bool isValidIp(IPAddress ip) +{ + return ip != IPAddress(0, 0, 0, 0); +} + +String getHostFromUrl(const String &url) +{ + int startIndex = url.indexOf("://") + 3; + int endIndex = url.indexOf('/', startIndex); + if (endIndex == -1) + { + endIndex = url.length(); + } + return url.substring(startIndex, endIndex); +} + +String getRadioRoleKey() +{ + String role; + switch (systemCfg.zbRole) + { + case COORDINATOR: + role = "coordinator"; + break; + case ROUTER: + role = "router"; + break; + case OPENTHREAD: + role = "thread"; + break; + default: + role = "unknown"; + break; + } + return role; +} + +// Функция для удаления ведущих нулей из блока IPv6 +String removeLeadingZeros(const String &block) +{ + int i = 0; + while (i < block.length() - 1 && block[i] == '0') + { + i++; + } + return block.substring(i); +} + +// Функция для сокращения IPv6-адреса +String getShortenedIPv6(const String &ipv6) +{ + String result = ""; + int start = 0; + int end = ipv6.indexOf(':'); + bool zeroBlock = false; + bool inZeroBlock = false; + + while (end != -1) + { + String block = ipv6.substring(start, end); + block = removeLeadingZeros(block); + + if (block == "0") + { + if (!inZeroBlock) + { + if (zeroBlock) + { + result += ":"; + } + result += ":"; + inZeroBlock = true; + } + } + else + { + if (inZeroBlock) + { + inZeroBlock = false; + } + else if (zeroBlock) + { + result += ":"; + } + result += block; + zeroBlock = true; + } + + start = end + 1; + end = ipv6.indexOf(':', start); + } + + // Обработка последнего блока + String block = ipv6.substring(start); + block = removeLeadingZeros(block); + if (block == "0") + { + if (!inZeroBlock) + { + if (zeroBlock) + { + result += ":"; + } + result += ":"; + } + } + else + { + if (inZeroBlock) + { + inZeroBlock = false; + } + else if (zeroBlock) + { + result += ":"; + } + result += block; + } + + // Удаление лишнего двоеточия в начале, если есть + if (result.startsWith("::") && result.length() > 2) + { + result = result.substring(1); + } + + return result; +} + +void restartDevice() +{ + LOGD("Restarting device..."); + delay(100); + ESP.restart(); +} + +void freeHeapPrint() +{ + heap_caps_free(NULL); + size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t minFreeHeap = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT); + LOGD("Free heap: %d, min free heap: %d", freeHeap, minFreeHeap); +} + +bool dnsLookup(const String &url) +{ + IPAddress ip; + String host = getHostFromUrl(url); + + // DNS lookup + if (!WiFi.hostByName(host.c_str(), ip)) + { + printLogMsg("DNS lookup failed for host: " + host + ". Check your network connection and DNS settings."); + return false; + } + else + { + return true; + } +} + +void firstUpdCheck() +{ + int retryCount = 0; + int maxRetries = 3; + const int retryDelay = 500; + bool isSuccess = false; + + while (retryCount < maxRetries && !isSuccess) + { + if (!dnsLookup("google.com")) + { + retryCount++; + printLogMsg("DNS lookup failed. Retrying..."); + delay(retryDelay * 3); + } + else + { + isSuccess = true; + vars.firstUpdCheck = true; + checkUpdateAvail(); + checkFileSys(); + } + } +} \ No newline at end of file diff --git a/src/etc.h b/src/etc.h index 1b803a6a..9063bb9d 100644 --- a/src/etc.h +++ b/src/etc.h @@ -1,3 +1,8 @@ +#ifndef ETC_H +#define ETC_H + +#include + #include #include "const/hw.h" @@ -5,6 +10,10 @@ void getReadableTime(String &readableTime, unsigned long beginTime); String sha1(String payloadStr); +int check1wire(); +void setup1wire(int pin); +float get1wire(); + extern BrdConfigStruct brdConfigs[BOARD_CFG_CNT]; ThisConfigStruct *findBrdConfig(int searchId); @@ -18,7 +27,7 @@ void zigbeeRestart(); void usbModeSet(usbMode mode); void getDeviceID(char *arr); -void writeDefaultConfig(const char *path, DynamicJsonDocument &doc); +//void writeDefaultConfig(const char *path, DynamicJsonDocument &doc); #define TIMEOUT_FACTORY_RESET 3 @@ -27,6 +36,7 @@ void factoryReset(); void setLedsDisable(bool all = false); void cronTest(); void nmActivate(); +//bool checkDNS(bool setup = false); void setupCron(); void setClock(void *pvParameters); @@ -42,4 +52,23 @@ void hnBegin(); void ledTask(void *parameter); String getTime(); -void checkEspUpdateAvail(); +void checkUpdateAvail(); + +bool isIpInSubnet(IPAddress ip, IPAddress subnet, IPAddress subnetMask); +bool isValidIp(IPAddress ip); +String getHostFromUrl(const String& url); +String getRadioRoleKey(); +String removeLeadingZeros(const String& block); +String getShortenedIPv6(const String& ipv6); +void restartDevice(); +void freeHeapPrint(); +bool dnsLookup(const String &url); +void firstUpdCheck(); + +struct FirmwareInfo { + String url; + String version; + String sha; +}; + +#endif // ETC_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d4fbea65..8d776da9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,7 @@ #include "version.h" // #include "const/hw.h" #include "per.h" +#include "main.h" #include "esp_system.h" #include "esp_task_wdt.h" @@ -54,15 +55,10 @@ extern int btnFlag; bool updWeb = false; -void mDNS_start(); -void connectWifi(); -// void handleLongBtn(); -void handleTmrNetworkOverseer(); -void setupCoordinatorMode(); -void startAP(const bool start); -// void toggleUsbMode(); +int networkOverseerCounter = 0; -Ticker tmrNetworkOverseer(handleTmrNetworkOverseer, overseerInterval, 0, MILLIS); +// Ticker tmrNetworkOverseer(handleTmrNetworkOverseer, overseerInterval, 0, MILLIS); +Ticker tmrNetworkOverseer; IPAddress apIP(192, 168, 1, 1); DNSServer dnsServer; @@ -74,17 +70,25 @@ MDNSResponder mDNS; void initLan() { - LOGD("Try to use %s", hwConfig.board); - - if (ETH.begin(hwConfig.eth.addr, hwConfig.eth.pwrPin, hwConfig.eth.mdcPin, hwConfig.eth.mdiPin, hwConfig.eth.phyType, hwConfig.eth.clkMode, hwConfig.eth.pwrAltPin)) +// ETH.begin(ethConfigs[ethIdx].phyType, ethConfigs[ethIdx].addr, ethConfigs[ethIdx].mdcPin, ethConfigs[ethIdx].mdiPin, ethConfigs[ethIdx].pwrPin, ethConfigs[ethIdx].clkMode) +// +#ifdef TASMOTA_PLATFORM + if (ETH.begin(hwConfig.eth.phyType, hwConfig.eth.addr, hwConfig.eth.mdcPin, hwConfig.eth.mdiPin, hwConfig.eth.pwrPin, hwConfig.eth.clkMode)) +#else + if (ETH.begin(hwConfig.eth.addr, hwConfig.eth.pwrPin, hwConfig.eth.mdcPin, hwConfig.eth.mdiPin, hwConfig.eth.phyType, hwConfig.eth.clkMode)) // hwConfig.eth.pwrAltPin)) +#endif { String modeString = networkCfg.ethDhcp ? "DHCP" : "Static"; - LOGD("LAN start ok, %s", modeString); + LOGD("LAN start ok, %s", modeString.c_str()); // ConfigSettings.disconnectEthTime = millis(); if (!networkCfg.ethDhcp) { + LOGD("Static IP"); ETH.config(networkCfg.ethIp, networkCfg.ethGate, networkCfg.ethMask, networkCfg.ethDns1, networkCfg.ethDns2); } + ETH.enableIpV6(); + // ETH.enableIPv6(); + // ETH.printTo(Serial); } else { @@ -123,14 +127,15 @@ void startServers(bool usb = false) startAP(false); - if (!vars.apStarted) + /*if (!vars.apStarted) { if (vpnCfg.wgEnable) { wgBegin(); } - } + }*/ + mDNS_start(); /* //not available now if (vpnCfg.hnEnable) { @@ -141,28 +146,38 @@ void startServers(bool usb = false) void handleTmrNetworkOverseer() { - switch (systemCfg.workMode) + // switch (systemCfg.workMode) + //{ + // case WORK_MODE_NETWORK: + networkOverseerCounter++; + if (!networkCfg.wifiEnable && !networkCfg.ethEnable) { - case WORK_MODE_NETWORK: - if (!networkCfg.wifiEnable && !networkCfg.ethEnable) + if (!vars.apStarted) { LOGD("Both interfaces disabled. Start AP"); startAP(true); connectWifi(); } - if (networkCfg.wifiEnable) - { - LOGD("WiFi.status = %s", String(WiFi.status())); + } + if (networkCfg.wifiEnable) + { + LOGD("WiFi.status = %s", String(WiFi.status()).c_str()); - if (WiFi.isConnected()) + if (WiFi.isConnected()) + { + LOGD("WIFI CONNECTED"); + // tmrNetworkOverseer.stop(); + tmrNetworkOverseer.detach(); + if (!vars.firstUpdCheck) { - LOGD("WIFI CONNECTED"); - mDNS_start(); - tmrNetworkOverseer.stop(); + firstUpdCheck(); } - else + } + else + { + if (!vars.zbFlashing) { - if (tmrNetworkOverseer.counter() > overseerMaxRetry) + if (networkOverseerCounter > overseerMaxRetry) { LOGD("WIFI counter overflow"); startAP(true); @@ -170,31 +185,41 @@ void handleTmrNetworkOverseer() } } } - if (networkCfg.ethEnable) + } + if (networkCfg.ethEnable) + { + if (vars.connectedEther) { - if (vars.connectedEther) + LOGD("LAN CONNECTED"); + // tmrNetworkOverseer.stop(); + tmrNetworkOverseer.detach(); + if (vars.apStarted) { - LOGD("LAN CONNECTED"); - mDNS_start(); - tmrNetworkOverseer.stop(); + startAP(false); } - else + if (!vars.firstUpdCheck) { - if (tmrNetworkOverseer.counter() > overseerMaxRetry) - { - LOGD("LAN counter overflow"); - startAP(true); - } + firstUpdCheck(); } } - break; - case WORK_MODE_USB: + else + { + // if (tmrNetworkOverseer.counter() > overseerMaxRetry) + if (networkOverseerCounter > overseerMaxRetry) + { + LOGD("LAN counter overflow!"); + startAP(true); + } + } + } + // break; + /*case WORK_MODE_USB: if (tmrNetworkOverseer.counter() > 3) { // 10 seconds for wifi connect if (WiFi.isConnected()) { tmrNetworkOverseer.stop(); - startServers(true); + // startServers(true); } else { @@ -204,7 +229,7 @@ void handleTmrNetworkOverseer() if (vars.connectedEther) { tmrNetworkOverseer.stop(); - startServers(true); + // startServers(true); } else { // no network interfaces @@ -216,15 +241,15 @@ void handleTmrNetworkOverseer() } break; default: - break; - } + break;*/ + //} } void NetworkEvent(WiFiEvent_t event) { const char *wifiKey = "WiFi"; const char *ethKey = "ETH"; - + // esp_err_t result5; switch (event) { case ARDUINO_EVENT_ETH_START: // 18: // SYSTEM_EVENT_ETH_START: @@ -236,49 +261,92 @@ void NetworkEvent(WiFiEvent_t event) LOGD("%s Connected", ethKey); break; case ARDUINO_EVENT_ETH_GOT_IP: // 22: // SYSTEM_EVENT_ETH_GOT_IP: - startServers(); - LOGI("%s MAC: %s, IP: %s, Mask: %s, Gw: %s, %dMbps", ethKey, + // startServers(); + LOGI("%s MAC: %s, IP: %s, Mask: %s, Gw: %s, DNS: %s, %dMbps", ethKey, ETH.macAddress().c_str(), ETH.localIP().toString().c_str(), ETH.subnetMask().toString().c_str(), ETH.gatewayIP().toString().c_str(), + ETH.dnsIP().toString().c_str(), ETH.linkSpeed()); vars.connectedEther = true; - // ConfigSettings.disconnectEthTime = 0; + // checkDNS(true); + // ConfigSettings.disconnectEthTime = 0; break; + + case ARDUINO_EVENT_ETH_GOT_IP6: + LOGI("ETH IPv6 %s", ETH.localIPv6().toString().c_str()); + vars.connectedEther = true; + vars.ethIPv6 = true; + break; + case ARDUINO_EVENT_ETH_DISCONNECTED: // 21: //SYSTEM_EVENT_ETH_DISCONNECTED: LOGD("%s Disconnected", ethKey); vars.connectedEther = false; + vars.ethIPv6 = false; // ConfigSettings.disconnectEthTime = millis(); - if (tmrNetworkOverseer.state() == STOPPED && systemCfg.workMode == WORK_MODE_NETWORK) + // if (tmrNetworkOverseer.state() == STOPPED) //&& systemCfg.workMode == WORK_MODE_NETWORK) + if (!tmrNetworkOverseer.active()) { - tmrNetworkOverseer.start(); + // tmrNetworkOverseer.start(); + tmrNetworkOverseer.attach(overseerInterval, handleTmrNetworkOverseer); } break; - case SYSTEM_EVENT_ETH_STOP: // 27: + case 27: // case SYSTEM_EVENT_ETH_STOP: // 27: case ARDUINO_EVENT_ETH_STOP: LOGD("%s Stopped", ethKey); vars.connectedEther = false; // ConfigSettings.disconnectEthTime = millis(); - if (tmrNetworkOverseer.state() == STOPPED) + // if (tmrNetworkOverseer.state() == STOPPED) + if (!tmrNetworkOverseer.active()) { - tmrNetworkOverseer.start(); + // tmrNetworkOverseer.start(); + tmrNetworkOverseer.attach(overseerInterval, handleTmrNetworkOverseer); } break; case ARDUINO_EVENT_WIFI_STA_GOT_IP: // SYSTEM_EVENT_STA_GOT_IP: - startServers(); - LOGI("%s MAC: %s, IP: %s, Mask: %s, Gw: %s", wifiKey, + // startServers(); + LOGI("%s MAC: %s, IP: %s, Mask: %s, Gw: %s, DNS: %s", wifiKey, WiFi.macAddress().c_str(), WiFi.localIP().toString().c_str(), WiFi.subnetMask().toString().c_str(), - WiFi.gatewayIP().toString().c_str()); + WiFi.gatewayIP().toString().c_str(), + WiFi.dnsIP().toString().c_str()); + // checkDNS(true); + LOGD("WiFi TX %s", String(WiFi.getTxPower()).c_str()); + + /*result5 = esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N); + if (result5 == ESP_OK) + { + Serial.println("Wi-Fi protocol set successfully."); + } + else + { + Serial.printf("Error setting Wi-Fi protocol: %d\n", result5); + } + + uint8_t cur_mode; + esp_wifi_get_protocol(WIFI_IF_STA, &cur_mode); + Serial.print("Current Wi-Fi protocol: "); + if (cur_mode & WIFI_PROTOCOL_11B) + Serial.print("802.11b "); + if (cur_mode & WIFI_PROTOCOL_11G) + Serial.print("802.11g "); + if (cur_mode & WIFI_PROTOCOL_11N) + Serial.print("802.11n "); + Serial.println();*/ + break; + case ARDUINO_EVENT_WIFI_STA_GOT_IP6: // SYSTEM_EVENT_STA_GOT_IP6: + LOGI("WiFi IPv6 %s", WiFi.localIPv6().toString().c_str()); break; case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: // SYSTEM_EVENT_STA_DISCONNECTED: LOGD("%s STA DISCONNECTED", wifiKey); - if (tmrNetworkOverseer.state() == STOPPED) + // if (tmrNetworkOverseer.state() == STOPPED) + if (!tmrNetworkOverseer.active()) { - tmrNetworkOverseer.start(); + // tmrNetworkOverseer.start(); + tmrNetworkOverseer.attach(overseerInterval, handleTmrNetworkOverseer); } break; default: @@ -289,7 +357,7 @@ void NetworkEvent(WiFiEvent_t event) void startAP(const bool start) { String tag = "sAP"; - LOGD("begin s=%d, v=%d", start, vars.apStarted); + LOGD("begin cmd=%d, state=%d", start, vars.apStarted); if (vars.apStarted) { @@ -316,19 +384,26 @@ void startAP(const bool start) WiFi.disconnect(); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); WiFi.softAP(vars.deviceId); //, WIFIPASS); - + // if DNSServer is started with "*" for domain name, it will reply with // provided IP to all DNS request dnsServer.setErrorReplyCode(DNSReplyCode::NoError); dnsServer.start(53, "*", apIP); WiFi.setSleep(false); // ConfigSettings.wifiAPenblTime = millis(); - LOGD("startServers()"); + // LOGD("startServers()"); startServers(); vars.apStarted = true; } } +void stopWifi() +{ + LOGD("stopWifi"); + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); +} + void connectWifi() { static uint8_t timeout = 0; @@ -344,14 +419,36 @@ void connectWifi() LOGD("timeout"); } WiFi.persistent(false); - esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B); + + // Dont work on Arduino framework + + /*uint8_t cur_mode; + esp_wifi_get_protocol(WIFI_IF_STA, &cur_mode); + Serial.print("wifi mode "); + String result = ""; + result += String(cur_mode, DEC); + Serial.println(result); + + esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N); // networkCfg.wifiMode); // WIFI_PROTOCOL_11B | ); // + + Serial.print("wifi mode setup "); + esp_err_t result2 = esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N); + Serial.println(result2); + + cur_mode = -1; + esp_wifi_get_protocol(WIFI_IF_STA, &cur_mode); + Serial.print("wifi mode "); + result = ""; + result += String(cur_mode, DEC); + Serial.println(result);*/ + if ((strlen(networkCfg.wifiSsid) >= 2) && (strlen(networkCfg.wifiPass) >= 8)) { LOGD("Ok SSID & PASS"); if (vars.apStarted) { - // LOGD("WiFi.mode(WIFI_AP_STA)"); - // WiFi.mode(WIFI_AP_STA); + LOGD("WiFi.mode(WIFI_AP_STA)"); + WiFi.mode(WIFI_AP_STA); } else { @@ -373,31 +470,104 @@ void connectWifi() WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); LOGD("Try DHCP"); } - WiFi.begin(networkCfg.wifiSsid, networkCfg.wifiPass); - LOGD("WiFi.begin"); - } - else - { - if (!(systemCfg.workMode == WORK_MODE_USB && systemCfg.keepWeb)) - { // dont start ap in keepWeb - LOGD("NO SSID & PASS "); - if (!vars.connectedEther) + WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); + WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); + + WiFi.setAutoReconnect(true); + + // Dont work on Arduino framework + /*uint8_t wifiProtocols = WIFI_PROTOCOL_LR; + + uint8_t currentProtocols; + esp_err_t resultGetProt = esp_wifi_get_protocol(WIFI_IF_STA, ¤tProtocols); + if (resultGetProt == ESP_OK) + { + Serial.printf("Current WiFi protocols: 0x%X\n", currentProtocols); + } + else + { + Serial.printf("Failed to get current WiFi protocols: 0x%X\n", resultGetProt); + } + + // Объединение текущих и желаемых настроек протоколов + uint8_t newProtocols = wifiProtocols; + + // Установка новых протоколов WiFi перед началом подключения + esp_err_t resultWifiProtSet = esp_wifi_set_protocol(WIFI_IF_STA, newProtocols); + if (resultWifiProtSet == ESP_OK) + { + Serial.println("WiFi protocols set successfully"); + resultGetProt = esp_wifi_get_protocol(WIFI_IF_STA, ¤tProtocols); + if (resultGetProt == ESP_OK) { - LOGD("and problem with LAN"); - startAP(true); - LOGD("so setupWifiAP"); + Serial.printf("Current WiFi protocols: 0x%X\n", currentProtocols); } else { - LOGD("but LAN is OK"); + Serial.printf("Failed to get current WiFi protocols: 0x%X\n", resultGetProt); } } + else + { + Serial.printf("Failed to set WiFi protocols: 0x%X\n", resultWifiProtSet); + if (resultWifiProtSet == ESP_ERR_WIFI_NOT_INIT) + { + Serial.println("WiFi is not initialized by esp_wifi_init"); + } + else if (resultWifiProtSet == ESP_ERR_WIFI_IF) + { + Serial.println("Invalid interface"); + } + else if (resultWifiProtSet == ESP_ERR_INVALID_ARG) + { + Serial.println("Invalid argument"); + } + else + { + Serial.println("Unknown error"); + } + } + */ + + WiFi.begin(networkCfg.wifiSsid, networkCfg.wifiPass); + WiFi.setTxPower(networkCfg.wifiPower); + WiFi.enableIpV6(); + // LOGD("WiFi TX %s", String(WiFi.getTxPower()).c_str()); + + /*esp_err_t result = esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N); + if (result == ESP_OK) + { + Serial.println("Wi-Fi protocol set successfully."); + } + else + { + Serial.printf("Error setting Wi-Fi protocol: %d\n", result); + }*/ + LOGD("WiFi.begin"); + } + else + { + // if (!(systemCfg.workMode == WORK_MODE_USB && systemCfg.keepWeb)) + //{ // dont start ap in keepWeb + LOGD("NO SSID & PASS "); + if (!vars.connectedEther) + { + LOGD("and problem with LAN"); + startAP(true); + LOGD("so setupWifiAP"); + } + else + { + LOGD("but LAN is OK"); + } + // } } } void mDNS_start() { - const char *host = "_xzg"; + const char *names[] = {"_xzg", "_zigbee-gateway", "_zigstar_gw"}; + const char *http = "_http"; const char *tcp = "_tcp"; if (!mDNS.begin(systemCfg.hostname)) @@ -406,19 +576,48 @@ void mDNS_start() } else { - LOGI("mDNS responder started on %s.local", String(systemCfg.hostname)); + LOGI("mDNS responder started on %s.local", String(systemCfg.hostname).c_str()); //----- WEB ------ mDNS.addService(http, tcp, 80); //--zeroconf zha-- - mDNS.addService(host, tcp, systemCfg.socketPort); - mDNS.addServiceTxt(host, tcp, "version", "1.0"); - mDNS.addServiceTxt(host, tcp, "radio_type", "znp"); - mDNS.addServiceTxt(host, tcp, "baud_rate", String(systemCfg.serialSpeed)); - mDNS.addServiceTxt(host, tcp, "data_flow_control", "software"); - mDNS.addServiceTxt(host, tcp, "board", String(hwConfig.board)); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) + { + mDNS.addService(names[i], tcp, systemCfg.socketPort); + mDNS.addServiceTxt(names[i], tcp, "version", "1.0"); + mDNS.addServiceTxt(names[i], tcp, "radio_type", "znp"); + mDNS.addServiceTxt(names[i], tcp, "serial_number", String(CCTool.chip.ieee)); + mDNS.addServiceTxt(names[i], tcp, "baud_rate", String(systemCfg.serialSpeed)); + mDNS.addServiceTxt(names[i], tcp, "data_flow_control", "software"); + mDNS.addServiceTxt(names[i], tcp, "board", String(hwConfig.board)); + } } } +void networkStart() +{ + // if ((systemCfg.workMode != WORK_MODE_USB) || systemCfg.keepWeb) + //{ // start network overseer + // if (tmrNetworkOverseer.state() == STOPPED) + if (!tmrNetworkOverseer.active()) + { + // tmrNetworkOverseer.start(); + tmrNetworkOverseer.attach(overseerInterval, handleTmrNetworkOverseer); + } + WiFi.onEvent(NetworkEvent); + if (networkCfg.ethEnable) + initLan(); + if (networkCfg.wifiEnable) + connectWifi(); + //} + + // if (!systemCfg.disableWeb && ((systemCfg.workMode != WORK_MODE_USB) || systemCfg.keepWeb)) + // updWeb = true; // handle web server + if (!systemCfg.disableWeb) + updWeb = true; // handle web server + // if (systemCfg.workMode == WORK_MODE_USB && systemCfg.keepWeb) + // connectWifi(); // try 2 connect wifi +} + void setupCoordinatorMode() { if (systemCfg.workMode > 2 || systemCfg.workMode < 0) @@ -428,46 +627,85 @@ void setupCoordinatorMode() } String workModeString = systemCfg.workMode ? "USB" : "Network"; - LOGI("%s", workModeString); - - if ((systemCfg.workMode != WORK_MODE_USB) || systemCfg.keepWeb) - { // start network overseer - if (tmrNetworkOverseer.state() == STOPPED) - { - tmrNetworkOverseer.start(); - } - WiFi.onEvent(NetworkEvent); - if (networkCfg.ethEnable) - initLan(); - if (networkCfg.wifiEnable) - connectWifi(); - } + LOGI("%s", workModeString.c_str()); switch (systemCfg.workMode) { case WORK_MODE_USB: ledControl.modeLED.mode = LED_ON; - delay(500); + delay(100); usbModeSet(ZIGBEE); + startServers(true); break; case WORK_MODE_NETWORK: ledControl.powerLED.mode = LED_BLINK_1Hz; + delay(100); + usbModeSet(XZG); + startServers(); break; default: break; } +} - if (!systemCfg.disableWeb && ((systemCfg.workMode != WORK_MODE_USB) || systemCfg.keepWeb)) - updWeb = true; // handle web server - if (systemCfg.workMode == WORK_MODE_USB && systemCfg.keepWeb) - connectWifi(); // try 2 connect wifi +void getEspUpdateTask(void *pvParameters) +{ + TaskParams *params = static_cast(pvParameters); + LOGI("getEspUpdateTask %s", params->url); + getEspUpdate(params->url); + vTaskDelete(NULL); +} + +void timerCallback(TimerHandle_t xTimer) +{ + TaskParams *params = static_cast(pvTimerGetTimerID(xTimer)); + xTaskCreate(getEspUpdateTask, "getEspUpdateTask", 8192, params, 1, NULL); +} + +void checkFileSys() +{ + FirmwareInfo fwInfo = fetchLatestEspFw("fs"); + File commitFile = LittleFS.open("/x/commit", "r"); + if (!commitFile) + { + LOGI("Commit file not found"); + vars.needFsDownload = true; + } + else + { + String gitSha = fwInfo.sha.substring(0, 7); + String fileSha = commitFile.readString().substring(0, 7).c_str(); + + LOGI("Commit file found: Git: %s, File: %s", gitSha.c_str(), fileSha.c_str()); + + if (gitSha.length() == 7 && gitSha != fileSha) + { + LOGI("Found new FS commit"); + vars.needFsDownload = true; + } + commitFile.close(); + } + + if (vars.needFsDownload) + { + LOGI("Downloading FS"); + + static String urlString = fwInfo.url; + static TaskParams params = {urlString.c_str()}; + + TimerHandle_t timer = xTimerCreate("StartTaskTimer", pdMS_TO_TICKS(5000), pdFALSE, ¶ms, timerCallback); + if (timer != NULL) + { + xTimerStart(timer, 0); + } + } } void setup() { Serial.begin(115200); // todo ifdef DEBUG - String tag = "SETUP"; + // String tag = "SETUP"; initNVS(); @@ -479,32 +717,79 @@ void setup() loadMqttConfig(mqttCfg); // LOAD System vars and create FS / start - if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED, "/lfs2", 10)) + if (!LittleFS.begin(false, "/lfs2", 10)) { - LOGD("Error with LITTLEFS"); - return; + LOGD("Error with FS - try to download"); + vars.needFsDownload = true; + // return; } - // LOAD System vars and create FS / end + loadHwConfig(hwConfig); + if (String(hwConfig.board).length() < 4) + { + LOGD("No HW config in NVS. Try to load from file"); + if (!loadFileConfigHW()) + { + int searchId = 0; + if (hwConfig.board[0] == 'i') + { + // Проверка, что второй и третий символы являются цифрами + if (isdigit(hwConfig.board[1]) && isdigit(hwConfig.board[2])) + { + searchId = (hwConfig.board[1] - '0') * 10 + (hwConfig.board[2] - '0'); + } + else + { + LOGD("Invalid board ID format: %s", hwConfig.board); + } + } + LOGD("hwConfig.board: %s", hwConfig.board); + LOGD("searchId: %d", searchId); + ThisConfigStruct *newConfig = findBrdConfig(searchId); + if (newConfig) + { + LOGD("Find. Saving config"); + saveHwConfig(*newConfig); + + if (!newConfig->eth.mdcPin == -1 && !newConfig->eth.mdiPin == -1) + { + networkCfg.ethEnable = true; + saveNetworkConfig(networkCfg); + } - // READ file to support migrate from old firmware - loadFileSystemVar(); - loadFileConfigSerial(); - loadFileConfigWifi(); - loadFileConfigEther(); - loadFileConfigGeneral(); - loadFileConfigSecurity(); - loadFileConfigMqtt(); - loadFileConfigWg(); - // READ file to support migrate from old firmware + LOGD("Calc and save temp offset"); + float CPUtemp = getCPUtemp(true); + int offset = CPUtemp - 30; + systemCfg.tempOffset = int(offset); + saveSystemConfig(systemCfg); + + restartDevice(); + } + } + } - // String cfg = makeJsonConfig(&networkCfg, &vpnCfg, &mqttCfg, &systemCfg, &vars); - // LOGD("After READ OLD config\n %s", cfg.c_str()); + if (hwConfig.eth.mdcPin == -1 || hwConfig.eth.mdiPin == -1) + { + if (networkCfg.ethEnable) + { + networkCfg.ethEnable = false; + saveNetworkConfig(networkCfg); + } + } - loadFileConfigHW(); + String cfg = makeJsonConfig(&networkCfg, &vpnCfg, &mqttCfg, &systemCfg, NULL, NULL); + LOGI("\n%s", cfg.c_str()); vars.apStarted = false; + networkStart(); + + /*while (WiFi.status() != WL_CONNECTED && !vars.connectedEther) + { + delay(1000); + LOGD("Wait for network"); + }*/ + // AVOID USING PIN 0 if (hwConfig.mist.btnPin > 0) { @@ -524,8 +809,10 @@ void setup() if (hwConfig.mist.uartSelPin > 0) { pinMode(hwConfig.mist.uartSelPin, OUTPUT); - vars.hwUartSelIs = true; - usbModeSet(XZG); + // vars.hwUartSelIs = true; + // usbModeSet(XZG); + bool fixState = (hwConfig.mist.uartSelPlr == 1) ? LOW : HIGH; + digitalWrite(hwConfig.mist.uartSelPin, fixState); } if ((hwConfig.zb.txPin > 0) && (hwConfig.zb.rxPin > 0) && (hwConfig.zb.rstPin > 0) && (hwConfig.zb.bslPin > 0)) @@ -543,24 +830,43 @@ void setup() buttonSetup(); } - String cfg = makeJsonConfig(&networkCfg, &vpnCfg, &mqttCfg, &systemCfg); - LOGD("Config:\n%s", cfg.c_str()); - - cfg = makeJsonConfig(NULL, NULL, NULL, NULL, &vars); - LOGI("VARS:\n%s", cfg.c_str()); - setLedsDisable(); // with setup ?? // move to vars ? - setupCoordinatorMode(); vars.connectedClients = 0; - xTaskCreate(updateWebTask, "update Web Task", 2048, NULL, 7, NULL); + xTaskCreate(updateWebTask, "update Web Task", 8192, NULL, 8, NULL); printNVSFreeSpace(); - zbFwCheck(); + if (systemCfg.zbRole == COORDINATOR || systemCfg.zbRole == UNDEFINED) + { + /*if (zbFwCheck()) + { + if (systemCfg.zbRole == UNDEFINED) + { + systemCfg.zbRole = COORDINATOR; + saveSystemConfig(systemCfg); + } + }*/ + zbFwCheck(); + LOGI("[RCP] FW: %s", String(CCTool.chip.fwRev).c_str()); + } + else + { + LOGI("[RCP] role: %s", String(systemCfg.zbRole).c_str()); + } + LOGI("[ESP] FW: %s", String(VERSION).c_str()); + + // LOGI("Load cfg %s", hwConfig.board); - LOGD("done"); + setupCoordinatorMode(); + + setup1wire(check1wire()); + + cfg = makeJsonConfig(NULL, NULL, NULL, NULL, &vars, NULL); + LOGI("\n%s", cfg.c_str()); + + LOGI("done"); } WiFiClient client[10]; @@ -599,7 +905,7 @@ void socketClientDisconnected(int client) void printRecvSocket(size_t bytes_read, uint8_t net_buf[BUFFER_SIZE]) { - char output_sprintf[2]; + char output_sprintf[3]; if (bytes_read > 0) { String tmpTime; @@ -629,7 +935,7 @@ void printRecvSocket(size_t bytes_read, uint8_t net_buf[BUFFER_SIZE]) void printSendSocket(size_t bytes_read, uint8_t serial_buf[BUFFER_SIZE]) { - char output_sprintf[2]; + char output_sprintf[3]; String tmpTime; String buff = ""; unsigned long timeLog = millis(); @@ -667,7 +973,7 @@ void loop(void) buttonLoop(); } - tmrNetworkOverseer.update(); + // tmrNetworkOverseer.update(); if (updWeb) { webServerHandleClient(); @@ -680,102 +986,128 @@ void loop(void) } } - if (systemCfg.workMode != WORK_MODE_USB) + if (!vars.zbFlashing) { - uint16_t net_bytes_read = 0; - uint8_t net_buf[BUFFER_SIZE]; - uint16_t serial_bytes_read = 0; - uint8_t serial_buf[BUFFER_SIZE]; - if (server.hasClient()) + if (systemCfg.workMode == WORK_MODE_USB) + { + if (Serial2.available()) + { + Serial.write(Serial2.read()); + Serial.flush(); + } + if (Serial.available()) + { + Serial2.write(Serial.read()); + Serial2.flush(); + } + return; + } + + else if (systemCfg.workMode == WORK_MODE_NETWORK) { - for (byte i = 0; i < MAX_SOCKET_CLIENTS; i++) + uint16_t net_bytes_read = 0; + uint8_t net_buf[BUFFER_SIZE]; + uint16_t serial_bytes_read = 0; + uint8_t serial_buf[BUFFER_SIZE]; + + if (server.hasClient()) { - if (!client[i] || !client[i].connected()) + for (byte i = 0; i < MAX_SOCKET_CLIENTS; i++) { - if (client[i]) - { - client[i].stop(); - } - if (systemCfg.fwEnabled) + if (!client[i] || !client[i].connected()) { - WiFiClient TempClient2 = server.available(); - if (TempClient2.remoteIP() == systemCfg.fwIp) + if (client[i]) { - printLogMsg(String("[SOCK IP WHITELIST] Accepted connection from IP: ") + TempClient2.remoteIP().toString()); - client[i] = TempClient2; - continue; + client[i].stop(); + } + if (systemCfg.fwEnabled) + { + if (server.hasClient()) + { + WiFiClient TempClient2 = server.available(); + IPAddress clientIp = TempClient2.remoteIP(); + + if (isValidIp(clientIp) && isIpInSubnet(clientIp, systemCfg.fwIp, systemCfg.fwMask)) + { + printLogMsg(String("[SOCK IP FW] Accepted from IP: ") + clientIp.toString()); + client[i] = TempClient2; + // TempClient2.stop(); + continue; + } + else + { + printLogMsg(String("[SOCK IP FW] Rejected from IP: ") + clientIp.toString()); + // TempClient2.stop(); + } + } } else { - printLogMsg(String("[SOCK IP WHITELIST] Rejected connection from unknown IP: ") + TempClient2.remoteIP().toString()); + client[i] = server.available(); + continue; } } - else - { - client[i] = server.available(); - continue; - } } + WiFiClient TempClient = server.available(); + TempClient.stop(); } - WiFiClient TempClient = server.available(); - TempClient.stop(); - } - for (byte cln = 0; cln < MAX_SOCKET_CLIENTS; cln++) - { - if (client[cln]) + for (byte cln = 0; cln < MAX_SOCKET_CLIENTS; cln++) { - socketClientConnected(cln, client[cln].remoteIP()); - while (client[cln].available()) - { // read from LAN - net_buf[net_bytes_read] = client[cln].read(); - if (net_bytes_read < BUFFER_SIZE - 1) - net_bytes_read++; - } // send to Zigbee - Serial2.write(net_buf, net_bytes_read); - // print to web console - printRecvSocket(net_bytes_read, net_buf); - net_bytes_read = 0; + if (client[cln]) + { + socketClientConnected(cln, client[cln].remoteIP()); + while (client[cln].available()) + { // read from LAN + net_buf[net_bytes_read] = client[cln].read(); + if (net_bytes_read < BUFFER_SIZE - 1) + net_bytes_read++; + } // send to Zigbee + Serial2.write(net_buf, net_bytes_read); + // print to web console + printRecvSocket(net_bytes_read, net_buf); + net_bytes_read = 0; + } + else + { + socketClientDisconnected(cln); + } } - else + + if (Serial2.available()) { - socketClientDisconnected(cln); + while (Serial2.available()) + { // read from Zigbee + serial_buf[serial_bytes_read] = Serial2.read(); + if (serial_bytes_read < BUFFER_SIZE - 1) + serial_bytes_read++; + } + // send to LAN + for (byte cln = 0; cln < MAX_SOCKET_CLIENTS; cln++) + { + if (client[cln]) + client[cln].write(serial_buf, serial_bytes_read); + } + // print to web console + printSendSocket(serial_bytes_read, serial_buf); + serial_bytes_read = 0; } - } - if (Serial2.available()) - { - while (Serial2.available()) - { // read from Zigbee - serial_buf[serial_bytes_read] = Serial2.read(); - if (serial_bytes_read < BUFFER_SIZE - 1) - serial_bytes_read++; - } - // send to LAN - for (byte cln = 0; cln < MAX_SOCKET_CLIENTS; cln++) + /*if (mqttCfg.enable) { - if (client[cln]) - client[cln].write(serial_buf, serial_bytes_read); - } - // print to web console - printSendSocket(serial_bytes_read, serial_buf); - serial_bytes_read = 0; + // mqttLoop(); + }*/ } - - /*if (mqttCfg.enable) + if (vpnCfg.wgEnable && vars.vpnWgInit) { - // mqttLoop(); - }*/ - } - if (vpnCfg.wgEnable && vars.vpnWgInit) - { - wgLoop(); - } + wgLoop(); + } - if (WiFi.getMode() == WIFI_MODE_AP || WiFi.getMode() == WIFI_MODE_APSTA) - { - dnsServer.processNextRequest(); + if (WiFi.getMode() == WIFI_MODE_AP || WiFi.getMode() == WIFI_MODE_APSTA) + { + dnsServer.processNextRequest(); + } + Cron.delay(); } - Cron.delay(); } diff --git a/src/main.h b/src/main.h new file mode 100644 index 00000000..847ff61c --- /dev/null +++ b/src/main.h @@ -0,0 +1,24 @@ +#ifndef MAIN_H +#define MAIN_H + +void mDNS_start(); + +void connectWifi(); +// void handleLongBtn(); +void handleTmrNetworkOverseer(); +void setupCoordinatorMode(); +void startAP(const bool start); + +// void toggleUsbMode(); + + +void stopWifi(); +void checkFileSys(); + +struct TaskParams +{ + const char *url; +}; + + +#endif // MAIN_H \ No newline at end of file diff --git a/src/mqtt.cpp b/src/mqtt.cpp index bf30cb4f..d7b3c9ef 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -30,9 +30,12 @@ TimerHandle_t mqttPubStateTimer; const char *homeAssistant = "homeassistant"; const char *haSensor = "sensor"; +const char *haUpdate = "update"; const char *haButton = "button"; const char *haBinarySensor = "binary_sensor"; const char *willMessage = "offline"; // to do +const char *haUpdateLogoUrl = "https://xzg.xyzroe.cc/assets/images/logo.svg"; +const char *haUpdateAttrTemplate = "{\"in_progress\": {{ value_json['state'] }} }"; const char *availabilityTopic = "/avty"; const char *configTopic = "/config"; @@ -55,21 +58,28 @@ String getUptime() return readableTime; } -String getTemperature() +String getStrCpuTemp() { float CPUtemp = getCPUtemp(); return String(CPUtemp); } +String getStr1wTemp() +{ + float temp = get1wire(); + return String(temp); +} + String getWlanIp() { return networkCfg.wifiDhcp ? WiFi.localIP().toString() : networkCfg.wifiIp.toString(); } String getWlanSsid() -{ +{ String ssid = WiFi.SSID(); - if (ssid.isEmpty()) { + if (ssid.isEmpty()) + { ssid = "-"; } return ssid; @@ -97,7 +107,16 @@ String getConnections() String getZigbeeFWver() { - return String(CCTool.chip.fwRev); + String fw; + if (CCTool.chip.fwRev > 0) + { + fw = CCTool.chip.fwRev; + } + else + { + fw = systemCfg.zbFw; + } + return fw; } String getZigbeeHWrev() @@ -119,67 +138,108 @@ String getWorkMode() return "Error"; } +/* +String getRole() +{ + String role; + switch (systemCfg.zbRole) + { + case COORDINATOR: + role = "Coordinator"; + break; + case ROUTER: + role = "Router"; + break; + case OPENTHREAD: + role = "Thread"; + break; + default: + role = "Unknown"; + break; + } + return role; +} +*/ + +String getRole() +{ + String role = getRadioRoleKey(); + if (role.length() > 0) + { + role[0] = role[0] == ' ' ? role[0] : role[0] - 32; + } + return role; +} + +const char *rst_esp = "rst_esp"; +const char *rst_zig = "rst_zig"; +const char *enbl_bsl = "enbl_bsl"; +const char *io = "/io/"; +const char *cmd = "/cmd"; +const char *upd_esp = "upd_esp"; +const char *upd_zb = "upd_zb"; +const char *upd_esp_slash = "/upd/esp"; +const char *upd_zb_slash = "/upd/zb"; +const char *mdiPrefix = "mdi:"; +const char *coolant_temperature = "coolant-temperature"; +const char *access_point = "access-point"; +const char *temperature = "temperature"; +const char *diagnostic = "diagnostic"; +const char *config = "config"; +const char *restart = "restart"; + mqttTopicsConfig mqttTopicsConfigs[] = { {.name = "Restart ESP", .sensorType = haButton, - .sensorId = "rst_esp", - .stateTopic = "/io/rst_esp", - .commandTopic = "/cmd", - .icon = "mdi:restore-alert", - .payloadPress = "{cmd:\"rst_esp\"}", - .deviceClass = "restart"}, + .sensorId = rst_esp, + .stateTopic = String(io) + rst_esp, + .commandTopic = cmd, + .icon = String(mdiPrefix) + "restore-alert", + .payloadPress = rst_esp, + .deviceClass = restart}, {.name = "Restart Zigbee", .sensorType = haButton, - .sensorId = "rst_zig", - .stateTopic = "/io/rst_zig", - .commandTopic = "/cmd", - .icon = "mdi:restart", - .payloadPress = "{cmd:\"rst_zig\"}", - .deviceClass = "restart"}, + .sensorId = rst_zig, + .stateTopic = String(io) + rst_zig, + .commandTopic = cmd, + .icon = String(mdiPrefix) + restart, + .payloadPress = rst_zig, + .deviceClass = restart}, {.name = "Enable BSL", .sensorType = haButton, - .sensorId = "enbl_bsl", - .stateTopic = "/io/enbl_bsl", - .commandTopic = "/cmd", + .sensorId = enbl_bsl, + .stateTopic = String(io) + enbl_bsl, + .commandTopic = cmd, .icon = "mdi:flash", - .payloadPress = "{cmd:\"enbl_bsl\"}", + .payloadPress = enbl_bsl, .deviceClass = "identify"}, {.name = "Socket", .sensorType = haBinarySensor, .sensorId = "socket", .stateTopic = "/io/socket", .deviceClass = "connectivity"}, - {.name = "Update ESP32", - .sensorType = haBinarySensor, - .sensorId = "update_esp", - .stateTopic = "/io/update_esp", - .deviceClass = "update"}, {.name = "Uptime", .sensorType = haSensor, .sensorId = "uptime", .stateTopic = stateTopic, - .icon = "mdi:clock", - .valueTemplate = "{{ value_json.uptime }}", + .icon = String(mdiPrefix) + "clock", .getSensorValue = getUptime}, {.name = "WLAN IP", .sensorType = haSensor, .sensorId = "wlanIp", .stateTopic = stateTopic, - .icon = "mdi:wifi-check", - .valueTemplate = "{{ value_json.wlanIp }}", + .icon = String(mdiPrefix) + "wifi-check", .getSensorValue = getWlanIp}, {.name = "WLAN SSID", .sensorType = haSensor, .sensorId = "wlanSsid", .stateTopic = stateTopic, - .icon = "mdi:wifi-marker", - .valueTemplate = "{{ value_json.wlanSsid }}", + .icon = String(mdiPrefix) + "wifi-marker", .getSensorValue = getWlanSsid}, {.name = "WLAN RSSI", .sensorType = haSensor, .sensorId = "wlanRssi", .stateTopic = stateTopic, - .valueTemplate = "{{ value_json.wlanRssi }}", .deviceClass = "signal_strength", .unitOfMeasurement = "dBm", .getSensorValue = getWlanRssi}, @@ -187,120 +247,220 @@ mqttTopicsConfig mqttTopicsConfigs[] = { .sensorType = haSensor, .sensorId = "lanIp", .stateTopic = stateTopic, - .icon = "mdi:check-network", - .valueTemplate = "{{ value_json.lanIp }}", + .icon = String(mdiPrefix) + "check-network", .getSensorValue = getLanIp}, {.name = "ESP Temperature", .sensorType = haSensor, - .sensorId = "temperature", + .sensorId = temperature, + .stateTopic = stateTopic, + .icon = String(mdiPrefix) + coolant_temperature, + .deviceClass = temperature, + .unitOfMeasurement = "°C", + .getSensorValue = getStrCpuTemp}, + {.name = "1W Temperature", + .sensorType = haSensor, + .sensorId = "temperature1w", .stateTopic = stateTopic, - .icon = "mdi:coolant-temperature", - .valueTemplate = "{{ value_json.temperature }}", - .deviceClass = "temperature", + .icon = String(mdiPrefix) + coolant_temperature, + .deviceClass = temperature, .unitOfMeasurement = "°C", - .getSensorValue = getTemperature}, + .getSensorValue = getStr1wTemp}, {.name = "Hostname", .sensorType = haSensor, .sensorId = "hostname", .stateTopic = stateTopic, - .icon = "mdi:account-network", - .valueTemplate = "{{ value_json.hostname }}", + .icon = String(mdiPrefix) + "account-network", .getSensorValue = getHostname}, {.name = "Socket Connections", .sensorType = haSensor, .sensorId = "connections", .stateTopic = stateTopic, - .icon = "mdi:check-network-outline", - .valueTemplate = "{{ value_json.connections }}", + .icon = String(mdiPrefix) + "check-network-outline", .getSensorValue = getConnections}, {.name = "Mode", .sensorType = haSensor, .sensorId = "mode", .stateTopic = stateTopic, - .icon = "mdi:access-point-network", - .valueTemplate = "{{ value_json.mode }}", + .icon = String(mdiPrefix) + access_point + "-network", .deviceClass = "enum", + .entityCategory = diagnostic, .getSensorValue = getWorkMode}, - {.name = "Zigbee FW version", + {.name = "Radio chip", .sensorType = haSensor, - .sensorId = "zbfw", + .sensorId = "zbhw", .stateTopic = stateTopic, - .icon = "mdi:access-point", - .valueTemplate = "{{ value_json.zbfw }}", - .getSensorValue = getZigbeeFWver}, - {.name = "Zigbee HW revision", + .icon = String(mdiPrefix) + access_point, + .entityCategory = diagnostic, + .getSensorValue = getZigbeeHWrev}, + {.name = "Zigbee Role", .sensorType = haSensor, - .sensorId = "zbhw", + .sensorId = "zbrl", .stateTopic = stateTopic, - .icon = "mdi:access-point", - .valueTemplate = "{{ value_json.zbhw }}", - .getSensorValue = getZigbeeHWrev}}; + .icon = String(mdiPrefix) + access_point, + .entityCategory = diagnostic, + .getSensorValue = getRole}, + {.name = "ESP32 firmware", + .sensorType = haUpdate, + .sensorId = upd_esp, + .stateTopic = upd_esp_slash, + .commandTopic = cmd, + .entityCategory = config, + .entityPicture = haUpdateLogoUrl, + .payloadInstall = upd_esp, + //.releaseUrl = "zb_upd", + .jsonAttrTemplate = haUpdateAttrTemplate, + .jsonAttrTopic = upd_esp_slash}, + {.name = "Radio firmware", + .sensorType = haUpdate, + .sensorId = upd_zb, + .stateTopic = upd_zb_slash, + .commandTopic = cmd, + .entityCategory = config, + .entityPicture = haUpdateLogoUrl, + .payloadInstall = upd_zb, + //.releaseUrl = "zb_upd", + .jsonAttrTemplate = haUpdateAttrTemplate, + .jsonAttrTopic = upd_zb_slash}}; + +/*{.name = "Zigbee FW version", +.sensorType = haSensor, +.sensorId = "zbfw", +.stateTopic = stateTopic, +.icon = "mdi:access-point", +.valueTemplate = "{{ value_json.zbfw }}", +.getSensorValue = getZigbeeFWver},*/ void mqttConnectSetup() { LOGD("mqttConnectSetup"); - - if (mqttCfg.reconnectInt == 0) - { - mqttCfg.reconnectInt = 30; - LOGI("Reconnect Int didn't set. So use %d seconds", mqttCfg.reconnectInt); - } - if (mqttCfg.updateInt == 0) + if (true) // checkDNS()) { - mqttCfg.updateInt = 60; - LOGI("Update Int didn't set. So use %d seconds", mqttCfg.updateInt); - } - mqttReconnectTimer = xTimerCreate("mqttReconnectTimer", pdMS_TO_TICKS(mqttCfg.reconnectInt * 1000), pdFALSE, (void *)0, reinterpret_cast(connectToMqtt)); - mqttPubStateTimer = xTimerCreate("mqttPubStateTimer", pdMS_TO_TICKS(mqttCfg.updateInt * 1000), pdFALSE, (void *)0, reinterpret_cast(mqttPublishState)); + if (mqttCfg.reconnectInt == 0) + { + mqttCfg.reconnectInt = 30; + LOGD("Reconnect Int didn't set. So use %d seconds", mqttCfg.reconnectInt); + } + if (mqttCfg.updateInt == 0) + { + mqttCfg.updateInt = 60; + LOGD("Update Int didn't set. So use %d seconds", mqttCfg.updateInt); + } + mqttReconnectTimer = xTimerCreate("mqttReconnectTimer", pdMS_TO_TICKS(mqttCfg.reconnectInt * 1000), pdFALSE, (void *)0, reinterpret_cast(connectToMqtt)); + mqttPubStateTimer = xTimerCreate("mqttPubStateTimer", pdMS_TO_TICKS(mqttCfg.updateInt * 1000), pdFALSE, (void *)0, reinterpret_cast(mqttPublishState)); - if (mqttReconnectTimer == NULL) - { - LOGD("Failed to create mqttReconnectTimer"); - } + if (mqttReconnectTimer == NULL) + { + LOGD("Failed to create mqttReconnectTimer"); + } - if (mqttPubStateTimer == NULL) - { - LOGD("Failed to create mqttPubStateTimer"); - } + if (mqttPubStateTimer == NULL) + { + LOGD("Failed to create mqttPubStateTimer"); + } - uint16_t keepAlive = mqttCfg.updateInt + 10; - mqttClient.setKeepAlive(keepAlive); + uint16_t keepAlive = mqttCfg.updateInt + 10; + mqttClient.setKeepAlive(keepAlive); - const char* clientId = vars.deviceId; - mqttClient.setClientId(clientId); + const char *clientId = vars.deviceId; + mqttClient.setClientId(clientId); - mqttClient.setCredentials(mqttCfg.user, mqttCfg.pass); + mqttClient.setCredentials(mqttCfg.user, mqttCfg.pass); - //String topic = String(mqttCfg.topic) + "/avty"; - //mqttClient.setWill(topic.c_str(), 1, true, "offline"); + // String topic = String(mqttCfg.topic) + "/avty"; + // mqttClient.setWill(topic.c_str(), 1, true, "offline"); - mqttClient.setServer(mqttCfg.server, mqttCfg.port); + mqttClient.setServer(mqttCfg.server, mqttCfg.port); -/* NO SSL SUPPORT in current SDK -#if ASYNC_TCP_SSL_ENABLED - mqttClient.setSecure(MQTT_SECURE); - if (MQTT_SECURE) - { - mqttClient.addServerFingerprint((const uint8_t[])MQTT_SERVER_FINGERPRINT); + /* NO SSL SUPPORT in current SDK + #if ASYNC_TCP_SSL_ENABLED + mqttClient.setSecure(MQTT_SECURE); + if (MQTT_SECURE) + { + mqttClient.addServerFingerprint((const uint8_t[])MQTT_SERVER_FINGERPRINT); + } + #endif + */ + + mqttClient.onConnect(onMqttConnect); + mqttClient.onDisconnect(onMqttDisconnect); + + mqttClient.onMessage(onMqttMessage); + + connectToMqtt(); + /*if (xTimerStart(mqttReconnectTimer, 0) != pdPASS) + { + LOGD("Failed to start timer"); + }*/ } -#endif -*/ +} - mqttClient.onConnect(onMqttConnect); - mqttClient.onDisconnect(onMqttDisconnect); +void mqttDisconnectCleanup() +{ + LOGD("mqttDisconnectCleanup"); - mqttClient.onMessage(onMqttMessage); + // Остановить и удалить таймеры + if (mqttReconnectTimer != NULL) + { + if (xTimerStop(mqttReconnectTimer, 0) == pdPASS) + { + if (xTimerDelete(mqttReconnectTimer, 0) == pdPASS) + { + mqttReconnectTimer = NULL; + LOGD("mqttReconnectTimer stopped and deleted"); + } + else + { + LOGD("Failed to delete mqttReconnectTimer"); + } + } + else + { + LOGD("Failed to stop mqttReconnectTimer"); + } + } + + if (mqttPubStateTimer != NULL) + { + if (xTimerStop(mqttPubStateTimer, 0) == pdPASS) + { + if (xTimerDelete(mqttPubStateTimer, 0) == pdPASS) + { + mqttPubStateTimer = NULL; + LOGD("mqttPubStateTimer stopped and deleted"); + } + else + { + LOGD("Failed to delete mqttPubStateTimer"); + } + } + else + { + LOGD("Failed to stop mqttPubStateTimer"); + } + } - if (xTimerStart(mqttReconnectTimer, 0) != pdPASS) + // Отключить MQTT-клиент + if (mqttClient.connected()) { - LOGD("Failed to start timer"); + mqttClient.disconnect(); + LOGD("MQTT client disconnected"); } + + // Очистить любые другие ресурсы, если необходимо + // Например, очистить буферы, закрыть соединения и т.д. } void connectToMqtt() { - LOGD("Connecting to MQTT..."); - mqttClient.connect(); + if (!vars.zbFlashing) + { + LOGD("Connecting..."); + mqttClient.connect(); + } + else + { + LOGD("only after ZB flash"); + } } void onMqttConnect(bool sessionPresent) @@ -311,16 +471,22 @@ void onMqttConnect(bool sessionPresent) vars.mqttConn = true; - mqttPublishDiscovery(); + if (mqttCfg.discovery) + { + mqttPublishDiscovery(); + } mqttPublishIo("rst_esp", 0); mqttPublishIo("rst_zig", 0); mqttPublishIo("enbl_bsl", 0); - bool socket_state = vars.connectedClients ? 1 : 0; - mqttPublishIo("socket", socket_state); - mqttPublishIo("update_esp", vars.updateEspAvail); + mqttPublishIo("socket", vars.connectedClients ? 1 : 0); + // mqttPublishIo("update_esp", vars.updateEspAvail); + // mqttPublishIo("update_zb", vars.updateZbAvail); mqttPublishState(); + mqttPublishUpdate("esp"); + mqttPublishUpdate("zb"); + mqttPublishAvail(); } @@ -330,29 +496,42 @@ void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) vars.mqttConn = false; - xTimerStart(mqttReconnectTimer, 0); + if (!vars.zbFlashing) + { + xTimerStart(mqttReconnectTimer, 0); + } } void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { + static char json[512]; + // static DynamicJsonDocument doc(512); + String topicStr(topic); if (topicStr.endsWith("/cmd")) { - char json[len + 1]; - memcpy(json, payload, len); - json[len] = '\0'; - - DynamicJsonDocument doc(512); - deserializeJson(doc, json); - const char *command = doc["cmd"]; + if (len < sizeof(json)) + { + memcpy(json, payload, len); + json[len] = '\0'; - LOGD("cmd - %s", String(json)); + LOGD("json - %s", String(json)); - if (command) + if (strlen(json) > 0) + { + executeCommand(json); + } + else + { + LOGD("empty cmnd"); + } + } + else { - executeCommand(command); + LOGW("cmnd too large"); } } + topicStr.clear(); } void executeCommand(const char *command) @@ -361,7 +540,7 @@ void executeCommand(const char *command) if (strcmp(command, "rst_esp") == 0) { printLogMsg("ESP restart MQTT"); - ESP.restart(); + restartDevice(); } if (strcmp(command, "rst_zig") == 0) @@ -375,6 +554,20 @@ void executeCommand(const char *command) printLogMsg("Zigbee BSL enable MQTT"); zigbeeEnableBSL(); } + + if (strcmp(command, "upd_esp") == 0) + { + mqttPublishUpdate("esp", true); + printLogMsg("Going to update ESP"); + // to - do + } + + if (strcmp(command, "upd_zb") == 0) + { + mqttPublishUpdate("zb", true); + printLogMsg("Going to update ESP"); + // to - do + } } void mqttPublishAvail() @@ -394,36 +587,97 @@ void mqttPublishIo(const String &io, bool st) } } +void mqttPublishUpdate(const String &chip, bool instState) +{ + // String state = st ? "ON" : "OFF"; + if (mqttClient.connected()) + { + /* + String topic = String(mqttCfg.topic) + "/io/" + io; + LOGD("Pub Io %s at %s", state.c_str(), topic.c_str()); + mqttClient.publish(topic.c_str(), 0, true, state.c_str());*/ + static DynamicJsonDocument jsonBuff(256); + static String mqttBuffer; + char verArr[25]; + const char *env = STRINGIFY(BUILD_ENV_NAME); + + const char *installed_version = "installed_version"; + const char *latest_version = "latest_version"; + const char *state = "state"; + + if (chip == "esp") + { + sprintf(verArr, "%s (%s)", VERSION, env); + jsonBuff[installed_version] = String(verArr); + jsonBuff[latest_version] = String(vars.lastESPVer); + jsonBuff[state] = int(instState); + } + else if (chip == "zb") + { + jsonBuff[installed_version] = String(getZigbeeFWver()); + jsonBuff[latest_version] = String(vars.lastZBVer); + jsonBuff[state] = int(instState); + } + + String topic = String(mqttCfg.topic) + "/upd/" + chip; + LOGD("Pub upd %s at %s", chip, topic.c_str()); + + serializeJson(jsonBuff, mqttBuffer); + + mqttClient.publish(topic.c_str(), 1, true, mqttBuffer.c_str()); + jsonBuff.clear(); + mqttBuffer.clear(); + } +} + void mqttPublishState() { - DynamicJsonDocument buffJson(512); - for (const auto &item : mqttTopicsConfigs) + if (!vars.zbFlashing) { - if (item.getSensorValue != nullptr) + static DynamicJsonDocument buffJson(512); + static String mqttBuffer; + + buffJson.clear(); + for (const auto &item : mqttTopicsConfigs) { - String sensorValue = item.getSensorValue(); - if (sensorValue.length() > 0) + if (item.sensorId != "temperature1w" || vars.oneWireIs) { - buffJson[item.sensorId] = sensorValue; + if (item.getSensorValue != nullptr) + { + String sensorValue = item.getSensorValue(); + if (sensorValue.length() > 0) + { + buffJson[item.sensorId] = sensorValue; + } + } } } - } - String topic = mqttCfg.topic + String(stateTopic); - String mqttBuffer; - serializeJson(buffJson, mqttBuffer); + String topic = mqttCfg.topic + String(stateTopic); + serializeJson(buffJson, mqttBuffer); - LOGD("%s", mqttBuffer.c_str()); + LOGD("%s", mqttBuffer.c_str()); - mqttClient.publish(topic.c_str(), 0, true, mqttBuffer.c_str()); + mqttClient.publish(topic.c_str(), 0, true, mqttBuffer.c_str()); + topic.clear(); + mqttBuffer.clear(); + } + else + { + LOGD("only after ZB flash"); + } xTimerStart(mqttPubStateTimer, 0); } void mqttPublishDiscovery() { - DynamicJsonDocument devInfo(256); + static DynamicJsonDocument devInfo(256); + static DynamicJsonDocument buffJson(2048); + static String mqttBuffer; + + devInfo.clear(); devInfo["ids"] = vars.deviceId; - devInfo["name"] = systemCfg.hostname; + devInfo["name"] = systemCfg.hostname; // mqttCfg.topic; devInfo["mf"] = "XZG"; devInfo["mdl"] = hwConfig.board; char verArr[25]; @@ -431,50 +685,76 @@ void mqttPublishDiscovery() sprintf(verArr, "%s (%s)", VERSION, env); devInfo["sw"] = String(verArr); - DynamicJsonDocument buffJson(2048); - String mqttBuffer; - + buffJson.clear(); for (const auto &item : mqttTopicsConfigs) { - buffJson["dev"] = devInfo; - buffJson["name"] = item.name; - buffJson["uniq_id"] = String(mqttCfg.topic) + "/" + item.sensorId; - buffJson["stat_t"] = mqttCfg.topic + item.stateTopic; - buffJson["avty_t"] = mqttCfg.topic + String(availabilityTopic); - if (!String(item.commandTopic).isEmpty()) + if (item.sensorId != "temperature1w" || vars.oneWireIs) { - buffJson["cmd_t"] = mqttCfg.topic + item.commandTopic; - } + buffJson["dev"] = devInfo; + buffJson["name"] = item.name; + buffJson["uniq_id"] = String(vars.deviceId) + "_" + item.sensorId; + buffJson["stat_t"] = mqttCfg.topic + item.stateTopic; + buffJson["avty_t"] = mqttCfg.topic + String(availabilityTopic); + if (!String(item.commandTopic).isEmpty()) + { + buffJson["cmd_t"] = mqttCfg.topic + item.commandTopic; + } - if (!String(item.icon).isEmpty()) - { - buffJson["icon"] = item.icon; - } - if (!String(item.payloadPress).isEmpty()) - { - buffJson["payload_press"] = item.payloadPress; - } - if (!String(item.valueTemplate).isEmpty()) - { - buffJson["val_tpl"] = item.valueTemplate; - } - if (!String(item.jsonAttributeTopic).isEmpty()) - { - buffJson["json_attr_t"] = mqttCfg.topic + item.jsonAttributeTopic; - } - if (!String(item.deviceClass).isEmpty()) - { - buffJson["dev_cla"] = item.deviceClass; - } - if (!String(item.unitOfMeasurement).isEmpty()) - { - buffJson["unit_of_meas"] = item.unitOfMeasurement; - } + if (!String(item.icon).isEmpty()) + { + buffJson["ic"] = item.icon; + } + if (!String(item.payloadPress).isEmpty()) + { + buffJson["pl_prs"] = item.payloadPress; + } + if (item.sensorType == haSensor) + { + buffJson["val_tpl"] = "{{ value_json." + item.sensorId + " }}"; + } + if (!String(item.deviceClass).isEmpty()) + { + buffJson["dev_cla"] = item.deviceClass; + } + if (!String(item.unitOfMeasurement).isEmpty()) + { + buffJson["unit_of_meas"] = item.unitOfMeasurement; + } + if (!String(item.entityCategory).isEmpty()) + { + buffJson["ent_cat"] = item.entityCategory; + } + if (!String(item.entityPicture).isEmpty()) + { + buffJson["ent_pic"] = item.entityPicture; + } + if (!String(item.payloadInstall).isEmpty()) + { + buffJson["pl_inst"] = item.payloadInstall; + } + if (!String(item.releaseUrl).isEmpty()) + { + buffJson["rel_u"] = item.releaseUrl; + } + if (!String(item.jsonAttrTemplate).isEmpty()) + { + buffJson["json_attr_tpl"] = item.jsonAttrTemplate; + } + if (!String(item.jsonAttrTopic).isEmpty()) + { + buffJson["json_attr_t"] = mqttCfg.topic + item.jsonAttrTopic; + } - String topic = String(homeAssistant) + "/" + item.sensorType + "/" + mqttCfg.topic + "/" + item.sensorId + configTopic; - serializeJson(buffJson, mqttBuffer); - mqttClient.publish(topic.c_str(), 1, true, mqttBuffer.c_str()); - buffJson.clear(); - mqttBuffer.clear(); + String topic = String(homeAssistant) + "/" + item.sensorType + "/" + mqttCfg.topic + "/" + item.sensorId + configTopic; + serializeJson(buffJson, mqttBuffer); + LOGD("%s at %s", mqttBuffer.c_str(), topic.c_str()); + mqttClient.publish(topic.c_str(), 1, true, mqttBuffer.c_str()); + buffJson.clear(); + mqttBuffer.clear(); + } } + + devInfo.clear(); + buffJson.clear(); + mqttBuffer.clear(); } \ No newline at end of file diff --git a/src/mqtt.h b/src/mqtt.h index 745648e2..09beeddc 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -2,6 +2,7 @@ #include void mqttConnectSetup(); +void mqttDisconnectCleanup(); void connectToMqtt(); void onMqttConnect(bool sessionPresent); void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); @@ -9,6 +10,7 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties void executeCommand(const char *command); void mqttPublishAvail(); void mqttPublishIo(const String &io, bool st); +void mqttPublishUpdate(const String &chip, bool state = false); void mqttPublishState(); void mqttPublishDiscovery(); @@ -23,8 +25,13 @@ struct mqttTopicsConfig { String icon; String payloadPress; String valueTemplate; - String jsonAttributeTopic; String deviceClass; String unitOfMeasurement; + String entityCategory; + String entityPicture; + String payloadInstall; + String releaseUrl; + String jsonAttrTemplate; + String jsonAttrTopic; SensorValueFunction getSensorValue; }; \ No newline at end of file diff --git a/src/per.cpp b/src/per.cpp index 8f0fded8..824c837a 100644 --- a/src/per.cpp +++ b/src/per.cpp @@ -40,7 +40,10 @@ extern CCTools CCTool; // extern int btnFlag; int btnFlag = 0; -Ticker tmrBtnLongPress(handleLongBtn, 1000, 0, MILLIS); +#include + +//Ticker tmrBtnLongPress(handleLongBtn, 1000, 0, MILLIS); +Ticker tmrBtnLongPress; // Объявление объекта Ticker без параметров void handleLongBtn() { @@ -71,7 +74,8 @@ void handleLongBtn() setLedsDisable(!vars.disableLeds); vars.disableLeds = !vars.disableLeds; } - tmrBtnLongPress.stop(); + //tmrBtnLongPress.stop(); + tmrBtnLongPress.detach(); // Использование метода detach() вместо stop() btnFlag = false; } if (btnFlag >= 5) @@ -79,7 +83,8 @@ void handleLongBtn() ledControl.modeLED.mode = LED_FLASH_3Hz; printLogMsg("BTN - 5sec - zigbeeEnableBSL"); zigbeeEnableBSL(); - tmrBtnLongPress.stop(); + //tmrBtnLongPress.stop(); + tmrBtnLongPress.detach(); // Использование метода detach() вместо stop() btnFlag = false; } } @@ -95,13 +100,13 @@ void toggleUsbMode() systemCfg.workMode = WORK_MODE_NETWORK; } saveSystemConfig(systemCfg); - LOGD("Change mode to %s", String(systemCfg.workMode)); + LOGD("Change mode to %s", String(systemCfg.workMode).c_str()); if (vars.hwLedUsbIs) { ledControl.modeLED.mode = LED_ON; } - ESP.restart(); + restartDevice(); } void buttonInit() @@ -148,12 +153,14 @@ void buttonLoop() { if (digitalRead(hwConfig.mist.btnPin) != hwConfig.mist.btnPlr) // pressed { - if (tmrBtnLongPress.state() == STOPPED) + //if (tmrBtnLongPress.state() == STOPPED) + if (!tmrBtnLongPress.active()) // Проверка активности таймера с помощью метода active() { - tmrBtnLongPress.start(); + //tmrBtnLongPress.start(); + tmrBtnLongPress.attach(1, handleLongBtn); // Запуск таймера с интервалом 1 секунда и функцией обратного вызова handleLongBtn } } - tmrBtnLongPress.update(); + //tmrBtnLongPress.update(); } IRAM_ATTR bool debounce() @@ -190,7 +197,7 @@ void ledModeSetup() LOGD("%d", ledControl.modeLED.mode); - xTaskCreate(ledTask, "MODE LED Task", 2048, &ledControl.modeLED, 6, NULL); + xTaskCreate(ledTask, "MODE LED Task", 2048, &ledControl.modeLED, 7, NULL); } void ledPwrSetup() diff --git a/src/version.h b/src/version.h index aa76c0fe..5f23cfb8 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ // AUTO GENERATED FILE #ifndef VERSION - #define VERSION "20240526" + #define VERSION "20241001" #endif diff --git a/src/web.cpp b/src/web.cpp index 56d51c19..cbf1854d 100644 --- a/src/web.cpp +++ b/src/web.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +// #include #include #include #include @@ -10,6 +10,7 @@ #include #include #include +#include #include "config.h" #include "web.h" @@ -20,6 +21,7 @@ #include "const/keys.h" // #include "const/hw.h" +/* #include "webh/html/PAGE_VPN.html.gz.h" #include "webh/html/PAGE_MQTT.html.gz.h" #include "webh/html/PAGE_ABOUT.html.gz.h" @@ -31,18 +33,21 @@ #include "webh/html/PAGE_TOOLS.html.gz.h" #include "webh/html/PAGE_NETWORK.html.gz.h" #include "webh/html/PAGE_LOGIN.html.gz.h" - +*/ +/* #include "webh/js/i18next.min.js.gz.h" #include "webh/js/i18nextHttpBackend.min.js.gz.h" #include "webh/js/functions.js.gz.h" #include "webh/js/bootstrap.bundle.min.js.gz.h" #include "webh/js/jquery-min.js.gz.h" #include "webh/js/masonry.js.gz.h" +*/ -#include "webh/css/style.css.gz.h" -#include "webh/img/icons.svg.gz.h" -#include "webh/img/logo.svg.gz.h" +// #include "webh/css/style.css.gz.h" +// #include "webh/img/icons.svg.gz.h" +// #include "webh/img/logo.svg.gz.h" +/* #include "webh/json/en.json.gz.h" #include "webh/json/uk.json.gz.h" #include "webh/json/zh.json.gz.h" @@ -56,6 +61,7 @@ #include "webh/json/it.json.gz.h" #include "webh/json/pl.json.gz.h" #include "webh/json/cz.json.gz.h" +*/ // #define HTTP_DOWNLOAD_UNIT_SIZE 3000 @@ -71,7 +77,6 @@ extern CCTools CCTool; extern struct SysVarsStruct vars; extern struct ThisConfigStruct hwConfig; extern BrdConfigStruct brdConfigs[BOARD_CFG_CNT]; - extern struct SystemConfigStruct systemCfg; extern struct NetworkConfigStruct networkCfg; extern struct VpnConfigStruct vpnCfg; @@ -84,33 +89,98 @@ extern IPAddress apIP; bool wifiWebSetupInProgress = false; bool eventOK = false; -// extern const char *coordMode; - -// extern const char *deviceModel; +// API strings +const char *argAction = "action"; +const char *argPage = "page"; +const char *argParam = "param"; +const char *argFilename = "filename"; +const char *argCmd = "cmd"; +const char *argUrl = "url"; +const char *argType = "type"; +const char *argConf = "conf"; +const char *argLed = "led"; +const char *argAct = "act"; +const char *apiWrongArgs = "wrong args"; +const char *apiOk = "ok"; +const char *errLink = "Error getting link"; + +// MIME types const char *contTypeTextHtml = "text/html"; const char *contTypeTextJs = "text/javascript"; const char *contTypeTextCss = "text/css"; const char *contTypeTextSvg = "image/svg+xml"; +const char *contTypeJson = "application/json"; + +// Misc. strings const char *checked = "true"; const char *respHeaderName = "respValuesArr"; const char *respTimeZonesName = "respTimeZones"; -const char *contTypeJson = "application/json"; -const char *contTypeText = "text/plain"; - const char *tempFile = "/fw.hex"; bool opened = false; File fwFile; -extern bool loadFileConfigMqtt(); -extern bool loadFileConfigWg(); +// extern bool loadFileConfigMqtt(); +// extern bool loadFileConfigWg(); WebServer serverWeb(80); -// HTTPClient clientWeb; WiFiClient eventsClient; +// apiHandler() handler functions, +// listed in index order +static void apiDefault(); +static void apiGetPage(); +static void apiGetParam(); +static void apiStartWifiScan(); +static void apiWifiScanStatus(); +static void apiGetLog(); +static void apiCmd(); +static void apiWifiConnnectStat(); +static void apiGetFile(); +static void apiDelFile(); +static void apiGetFileList(); + +// apiCMD() handler functions, +// listed in index order +static void apiCmdDefault(String &result); +static void apiCmdZbRouterRecon(String &result); +static void apiCmdZbRestart(String &result); +static void apiCmdZbEnableBsl(String &result); +static void apiCmdEspReset(String &result); +static void apiCmdAdapterLan(String &result); +static void apiCmdAdapterUsb(String &result); +static void apiCmdLedAct(String &result); +static void apiCmdZbFlash(String &result); +static void apiCmdClearLog(String &result); +static void apiCmdUpdateUrl(String &result); +static void apiCmdZbCheckFirmware(String &result); +static void apiCmdZbLedToggle(String &result); +static void apiCmdFactoryReset(String &result); +static void apiCmdDnsCheck(String &result); +static void apiCmdBoardName(String &result); + +// functions called exactly once each +// from getRootData(): +static inline void getRootEthTab(DynamicJsonDocument &doc, + bool update, + const String &noConn); +static inline void getRootWifi(DynamicJsonDocument &doc, + bool update, + const String &noConn); +static inline void getRootHwMisc(DynamicJsonDocument &doc, + bool update); +static inline void getRootVpnWireGuard(DynamicJsonDocument &doc); +static inline void getRootVpnHusarnet(DynamicJsonDocument &doc); +static inline void getRootUptime(DynamicJsonDocument &doc); +static inline void getRootCpuTemp(DynamicJsonDocument &doc); +static inline void getRootOneWireTemp(DynamicJsonDocument &doc); +static inline void getRootHeapsize(DynamicJsonDocument &doc); +static inline void getRootNvsStats(DynamicJsonDocument &doc); +static inline void getRootSockets(DynamicJsonDocument &doc); +static inline void getRootTime(DynamicJsonDocument &doc); + void webServerHandleClient() { serverWeb.handleClient(); @@ -126,7 +196,7 @@ void redirectLogin(int msg = 0) serverWeb.sendHeader("Location", path); serverWeb.sendHeader("Cache-Control", "no-cache"); - serverWeb.send(301); + serverWeb.send(HTTP_CODE_MOVED_PERMANENTLY); } void handleLoader() @@ -140,12 +210,14 @@ void handleLoader() redirectLogin(); return; } - sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); + // sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); + sendGzipFromFS("/html/loader.html.gz", contTypeTextHtml); } void initWebServer() { /* ----- LANG FILES | START -----*/ + /* serverWeb.on("/lg/en.json", []() { sendGzip(contTypeJson, en_json_gz, en_json_gz_len); }); serverWeb.on("/lg/uk.json", []() @@ -172,28 +244,78 @@ void initWebServer() { sendGzip(contTypeJson, pl_json_gz, pl_json_gz_len); }); serverWeb.on("/lg/cz.json", []() { sendGzip(contTypeJson, cz_json_gz, cz_json_gz_len); }); + */ + serverWeb.on("/lg/en.json", []() + { sendGzipFromFS("/json/en.json.gz", contTypeJson); }); + serverWeb.on("/lg/uk.json", []() + { sendGzipFromFS("/json/uk.json.gz", contTypeJson); }); + serverWeb.on("/lg/zh.json", []() + { sendGzipFromFS("/json/zh.json.gz", contTypeJson); }); + serverWeb.on("/lg/es.json", []() + { sendGzipFromFS("/json/es.json.gz", contTypeJson); }); + serverWeb.on("/lg/pt.json", []() + { sendGzipFromFS("/json/pt.json.gz", contTypeJson); }); + serverWeb.on("/lg/ru.json", []() + { sendGzipFromFS("/json/ru.json.gz", contTypeJson); }); + serverWeb.on("/lg/fr.json", []() + { sendGzipFromFS("/json/fr.json.gz", contTypeJson); }); + serverWeb.on("/lg/de.json", []() + { sendGzipFromFS("/json/de.json.gz", contTypeJson); }); + serverWeb.on("/lg/ja.json", []() + { sendGzipFromFS("/json/ja.json.gz", contTypeJson); }); + serverWeb.on("/lg/tr.json", []() + { sendGzipFromFS("/json/tr.json.gz", contTypeJson); }); + serverWeb.on("/lg/it.json", []() + { sendGzipFromFS("/json/it.json.gz", contTypeJson); }); + serverWeb.on("/lg/pl.json", []() + { sendGzipFromFS("/json/pl.json.gz", contTypeJson); }); + serverWeb.on("/lg/cz.json", []() + { sendGzipFromFS("/json/cz.json.gz", contTypeJson); }); + /* ----- LANG FILES | END -----*/ /* ----- JS and CSS FILES | START -----*/ + /* serverWeb.on("/js/i18next.min.js", []() { sendGzip(contTypeTextJs, i18next_min_js_gz, i18next_min_js_gz_len); }); +serverWeb.on("/js/i18nextHttpBackend.min.js", []() + { sendGzip(contTypeTextJs, i18nextHttpBackend_min_js_gz, i18nextHttpBackend_min_js_gz_len); }); +serverWeb.on("/js/bootstrap.bundle.min.js", []() + { sendGzip(contTypeTextJs, bootstrap_bundle_min_js_gz, bootstrap_bundle_min_js_gz_len); }); +serverWeb.on("/js/masonry.js", []() + { sendGzip(contTypeTextJs, masonry_js_gz, masonry_js_gz_len); }); +serverWeb.on("/js/functions.js", []() + { sendGzip(contTypeTextJs, functions_js_gz, functions_js_gz_len); }); +serverWeb.on("/js/jquery-min.js", []() + { sendGzip(contTypeTextJs, jquery_min_js_gz, jquery_min_js_gz_len); }); + */ + serverWeb.on("/js/i18next.min.js", []() + { sendGzipFromFS("/js/i18next.min.js.gz", contTypeTextJs); }); serverWeb.on("/js/i18nextHttpBackend.min.js", []() - { sendGzip(contTypeTextJs, i18nextHttpBackend_min_js_gz, i18nextHttpBackend_min_js_gz_len); }); + { sendGzipFromFS("/js/i18nextHttpBackend.min.js.gz", contTypeTextJs); }); serverWeb.on("/js/bootstrap.bundle.min.js", []() - { sendGzip(contTypeTextJs, bootstrap_bundle_min_js_gz, bootstrap_bundle_min_js_gz_len); }); + { sendGzipFromFS("/js/bootstrap.bundle.min.js.gz", contTypeTextJs); }); serverWeb.on("/js/masonry.js", []() - { sendGzip(contTypeTextJs, masonry_js_gz, masonry_js_gz_len); }); + { sendGzipFromFS("/js/masonry.js.gz", contTypeTextJs); }); serverWeb.on("/js/functions.js", []() - { sendGzip(contTypeTextJs, functions_js_gz, functions_js_gz_len); }); + { sendGzipFromFS("/js/functions.js.gz", contTypeTextJs); }); serverWeb.on("/js/jquery-min.js", []() - { sendGzip(contTypeTextJs, jquery_min_js_gz, jquery_min_js_gz_len); }); + { sendGzipFromFS("/js/jquery-min.js.gz", contTypeTextJs); }); + + /*serverWeb.on("/css/style.css", []() + { sendGzip(contTypeTextCss, required_css_gz, required_css_gz_len); });*/ serverWeb.on("/css/style.css", []() - { sendGzip(contTypeTextCss, required_css_gz, required_css_gz_len); }); + { sendGzipFromFS("/css/style.css.gz", contTypeTextCss); }); /* ----- JS and CSS FILES | END -----*/ /* ----- SVG FILES | START -----*/ + /* serverWeb.on("/logo.svg", []() + { sendGzip(contTypeTextSvg, logo_svg_gz, logo_svg_gz_len); }); */ serverWeb.on("/logo.svg", []() - { sendGzip(contTypeTextSvg, logo_svg_gz, logo_svg_gz_len); }); + { sendGzipFromFS("/img/logo.svg.gz", contTypeTextSvg); }); + serverWeb.on("/icons.svg", []() - { sendGzip(contTypeTextSvg, icons_svg_gz, icons_svg_gz_len); }); + { sendGzipFromFS("/img/icons.svg.gz", contTypeTextSvg); }); + /* serverWeb.on("/icons.svg", []() + { sendGzip(contTypeTextSvg, icons_svg_gz, icons_svg_gz_len); }); */ serverWeb.onNotFound(handleNotFound); @@ -237,7 +359,7 @@ void initWebServer() /* ----- OTA | END -----*/ const char *headerkeys[] = {"Content-Length", "Cookie"}; - size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *); + constexpr size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *); serverWeb.collectHeaders(headerkeys, headerkeyssize); serverWeb.begin(); LOGD("done"); @@ -254,7 +376,7 @@ bool captivePortal() { LOGD("Request redirected to captive portal"); serverWeb.sendHeader("Location", String("http://") + apIP.toString(), true); - serverWeb.send(302, "text/plain", ""); + serverWeb.send(HTTP_CODE_FOUND, contTypeText, ""); serverWeb.client().stop(); return true; } @@ -298,13 +420,16 @@ void handleNotFound() serverWeb.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); serverWeb.sendHeader("Pragma", "no-cache"); serverWeb.sendHeader("Expires", "-1"); - serverWeb.send(404, "text/plain", message); + serverWeb.send(HTTP_CODE_NOT_FOUND, contTypeText, message); } void handleUpdateRequest() { - serverWeb.sendHeader("Connection", "close"); - serverWeb.send(HTTP_CODE_OK, contTypeText, "Upload OK. Try to flash..."); + serverWeb.sendHeader("Connection", + "close"); + serverWeb.send(HTTP_CODE_OK, + contTypeText, + "Upload OK. Try to flash..."); } void handleEspUpdateUpload() @@ -316,16 +441,35 @@ void handleEspUpdateUpload() HTTPUpload &upload = serverWeb.upload(); static long contentLength = 0; + static bool isSpiffsUpdate = false; + if (upload.status == UPLOAD_FILE_START) { - LOGD("hostHeader: %s", serverWeb.hostHeader()); contentLength = serverWeb.header("Content-Length").toInt(); - LOGD("contentLength: %s", String(contentLength)); - LOGD("Update ESP from file %s size: %s", String(upload.filename.c_str()), String(upload.totalSize)); - LOGD("upload.currentSize %s", String(upload.currentSize)); - if (!Update.begin(contentLength)) + String filename = upload.filename.c_str(); + LOGD("hostHeader: %s", serverWeb.hostHeader().c_str()); + LOGD("contentLength: %s", String(contentLength).c_str()); + LOGD("Update ESP from file %s size: %s", filename.c_str(), String(upload.totalSize).c_str()); + LOGD("upload.currentSize %s", String(upload.currentSize).c_str()); + + // Определяем, является ли это обновлением файловой системы + if (filename.endsWith(".fs.bin")) { - Update.printError(Serial); + isSpiffsUpdate = true; + if (!Update.begin(contentLength, U_SPIFFS)) // contentLength + { + Update.printError(Serial); + return; + } + } + else + { + isSpiffsUpdate = false; + if (!Update.begin(contentLength, U_FLASH)) // contentLength + { + Update.printError(Serial); + return; + } } Update.onProgress(progressFunc); } @@ -340,24 +484,43 @@ void handleEspUpdateUpload() { if (Update.end(true)) { - LOGD("Update success. Rebooting..."); - ESP.restart(); + serverWeb.send(HTTP_CODE_OK, contTypeText, ""); + if (isSpiffsUpdate) + { + LOGD("SPIFFS Update success. Rebooting..."); + } + else + { + LOGD("Firmware Update success. Rebooting..."); + } + restartDevice(); } else { + serverWeb.send(HTTP_CODE_NOT_FOUND, contTypeText, "Error"); LOGD("Update error: "); Update.printError(Serial); } } + else if (upload.status == UPLOAD_FILE_ABORTED) + { + serverWeb.send(HTTP_CODE_INTERNAL_SERVER_ERROR, contTypeText, "Aborted"); + Update.end(); + LOGD("Update aborted"); + } } void handleEvents() { if (is_authenticated()) { + if (eventsClient) + { + eventsClient.stop(); + } eventsClient = serverWeb.client(); if (eventsClient) - { // send events header + { eventsClient.println("HTTP/1.1 200 OK"); eventsClient.println("Content-Type: text/event-stream;"); eventsClient.println("Connection: close"); @@ -369,25 +532,62 @@ void handleEvents() } } -void sendEvent(const char *event, const uint8_t evsz, const String data) +void sendEvent(const char *event, + const uint8_t evsz, + const String data) { if (eventsClient) { char evnmArr[10 + evsz]; - sprintf(evnmArr, "event: %s\n", event); + snprintf(evnmArr, sizeof(evnmArr), "event: %s\n", event); eventsClient.print(evnmArr); eventsClient.print(String("data: ") + data + "\n\n"); - // eventsClient.println(); eventsClient.flush(); } } -void sendGzip(const char *contentType, const uint8_t content[], uint16_t contentLen) +void sendGzipFromFS(const char *path, const char *contentType) +{ + File file = LittleFS.open(path, FILE_READ); + if (!file) + { + serverWeb.send(HTTP_CODE_NOT_FOUND, contTypeText, "File not found"); + LOGD("File not found: %s", path); + return; + } + + size_t fileSize = file.size(); + if (fileSize == 0) + { + serverWeb.send(HTTP_CODE_INTERNAL_SERVER_ERROR, contTypeText, "File is empty"); + LOGD("File is empty: %s", path); + file.close(); + return; + } + + LOGD("File - %s, Size - %d", path, fileSize); + + serverWeb.streamFile(file, contentType); + file.close(); +} + +/* +void sendGzip(const char *contentType, + const uint8_t content[], + uint16_t contentLen) { - serverWeb.sendHeader(F("Content-Encoding"), F("gzip")); - serverWeb.send_P(HTTP_CODE_OK, contentType, (const char *)content, contentLen); + serverWeb.sendHeader(F("Content-Encoding"), + F("gzip")); + serverWeb.send_P(HTTP_CODE_OK, + contentType, + (const char *)content, + contentLen); } +*/ +// This isn't called from anywhere, +// does it even work? +/* void hex2bin(uint8_t *out, const char *in) { // uint8_t sz = 0; @@ -410,482 +610,605 @@ void hex2bin(uint8_t *out, const char *in) // sz++; } } +*/ -void handleApi() -{ // http://xzg.local/api?action=0&page=0 - enum API_ACTION_t : uint8_t - { - API_GET_PAGE, - API_GET_PARAM, - API_STARTWIFISCAN, - API_WIFISCANSTATUS, - API_GET_FILELIST, - API_GET_FILE, - API_SEND_HEX, - API_WIFICONNECTSTAT, - API_CMD, - API_GET_LOG, - API_DEL_FILE //, - // API_FLASH_ZB - }; - const char *action = "action"; - const char *page = "page"; - // const char *Authentication = "Authentication"; - const char *param = "param"; - const char *wrongArgs = "wrong args"; - const char *ok = "ok"; - const char *argFilename = "filename"; - - if (!is_authenticated()) - { - redirectLogin(1); - return; - } +static void apiGetLog() +{ + String result; + result = logPrint(); + serverWeb.send(HTTP_CODE_OK, contTypeText, result); +} - if (serverWeb.argName(0) != action) +static void apiCmdUpdateUrl(String &result) +{ + if (serverWeb.hasArg(argUrl)) { - serverWeb.send(500, contTypeText, wrongArgs); + getEspUpdate(serverWeb.arg(argUrl)); } else { - const uint8_t action = serverWeb.arg(action).toInt(); - switch (action) - { - case API_GET_LOG: - { - String result; - result = logPrint(); - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - } - break; - case API_CMD: - { - enum CMD_t : uint8_t - { // cmd list for buttons starts from 0 - CMD_ZB_ROUTER_RECON, - CMD_ZB_RST, - CMD_ZB_BSL, - CMD_ESP_RES, - CMD_ADAP_LAN, - CMD_ADAP_USB, - CMD_LED_ACT, - CMD_ZB_FLASH, - CMD_CLEAR_LOG, - CMD_ESP_UPD_URL, - CMD_ZB_CHK_FW, - CMD_ZB_CHK_HW, - CMD_ZB_LED_TOG, - CMD_ESP_FAC_RES, - CMD_ZB_ERASE_NVRAM - }; - String result = wrongArgs; - const char *argCmd = "cmd"; - const char *argUrl = "url"; - const char *argConf = "conf"; - const char *argLed = "led"; - const char *argAct = "act"; - - if (serverWeb.hasArg(argCmd)) - { - result = "ok"; - switch (serverWeb.arg(argCmd).toInt()) - { - case CMD_CLEAR_LOG: - logClear(); - break; - case CMD_ZB_ROUTER_RECON: - zigbeeRouterRejoin(); - break; - case CMD_ZB_RST: - zigbeeRestart(); - break; - case CMD_ZB_BSL: - zigbeeEnableBSL(); - break; - case CMD_ZB_ERASE_NVRAM: - xTaskCreate(zbEraseNV, "zbEraseNV", 2048, NULL, 5, NULL); - break; - case CMD_ESP_RES: - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - delay(250); - ESP.restart(); - break; - case CMD_ADAP_LAN: - usbModeSet(XZG); - break; - case CMD_ADAP_USB: - usbModeSet(ZIGBEE); - break; - case CMD_ESP_UPD_URL: - if (serverWeb.hasArg(argUrl)) - getEspUpdate(serverWeb.arg(argUrl)); - else - { - String link = fetchGitHubReleaseInfo(); - LOGD("%s", link.c_str()); - if (link) - { - getEspUpdate(link); - } - else - { - LOGW("Error getting link"); - } - } - break; - case CMD_ZB_CHK_FW: - if (zbFwCheck()) - { - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - } - else - { - serverWeb.send(HTTP_CODE_INTERNAL_SERVER_ERROR, contTypeText, result); - } - break; - case CMD_ZB_CHK_HW: - zbHwCheck(); - break; - case CMD_ZB_LED_TOG: - if (zbLedToggle()) - { - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - } - else - { - serverWeb.send(HTTP_CODE_INTERNAL_SERVER_ERROR, contTypeText, result); - } - break; - case CMD_ESP_FAC_RES: - if (serverWeb.hasArg(argConf)) - if (serverWeb.arg(argConf).toInt() == 1) - { - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - factoryReset(); - } - else - { - serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); - } - break; - case CMD_LED_ACT: - if (serverWeb.hasArg(argLed) && serverWeb.hasArg(argAct)) - { - int ledNum = serverWeb.arg(argLed).toInt(); - int actNum = serverWeb.arg(argAct).toInt(); - - LED_t ledEnum = static_cast(ledNum); - LEDMode actEnum = static_cast(actNum); - - if (static_cast(ledEnum) == ledNum && static_cast(actEnum) == actNum) - { - String tag = "API"; - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - if (ledNum == MODE_LED) - { - LOGD("%s led %d", ledControl.modeLED.name, actNum); - ledControl.modeLED.mode = static_cast(actNum); - } - else if (ledNum == POWER_LED) - { - LOGD("%s led %d", ledControl.powerLED.name, actNum); - ledControl.powerLED.mode = static_cast(actNum); - } - } - else - { - serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); - } - } - else - { - serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); - } - break; - case CMD_ZB_FLASH: - if (serverWeb.hasArg(argUrl)) - { - flashZbUrl(serverWeb.arg(argUrl)); - } - else - { - flashZbUrl("https://github.com/xyzroe/XZG/raw/zb_fws/ti/coordinator/CC1352P2_CC2652P_launchpad_coordinator_20240315.bin"); - } - break; - default: - serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); - break; - } - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - } - } - break; - case API_WIFICONNECTSTAT: + if (serverWeb.hasArg(argType)) { - String result; - StaticJsonDocument<70> doc; - const char *connected = "connected"; - if (WiFi.status() == WL_CONNECTED) + // String link = fetchLatestEspFw(); + FirmwareInfo fwInfo = fetchLatestEspFw(serverWeb.arg(argType)); + if (fwInfo.url) { - doc[connected] = true; - doc["ip"] = WiFi.localIP().toString(); + getEspUpdate(fwInfo.url); } else { - doc[connected] = false; + LOGW("%s", String(errLink).c_str()); } - serializeJson(doc, result); - serverWeb.send(HTTP_CODE_OK, contTypeJson, result); } - break; - case API_GET_FILE: - { - String result = wrongArgs; + } +} - if (serverWeb.hasArg(argFilename)) - { - String filename = "/" + serverWeb.arg(argFilename); - File file = LittleFS.open(filename, "r"); - if (!file) - return; - result = ""; - while (file.available() && result.length() < 500) - { - result += (char)file.read(); - } - file.close(); - } - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - } - break; - case API_DEL_FILE: - { - String result = wrongArgs; +static void apiCmdZbCheckFirmware(String &result) +{ + if (zbFwCheck()) + { + printLogMsg("[RCP] " + CCTool.chip.fwRev); + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + } + else + { + serverWeb.send(HTTP_CODE_INTERNAL_SERVER_ERROR, contTypeText, result); + } +} - if (serverWeb.hasArg(argFilename)) - { - String filename = "/" + serverWeb.arg(argFilename); - LOGW("Remove file %s", filename.c_str()); - LittleFS.remove(filename); - } +static void apiCmdZbLedToggle(String &result) +{ + if (zbLedToggle()) + { + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + } + else + { + serverWeb.send(HTTP_CODE_INTERNAL_SERVER_ERROR, contTypeText, result); + } +} + +static void apiCmdFactoryReset(String &result) +{ + if (serverWeb.hasArg(argConf)) + { + if (serverWeb.arg(argConf).toInt() == 1) + { serverWeb.send(HTTP_CODE_OK, contTypeText, result); + factoryReset(); } - break; - case API_GET_PARAM: + else { - String resp = wrongArgs; - if (serverWeb.hasArg(param)) - { - if (serverWeb.arg(param) == "refreshLogs") - { - resp = (String)systemCfg.refreshLogs; - } - else if (serverWeb.arg(param) == "update_root") - { - resp = getRootData(true); - } - else if (serverWeb.arg(param) == "coordMode") - { - if (wifiWebSetupInProgress) - { - resp = "1"; - } - else - { - resp = (String)systemCfg.workMode; - } - } - else if (serverWeb.arg(param) == "zbFwVer") - { - resp = String(CCTool.chip.fwRev); - } - else if (serverWeb.arg(param) == "zbHwVer") - { - resp = String(CCTool.chip.hwRev); - } - else if (serverWeb.arg(param) == "espVer") - { - resp = VERSION; - } - else if (serverWeb.arg(param) == "wifiEnable") - { - resp = networkCfg.wifiEnable; - } - else if (serverWeb.arg(param) == "all") - { - resp = makeJsonConfig(&networkCfg, &vpnCfg, &mqttCfg, &systemCfg); - } - else if (serverWeb.arg(param) == "vars") - { - resp = makeJsonConfig(NULL, NULL, NULL, NULL, &vars); - } - else if (serverWeb.arg(param) == "root") - { - resp = getRootData(); - } - } - serverWeb.send(HTTP_CODE_OK, contTypeText, resp); + serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); } - break; - case API_STARTWIFISCAN: - if (WiFi.getMode() == WIFI_OFF) - { // enable wifi for scan - WiFi.mode(WIFI_STA); - } - // } else if (WiFi.getMode() == WIFI_AP) { // enable sta for scan - // WiFi.mode(WIFI_AP_STA); - // } - WiFi.scanNetworks(true); - serverWeb.send(HTTP_CODE_OK, contTypeTextHtml, ok); - break; - case API_WIFISCANSTATUS: + } +} + +static void apiCmdLedAct(String &result) +{ + if (serverWeb.hasArg(argLed) && serverWeb.hasArg(argAct)) + { + int ledNum = serverWeb.arg(argLed).toInt(); + int actNum = serverWeb.arg(argAct).toInt(); + + LED_t ledEnum = static_cast(ledNum); + LEDMode actEnum = static_cast(actNum); + + if (static_cast(ledEnum) == ledNum && static_cast(actEnum) == actNum) { - static uint8_t timeout = 0; - DynamicJsonDocument doc(1024); - String result = ""; - int16_t scanRes = WiFi.scanComplete(); - const char *scanDone = "scanDone"; - doc[scanDone] = false; - if (scanRes == -2) - { - WiFi.scanNetworks(true); - } - else if (scanRes > 0) - { - doc[scanDone] = true; - JsonArray wifi = doc.createNestedArray("wifi"); - for (int i = 0; i < scanRes; ++i) - { - JsonObject wifi_0 = wifi.createNestedObject(); - wifi_0["ssid"] = WiFi.SSID(i); - wifi_0["rssi"] = WiFi.RSSI(i); - wifi_0["channel"] = WiFi.channel(i); - wifi_0["secure"] = WiFi.encryptionType(i); - } - WiFi.scanDelete(); - } - if (timeout < 10) + String tag = "API"; + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + if (ledNum == MODE_LED) { - timeout++; + LOGD("%s led %d", ledControl.modeLED.name.c_str(), actNum); + ledControl.modeLED.mode = static_cast(actNum); } - else + else if (ledNum == POWER_LED) { - doc[scanDone] = true; - WiFi.scanDelete(); - timeout = 0; + LOGD("%s led %d", ledControl.powerLED.name.c_str(), actNum); + ledControl.powerLED.mode = static_cast(actNum); } - serializeJson(doc, result); - serverWeb.send(HTTP_CODE_OK, contTypeJson, result); - break; } - case API_GET_PAGE: - if (!serverWeb.arg(page).length() > 0) - { - LOGW("wrong arg 'page' %s", serverWeb.argName(1)); - serverWeb.send(500, contTypeText, wrongArgs); - return; - } - switch (serverWeb.arg(page).toInt()) - { - case API_PAGE_ROOT: - handleRoot(); - sendGzip(contTypeTextHtml, PAGE_ROOT_html_gz, PAGE_ROOT_html_gz_len); - break; - case API_PAGE_GENERAL: - handleGeneral(); - sendGzip(contTypeTextHtml, PAGE_GENERAL_html_gz, PAGE_GENERAL_html_gz_len); - break; - case API_PAGE_NETWORK: - handleNetwork(); - sendGzip(contTypeTextHtml, PAGE_NETWORK_html_gz, PAGE_NETWORK_html_gz_len); - break; - case API_PAGE_ZIGBEE: - handleSerial(); - sendGzip(contTypeTextHtml, PAGE_ZIGBEE_html_gz, PAGE_ZIGBEE_html_gz_len); - break; - case API_PAGE_SECURITY: - handleSecurity(); - sendGzip(contTypeTextHtml, PAGE_SECURITY_html_gz, PAGE_SECURITY_html_gz_len); - break; - case API_PAGE_TOOLS: - handleTools(); - sendGzip(contTypeTextHtml, PAGE_TOOLS_html_gz, PAGE_TOOLS_html_gz_len); - break; - case API_PAGE_ABOUT: - // handleAbout(); - sendGzip(contTypeTextHtml, PAGE_ABOUT_html_gz, PAGE_ABOUT_html_gz_len); - break; - case API_PAGE_MQTT: - handleMqtt(); - sendGzip(contTypeTextHtml, PAGE_MQTT_html_gz, PAGE_MQTT_html_gz_len); - break; - case API_PAGE_VPN: - handleVpn(); - sendGzip(contTypeTextHtml, PAGE_VPN_html_gz, PAGE_VPN_html_gz_len); - break; - default: - break; - } - break; - case API_GET_FILELIST: + else { - String fileList = ""; - DynamicJsonDocument doc(512); - JsonArray files = doc.createNestedArray("files"); - File root = LittleFS.open("/"); - File file = root.openNextFile(); - while (file) - { - JsonObject jsonfile = files.createNestedObject(); - jsonfile["filename"] = String(file.name()); - jsonfile["size"] = file.size(); - file = root.openNextFile(); - } - root = LittleFS.open("/config/"); - file = root.openNextFile(); - while (file) - { - JsonObject jsonfile = files.createNestedObject(); - jsonfile["filename"] = String("/config/" + String(file.name())); - jsonfile["size"] = file.size(); - file = root.openNextFile(); - } - serializeJson(doc, fileList); - serverWeb.send(HTTP_CODE_OK, contTypeJson, fileList); - break; - } - - default: - LOGW("switch (action) err"); - break; + serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); } } + else + { + serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); + } } -void handleSaveParams() +static void apiCmdZbFlash(String &result) { - if (!is_authenticated()) - return; - updateConfiguration(serverWeb, systemCfg, networkCfg, vpnCfg, mqttCfg); + if (serverWeb.hasArg(argUrl)) + { + flashZbUrl(serverWeb.arg(argUrl)); + } + else + { + String link = fetchLatestZbFw(); + if (link) + { + flashZbUrl(link); + } + else + { + LOGW("%s", String(errLink).c_str()); + } + } } -void printEachKeyValuePair(const String &jsonString) +static void apiCmdBoardName(String &result) { - DynamicJsonDocument doc(1024); - DeserializationError error = deserializeJson(doc, jsonString); - - if (error) + if (serverWeb.hasArg("board")) { + String brdName = serverWeb.arg("board"); + brdName.toCharArray(hwConfig.board, sizeof(hwConfig.board)); - return; - } + /*File configFile = LittleFS.open(configFileHw, FILE_READ); + if (!configFile) + { + Serial.println("Failed to open config file for reading"); + return; + } - const uint8_t eventLen = 100; + DynamicJsonDocument config(1024); + DeserializationError error = deserializeJson(config, configFile); + if (error) + { + Serial.println("Failed to parse config file"); + configFile.close(); + return; + } - for (JsonPair kv : doc.as()) - { - DynamicJsonDocument pairDoc(256); - pairDoc[kv.key().c_str()] = kv.value(); + configFile.close(); + config["board"] = hwConfig.board; + + writeDefaultConfig(configFileHw, config);*/ + + brdName.toCharArray(hwConfig.board, sizeof(hwConfig.board)); + + hwConfig.board[sizeof(hwConfig.board) - 1] = '\0'; + + saveHwConfig(hwConfig); + + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + } + else + { + serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); + } +} + +static void apiCmdDefault(String &result) +{ + serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, result); +} + +static void apiCmdZbRouterRecon(String &result) +{ + zigbeeRouterRejoin(); +} + +static void apiCmdZbRestart(String &result) +{ + zigbeeRestart(); +} + +static void apiCmdZbEnableBsl(String &result) +{ + zigbeeEnableBSL(); +} + +static void apiCmdEspReset(String &result) +{ + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + delay(250); + restartDevice(); +} + +static void apiCmdAdapterLan(String &result) +{ + usbModeSet(XZG); +} + +static void apiCmdAdapterUsb(String &result) +{ + usbModeSet(ZIGBEE); +} + +static void apiCmdClearLog(String &result) +{ + logClear(); +} + +static void apiCmdZbCheckHardware(String &result) +{ + zbHwCheck(); +} + +static void apiCmdEraseNvram(String &result) +{ + xTaskCreate(zbEraseNV, "zbEraseNV", 2048, NULL, 5, NULL); +} + +static void apiCmdDnsCheck(String &result) +{ + // checkDNS(); +} + +static void apiCmd() +{ + static void (*apiCmdFunctions[])(String &result) = + { + apiCmdDefault, + apiCmdZbRouterRecon, + apiCmdZbRestart, + apiCmdZbEnableBsl, + apiCmdEspReset, + apiCmdAdapterLan, + apiCmdAdapterUsb, + apiCmdLedAct, + apiCmdZbFlash, + apiCmdClearLog, + apiCmdUpdateUrl, + apiCmdZbCheckFirmware, + apiCmdZbCheckHardware, + apiCmdZbLedToggle, + apiCmdFactoryReset, + apiCmdEraseNvram, + apiCmdDnsCheck, + apiCmdBoardName}; + constexpr int numFunctions = sizeof(apiCmdFunctions) / sizeof(apiCmdFunctions[0]); + String result = apiWrongArgs; + + if (serverWeb.hasArg(argCmd)) + { + result = "ok"; + + // I add 1 to allow 0 to be default+overflow/invalid case. + // Ideally, the client would send 1-indexed "cmd"s + // but then I'd need to modify this in ALL of the client code... + uint8_t command = serverWeb.arg(argCmd).toInt() + 1; + bool boundsCheck = command < numFunctions; + + apiCmdFunctions[command * boundsCheck](result); + + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + } +} + +static void apiWifiConnnectStat() +{ + String result; + StaticJsonDocument<70> doc; + const char *connected = "connected"; + if (WiFi.status() == WL_CONNECTED) + { + doc[connected] = true; + doc["ip"] = WiFi.localIP().toString(); + } + else + { + doc[connected] = false; + } + serializeJson(doc, result); + serverWeb.send(HTTP_CODE_OK, contTypeJson, result); +} + +static void apiGetFile() +{ + String result = apiWrongArgs; + + if (serverWeb.hasArg(argFilename)) + { + String filename = "/" + serverWeb.arg(argFilename); + File file = LittleFS.open(filename, "r"); + if (!file) + { + return; + } + result = ""; + while (file.available() && result.length() < 500) + { + result += (char)file.read(); + } + file.close(); + } + serverWeb.send(HTTP_CODE_OK, contTypeText, result); +} + +static void apiDelFile() +{ + String result = apiWrongArgs; + + if (serverWeb.hasArg(argFilename)) + { + String filename = "/" + serverWeb.arg(argFilename); + LOGW("Remove file %s", filename.c_str()); + LittleFS.remove(filename); + } + serverWeb.send(HTTP_CODE_OK, contTypeText, result); +} + +static void apiGetParam() +{ + String resp = apiWrongArgs; + if (serverWeb.hasArg(argParam)) + { + if (serverWeb.arg(argParam) == "refreshLogs") + { + resp = (String)systemCfg.refreshLogs; + } + else if (serverWeb.arg(argParam) == "update_root") + { + resp = getRootData(true); + } + else if (serverWeb.arg(argParam) == "coordMode") + { + if (wifiWebSetupInProgress) + { + resp = "1"; + } + else + { + resp = (String)systemCfg.workMode; + } + } + else if (serverWeb.arg(argParam) == "zbFwVer") + { + resp = String(CCTool.chip.fwRev); + } + else if (serverWeb.arg(argParam) == "zbHwVer") + { + resp = String(CCTool.chip.hwRev); + } + else if (serverWeb.arg(argParam) == "espVer") + { + resp = VERSION; + } + else if (serverWeb.arg(argParam) == "wifiEnable") + { + resp = networkCfg.wifiEnable; + } + else if (serverWeb.arg(argParam) == "all") + { + resp = makeJsonConfig(&networkCfg, &vpnCfg, &mqttCfg, &systemCfg); + } + else if (serverWeb.arg(argParam) == "vars") + { + resp = makeJsonConfig(NULL, NULL, NULL, NULL, &vars); + } + else if (serverWeb.arg(argParam) == "root") + { + resp = getRootData(); + } + } + serverWeb.send(HTTP_CODE_OK, contTypeText, resp); +} + +static void apiStartWifiScan() +{ + if (WiFi.getMode() == WIFI_OFF) + { // enable wifi for scan + WiFi.mode(WIFI_STA); + } + // } else if (WiFi.getMode() == WIFI_AP) { // enable sta for scan + // WiFi.mode(WIFI_AP_STA); + // } + WiFi.scanNetworks(true); + serverWeb.send(HTTP_CODE_OK, contTypeTextHtml, apiOk); +} + +static void apiWifiScanStatus() +{ + static uint8_t timeout = 0; + DynamicJsonDocument doc(1024); + String result = ""; + int16_t scanRes = WiFi.scanComplete(); + const char *scanDone = "scanDone"; + + doc[scanDone] = false; + if (scanRes == -2) + { + WiFi.scanNetworks(true); + } + else if (scanRes > 0) + { + doc[scanDone] = true; + JsonArray wifi = doc.createNestedArray("wifi"); + for (int i = 0; i < scanRes; ++i) + { + JsonObject wifi_0 = wifi.createNestedObject(); + wifi_0["ssid"] = WiFi.SSID(i); + wifi_0["rssi"] = WiFi.RSSI(i); + wifi_0["channel"] = WiFi.channel(i); + wifi_0["secure"] = WiFi.encryptionType(i); + } + WiFi.scanDelete(); + } + if (timeout < 10) + { + timeout++; + } + else + { + doc[scanDone] = true; + WiFi.scanDelete(); + timeout = 0; + } + serializeJson(doc, result); + serverWeb.send(HTTP_CODE_OK, contTypeJson, result); +} + +static void apiGetPage() +{ + if (!serverWeb.arg(argPage).length()) + { + LOGW("wrong arg 'page' %s", serverWeb.argName(1).c_str()); + serverWeb.send(HTTP_CODE_INTERNAL_SERVER_ERROR, contTypeText, apiWrongArgs); + return; + } + switch (serverWeb.arg(argPage).toInt()) + { + case API_PAGE_ROOT: + handleRoot(); + // sendGzip(contTypeTextHtml, PAGE_ROOT_html_gz, PAGE_ROOT_html_gz_len); + sendGzipFromFS("/html/root.html.gz", contTypeTextHtml); + break; + case API_PAGE_GENERAL: + handleGeneral(); + // sendGzip(contTypeTextHtml, PAGE_GENERAL_html_gz, PAGE_GENERAL_html_gz_len); + sendGzipFromFS("/html/general.html.gz", contTypeTextHtml); + break; + case API_PAGE_NETWORK: + handleNetwork(); + // sendGzip(contTypeTextHtml, PAGE_NETWORK_html_gz, PAGE_NETWORK_html_gz_len); + sendGzipFromFS("/html/network.html.gz", contTypeTextHtml); + break; + case API_PAGE_ZIGBEE: + handleSerial(); + // sendGzip(contTypeTextHtml, PAGE_ZIGBEE_html_gz, PAGE_ZIGBEE_html_gz_len); + sendGzipFromFS("/html/zigbee.html.gz", contTypeTextHtml); + break; + case API_PAGE_SECURITY: + handleSecurity(); + // sendGzip(contTypeTextHtml, PAGE_SECURITY_html_gz, PAGE_SECURITY_html_gz_len); + sendGzipFromFS("/html/security.html.gz", contTypeTextHtml); + break; + case API_PAGE_TOOLS: + handleTools(); + // sendGzip(contTypeTextHtml, PAGE_TOOLS_html_gz, PAGE_TOOLS_html_gz_len); + sendGzipFromFS("/html/tools.html.gz", contTypeTextHtml); + break; + case API_PAGE_ABOUT: + // handleAbout(); + // sendGzip(contTypeTextHtml, PAGE_ABOUT_html_gz, PAGE_ABOUT_html_gz_len); + sendGzipFromFS("/html/about.html.gz", contTypeTextHtml); + break; + case API_PAGE_MQTT: + handleMqtt(); + // sendGzip(contTypeTextHtml, PAGE_MQTT_html_gz, PAGE_MQTT_html_gz_len); + sendGzipFromFS("/html/mqtt.html.gz", contTypeTextHtml); + break; + case API_PAGE_VPN: + handleVpn(); + // sendGzip(contTypeTextHtml, PAGE_VPN_html_gz, PAGE_VPN_html_gz_len); + sendGzipFromFS("/html/vpn.html.gz", contTypeTextHtml); + break; + default: + break; + } +} + +static void apiGetFileList() +{ + String fileList = ""; + DynamicJsonDocument doc(512); + JsonArray files = doc.createNestedArray("files"); + + // Открываем корневую директорию + File root = LittleFS.open("/"); + File file = root.openNextFile(); + while (file) + { + JsonObject jsonfile = files.createNestedObject(); + jsonfile["filename"] = String(file.name()); + jsonfile["size"] = file.size(); + file.close(); // Закрываем файл после использования + file = root.openNextFile(); + } + root.close(); // Закрываем корневую директорию + + // Открываем директорию /x/ + root = LittleFS.open("/x/"); + file = root.openNextFile(); + while (file) + { + JsonObject jsonfile = files.createNestedObject(); + jsonfile["filename"] = String("/x/" + String(file.name())); + jsonfile["size"] = file.size(); + file.close(); // Закрываем файл после использования + file = root.openNextFile(); + } + root.close(); // Закрываем директорию /x/ + + serializeJson(doc, fileList); + serverWeb.send(HTTP_CODE_OK, contTypeJson, fileList); +} + +static void apiDefault() +{ + LOGW("switch (action) err"); +} + +void handleApi() +{ + // Example api invocation: + // http://xzg.local/api?action=0&page=0 + + // apiFunctions[] will need to correspond to the + // values set in the JS frontend for "action" + static void (*apiFunctions[])() = { + apiDefault, + apiGetPage, + apiGetParam, + apiStartWifiScan, + apiWifiScanStatus, + apiGetFileList, + apiGetFile, + apiDefault, // invoked by what was previously API_SEND_HEX. + apiWifiConnnectStat, + apiCmd, + apiGetLog, + apiDelFile}; + constexpr int numFunctions = sizeof(apiFunctions) / sizeof(apiFunctions[0]); + if (!is_authenticated()) + { + redirectLogin(1); + return; + } + if (serverWeb.argName(0) == argAction) + { + // I add 1 to allow 0 to be default+overflow/invalid case. + // Ideally, the client would send 1-indexed "action"s + // but then I'd need to modify this in ALL of the client code... + const uint8_t action = serverWeb.arg(argAction).toInt() + 1; + const bool boundsCheck = action < numFunctions; + apiFunctions[action * boundsCheck](); + } + else + { + serverWeb.send(HTTP_CODE_INTERNAL_SERVER_ERROR, contTypeText, apiWrongArgs); + } +} + +void handleSaveParams() +{ + if (!is_authenticated()) + return; + updateConfiguration(serverWeb, systemCfg, networkCfg, vpnCfg, mqttCfg); +} + +void printEachKeyValuePair(const String &jsonString) +{ + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, jsonString); + + if (error) + { + + return; + } + + const uint8_t eventLen = 100; + + for (JsonPair kv : doc.as()) + { + DynamicJsonDocument pairDoc(256); + pairDoc[kv.key().c_str()] = kv.value(); String output; serializeJson(pairDoc, output); @@ -903,6 +1226,7 @@ void updateWebTask(void *parameter) { String root_data = getRootData(true); printEachKeyValuePair(root_data); + root_data = String(); // free memory vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(systemCfg.refreshLogs * 1000)); } } @@ -912,14 +1236,15 @@ void handleLoginGet() if (!is_authenticated()) { // LOGD("handleLoginGet !is_authenticated"); - sendGzip(contTypeTextHtml, PAGE_LOGIN_html_gz, PAGE_LOGIN_html_gz_len); + // sendGzip(contTypeTextHtml, PAGE_LOGIN_html_gz, PAGE_LOGIN_html_gz_len); + sendGzipFromFS("/html/login.html.gz", contTypeTextHtml); } else { // LOGD("handleLoginGet else"); serverWeb.sendHeader("Location", "/"); serverWeb.sendHeader("Cache-Control", "no-cache"); - serverWeb.send(301); + serverWeb.send(HTTP_CODE_MOVED_PERMANENTLY); // sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); } } @@ -948,7 +1273,7 @@ void handleLoginPost() String token = sha1(String(systemCfg.webUser) + ":" + String(systemCfg.webPass) + ":" + serverWeb.client().remoteIP().toString()); serverWeb.sendHeader("Set-Cookie", "XZG_UID=" + token); - serverWeb.send(301); + serverWeb.send(HTTP_CODE_MOVED_PERMANENTLY); // Serial.println("Log in Successful"); return; } @@ -1003,11 +1328,11 @@ void handleGeneral() String result; doc[hwBtnIsKey] = vars.hwBtnIs; - doc[hwUartSelIsKey] = vars.hwUartSelIs; + // doc[hwUartSelIsKey] = vars.hwUartSelIs; doc[hwLedPwrIsKey] = vars.hwLedPwrIs; doc[hwLedUsbIsKey] = vars.hwLedUsbIs; - switch (systemCfg.workMode) + /*switch (systemCfg.workMode) { case WORK_MODE_USB: doc["checkedUsbMode"] = checked; @@ -1022,7 +1347,7 @@ void handleGeneral() if (systemCfg.keepWeb) { doc[keepWebKey] = checked; - } + }*/ if (systemCfg.disableLedPwr) { @@ -1050,10 +1375,17 @@ void handleGeneral() doc[nmEnableKey] = checked; } + if (systemCfg.updAutoInst) + { + doc[updAutoInstKey] = checked; + } + doc[updCheckTimeKey] = systemCfg.updCheckTime; + doc[updCheckDayKey] = systemCfg.updCheckDay; + serializeJson(doc, result); serverWeb.sendHeader(respHeaderName, result); - DynamicJsonDocument zones(10240); + DynamicJsonDocument zones(8000); String results; JsonArray zonesArray = zones.to(); @@ -1062,6 +1394,9 @@ void handleGeneral() zonesArray.add(timeZones[i].zone); } + // size_t usedMemory = zones.memoryUsage(); + // LOGD("Zones used: %s bytes", String(usedMemory)); + serializeJson(zones, results); serverWeb.sendHeader(respTimeZonesName, results); } @@ -1086,7 +1421,8 @@ void handleSecurity() { doc[fwEnabledKey] = checked; } - doc[fwIpKey] = systemCfg.fwIp; //.toString(); + doc[fwIpKey] = systemCfg.fwIp; //.toString(); + doc[fwMaskKey] = systemCfg.fwMask; //.toString(); serializeJson(doc, result); serverWeb.sendHeader(respHeaderName, result); @@ -1130,6 +1466,68 @@ void handleNetwork() doc[wifiDns1Key] = networkCfg.wifiDns1; doc[wifiDns2Key] = networkCfg.wifiDns2; + switch (networkCfg.wifiMode) + { + case WIFI_PROTOCOL_11B: + doc["1"] = checked; + break; + case WIFI_PROTOCOL_11G: + doc["2"] = checked; + break; + case WIFI_PROTOCOL_11N: + doc["4"] = checked; + break; + case WIFI_PROTOCOL_LR: + doc["8"] = checked; + break; + default: + break; + } + + switch (networkCfg.wifiPower) + { + case WIFI_POWER_19_5dBm: + doc["78"] = checked; + break; + case WIFI_POWER_19dBm: + doc["76"] = checked; + break; + case WIFI_POWER_18_5dBm: + doc["74"] = checked; + break; + case WIFI_POWER_17dBm: + doc["68"] = checked; + break; + case WIFI_POWER_15dBm: + doc["60"] = checked; + break; + case WIFI_POWER_13dBm: + doc["52"] = checked; + break; + case WIFI_POWER_11dBm: + doc["44"] = checked; + break; + case WIFI_POWER_8_5dBm: + doc["34"] = checked; + break; + case WIFI_POWER_7dBm: + doc["28"] = checked; + break; + case WIFI_POWER_5dBm: + doc["20"] = checked; + break; + case WIFI_POWER_2dBm: + doc["8"] = checked; + break; + default: + break; + } + + if (hwConfig.eth.mdcPin == -1 || hwConfig.eth.mdiPin == -1) + { + doc["no_eth"] = 1; + } + serializeJson(doc, result); serverWeb.sendHeader(respHeaderName, result); } @@ -1139,31 +1537,48 @@ void handleSerial() String result; DynamicJsonDocument doc(1024); - if (systemCfg.serialSpeed == 9600) + switch (systemCfg.workMode) { - doc["9600"] = checked; + case WORK_MODE_USB: + doc["usbMode"] = checked; + break; + case WORK_MODE_NETWORK: + doc["lanMode"] = checked; + break; + default: + break; } - else if (systemCfg.serialSpeed == 19200) + + switch (systemCfg.serialSpeed) { + case 9600: + doc["9600"] = checked; + break; + case 19200: doc["19200"] = checked; - } - else if (systemCfg.serialSpeed == 38400) - { - doc["8400"] = checked; - } - else if (systemCfg.serialSpeed == 57600) - { + break; + case 38400: + doc["38400"] = checked; + break; + case 57600: doc["57600"] = checked; - } - else if (systemCfg.serialSpeed == 115200) - { + break; + case 115200: doc["115200"] = checked; - } - else - { + break; + case 230400: + doc["230400"] = checked; + break; + case 460800: + doc["460800"] = checked; + break; + default: doc["115200"] = checked; + break; } + doc[socketPortKey] = String(systemCfg.socketPort); + doc[zbRoleKey] = systemCfg.zbRole; serializeJson(doc, result); serverWeb.sendHeader(respHeaderName, result); @@ -1232,41 +1647,10 @@ void handleVpn() serverWeb.sendHeader(respHeaderName, result); } -String getRootData(bool update) +static void getRootEthTab(DynamicJsonDocument &doc, + bool update, + const String &noConn) { - String tag = "root_data"; - DynamicJsonDocument doc(2048); - - String readableTime; - getReadableTime(readableTime, vars.socketTime); - const char *connectedSocketStatus = "connectedSocketStatus"; - const char *connectedSocket = "connectedSocket"; - const char *noConn = "noConn"; - - doc[connectedSocketStatus] = vars.connectedClients; - doc[connectedSocket] = vars.socketTime; - doc["localTime"] = getTime(); - - if (!update) - { - char verArr[25]; - const char *env = STRINGIFY(BUILD_ENV_NAME); - - if (strcasestr(env, "debug") != NULL) - { - sprintf(verArr, "%s (%s)", VERSION, env); - } - else - { - sprintf(verArr, "%s", VERSION); - } - - doc["VERSION"] = String(verArr); - - const char *operationalMode = "operationalMode"; - doc[operationalMode] = systemCfg.workMode; - } - // ETHERNET TAB const char *ethConn = "ethConn"; const char *ethMac = "ethMac"; @@ -1287,7 +1671,9 @@ String getRootData(bool update) doc[ethIpKey] = ETH.localIP().toString(); doc[ethMaskKey] = ETH.subnetMask().toString(); doc[ethGateKey] = ETH.gatewayIP().toString(); - doc[ethDns] = ETH.dnsIP().toString(); + doc[ethDns] = ETH.dnsIP().toString(); // vars.savedEthDNS.toString(); // ETH.dnsIP().toString(); + if (vars.ethIPv6) + doc[ethIPv6Key] = getShortenedIPv6(ETH.localIPv6().toString()); } else { @@ -1295,72 +1681,59 @@ String getRootData(bool update) doc[ethIpKey] = networkCfg.ethDhcp ? noConn : ETH.localIP().toString(); doc[ethMaskKey] = networkCfg.ethDhcp ? noConn : ETH.subnetMask().toString(); doc[ethGateKey] = networkCfg.ethDhcp ? noConn : ETH.gatewayIP().toString(); - doc[ethDns] = networkCfg.ethDhcp ? noConn : ETH.dnsIP().toString(); + doc[ethDns] = networkCfg.ethDhcp ? noConn : ETH.dnsIP().toString(); // vars.savedEthDNS.toString(); // ETH.dnsIP().toString(); + doc[ethIPv6Key] = noConn; } } +} - doc["uptime"] = millis(); // readableTime; - - float CPUtemp = getCPUtemp(); - doc["deviceTemp"] = String(CPUtemp); +static inline void getRootWifi(DynamicJsonDocument &doc, + bool update, + const String &noConn) +{ + const char *wifiRssi = "wifiRssi"; + const char *wifiConn = "wifiConn"; + const char *wifiMode = "wifiMode"; + const char *wifiDns = "wifiDns"; if (!update) { - doc["hwRev"] = hwConfig.board; - doc["espModel"] = String(ESP.getChipModel()); - doc["espCores"] = ESP.getChipCores(); - doc["espFreq"] = ESP.getCpuFreqMHz(); - - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); - - const char *espFlashType = "espFlashType"; - if (chip_info.features & CHIP_FEATURE_EMB_FLASH) - { - doc[espFlashType] = 1; - } - else + doc["wifiMac"] = WiFi.macAddress(); + String boardStr = hwConfig.board; + if (boardStr.startsWith("Multi")) { - doc[espFlashType] = 2; - } - - doc["espFlashSize"] = ESP.getFlashChipSize() / (1024 * 1024); - - doc["zigbeeFwRev"] = String(CCTool.chip.fwRev); - - doc["zigbeeHwRev"] = CCTool.chip.hwRev; - - doc["zigbeeIeee"] = CCTool.chip.ieee; - - doc["zigbeeFlSize"] = String(CCTool.chip.flashSize / 1024); - - unsigned int totalFs = LittleFS.totalBytes() / 1024; - unsigned int usedFs = LittleFS.usedBytes() / 1024; + int underscoreIndex = boardStr.indexOf('_'); + if (underscoreIndex != -1) + { + String boardNumber = boardStr.substring(underscoreIndex + 1); + boardNumber.trim(); + const int boardNum = boardNumber.toInt(); - doc["espFsSize"] = totalFs; - doc["espFsUsed"] = usedFs; - } - int heapSize = ESP.getHeapSize() / 1024; - int heapFree = ESP.getFreeHeap() / 1024; + String boardArray[BOARD_CFG_CNT]; + int arrayIndex = 0; - doc["espHeapSize"] = heapSize; - doc["espHeapUsed"] = heapSize - heapFree; + for (int brdNewIdx = 0; brdNewIdx < BOARD_CFG_CNT; brdNewIdx++) + { + if (brdConfigs[brdNewIdx].ethConfigIndex == brdConfigs[boardNum].ethConfigIndex && brdConfigs[brdNewIdx].zbConfigIndex == brdConfigs[boardNum].zbConfigIndex && brdConfigs[brdNewIdx].mistConfigIndex == brdConfigs[boardNum].mistConfigIndex) + { + boardArray[arrayIndex++] = brdConfigs[brdNewIdx].board; + } + } - int total, used; - getNvsStats(&total, &used); + DynamicJsonDocument jsonDoc(512); + JsonArray jsonArray = jsonDoc.to(); - doc["espNvsSize"] = total; - doc["espNvsUsed"] = used; + for (int i = 0; i < arrayIndex; i++) + { + jsonArray.add(boardArray[i]); + } - // wifi - const char *wifiRssi = "wifiRssi"; - const char *wifiConn = "wifiConn"; - const char *wifiMode = "wifiMode"; - const char *wifiDns = "wifiDns"; + String jsonString; + serializeJson(jsonArray, jsonString); - if (!update) - { - doc["wifiMac"] = WiFi.macAddress(); + doc["boardArray"] = jsonString; + } + } } if (networkCfg.wifiEnable) @@ -1375,7 +1748,8 @@ String getRootData(bool update) doc[wifiIpKey] = WiFi.localIP().toString(); doc[wifiMaskKey] = WiFi.subnetMask().toString(); doc[wifiGateKey] = WiFi.gatewayIP().toString(); - doc[wifiDns] = WiFi.dnsIP().toString(); + doc[wifiDns] = WiFi.dnsIP().toString(); // vars.savedWifiDNS.toString(); // WiFi.dnsIP().toString(); + doc[wifiIPv6Key] = WiFi.localIPv6().toString(); } else { @@ -1385,7 +1759,7 @@ String getRootData(bool update) doc[wifiIpKey] = networkCfg.wifiDhcp ? noConn : WiFi.localIP().toString(); doc[wifiMaskKey] = networkCfg.wifiDhcp ? noConn : WiFi.subnetMask().toString(); doc[wifiGateKey] = networkCfg.wifiDhcp ? noConn : WiFi.gatewayIP().toString(); - doc[wifiDns] = networkCfg.wifiDhcp ? noConn : WiFi.dnsIP().toString(); + doc[wifiDns] = networkCfg.wifiDhcp ? noConn : WiFi.dnsIP().toString(); // vars.savedWifiDNS.toString(); // WiFi.dnsIP().toString(); } } @@ -1403,19 +1777,22 @@ String getRootData(bool update) doc[wifiMode] = 2; //"AP"; doc[wifiRssi] = noConn; //"N/A"; } +} - // MQTT +static inline void getRootMqtt(DynamicJsonDocument &doc) +{ if (mqttCfg.enable) { const char *mqConnect = "mqConnect"; const char *mqBroker = "mqBroker"; doc[mqBroker] = mqttCfg.server; - doc[mqConnect] = vars.mqttConn ? 1 : 0; } +} - // VPN WireGuard +static inline void getRootVpnWireGuard(DynamicJsonDocument &doc) +{ if (vpnCfg.wgEnable) { const char *wgInit = "wgInit"; @@ -1428,13 +1805,14 @@ String getRootData(bool update) doc[wgInit] = vars.vpnWgInit ? 1 : 0; doc[wgDeviceAddr] = vpnCfg.wgLocalIP.toString(); doc[wgRemoteAddr] = vpnCfg.wgEndAddr; - // doc[wgEndPort] = vpnCfg.wgEndPort; - doc[wgConnect] = vars.vpnWgConnect ? 1 : 0; - doc[wgRemoteIP] = vars.vpnWgPeerIp.toString(); + // doc[wgEndPort] = vpnCfg.wgEndPort; } - // VPN Husarnet +} + +static inline void getRootVpnHusarnet(DynamicJsonDocument &doc) +{ if (vpnCfg.hnEnable) { const char *hnInit = "hnInit"; @@ -1442,9 +1820,154 @@ String getRootData(bool update) // doc[wgDeviceAddr] = vpnCfg.wgLocalIP.toString();//WgSettings.localAddr; doc[hnHostName] = vpnCfg.hnHostName; - doc[hnInit] = vars.vpnHnInit ? 1 : 0; } +} + +static inline void getRootUptime(DynamicJsonDocument &doc) +{ + doc["uptime"] = millis(); // readableTime; +} + +static inline void getRootCpuTemp(DynamicJsonDocument &doc) +{ + float CPUtemp = getCPUtemp(); + doc["deviceTemp"] = String(CPUtemp); +} + +static inline void getRootOneWireTemp(DynamicJsonDocument &doc) +{ + if (vars.oneWireIs) + { + float temp = get1wire(); + doc["1wTemp"] = String(temp); + } +} + +// Some misc hardware info, +// maybe organise this more +static inline void getRootHwMisc(DynamicJsonDocument &doc, bool update) +{ + if (update) + { + return; + } + char verArr[25]; + const char *env = STRINGIFY(BUILD_ENV_NAME); + + if (strcasestr(env, "debug") != NULL) + { + sprintf(verArr, "%s (%s)", VERSION, env); + } + else + { + sprintf(verArr, "%s", VERSION); + } + + doc["VERSION"] = String(verArr); + + const char *operationalMode = "operationalMode"; + doc[operationalMode] = systemCfg.workMode; + + doc[espUpdAvailKey] = vars.updateEspAvail; + doc[rcpUpdAvailKey] = vars.updateZbAvail; + + doc["hwRev"] = hwConfig.board; + doc["espModel"] = String(ESP.getChipModel()); + doc["espCores"] = ESP.getChipCores(); + doc["espFreq"] = ESP.getCpuFreqMHz(); + + // esp_chip_info_t chip_info; + // esp_chip_info(&chip_info); + + const char *espFlashType = "espFlashType"; + if (true) // chip_info.features & CHIP_FEATURE_EMB_FLASH) + { + doc[espFlashType] = 1; + } + else + { + doc[espFlashType] = 2; + } + + doc["espFlashSize"] = ESP.getFlashChipSize() / (1024 * 1024); + + // doc["zigbeeFwRev"] = String(CCTool.chip.fwRev); + if (CCTool.chip.fwRev > 0) + { + doc["zigbeeFwRev"] = String(CCTool.chip.fwRev); + } + else + { + doc["zigbeeFwRev"] = String(systemCfg.zbFw); + doc["zbFwSaved"] = true; + } + + doc["zigbeeHwRev"] = CCTool.chip.hwRev; + doc["zigbeeIeee"] = CCTool.chip.ieee; + doc["zigbeeFlSize"] = String(CCTool.chip.flashSize / 1024); + + unsigned int totalFs = LittleFS.totalBytes() / 1024; + unsigned int usedFs = LittleFS.usedBytes() / 1024; + doc["espFsSize"] = totalFs; + doc["espFsUsed"] = usedFs; + + doc[zbRoleKey] = systemCfg.zbRole; + doc["zigbeeFwSaved"] = systemCfg.zbFw; +} + +static inline void getRootHeapsize(DynamicJsonDocument &doc) +{ + int heapSize = ESP.getHeapSize() / 1024; + int heapFree = ESP.getFreeHeap() / 1024; + + doc["espHeapSize"] = heapSize; + doc["espHeapUsed"] = heapSize - heapFree; +} + +static inline void getRootNvsStats(DynamicJsonDocument &doc) +{ + int total, used; + getNvsStats(&total, &used); + + doc["espNvsSize"] = total; + doc["espNvsUsed"] = used; +} + +static inline void getRootSockets(DynamicJsonDocument &doc) +{ + const char *connectedSocketStatus = "connectedSocketStatus"; + const char *connectedSocket = "connectedSocket"; + + doc[connectedSocketStatus] = vars.connectedClients; + doc[connectedSocket] = vars.socketTime; +} + +static inline void getRootTime(DynamicJsonDocument &doc) +{ + doc["localTime"] = getTime(); +} + +String getRootData(bool update) +{ + DynamicJsonDocument doc(2048); + + const char *noConn = "noConn"; + getRootEthTab(doc, update, noConn); + getRootWifi(doc, update, noConn); + + getRootHwMisc(doc, update); + + getRootSockets(doc); + getRootTime(doc); + getRootUptime(doc); + getRootCpuTemp(doc); + getRootOneWireTemp(doc); + getRootHeapsize(doc); + getRootNvsStats(doc); + getRootMqtt(doc); + getRootVpnWireGuard(doc); + getRootVpnHusarnet(doc); String result; serializeJson(doc, result); @@ -1464,11 +1987,11 @@ void handleTools() DynamicJsonDocument doc(512); doc[hwBtnIsKey] = vars.hwBtnIs; - doc[hwUartSelIsKey] = vars.hwUartSelIs; doc[hwLedPwrIsKey] = vars.hwLedPwrIs; doc[hwLedUsbIsKey] = vars.hwLedUsbIs; - // doc["hostname"] = systemCfg.hostname; - // doc["refreshLogs"] = systemCfg.refreshLogs; + // doc[hwUartSelIsKey] = vars.hwUartSelIs; + // doc["hostname"] = systemCfg.hostname; + // doc["refreshLogs"] = systemCfg.refreshLogs; serializeJson(doc, result); serverWeb.sendHeader(respHeaderName, result); @@ -1487,6 +2010,7 @@ void handleSavefile() String filename = "/" + serverWeb.arg(0); String content = serverWeb.arg(1); File file = LittleFS.open(filename, "w"); + LOGD("try %s", filename.c_str()); if (!file) @@ -1494,7 +2018,6 @@ void handleSavefile() LOGW("Failed to open file for reading"); return; } - int bytesWritten = file.print(content); if (bytesWritten > 0) { @@ -1514,7 +2037,6 @@ void handleSavefile() /* ----- Multi-tool support | START -----*/ void handleZigbeeBSL() { - zigbeeEnableBSL(); serverWeb.send(HTTP_CODE_OK, contTypeText, "Zigbee BSL"); } @@ -1552,16 +2074,16 @@ void printLogMsg(String msg) logPush('\n'); LOGI("%s", msg.c_str()); } - +/* void progressNvRamFunc(unsigned int progress, unsigned int total) { - const char *tagESP_FW_progress = "ESP_FW_prgs"; + const char *tagESP_FW_prgs = "ESP_FW_prgs"; const uint8_t eventLen = 11; float percent = ((float)progress / total) * 100.0; - sendEvent(tagESP_FW_progress, eventLen, String(percent)); + sendEvent(tagESP_FW_prgs, eventLen, String(percent)); // printLogMsg(String(percent)); #ifdef DEBUG @@ -1571,22 +2093,22 @@ void progressNvRamFunc(unsigned int progress, unsigned int total) } #endif }; +*/ void progressFunc(unsigned int progress, unsigned int total) { - const char *tagESP_FW_progress = "ESP_FW_prgs"; const uint8_t eventLen = 11; float percent = ((float)progress / total) * 100.0; - sendEvent(tagESP_FW_progress, eventLen, String(percent)); + sendEvent(tagESP_FW_prgs, eventLen, String(percent)); // printLogMsg(String(percent)); #ifdef DEBUG if (int(percent) % 5 == 0) { - LOGD("Update ESP32 progress: %s of %s | %s%", String(progress), String(total), String(percent)); + LOGD("Update ESP32 progress: %s of %s | %s%%", String(progress).c_str(), String(total).c_str(), String(percent).c_str()); } #endif }; @@ -1598,55 +2120,77 @@ void getEspUpdate(String esp_fw_url) { LOGI("getEspUpdate: %s", esp_fw_url.c_str()); - HTTPClient clientWeb; - WiFiClientSecure client; - client.setInsecure(); // the magic line, use with caution - clientWeb.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); - clientWeb.begin(client, esp_fw_url); - clientWeb.addHeader("Content-Type", "application/octet-stream"); - - // Get file, just to check if each reachable - int resp = clientWeb.GET(); - LOGD("Response: %s", String(resp)); - // If file is reachable, start downloading - if (resp == HTTP_CODE_OK) - { - // get length of document (is -1 when Server sends no Content-Length header) - totalLength = clientWeb.getSize(); - // transfer to local variable - int len = totalLength; - // this is required to start firmware update process - Update.begin(totalLength); - Update.onProgress(progressFunc); - LOGI("FW Size: %s", String(totalLength)); - // create buffer for read - uint8_t buff[128] = {0}; - // get tcp stream - WiFiClient *stream = clientWeb.getStreamPtr(); - // read all data from server - LOGI("Updating firmware..."); - while (clientWeb.connected() && (len > 0 || len == -1)) + // checkDNS(); + HTTPClient http; + // WiFiClientSecure client; + // client.setInsecure(); // the magic line, use with caution + http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + // http.begin(client, esp_fw_url); + + if (dnsLookup(esp_fw_url)) + { + http.begin(esp_fw_url); + http.addHeader("Content-Type", "application/octet-stream"); + + // Get file, just to check if each reachable + int resp = http.GET(); + LOGD("Response: %s", String(resp).c_str()); + // If file is reachable, start downloading + if (resp == HTTP_CODE_OK) { - // get available data size - size_t size = stream->available(); - if (size) + // get length of document (is -1 when Server sends no Content-Length header) + totalLength = http.getSize(); + // transfer to local variable + int len = totalLength; + // this is required to start firmware update process + + int updateType = 0; + bool isSpiffsUpdate = false; + if (esp_fw_url.endsWith(".fs.bin")) + { - // read up to 128 byte - int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); - // pass to function - runEspUpdateFirmware(buff, c); - if (len > 0) + updateType = U_SPIFFS; + isSpiffsUpdate = true; + } + else + { + updateType = U_FLASH; + isSpiffsUpdate = false; + } + + Update.begin(totalLength, updateType); + Update.onProgress(progressFunc); + LOGI("File size: %s", String(totalLength).c_str()); + // create buffer for read + uint8_t buff[128] = {0}; + // get tcp stream + WiFiClient *stream = http.getStreamPtr(); + // read all data from server + LOGI("Updating %s ...", isSpiffsUpdate ? "file system" : "firmware"); + while (http.connected() && (len > 0 || len == -1)) + { + esp_task_wdt_reset(); + // get available data size + size_t size = stream->available(); + if (size) { - len -= c; + // read up to 128 byte + int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); + // pass to function + runEspUpdateFirmware(buff, c); + if (len > 0) + { + len -= c; + } } } } + else + { + LOGI("Cannot download firmware file."); + } + http.end(); } - else - { - LOGI("Cannot download firmware file."); - } - clientWeb.end(); } void runEspUpdateFirmware(uint8_t *data, size_t len) @@ -1661,13 +2205,87 @@ void runEspUpdateFirmware(uint8_t *data, size_t len) Update.end(true); LOGD("Update success. Rebooting..."); // Restart ESP32 to see changes - ESP.restart(); + restartDevice(); +} + +FirmwareInfo fetchLatestEspFw(String type) +{ + FirmwareInfo info = {"", ""}; + + if (type == "fs" || type == "ota") + { + HTTPClient http; + http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + http.begin("https://raw.githubusercontent.com/xyzroe/XZG/refs/heads/releases/latest/xzg.json"); + int httpCode = http.GET(); + + if (httpCode > 0) + { + String payload = http.getString(); + + DynamicJsonDocument doc(4096); + DeserializationError error = deserializeJson(doc, payload); + + if (!error) + { + size_t usedMemory = doc.memoryUsage(); + LOGD("doc used: %s bytes", String(usedMemory).c_str()); + + JsonObject release = doc.as(); + + if (release.containsKey("version")) + { + info.version = release["version"].as(); + info.url = "https://github.com/xyzroe/XZG/releases/download/" + info.version + "/XZG_" + info.version + "." + type + ".bin"; + //info.url = "https://raw.githubusercontent.com/xyzroe/XZG/refs/heads/releases/latest/XZG." + type + ".bin"; + LOGD("Latest version: %s | url %s", info.version.c_str(), info.url.c_str()); + } + else + { + LOGD("No version found"); + } + + String needKey; + if (type == "fs") + { + needKey = "fs_sha"; + } + else + { + needKey = "fw_sha"; + } + if (release.containsKey(needKey)) + { + info.sha = release[needKey].as(); + } + else + { + LOGD("No SHA for %s found", type.c_str()); + } + } + else + { + LOGD("deserializeJson failed: %s", error.c_str()); + } + } + else + { + LOGD("Error on HTTP request: %d", httpCode); + } + http.end(); + } + else + { + LOGD("Invalid type: %s", type.c_str()); + } + return info; } -String fetchGitHubReleaseInfo() +String fetchLatestZbFw() { + // checkDNS(); HTTPClient http; - http.begin("https://api.github.com/repos/xyzroe/xzg/releases"); + http.begin("https://raw.githubusercontent.com/xyzroe/XZG/zb_fws/ti/manifest.json"); int httpCode = http.GET(); String browser_download_url = ""; @@ -1676,19 +2294,55 @@ String fetchGitHubReleaseInfo() { String payload = http.getString(); - DynamicJsonDocument doc(8192); - deserializeJson(doc, payload); - JsonArray releases = doc.as(); + DynamicJsonDocument doc(8192 * 2); + DeserializationError error = deserializeJson(doc, payload); + + if (error) + { + LOGD("deserializeJson() failed: %s", error.c_str()); + return ""; + } + + size_t usedMemory = doc.memoryUsage(); + LOGD("doc used: %s bytes", String(usedMemory).c_str()); + + String roleKey = getRadioRoleKey(); - if (releases.size() > 0 && releases[0]["assets"].size() > 1) + /*if (systemCfg.zbRole == COORDINATOR) { - browser_download_url = releases[0]["assets"][1]["browser_download_url"].as(); - LOGD("browser_download_url: %s", browser_download_url.c_str()); + roleKey = "coordinator"; + } + else if (systemCfg.zbRole == ROUTER) + { + roleKey = "router"; + } + else if (systemCfg.zbRole == OPENTHREAD) + { + roleKey = "thread"; + }*/ + + if (doc.containsKey(roleKey) && doc[roleKey].containsKey(CCTool.chip.hwRev)) + { + JsonObject roleObj = doc[roleKey][CCTool.chip.hwRev].as(); + for (JsonPair kv : roleObj) + { + JsonObject file = kv.value().as(); + if (file.containsKey("link")) + { + browser_download_url = file["link"].as(); + if (file.containsKey("baud")) + { + browser_download_url = browser_download_url + "?b=" + file["baud"].as(); + } + break; + } + } + // LOGD("browser_download_url: %s", browser_download_url.c_str()); } } else { - LOGD("Error on HTTP request"); + LOGD("Error on HTTP request: %d", httpCode); } http.end(); @@ -1697,8 +2351,13 @@ String fetchGitHubReleaseInfo() String extractVersionFromURL(String url) { - int startPos = url.indexOf("/download/") + 10; - int endPos = url.indexOf("/", startPos); + int startPos = url.lastIndexOf('_') + 1; + int endPos = url.indexOf(".ota.bin"); + if (endPos == -1) + { + endPos = url.indexOf(".bin"); + } + if (startPos != -1 && endPos != -1) { return url.substring(startPos, endPos); diff --git a/src/web.h b/src/web.h index b9bdce3a..b9fb0739 100644 --- a/src/web.h +++ b/src/web.h @@ -1,3 +1,8 @@ +#ifndef WEB_H +#define WEB_H + +#include "etc.h" // Включение заголовочного файла etc.h + #include void handleEvents(); void initWebServer(); @@ -23,7 +28,8 @@ void handleEspUpdateUpload(); void handleNotFound(); bool captivePortal(); -void sendGzip(const char* contentType, const uint8_t content[], uint16_t contentLen); +//void sendGzip(const char* contentType, const uint8_t content[], uint16_t contentLen); +void sendGzipFromFS(const char* path, const char* contentType); void handleTools(); void printLogTime(); void printLogMsg(String msg); @@ -36,7 +42,11 @@ void progressFunc(unsigned int progress, unsigned int total); void getEspUpdate(String esp_fw_url); void runEspUpdateFirmware(uint8_t *data, size_t len); -String fetchGitHubReleaseInfo(); + + + +FirmwareInfo fetchLatestEspFw(String type = "ota"); +String fetchLatestZbFw(); String extractVersionFromURL(String url); void updateWebTask(void *parameter); @@ -55,3 +65,5 @@ enum API_PAGE_t : uint8_t API_PAGE_VPN, }; + +#endif // WEB_H \ No newline at end of file diff --git a/src/websrc/html/PAGE_GENERAL.html b/src/websrc/html/PAGE_GENERAL.html deleted file mode 100644 index 1b969ee6..00000000 --- a/src/websrc/html/PAGE_GENERAL.html +++ /dev/null @@ -1,160 +0,0 @@ -
-
-
-
-
-
-
-
-
-
- - -
-
- - -
-
-
- -
-
-
-
- - - -
-
-
-
-
- - -
-
- - -
-
- - -
-
-
-
-
- -
-
-
\ No newline at end of file diff --git a/src/websrc/html/PAGE_LOADER.html b/src/websrc/html/PAGE_LOADER.html deleted file mode 100644 index edef5850..00000000 --- a/src/websrc/html/PAGE_LOADER.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - - - - - - -
-
-
-
- -
- - -
-
- -
-
- -
-
- xyzroe © 2024 -
-
-
-
- - - \ No newline at end of file diff --git a/src/websrc/html/PAGE_LOGIN.html b/src/websrc/html/PAGE_LOGIN.html deleted file mode 100644 index 32d312ff..00000000 --- a/src/websrc/html/PAGE_LOGIN.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - -
-
- Logo XZG -

- -
- - -
- -
- -

xyzroe © 2024

-
-
- - - \ No newline at end of file diff --git a/src/websrc/html/PAGE_MQTT.html b/src/websrc/html/PAGE_MQTT.html deleted file mode 100644 index b496bc9a..00000000 --- a/src/websrc/html/PAGE_MQTT.html +++ /dev/null @@ -1,83 +0,0 @@ -
-
-
-
-
-
- - - -
-
-
- -
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/websrc/html/PAGE_NETWORK.html b/src/websrc/html/PAGE_NETWORK.html deleted file mode 100644 index e1a8016c..00000000 --- a/src/websrc/html/PAGE_NETWORK.html +++ /dev/null @@ -1,173 +0,0 @@ -
-
-
-
-
- - - -
- -
-
-
- - -
-
- - -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
-
-
-
- - - -
-
- -
-
-
-
- - -
-
- - -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- - - - - - - - - - - - - - - - - -
-
-
-
-
-
- -
-
- -
-
-
-
-
- - -
-
- -
- - -
-
-
-
-
- -
-
-
-
-
- -
-
-
\ No newline at end of file diff --git a/src/websrc/html/PAGE_SECURITY.html b/src/websrc/html/PAGE_SECURITY.html deleted file mode 100644 index 54396dab..00000000 --- a/src/websrc/html/PAGE_SECURITY.html +++ /dev/null @@ -1,56 +0,0 @@ -
-
-
-
-
-
-
-
- - -
-
- - - -
-
- - -
-
-
-
- -
-
-
-
-
\ No newline at end of file diff --git a/src/websrc/html/PAGE_TOOLS.html b/src/websrc/html/PAGE_TOOLS.html deleted file mode 100644 index af7c7a57..00000000 --- a/src/websrc/html/PAGE_TOOLS.html +++ /dev/null @@ -1,266 +0,0 @@ -
- - -
- -
-
- -
-
-
-
-
- - - - -
-
- - - -
-
-
-
- -
-
-
-
- -
- - - - -
-
-
-
- -
-
- -
-
- -
-
-
- - - -
-
-
-
-
- -
- - - - -
-
-
-
-
-
- -
-
-
- - - -
-
- - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
- - - -
-
- -
-
- -
-
-
- - - -
-
-
- - -
- -
-
-
- -
-
-
- - - -
-
-
- - - -
-
-
-
- - - -
-
-
- - - -
-
-
- -
-
-
-
- -
-
- - - - -
\ No newline at end of file diff --git a/src/websrc/html/PAGE_VPN.html b/src/websrc/html/PAGE_VPN.html deleted file mode 100644 index 478ac325..00000000 --- a/src/websrc/html/PAGE_VPN.html +++ /dev/null @@ -1,106 +0,0 @@ -
-
-
-
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- -
- -
-
-
\ No newline at end of file diff --git a/src/websrc/html/PAGE_ABOUT.html b/src/websrc/html/about.html similarity index 59% rename from src/websrc/html/PAGE_ABOUT.html rename to src/websrc/html/about.html index d5281de1..da9b5749 100644 --- a/src/websrc/html/PAGE_ABOUT.html +++ b/src/websrc/html/about.html @@ -1,20 +1,16 @@ -
+

XZG logo

-


- -

+



-
-
@@ -22,12 +18,10 @@

<div

-

- -

+

-
@@ -35,21 +29,15 @@

-
@@ -57,13 +45,10 @@

-

- -

+

-
@@ -71,13 +56,10 @@

-

- -

+

-
-
@@ -112,15 +86,10 @@

-

- - - -

+

-
-
\ No newline at end of file diff --git a/src/websrc/html/general.html b/src/websrc/html/general.html new file mode 100644 index 00000000..115698c5 --- /dev/null +++ b/src/websrc/html/general.html @@ -0,0 +1,116 @@ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/websrc/html/loader.html b/src/websrc/html/loader.html new file mode 100644 index 00000000..4b0a8cd2 --- /dev/null +++ b/src/websrc/html/loader.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ + +
+
+ +
+
+
xyzroe © 2024
+
+
+
+ + + \ No newline at end of file diff --git a/src/websrc/html/login.html b/src/websrc/html/login.html new file mode 100644 index 00000000..8b571825 --- /dev/null +++ b/src/websrc/html/login.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + +
+
Logo XZG +

+
+
+
+ +

xyzroe © 2024

+
+
+ + + \ No newline at end of file diff --git a/src/websrc/html/mqtt.html b/src/websrc/html/mqtt.html new file mode 100644 index 00000000..6bf27268 --- /dev/null +++ b/src/websrc/html/mqtt.html @@ -0,0 +1,58 @@ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/websrc/html/network.html b/src/websrc/html/network.html new file mode 100644 index 00000000..ac67a257 --- /dev/null +++ b/src/websrc/html/network.html @@ -0,0 +1,151 @@ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+

+
+ + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/websrc/html/PAGE_ROOT.html b/src/websrc/html/root.html similarity index 80% rename from src/websrc/html/PAGE_ROOT.html rename to src/websrc/html/root.html index cb3d69b8..1a5706f3 100644 --- a/src/websrc/html/PAGE_ROOT.html +++ b/src/websrc/html/root.html @@ -1,13 +1,10 @@ -
-
- +
- -
+
@@ -23,6 +20,10 @@ + + + + @@ -37,12 +38,8 @@ - + -
- - -
@@ -50,11 +47,9 @@
-
- +
- -
+
@@ -77,18 +72,14 @@
-
- °C -
+
°C
- + @@ -97,9 +88,8 @@
-
- / - +
/
@@ -107,8 +97,7 @@ + data-r2v="espFlashType"> @@ -116,9 +105,8 @@
-
- / - +
/
@@ -129,10 +117,8 @@
-
- / - -
+
/
@@ -144,6 +130,17 @@ + + + +
@ - - - @
, -
@@ -151,11 +148,9 @@